Package: libpam-modules
Version: 0.99.7.1-5
Severity: wishlist
Tags: patch

Hi there,

I've been working on the debconf.org machines, which use ud-ldap the
same way the debian.org machines do.  Currently what happens when an
account is locked for wahtever reason is that the LDAP password field is
updated with a special prefix to indicate this, but the password expiry
field is not updated (this last is arguably a bug in ud-ldap).  In order
to work around this, DSA has been carrying around a patched sshd for
years to check the password field for this special marker.  The attached
pam module would solve this, either as a standalone module, or (perhaps
better) as something merged into pam_unix or the like.

Thanks for considering,

-- System Information:
Debian Release: lenny/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'stable'), (1, 'experimental')
Architecture: i386 (i686)

Kernel: Linux 2.6.22-2-686 (SMP w/4 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8) (ignored: LC_ALL 
set to en_US.UTF-8)
Shell: /bin/sh linked to /bin/bash

Versions of packages libpam-modules depends on:
ii  libc6                        2.6.1-6     GNU C Library: Shared libraries
ii  libdb4.6                     4.6.21-2    Berkeley v4.6 Database Libraries [
ii  libpam0g                     0.99.7.1-5  Pluggable Authentication Modules l
ii  libselinux1                  2.0.15-2+b1 SELinux shared libraries

libpam-modules recommends no packages.

-- no debconf information

-- 
 -----------------------------------------------------------------
|   ,''`.                                            Stephen Gran |
|  : :' :                                        [EMAIL PROTECTED] |
|  `. `'                        Debian user, admin, and developer |
|    `-                                     http://www.debian.org |
 -----------------------------------------------------------------
#include <syslog.h>
#include <shadow.h>
#include <stdlib.h>
#include <string.h>

#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#define PAM_SM_SESSION
#define PAM_SM_PASSWORD
#ifndef BUFSIZE
#define BUFSIZ 1024
#endif

#include <security/pam_modules.h>
#include <security/pam_ext.h>

/* 
 * _is_shadow_locked gets called with any arguments placed in argv.  
 * The only valid argument at the moment is marker, which can be used
 * either as marker=foo (single item) or marker='(foo|bar)' (list form)
 * We store all the tokens seen in marker[0] .. marker[n] and later check 
 * them against the shadow password string for the user.
 *
 * The point of this module is to check for certain characters at the beginning
 * of a shadow password marking the account as locked or disabled.  This will
 * fail logins even when an alternate auth mechanism (like ssh keys) is used
 * and passwords would normally not be consulted 
 */

static int
_is_shadow_locked(pam_handle_t *pamh, int argc, const char **argv) 
{
  int a, n = 0;
  char **marker;
  char *cpyptr = NULL;

  if ((marker = malloc(BUFSIZ)) == NULL) {
    pam_syslog(pamh, LOG_ERR, "malloc failed");
    return 1;
  }

  for (; argc-- > 0; ++argv) {
    if (!strncmp(*argv, "marker=", 7)) {
      char *sptr = NULL;
      char *saveptr = NULL;

      int len = strlen(*argv) - 7;
      if (len > BUFSIZ) {
        pam_syslog(pamh, LOG_ERR, "Saw too long a line, truncating");
        len = BUFSIZ;
      }

      if ((sptr = strndup(*argv+7,len)) == NULL)
        goto ERROR;

      /* sptr now contains something guaranteed to be <= BUFSIZ, so we don't need to 
         check lengths for greater than BUFSIZ later on */

      /* Save a copy of sptr, so we can free the memory later */
      cpyptr = sptr;

      if ( (!strncmp(sptr, "'(", 2)) && (!strncmp(sptr+len-2, ")'", 2)) ) {
        /* the line matches '(foo)' or '(foo|bar)', so we want to strip the outer brackets */
        sptr[len-2] = '\0';
        sptr = sptr+2;
        char *temp;

        for (n = 0 ; ; sptr = NULL, n++) {
          if ( (temp = strtok_r(sptr, "|", &saveptr) ) == NULL )
            break;
          if ((marker[n] = strdup(temp)) == NULL)
            goto ERROR;
        }
      } else {
        /* We only saw marker=foo.  Just take the value of marker */
        if ((marker[0] = strdup(sptr)) == NULL) {
          goto ERROR;
        }
        n = 1;
      }
      /* free sptr/cpyptr - this is the only way to do it, I think, since strtok is destructive */
      if (cpyptr)
        free(cpyptr);
      break;
    } else {
      pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
    }
  }

  /* we didn't see a marker= line, give it the defaults */
  if (n < 1) {
    if (((marker[0] = malloc(2)) == NULL) || ((marker[1] = malloc(2)) == NULL)) {
      goto ERROR;
    }
    strncpy (marker[0],"*\0",2);
    strncpy (marker[1],"!\0",2);
    n = 2;
  }

  /* Now do the actual comparison against the crypt password string */
  const char *user;
  int pamretval;
  int ret = 0;
  pamretval = pam_get_user(pamh, &user, NULL);
  if (pamretval == PAM_SUCCESS) {
    struct spwd *spw;
    int i;
    if ((spw = getspnam(user)) != NULL) {
      for (i = 0; i < n; i++) {
        if (!strncmp(spw->sp_pwdp,marker[i],strlen(marker[i]))) {
          pam_syslog(pamh, LOG_INFO, "Refusing locked account for %s", user);
          ret = 1;
        }
      }
    }
  }

  for (a = 0;a < n; a++) {
    if (marker[a])
      free(marker[a]);
  }
  free(marker);
  return ret;

ERROR:
  pam_syslog(pamh, LOG_ERR, "malloc failed");

  if (cpyptr)
    free(cpyptr);

  for (a = 0;a < n; a++) {
    if (marker[a])
      free(marker[a]);
  }
  if (marker)
    free(marker);

  return 1;
}

PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags,
		    int argc, const char **argv)
{
  if (_is_shadow_locked(pamh,argc,argv))
     return PAM_AUTH_ERR;
  return PAM_SUCCESS;
}

PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh, int flags,
	       int argc, const char **argv)
{
  if (_is_shadow_locked(pamh,argc,argv))
     return PAM_CRED_ERR;
  return PAM_SUCCESS;
}

/* --- account management functions --- */

PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
		 int argc, const char **argv)
{
  if (_is_shadow_locked(pamh,argc,argv))
     return PAM_AUTH_ERR;
  return PAM_SUCCESS;
}

/* --- password management --- */

PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t *pamh, int flags,
		 int argc, const char **argv)
{
  if (_is_shadow_locked(pamh,argc,argv))
     return PAM_AUTHTOK_ERR;
  return PAM_SUCCESS;
}

/* --- session management --- */

PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh, int flags,
		    int argc, const char **argv)
{
  if (_is_shadow_locked(pamh,argc,argv))
    return PAM_SESSION_ERR;
  return PAM_SUCCESS;
}

PAM_EXTERN int
pam_sm_close_session(pam_handle_t *pamh, int flags,
		     int argc, const char **argv)
{
  if (_is_shadow_locked(pamh,argc,argv))
     return PAM_SESSION_ERR;
  return PAM_SUCCESS;
}

/* end of module definition */

/* static module data */
#ifdef PAM_STATIC
struct pam_module _pam_shadow_locked_modstruct = {
    "pam_shadow_locked",
    pam_sm_authenticate,
    pam_sm_setcred,
    pam_sm_acct_mgmt,
    pam_sm_open_session,
    pam_sm_close_session,
    pam_sm_chauthtok
};
#endif

Attachment: signature.asc
Description: Digital signature

Reply via email to