diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2000-06-20 22:10:38 +0000 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2000-06-20 22:10:38 +0000 |
commit | ea488580c42e8918445a945484de3c8a5addc761 (patch) | |
tree | c992f3ba699caafedfadc16af38e6359c3c24698 /modules/pam_pwdb | |
download | pam-ea488580c42e8918445a945484de3c8a5addc761.tar.gz pam-ea488580c42e8918445a945484de3c8a5addc761.tar.bz2 pam-ea488580c42e8918445a945484de3c8a5addc761.zip |
Initial revision
Diffstat (limited to 'modules/pam_pwdb')
-rw-r--r-- | modules/pam_pwdb/.cvsignore | 2 | ||||
-rw-r--r-- | modules/pam_pwdb/BUGS | 14 | ||||
-rw-r--r-- | modules/pam_pwdb/CHANGELOG | 10 | ||||
-rw-r--r-- | modules/pam_pwdb/Makefile | 193 | ||||
-rw-r--r-- | modules/pam_pwdb/README | 41 | ||||
-rw-r--r-- | modules/pam_pwdb/TODO | 34 | ||||
-rw-r--r-- | modules/pam_pwdb/bigcrypt.-c | 114 | ||||
-rw-r--r-- | modules/pam_pwdb/md5.c | 270 | ||||
-rw-r--r-- | modules/pam_pwdb/md5.h | 34 | ||||
-rw-r--r-- | modules/pam_pwdb/md5_crypt.c | 153 | ||||
-rw-r--r-- | modules/pam_pwdb/pam_pwdb.c | 256 | ||||
-rw-r--r-- | modules/pam_pwdb/pam_unix_acct.-c | 298 | ||||
-rw-r--r-- | modules/pam_pwdb/pam_unix_auth.-c | 131 | ||||
-rw-r--r-- | modules/pam_pwdb/pam_unix_md.-c | 73 | ||||
-rw-r--r-- | modules/pam_pwdb/pam_unix_passwd.-c | 404 | ||||
-rw-r--r-- | modules/pam_pwdb/pam_unix_pwupd.-c | 260 | ||||
-rw-r--r-- | modules/pam_pwdb/pam_unix_sess.-c | 118 | ||||
-rw-r--r-- | modules/pam_pwdb/pwdb_chkpwd.c | 204 | ||||
-rw-r--r-- | modules/pam_pwdb/support.-c | 938 |
19 files changed, 3547 insertions, 0 deletions
diff --git a/modules/pam_pwdb/.cvsignore b/modules/pam_pwdb/.cvsignore new file mode 100644 index 00000000..f0420bac --- /dev/null +++ b/modules/pam_pwdb/.cvsignore @@ -0,0 +1,2 @@ +dynamic +pwdb_chkpwd diff --git a/modules/pam_pwdb/BUGS b/modules/pam_pwdb/BUGS new file mode 100644 index 00000000..e7170b70 --- /dev/null +++ b/modules/pam_pwdb/BUGS @@ -0,0 +1,14 @@ +$Id$ + +$Log$ +Revision 1.1 2000/06/20 22:11:46 agmorgan +Initial revision + +Revision 1.1.1.1 1998/07/12 05:17:16 morgan +Linux PAM sources pre-0.66 + +Revision 1.2 1996/09/05 06:36:16 morgan +revised for .52 to be released + + +As of Linux-PAM-0.52 this is new. No known bugs yet. diff --git a/modules/pam_pwdb/CHANGELOG b/modules/pam_pwdb/CHANGELOG new file mode 100644 index 00000000..7bad5cd8 --- /dev/null +++ b/modules/pam_pwdb/CHANGELOG @@ -0,0 +1,10 @@ +$Id$ + +Tue Apr 23 12:28:09 EDT 1996 (Alexander O. Yuriev alex@bach.cis.temple.edu) + + * PAM_DISALLOW_NULL_AUTHTOK implemented in the authentication module + * pam_sm_open_session() and pam_sm_close_session() implemented + A new "trace" flag added to flags of /etc/pam.conf. Using this + flag system administrator is able to make pam_unix module provide + very extensive audit trail sent so syslog with LOG_AUTHPRIV level. + * pam_sm_set_cred() is done diff --git a/modules/pam_pwdb/Makefile b/modules/pam_pwdb/Makefile new file mode 100644 index 00000000..fcb7aec4 --- /dev/null +++ b/modules/pam_pwdb/Makefile @@ -0,0 +1,193 @@ +# $Id$ +# +# This Makefile controls a build process of the pam_unix module +# for Linux-PAM. You should not modify this Makefile. +# +# rewritten to compile new module Andrew Morgan +# <morgan@parc.power.net> 1996/11/6 +# + +# +# Note, the STATIC module is commented out because it doesn't work. +# please fix! +# + +ifndef FULL_LINUX_PAM_SOURCE_TREE +export DYNAMIC=-DPAM_DYNAMIC +export CC=gcc +export CFLAGS=-O2 -Dlinux -DLINUX_PAM \ + -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings \ + -Wpointer-arith -Wcast-qual -Wcast-align -Wtraditional \ + -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline \ + -Wshadow -pedantic -fPIC +export MKDIR=mkdir -p +export LD_D=gcc -shared -Xlinker -x +export HAVE_PWDBLIB=yes +endif + +ifeq ($(shell if [ -f /lib/libcrypt.so.* ]; then echo yes ; else echo no ; fi),yes) +EXTRALS += -lcrypt +endif + +ifeq ($(HAVE_PWDBLIB),yes) + +TITLE=pam_pwdb +CHKPWD=pwdb_chkpwd + +# compilation flags +EXTRAS= +# extra object files +PLUS= +# extra files that may be needed to be created +CREATE= + +# NOTE: this module links dynamically to the libpwdb library. +EXTRALS += -lpwdb +EXTRAS += -DCHKPWD_HELPER=\"$(SUPLEMENTED)/$(CHKPWD)\" + +########################### don't edit below ########################## + +LIBSRC = $(TITLE).c +LIBOBJ = $(TITLE).o +LIBOBJD = $(addprefix dynamic/,$(LIBOBJ)) +#LIBOBJS = $(addprefix static/,$(LIBOBJ)) +LIBDEPS = pam_unix_acct.-c pam_unix_auth.-c pam_unix_passwd.-c \ + pam_unix_sess.-c pam_unix_pwupd.-c support.-c bigcrypt.-c + +PLUS += md5_good.o md5_broken.o md5_crypt_good.o md5_crypt_broken.o +CFLAGS += $(EXTRAS) + +ifdef DYNAMIC +LIBSHARED = $(TITLE).so +endif +#ifdef STATIC +#LIBSTATIC = lib$(TITLE).o +#endif + +all: info dirs $(PLUS) $(LIBSHARED) $(LIBSTATIC) register $(CHKPWD) + +dynamic/$(LIBOBJ) : $(LIBSRC) $(LIBDEPS) + $(CC) $(CFLAGS) $(DYNAMIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +#static/$(LIBOBJ) : $(LIBSRC) $(LIBDEPS) +# $(CC) $(CFLAGS) $(STATIC) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ + +info: + @echo + @echo "*** Building PAM_pwdb module..." + @echo + +$(CHKPWD): pwdb_chkpwd.o md5_good.o md5_broken.o \ + md5_crypt_good.o md5_crypt_broken.o + $(CC) -o $(CHKPWD) $^ -lpwdb + +pwdb_chkpwd.o: pwdb_chkpwd.c pam_unix_md.-c bigcrypt.-c + +md5_good.o: md5.c + $(CC) $(CFLAGS) $(CPPFLAGS) -DHIGHFIRST -D'MD5Name(x)=Good##x' \ + $(TARGET_ARCH) -c $< -o $@ + +md5_broken.o: md5.c + $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \ + $(TARGET_ARCH) -c $< -o $@ + +md5_crypt_good.o: md5_crypt.c + $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Good##x' \ + $(TARGET_ARCH) -c $< -o $@ + +md5_crypt_broken.o: md5_crypt.c + $(CC) $(CFLAGS) $(CPPFLAGS) -D'MD5Name(x)=Broken##x' \ + $(TARGET_ARCH) -c $< -o $@ + +dirs: +ifdef DYNAMIC + @$(MKDIR) ./dynamic +endif +#ifdef STATIC +# @$(MKDIR) ./static +#endif + +register: +#ifdef STATIC +# ( cd .. ; ./register_static $(TITLE) $(TITLE)/$(LIBSTATIC) ) +#endif + +ifdef DYNAMIC +$(LIBOBJD): $(LIBSRC) + +$(LIBSHARED): $(LIBOBJD) + $(LD_D) -o $@ $(LIBOBJD) $(PLUS) $(EXTRALS) +endif + +#ifdef STATIC +#$(LIBOBJS): $(LIBSRC) +# +#$(LIBSTATIC): $(LIBOBJS) +# $(LD) -r -o $@ $(LIBOBJS) $(PLUS) $(EXTRALS) +#endif + +install: all + $(MKDIR) $(FAKEROOT)$(SECUREDIR) +ifdef DYNAMIC + $(INSTALL) -m $(SHLIBMODE) $(LIBSHARED) $(FAKEROOT)$(SECUREDIR) +endif + $(MKDIR) $(FAKEROOT)$(SUPLEMENTED) + $(INSTALL) -m 4555 -o root -g root $(CHKPWD) $(FAKEROOT)$(SUPLEMENTED) + +remove: + rm -f $(FAKEROOT)$(SECUREDIR)/$(TITLE).so + rm -f $(FAKEROOT)$(SUPLEMENTED)/$(CHKPWD) + +clean: + rm -f $(CHKPWD) $(LIBOBJD) $(LIBOBJS) $(MOREDELS) core *~ *.o *.so + +extraclean: clean + rm -f *.a *.o *.so *.bak + +else + +include ../dont_makefile + +endif + +##################################################################### +# $Log$ +# Revision 1.1 2000/06/20 22:11:47 agmorgan +# Initial revision +# +# Revision 1.4 1999/08/01 16:18:27 morgan +# added a conditional for libcrypt +# +# Revision 1.3 1999/07/08 05:02:02 morgan +# glibc fixes (Thorsten Kukuk, Adam J. Richter) +# +# Revision 1.2 1999/07/04 23:22:38 morgan +# Andrey's MD5 (bigendian) work around + cleanup to address problems with +# applications that let an (ab)user kill them off without giving PAM the +# opportunity to end. [Problem report from Tani Hosokawa on bugtraq.] +# +# Revision 1.1.1.1 1998/07/12 05:17:16 morgan +# Linux PAM sources pre-0.66 +# +# Revision 1.7 1997/04/05 06:28:50 morgan +# fakeroot +# +# Revision 1.6 1997/02/15 17:25:32 morgan +# update for .56 . extra commands for new helper binary +# +# Revision 1.5 1997/01/04 20:39:08 morgan +# conditional on having libpwdb +# +# Revision 1.4 1996/12/01 03:02:03 morgan +# changed banner, removed linking libraries +# +# Revision 1.3 1996/11/10 20:14:42 morgan +# cross platform support +# +# Revision 1.2 1996/09/05 06:36:49 morgan +# options added and use of LD altered +# +# Revision 1.1 1996/08/29 13:23:29 morgan +# Initial revision +# +# diff --git a/modules/pam_pwdb/README b/modules/pam_pwdb/README new file mode 100644 index 00000000..4f420855 --- /dev/null +++ b/modules/pam_pwdb/README @@ -0,0 +1,41 @@ +This is the pam_unix module. It has been significantly rewritten since +.51 was released (due mostly to the efforts of Cristian Gafton), and +now takes more options and correctly updates vanilla UNIX/shadow/md5 +passwords. + +[Please read the source and make a note of all the warnings there, as +the license suggests -- use at your own risk.] + +So far as I am concerned this module is now pretty stable. If you find +any bugs, PLEASE tell me! <morgan@linux.kernel.org> + +Options recognized by this module are as follows: + + debug - log more debugging info + audit - a little more extreme than debug + use_first_pass - don't prompt the user for passwords + take them from PAM_ items instead + try_first_pass - don't prompt the user for the passwords + unless PAM_(OLD)AUTHTOK is unset + use_authtok - like try_first_pass, but *fail* if the new + PAM_AUTHTOK has not been previously set. + (intended for stacking password modules only) + not_set_pass - don't set the PAM_ items with the passwords + used by this module. + shadow - try to maintian a shadow based system. + unix - when changing passwords, they are placed + in the /etc/passwd file + md5 - when a user changes their password next, + encrypt it with the md5 algorithm. + bigcrypt - when a user changes their password next, + excrypt it with the DEC C2-algorithm(0). + nodelay - used to prevent failed authentication + resulting in a delay of about 1 second. + +There is some support for building a shadow file on-the-fly from an +/etc/passwd file. This is VERY alpha. If you want to play with it you +should read the source to find the appropriate #define that you will +need. + +--------------------- +Andrew Morgan <morgan@linux.kernel.org> diff --git a/modules/pam_pwdb/TODO b/modules/pam_pwdb/TODO new file mode 100644 index 00000000..9dc7fc7e --- /dev/null +++ b/modules/pam_pwdb/TODO @@ -0,0 +1,34 @@ +$Id$ + + * get NIS working + * .. including "nonis" argument + * add helper binary + +Wed Sep 4 23:40:09 PDT 1996 Andrew G. Morgan + + * verify that it works for everyone + * look more seriously at the issue of generating a shadow + system on the fly + * add some more password flavors + +Thu Aug 29 06:26:42 PDT 1996 Andrew G. Morgan + + * check that complete rewrite works! ;^) + * complete shadow support to the password changing code. + Also some code needed here for session managment? + (both pam.conf argument to turn it on/off, and some + conditional compilation.) + * md5 passwords... + * make the exclusive nature of the arguments work. That is, + only recognize the flags when appropriate. + +Wed May 8 19:08:49 EDT 1996 Alexander O. Yuriev + + * support.c should go. + +Tue Apr 23 21:43:55 EDT 1996 Alexander O. Yuriev + + * pam_sm_chauth_tok() should be written + * QUICK FIX: pam_sm_setcred() probably returns incorrect error code + + diff --git a/modules/pam_pwdb/bigcrypt.-c b/modules/pam_pwdb/bigcrypt.-c new file mode 100644 index 00000000..321f2491 --- /dev/null +++ b/modules/pam_pwdb/bigcrypt.-c @@ -0,0 +1,114 @@ +/* + * This function implements the "bigcrypt" algorithm specifically for + * Linux-PAM. + * + * This algorithm is algorithm 0 (default) shipped with the C2 secure + * implementation of Digital UNIX. + * + * Disclaimer: This work is not based on the source code to Digital + * UNIX, nor am I connected to Digital Equipment Corp, in any way + * other than as a customer. This code is based on published + * interfaces and reasonable guesswork. + * + * Description: The cleartext is divided into blocks of SEGMENT_SIZE=8 + * characters or less. Each block is encrypted using the standard UNIX + * libc crypt function. The result of the encryption for one block + * provides the salt for the suceeding block. + * + * Restrictions: The buffer used to hold the encrypted result is + * statically allocated. (see MAX_PASS_LEN below). This is necessary, + * as the returned pointer points to "static data that are overwritten + * by each call", (XPG3: XSI System Interface + Headers pg 109), and + * this is a drop in replacement for crypt(); + * + * Andy Phillips <atp@mssl.ucl.ac.uk> + */ + +/* + * Max cleartext password length in segments of 8 characters this + * function can deal with (16 segments of 8 chars= max 128 character + * password). + */ + +#define MAX_PASS_LEN 16 +#define SEGMENT_SIZE 8 +#define SALT_SIZE 2 +#define KEYBUF_SIZE ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE) +#define ESEGMENT_SIZE 11 +#define CBUF_SIZE ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1) + +static char *bigcrypt(const char *key, const char *salt) +{ + static char dec_c2_cryptbuf[CBUF_SIZE]; /* static storage area */ + + unsigned long int keylen,n_seg,j; + char *cipher_ptr,*plaintext_ptr,*tmp_ptr,*salt_ptr; + char keybuf[KEYBUF_SIZE+1]; + + D(("called with key='%s', salt='%s'.", key, salt)); + + /* reset arrays */ + memset(keybuf, 0, KEYBUF_SIZE+1); + memset(dec_c2_cryptbuf, 0, CBUF_SIZE); + + /* fill KEYBUF_SIZE with key */ + strncpy(keybuf, key, KEYBUF_SIZE); + + /* deal with case that we are doing a password check for a + conventially encrypted password: the salt will be + SALT_SIZE+ESEGMENT_SIZE long. */ + if (strlen(salt) == (SALT_SIZE+ESEGMENT_SIZE)) + keybuf[SEGMENT_SIZE] = '\0'; /* terminate password early(?) */ + + keylen = strlen(keybuf); + + if (!keylen) { + n_seg = 1; + } else { + /* work out how many segments */ + n_seg = 1 + ((keylen-1)/SEGMENT_SIZE); + } + + if (n_seg > MAX_PASS_LEN) + n_seg = MAX_PASS_LEN; /* truncate at max length */ + + /* set up some pointers */ + cipher_ptr = dec_c2_cryptbuf; + plaintext_ptr = keybuf; + + /* do the first block with supplied salt */ + tmp_ptr = crypt(plaintext_ptr,salt); /* libc crypt() */ + + /* and place in the static area */ + strncpy(cipher_ptr, tmp_ptr, 13); + cipher_ptr += ESEGMENT_SIZE + SALT_SIZE; + plaintext_ptr += SEGMENT_SIZE; /* first block of SEGMENT_SIZE */ + + /* change the salt (1st 2 chars of previous block) - this was found + by dowsing */ + + salt_ptr = cipher_ptr - ESEGMENT_SIZE; + + /* so far this is identical to "return crypt(key, salt);", if + there is more than one block encrypt them... */ + + if (n_seg > 1) { + for (j=2; j <= n_seg; j++) { + + tmp_ptr = crypt(plaintext_ptr, salt_ptr); + + /* skip the salt for seg!=0 */ + strncpy(cipher_ptr, (tmp_ptr+SALT_SIZE), ESEGMENT_SIZE); + + cipher_ptr += ESEGMENT_SIZE; + plaintext_ptr += SEGMENT_SIZE; + salt_ptr = cipher_ptr - ESEGMENT_SIZE; + } + } + + D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf)); + + /* this is the <NUL> terminated encrypted password */ + + return dec_c2_cryptbuf; +} diff --git a/modules/pam_pwdb/md5.c b/modules/pam_pwdb/md5.c new file mode 100644 index 00000000..d3f47763 --- /dev/null +++ b/modules/pam_pwdb/md5.c @@ -0,0 +1,270 @@ +/* $Id$ + * + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:48 agmorgan + * Initial revision + * + * Revision 1.2 1999/07/04 23:22:38 morgan + * Andrey's MD5 (bigendian) work around + cleanup to address problems with + * applications that let an (ab)user kill them off without giving PAM the + * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.] + * + * Revision 1.1.1.1 1998/07/12 05:17:17 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.1 1996/09/05 06:43:31 morgan + * Initial revision + * + */ + +#include <string.h> +#include "md5.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +static void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Name(MD5Init)(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301U; + ctx->buf[1] = 0xefcdab89U; + ctx->buf[2] = 0x98badcfeU; + ctx->buf[3] = 0x10325476U; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Name(MD5Update)(struct MD5Context *ctx, unsigned const char *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *) ctx->in)[14] = ctx->bits[0]; + ((uint32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Name(MD5Transform)(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif diff --git a/modules/pam_pwdb/md5.h b/modules/pam_pwdb/md5.h new file mode 100644 index 00000000..279ce46f --- /dev/null +++ b/modules/pam_pwdb/md5.h @@ -0,0 +1,34 @@ +#ifndef MD5_H +#define MD5_H + +#ifdef __alpha +typedef unsigned int uint32; +#else +typedef unsigned long uint32; +#endif + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; + +void GoodMD5Init(struct MD5Context *); +void GoodMD5Update(struct MD5Context *, unsigned const char *, unsigned); +void GoodMD5Final(unsigned char digest[16], struct MD5Context *); +void GoodMD5Transform(uint32 buf[4], uint32 const in[16]); +void BrokenMD5Init(struct MD5Context *); +void BrokenMD5Update(struct MD5Context *, unsigned const char *, unsigned); +void BrokenMD5Final(unsigned char digest[16], struct MD5Context *); +void BrokenMD5Transform(uint32 buf[4], uint32 const in[16]); + +char *Goodcrypt_md5(const char *pw, const char *salt); +char *Brokencrypt_md5(const char *pw, const char *salt); + +/* +* This is needed to make RSAREF happy on some MS-DOS compilers. +*/ + +typedef struct MD5Context MD5_CTX; + +#endif /* MD5_H */ diff --git a/modules/pam_pwdb/md5_crypt.c b/modules/pam_pwdb/md5_crypt.c new file mode 100644 index 00000000..1d755a08 --- /dev/null +++ b/modules/pam_pwdb/md5_crypt.c @@ -0,0 +1,153 @@ +/* $Id$ + * + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * Origin: Id: crypt.c,v 1.3 1995/05/30 05:42:22 rgrimes Exp + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:48 agmorgan + * Initial revision + * + * Revision 1.2 1999/07/04 23:22:38 morgan + * Andrey's MD5 (bigendian) work around + cleanup to address problems with + * applications that let an (ab)user kill them off without giving PAM the + * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.] + * + * Revision 1.1.1.1 1998/07/12 05:17:17 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.1 1996/09/05 06:43:31 morgan + * Initial revision + * + */ + +#include <string.h> +#include "md5.h" + +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void +to64(char *s, unsigned long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +/* + * UNIX password + * + * Use MD5 for what it is best at... + */ + +char * MD5Name(crypt_md5)(const char *pw, const char *salt) +{ + const char *magic = "$1$"; + /* This string is magic for this algorithm. Having + * it this way, we can get get better later on */ + static char passwd[120], *p; + static const char *sp,*ep; + unsigned char final[16]; + int sl,pl,i,j; + MD5_CTX ctx,ctx1; + unsigned long l; + + /* Refine the Salt first */ + sp = salt; + + /* If it starts with the magic string, then skip that */ + if(!strncmp(sp,magic,strlen(magic))) + sp += strlen(magic); + + /* It stops at the first '$', max 8 chars */ + for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++) + continue; + + /* get the length of the true salt */ + sl = ep - sp; + + MD5Name(MD5Init)(&ctx); + + /* The password first, since that is what is most unknown */ + MD5Name(MD5Update)(&ctx,(unsigned const char *)pw,strlen(pw)); + + /* Then our magic string */ + MD5Name(MD5Update)(&ctx,(unsigned const char *)magic,strlen(magic)); + + /* Then the raw salt */ + MD5Name(MD5Update)(&ctx,(unsigned const char *)sp,sl); + + /* Then just as many characters of the MD5(pw,salt,pw) */ + MD5Name(MD5Init)(&ctx1); + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl); + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + MD5Name(MD5Final)(final,&ctx1); + for(pl = strlen(pw); pl > 0; pl -= 16) + MD5Name(MD5Update)(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl); + + /* Don't leave anything around in vm they could use. */ + memset(final,0,sizeof final); + + /* Then something really weird... */ + for (j=0,i = strlen(pw); i ; i >>= 1) + if(i&1) + MD5Name(MD5Update)(&ctx, (unsigned const char *)final+j, 1); + else + MD5Name(MD5Update)(&ctx, (unsigned const char *)pw+j, 1); + + /* Now make the output string */ + strcpy(passwd,magic); + strncat(passwd,sp,sl); + strcat(passwd,"$"); + + MD5Name(MD5Final)(final,&ctx); + + /* + * and now, just to make sure things don't run too fast + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for(i=0;i<1000;i++) { + MD5Name(MD5Init)(&ctx1); + if(i & 1) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + else + MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16); + + if(i % 3) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl); + + if(i % 7) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + + if(i & 1) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16); + else + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + MD5Name(MD5Final)(final,&ctx1); + } + + p = passwd + strlen(passwd); + + l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; + l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; + l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; + l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; + l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; + l = final[11] ; to64(p,l,2); p += 2; + *p = '\0'; + + /* Don't leave anything around in vm they could use. */ + memset(final,0,sizeof final); + + return passwd; +} + diff --git a/modules/pam_pwdb/pam_pwdb.c b/modules/pam_pwdb/pam_pwdb.c new file mode 100644 index 00000000..68ca68fd --- /dev/null +++ b/modules/pam_pwdb/pam_pwdb.c @@ -0,0 +1,256 @@ +/* + * $Id$ + * + * This is the single file that will be compiled for pam_unix. + * it includes each of the modules that have beed defined in the .-c + * files in this directory. + * + * It is a little ugly to do it this way, but it is a simple way of + * defining static functions only once, and yet keeping the separate + * files modular. If you can think of something better, please email + * Andrew Morgan <morgan@linux.kernel.org> + * + * See the end of this file for Copyright information. + */ + +static const char rcsid[] = +"$Id$\n" +" - PWDB Pluggable Authentication module. <morgan@linux.kernel.org>" +; + +/* #define DEBUG */ + +#define _SVID_SOURCE +#define _BSD_SOURCE +#define _BSD_COMPAT + +#ifdef linux +# define _GNU_SOURCE +# include <features.h> +#endif + +#include <sys/types.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <syslog.h> +#include <time.h> /* for time() */ +#include <fcntl.h> +#include <ctype.h> + +#include <sys/time.h> +#include <unistd.h> + +#include <pwdb/pwdb_public.h> + +/* indicate the following groups are defined */ + +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#define PAM_SM_SESSION +#define PAM_SM_PASSWORD + +#include <security/_pam_macros.h> +#include <security/pam_modules.h> + +#ifndef LINUX_PAM +#include <security/pam_appl.h> +#endif /* LINUX_PAM */ + +#include "./support.-c" + +/* + * PAM framework looks for these entry-points to pass control to the + * authentication module. + */ + +#include "./pam_unix_auth.-c" + +PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags + , int argc, const char **argv) +{ + unsigned int ctrl; + int retval; + + D(("called.")); + + pwdb_start(); + ctrl = set_ctrl(flags, argc, argv); + retval = _unix_auth( pamh, ctrl ); + pwdb_end(); + + if ( on(UNIX_LIKE_AUTH, ctrl) ) { + D(("recording return code for next time [%d]", retval)); + pam_set_data(pamh, "pwdb_setcred_return", (void *) &retval, NULL); + } + + D(("done. [%s]", pam_strerror(pamh, retval))); + + return retval; +} + +PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags + , int argc, const char **argv) +{ + unsigned int ctrl; + int retval; + + D(("called.")); + + pwdb_start(); + ctrl = set_ctrl(flags, argc, argv); + retval = _unix_set_credentials(pamh, ctrl); + pwdb_end(); + + if ( on(UNIX_LIKE_AUTH, ctrl) ) { + int *pretval = &retval; + + D(("recovering return code from auth call")); + pam_get_data(pamh, "pwdb_setcred_return", (const void **) &pretval); + pam_set_data(pamh, "pwdb_setcred_return", NULL, NULL); + D(("recovered data indicates that old retval was %d", retval)); + } + + return retval; +} + +/* + * PAM framework looks for these entry-points to pass control to the + * account management module. + */ + +#include "./pam_unix_acct.-c" + +PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + unsigned int ctrl; + int retval; + + D(("called.")); + + pwdb_start(); + ctrl = set_ctrl(flags, argc, argv); + retval = _unix_acct_mgmt(pamh, ctrl); + pwdb_end(); + + D(("done.")); + + return retval; +} + +/* + * PAM framework looks for these entry-points to pass control to the + * session module. + */ + +#include "./pam_unix_sess.-c" + +PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + unsigned int ctrl; + int retval; + + D(("called.")); + + pwdb_start(); + ctrl = set_ctrl(flags, argc, argv); + retval = _unix_open_session(pamh, ctrl); + pwdb_end(); + + return retval; +} + +PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + unsigned int ctrl; + int retval; + + D(("called.")); + + pwdb_start(); + ctrl = set_ctrl(flags, argc, argv); + retval = _unix_close_session(pamh, ctrl); + pwdb_end(); + + return retval; +} + +/* + * PAM framework looks for these entry-points to pass control to the + * password changing module. + */ + +#include "./pam_unix_passwd.-c" + +PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + unsigned int ctrl; + int retval; + + D(("called.")); + + pwdb_start(); + ctrl = set_ctrl(flags, argc, argv); + retval = _unix_chauthtok(pamh, ctrl); + pwdb_end(); + + D(("done.")); + + return retval; +} + +/* static module data */ + +#ifdef PAM_STATIC +struct pam_module _pam_pwdb_modstruct = { + "pam_pwdb", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + pam_sm_open_session, + pam_sm_close_session, + pam_sm_chauthtok +}; + +#endif + +/* + * Copyright (c) Andrew G. Morgan, 1996. All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_pwdb/pam_unix_acct.-c b/modules/pam_pwdb/pam_unix_acct.-c new file mode 100644 index 00000000..16869054 --- /dev/null +++ b/modules/pam_pwdb/pam_unix_acct.-c @@ -0,0 +1,298 @@ +/* + * $Id$ + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:49 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:17 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.6 1997/01/04 20:37:15 morgan + * extra debugging + * + * Revision 1.5 1996/12/01 03:05:54 morgan + * debugging with _pam_macros.h + * + * Revision 1.4 1996/11/10 21:03:57 morgan + * pwdb conversion + * + * Revision 1.3 1996/09/05 06:45:45 morgan + * tidied shadow acct management + * + * Revision 1.2 1996/09/01 01:13:14 morgan + * Cristian Gafton's patches + * + * Revision 1.1 1996/08/29 13:27:51 morgan + * Initial revision + * + * + * See end of file for copyright information + */ + +static const char rcsid_acct[] = +"$Id$\n" +" - PAM_PWDB account management <gafton@redhat.com>"; + +/* the shadow suite has accout managment.. */ + +static int _shadow_acct_mgmt_exp(pam_handle_t *pamh, unsigned int ctrl, + const struct pwdb *pw, const char *uname) +{ + const struct pwdb_entry *pwe = NULL; + time_t curdays; + int last_change, max_change; + int retval; + + D(("called.")); + + /* Now start the checks */ + + curdays = time(NULL)/(60*60*24); /* today */ + + /* First: has account expired ? (CG) + * - expire < curdays + * - or (last_change + max_change + defer_change) < curdays + * - in both cases, deny access + */ + + D(("pwdb_get_entry")); + retval = pwdb_get_entry(pw, "expire", &pwe); + if (retval == PWDB_SUCCESS) { + int expire; + + expire = *( (const int *) pwe->value ); + (void) pwdb_entry_delete(&pwe); /* no longer needed */ + + if ((curdays > expire) && (expire > 0)) { + + _log_err(LOG_NOTICE + , "acct: account %s has expired (account expired)" + , uname); + make_remark(pamh, ctrl, PAM_ERROR_MSG + , "Your account has expired; " + "please contact your system administrator"); + + D(("account expired")); + return PAM_ACCT_EXPIRED; + } + } + + D(("pwdb_get_entry")); + retval = pwdb_get_entry(pw, "last_change", &pwe); + if ( retval == PWDB_SUCCESS ) { + last_change = *( (const int *) pwe->value ); + } else { + last_change = curdays; + } + (void) pwdb_entry_delete(&pwe); + + D(("pwdb_get_entry")); + retval = pwdb_get_entry(pw, "max_change", &pwe); + if ( retval == PWDB_SUCCESS ) { + max_change = *( (const int *) pwe->value ); + } else { + max_change = -1; + } + (void) pwdb_entry_delete(&pwe); + + D(("pwdb_get_entry")); + retval = pwdb_get_entry(pw, "defer_change", &pwe); + if (retval == PWDB_SUCCESS) { + int defer_change; + + defer_change = *( (const int *) pwe->value ); + (void) pwdb_entry_delete(&pwe); + + if ((curdays > (last_change + max_change + defer_change)) + && (max_change != -1) && (defer_change != -1) + && (last_change > 0)) { + + if ( on(UNIX_DEBUG, ctrl) ) { + _log_err(LOG_NOTICE, "acct: account %s has expired " + "(failed to change password)", uname); + } + make_remark(pamh, ctrl, PAM_ERROR_MSG + , "Your password has expired; " + "please see your system administrator"); + + D(("account expired2")); + return PAM_ACCT_EXPIRED; + } + } + + /* Now test if the password is expired, but the user still can + * change their password. (CG) + * - last_change = 0 + * - last_change + max_change < curdays + */ + + D(("when was the last change")); + if (last_change == 0) { + + if ( on(UNIX_DEBUG, ctrl) ) { + _log_err(LOG_NOTICE + , "acct: expired password for user %s (root enforced)" + , uname); + } + make_remark(pamh, ctrl, PAM_ERROR_MSG + , "You are required to change your password immediately" + ); + + D(("need a new password")); + return PAM_NEW_AUTHTOK_REQD; + } + + if (((last_change + max_change) < curdays) && + (max_change < 99999) && (max_change > 0)) { + + if ( on(UNIX_DEBUG, ctrl) ) { + _log_err(LOG_DEBUG + , "acct: expired password for user %s (password aged)" + , uname); + } + make_remark(pamh, ctrl, PAM_ERROR_MSG + , "Your password has expired; please change it!"); + + D(("need a new password 2")); + return PAM_NEW_AUTHTOK_REQD; + } + + /* + * Now test if the password is about to expire (CG) + * - last_change + max_change - curdays <= warn_change + */ + + retval = pwdb_get_entry(pw, "warn_change", &pwe); + if ( retval == PWDB_SUCCESS ) { + int warn_days, daysleft; + + daysleft = last_change + max_change - curdays; + warn_days = *((const int *) pwe->value); + (void) pwdb_entry_delete(&pwe); + + if ((daysleft <= warn_days) && (warn_days > 0)) { + char *s; + + if ( on(UNIX_DEBUG, ctrl) ) { + _log_err(LOG_DEBUG + , "acct: password for user %s will expire in %d days" + , uname, daysleft); + } + +#define LocalComment "Warning: your password will expire in %d day%s" + if ((s = (char *) malloc(30+sizeof(LocalComment))) == NULL) { + _log_err(LOG_CRIT, "malloc failure in " __FILE__); + retval = PAM_BUF_ERR; + } else { + + sprintf(s, LocalComment, daysleft, daysleft == 1 ? "":"s"); + + make_remark(pamh, ctrl, PAM_TEXT_INFO, s); + free(s); + } +#undef LocalComment + } + } else { + retval = PAM_SUCCESS; + } + + D(("all done")); + return retval; +} + + +/* + * this function checks for the account details. The user may not be + * permitted to log in at this time etc.. Within the context of + * vanilla Unix, this function simply does nothing. The shadow suite + * added password/account expiry, but PWDB takes care of this + * transparently. + */ + +static int _unix_acct_mgmt(pam_handle_t *pamh, unsigned int ctrl) +{ + const struct pwdb *pw = NULL; + + char *uname=NULL; + int retval; + + D(("called.")); + + /* identify user */ + + retval = pam_get_item(pamh,PAM_USER,(const void **)&uname); + D(("user = `%s'", uname)); + if (retval != PAM_SUCCESS || uname == NULL) { + _log_err(LOG_ALERT + , "acct; could not identify user (from uid=%d)" + , getuid()); + return PAM_USER_UNKNOWN; + } + + /* get database information for user */ + + retval = pwdb_locate("user", PWDB_DEFAULT, uname, PWDB_ID_UNKNOWN, &pw); + if (retval != PWDB_SUCCESS || pw == NULL) { + + _log_err(LOG_ALERT, "acct; %s (%s from uid=%d)" + , pwdb_strerror(retval), uname, getuid()); + if ( pw ) { + (void) pwdb_delete(&pw); + } + return PAM_USER_UNKNOWN; + } + + /* now check the user's times etc.. */ + + retval = _shadow_acct_mgmt_exp(pamh, ctrl, pw, uname); + if (retval != PAM_SUCCESS) { + _log_err(LOG_NOTICE, "expiry check failed for '%s'", uname); + } + + /* Done with pw */ + + (void) pwdb_delete(&pw); + + /* all done */ + + D(("done.")); + return retval; +} + +/* + * Copyright (c) Elliot Lee, 1996. + * Copyright (c) Andrew Morgan <morgan@parc.power.net> 1996. + * Copyright (c) Cristian Gafton <gafton@redhat.com> 1996. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_pwdb/pam_unix_auth.-c b/modules/pam_pwdb/pam_unix_auth.-c new file mode 100644 index 00000000..e4dfe136 --- /dev/null +++ b/modules/pam_pwdb/pam_unix_auth.-c @@ -0,0 +1,131 @@ +/* + * $Id$ + * + * See end of file for Copyright information. + */ + +static const char rcsid_auth[] = +"$Id$: pam_unix_auth.-c,v 1.2 1996/09/05 06:46:53 morgan Exp morgan $\n" +" - PAM_PWDB authentication functions. <morgan@parc.power.net>"; + +/* + * _unix_auth() is a front-end for UNIX/shadow authentication + * + * First, obtain the password from the user. Then use a + * routine in 'support.-c' to authenticate the user. + */ + +#define _UNIX_AUTHTOK "-UN*X-PASS" + +static int _unix_auth(pam_handle_t *pamh, unsigned int ctrl) +{ + int retval; + const char *name, *p; + + D(("called.")); + + /* get the user'name' */ + + retval = _unix_get_user(pamh, ctrl, NULL, &name); + if (retval != PAM_SUCCESS ) { + if (retval != PAM_CONV_AGAIN) { + if ( on(UNIX_DEBUG,ctrl) ) { + _log_err(LOG_DEBUG, "auth could not identify user"); + } + } else { + D(("pam_get_user/conv() function is not ready yet")); + /* it is safe to resume this function so we translate this + retval to the value that indicates we're happy to resume. */ + retval = PAM_INCOMPLETE; + } + return retval; + } + + /* if this user does not have a password... */ + + if ( _unix_blankpasswd(ctrl, name) ) { + D(("user '%s' has blank passwd", name)); + name = NULL; + return PAM_SUCCESS; + } + + /* get this user's authentication token */ + + retval = _unix_read_password(pamh, ctrl, NULL, "Password: ", NULL + , _UNIX_AUTHTOK, &p); + if (retval != PAM_SUCCESS) { + if (retval != PAM_CONV_AGAIN) { + _log_err(LOG_CRIT, "auth could not identify password for [%s]" + , name); + } else { + D(("conversation function is not ready yet")); + /* it is safe to resume this function so we translate this + retval to the value that indicates we're happy to resume. */ + retval = PAM_INCOMPLETE; + } + name = NULL; + return retval; + } + D(("user=%s, password=[%s]", name, p)); + + /* verify the password of this user */ + retval = _unix_verify_password(pamh, name, p, ctrl); + name = p = NULL; + + D(("done [%d]", retval)); + + return retval; +} + +/* + * This function is for setting unix credentials. Sun has indicated + * that there are *NO* authentication credentials for unix. The + * obvious credentials would be the group membership of the user as + * listed in the /etc/group file. However, Sun indicates that it is + * the responsibility of the application to set these. + */ + +static int _unix_set_credentials(pam_handle_t *pamh, unsigned int ctrl) +{ + D(("called <empty function> returning.")); + + return PAM_SUCCESS; +} + +/******************************************************************** + * Copyright (c) Alexander O. Yuriev, 1996. + * Copyright (c) Andrew G. Morgan <morgan@parc.power.net> 1996 + * Copyright (c) Cristian Gafton <gafton@redhat.com> 1996, 1997 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + diff --git a/modules/pam_pwdb/pam_unix_md.-c b/modules/pam_pwdb/pam_unix_md.-c new file mode 100644 index 00000000..d9b2c51b --- /dev/null +++ b/modules/pam_pwdb/pam_unix_md.-c @@ -0,0 +1,73 @@ +/* + * This function is a front-end for the message digest algorithms used + * to compute the user's encrypted passwords. No reversible encryption + * is used here and I intend to keep it that way. + * + * While there are many sources of encryption outside the United + * States, it *may* be illegal to re-export reversible encryption + * computer code. Until such time as it is legal to export encryption + * software freely from the US, please do not send me any. (AGM) + */ + +/* this should have been defined in a header file.. Why wasn't it? AGM */ +extern char *crypt(const char *key, const char *salt); + +#include "md5.h" +#include "bigcrypt.-c" + +struct cfns { + const char *salt; + int len; + char * (* mdfn)(const char *key, const char *salt); +}; + +/* array of non-standard digest algorithms available */ + +#define N_MDS 1 +const static struct cfns cfn_list[N_MDS] = { + { "$1$", 3, Goodcrypt_md5 }, +}; + +static char *_pam_md(const char *key, const char *salt) +{ + char *x,*e=NULL; + int i; + + D(("called with key='%s', salt='%s'", key, salt)); + + /* check for non-standard salts */ + + for (i=0; i<N_MDS; ++i) { + if ( !strncmp(cfn_list[i].salt, salt, cfn_list[i].len) ) { + e = cfn_list[i].mdfn(key, salt); + break; + } + } + + if ( i >= N_MDS ) { + e = bigcrypt(key, salt); /* (defaults to standard algorithm) */ + } + + x = x_strdup(e); /* put e in malloc()ed memory */ + _pam_overwrite(e); /* clean up */ + return x; /* this must be deleted elsewhere */ +} + +#ifndef PWDB_NO_MD_COMPAT +static char *_pam_md_compat(const char *key, const char *salt) +{ + char *x,*e=NULL; + + D(("called with key='%s', salt='%s'", key, salt)); + + if ( !strncmp("$1$", salt, 3) ) { + e = Brokencrypt_md5(key, salt); + x = x_strdup(e); /* put e in malloc()ed memory */ + _pam_overwrite(e); /* clean up */ + } else { + x = x_strdup("*"); + } + + return x; /* this must be deleted elsewhere */ +} +#endif /* PWDB_NO_MD_COMPAT */ diff --git a/modules/pam_pwdb/pam_unix_passwd.-c b/modules/pam_pwdb/pam_unix_passwd.-c new file mode 100644 index 00000000..13801ba3 --- /dev/null +++ b/modules/pam_pwdb/pam_unix_passwd.-c @@ -0,0 +1,404 @@ +/* $Id$ */ + +/* + * $Log$ + * Revision 1.1 2000/06/20 22:11:50 agmorgan + * Initial revision + * + * Revision 1.2 1999/07/04 23:22:38 morgan + * Andrey's MD5 (bigendian) work around + cleanup to address problems with + * applications that let an (ab)user kill them off without giving PAM the + * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.] + * + * Revision 1.1.1.1 1998/07/12 05:17:16 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.6 1997/04/05 06:31:06 morgan + * mostly a reformat. + * + * Revision 1.5 1996/12/01 03:05:54 morgan + * debugging with _pam_macros.h + * + * Revision 1.4 1996/11/10 21:04:51 morgan + * pwdb conversion + * + * Revision 1.3 1996/09/05 06:48:15 morgan + * A lot has changed. I'd recommend you study the diff. + * + * Revision 1.2 1996/09/01 16:33:27 morgan + * Cristian Gafton's changes + * + * Revision 1.1 1996/08/29 13:21:27 morgan + * Initial revision + * + */ + +static const char rcsid_pass[] = +"$Id$\n" +" - PAM_PWDB password module <morgan@parc.power.net>" +; + +#include "pam_unix_pwupd.-c" + +/* passwd/salt conversion macros */ + +#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.') +#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') + +/* data tokens */ + +#define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS" +#define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS" + +/* Implementation */ + +/* + * i64c - convert an integer to a radix 64 character + */ +static int i64c(int i) +{ + if (i < 0) + return ('.'); + else if (i > 63) + return ('z'); + if (i == 0) + return ('.'); + if (i == 1) + return ('/'); + if (i >= 2 && i <= 11) + return ('0' - 2 + i); + if (i >= 12 && i <= 37) + return ('A' - 12 + i); + if (i >= 38 && i <= 63) + return ('a' - 38 + i); + return ('\0'); +} + +/* + * FUNCTION: _pam_unix_chauthtok() + * + * this function works in two passes. The first, when UNIX__PRELIM is + * set, obtains the previous password. It sets the PAM_OLDAUTHTOK item + * or stores it as a data item. The second function obtains a new + * password (verifying if necessary, that the user types it the same a + * second time.) depending on the 'ctrl' flags this new password may + * be stored in the PAM_AUTHTOK item or a private data item. + * + * Having obtained a new password. The function updates the + * /etc/passwd (and optionally the /etc/shadow) file(s). + * + * Provision is made for the creation of a blank shadow file if none + * is available, but one is required to update the shadow file -- the + * intention being for shadow passwords to be seamlessly implemented + * from the generic UNIX scheme. -- THIS BIT IS PRE-ALPHA.. and included + * in this release (.52) mostly for the purpose of discussion. + */ + +static int _unix_chauthtok(pam_handle_t *pamh, unsigned int ctrl) +{ + int retval; + unsigned int lctrl; + + /* <DO NOT free() THESE> */ + const char *user; + const char *pass_old, *pass_new; + /* </DO NOT free() THESE> */ + + D(("called")); + + /* + * First get the name of a user + */ + + retval = _unix_get_user( pamh, ctrl, "Username: ", &user ); + if ( retval != PAM_SUCCESS ) { + if ( on(UNIX_DEBUG,ctrl) ) { + _log_err(LOG_DEBUG, "password - could not identify user"); + } + return retval; + } + + if ( on(UNIX__PRELIM, ctrl) ) { + /* + * obtain and verify the current password (OLDAUTHTOK) for + * the user. + */ + + char *Announce; + + D(("prelim check")); + + if ( _unix_blankpasswd(ctrl, user) ) { + + return PAM_SUCCESS; + + } else if ( off(UNIX__IAMROOT, ctrl) ) { + + /* instruct user what is happening */ +#define greeting "Changing password for " + Announce = (char *) malloc(sizeof(greeting)+strlen(user)); + if (Announce == NULL) { + _log_err(LOG_CRIT, "password - out of memory"); + return PAM_BUF_ERR; + } + (void) strcpy(Announce, greeting); + (void) strcpy(Announce+sizeof(greeting)-1, user); +#undef greeting + + lctrl = ctrl; + set(UNIX__OLD_PASSWD, lctrl); + retval = _unix_read_password( pamh, lctrl + , Announce + , "(current) UNIX password: " + , NULL + , _UNIX_OLD_AUTHTOK + , &pass_old ); + free(Announce); + + if ( retval != PAM_SUCCESS ) { + _log_err(LOG_NOTICE + , "password - (old) token not obtained"); + return retval; + } + + /* verify that this is the password for this user */ + + retval = _unix_verify_password(pamh, user, pass_old, ctrl); + } else { + D(("process run by root so do nothing this time around")); + pass_old = NULL; + retval = PAM_SUCCESS; /* root doesn't have too */ + } + + if ( retval != PAM_SUCCESS ) { + D(("Authentication failed")); + pass_old = NULL; + return retval; + } + + retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old); + pass_old = NULL; + if ( retval != PAM_SUCCESS ) { + _log_err(LOG_CRIT, "failed to set PAM_OLDAUTHTOK"); + } + + } else if ( on( UNIX__UPDATE, ctrl ) ) { + /* tpass is used below to store the _pam_md() return; it + * should be _pam_delete()'d. */ + + char *tpass=NULL; + + /* + * obtain the proposed password + */ + + D(("do update")); + + /* + * get the old token back. NULL was ok only if root [at this + * point we assume that this has already been enforced on a + * previous call to this function]. + */ + + if ( off(UNIX_NOT_SET_PASS, ctrl) ) { + retval = pam_get_item(pamh, PAM_OLDAUTHTOK + , (const void **)&pass_old); + } else { + retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK + , (const void **)&pass_old); + if (retval == PAM_NO_MODULE_DATA) { + retval = PAM_SUCCESS; + pass_old = NULL; + } + } + + if (retval != PAM_SUCCESS) { + _log_err(LOG_NOTICE, "user not authenticated"); + return retval; + } + + D(("get new password now")); + + lctrl = ctrl; + + /* + * use_authtok is to force the use of a previously entered + * password -- needed for pluggable password strength checking + */ + + if ( on(UNIX_USE_AUTHTOK, lctrl) ) { + set(UNIX_USE_FIRST_PASS, lctrl); + } + + retval = _unix_read_password( pamh, lctrl + , NULL + , "Enter new UNIX password: " + , "Retype new UNIX password: " + , _UNIX_NEW_AUTHTOK + , &pass_new ); + + if ( retval != PAM_SUCCESS ) { + if ( on(UNIX_DEBUG,ctrl) ) { + _log_err(LOG_ALERT + , "password - new password not obtained"); + } + pass_old = NULL; /* tidy up */ + return retval; + } + + D(("returned to _unix_chauthtok")); + + /* + * At this point we know who the user is and what they + * propose as their new password. Verify that the new + * password is acceptable. + */ + + if (pass_new[0] == '\0') { /* "\0" password = NULL */ + pass_new = NULL; + } + + retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new); + + if (retval != PAM_SUCCESS) { + _log_err(LOG_NOTICE, "new password not acceptable"); + pass_new = pass_old = NULL; /* tidy up */ + return retval; + } + + /* + * By reaching here we have approved the passwords and must now + * rebuild the password database file. + */ + + /* + * First we encrypt the new password. + * + * XXX - this is where we might need some code for RADIUS types + * of password handling... no encryption needed.. + */ + + if ( on(UNIX_MD5_PASS, ctrl) ) { + + /* + * Code lifted from Marek Michalkiewicz's shadow suite. (CG) + * removed use of static variables (AGM) + */ + + struct timeval tv; + MD5_CTX ctx; + unsigned char result[16]; + char *cp = (char *)result; + unsigned char tmp[16]; + int i; + + GoodMD5Init(&ctx); + gettimeofday(&tv, (struct timezone *) 0); + GoodMD5Update(&ctx, (void *) &tv, sizeof tv); + i = getpid(); + GoodMD5Update(&ctx, (void *) &i, sizeof i); + i = clock(); + GoodMD5Update(&ctx, (void *) &i, sizeof i); + GoodMD5Update(&ctx, result, sizeof result); + GoodMD5Final(tmp, &ctx); + strcpy(cp, "$1$"); /* magic for the MD5 */ + cp += strlen(cp); + for (i = 0; i < 8; i++) + *cp++ = i64c(tmp[i] & 077); + *cp = '\0'; + + /* no longer need cleartext */ + pass_new = tpass = _pam_md(pass_new, (const char *)result); + + } else { + /* + * Salt manipulation is stolen from Rick Faith's passwd + * program. Sorry Rick :) -- alex + */ + + time_t tm; + char salt[3]; + + time(&tm); + salt[0] = bin_to_ascii(tm & 0x3f); + salt[1] = bin_to_ascii((tm >> 6) & 0x3f); + salt[2] = '\0'; + + if ( off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8 ) { + /* to avoid using the _extensions_ of the bigcrypt() + function we truncate the newly entered password */ + char *temp = malloc(9); + + if (temp == NULL) { + _log_err(LOG_CRIT, "out of memory for password"); + pass_new = pass_old = NULL; /* tidy up */ + return PAM_BUF_ERR; + } + + /* copy first 8 bytes of password */ + strncpy(temp, pass_new, 8); + temp[8] = '\0'; + + /* no longer need cleartext */ + pass_new = tpass = _pam_md( temp, salt ); + + _pam_delete(temp); /* tidy up */ + } else { + /* no longer need cleartext */ + pass_new = tpass = _pam_md( pass_new, salt ); + } + } + + D(("password processed")); + + /* update the password database(s) -- race conditions..? */ + + retval = unix_update_db(pamh, ctrl, user, pass_old, pass_new); + pass_old = pass_new = NULL; + + } else { /* something has broken with the module */ + + _log_err(LOG_ALERT, "password received unknown request"); + retval = PAM_ABORT; + + } + + return retval; +} + +/* ****************************************************************** + * Copyright (c) Alexander O. Yuriev (alex@bach.cis.temple.edu), 1996. + * Copyright (c) Andrew Morgan <morgan@parc.power.net> 1996, 1997. + * Copyright (c) Cristian Gafton, <gafton@redhat.com> 1996, 1997. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_pwdb/pam_unix_pwupd.-c b/modules/pam_pwdb/pam_unix_pwupd.-c new file mode 100644 index 00000000..0f646369 --- /dev/null +++ b/modules/pam_pwdb/pam_unix_pwupd.-c @@ -0,0 +1,260 @@ +/* + * $Id$ + * + * This file contains the routines to update the passwd databases. + */ + +/* Implementation */ + +static int unix_update_db(pam_handle_t *pamh, int ctrl, const char *user, + const char *pass_old, const char *pass_new) +{ + const struct pwdb *pw=NULL; + const struct pwdb_entry *pwe=NULL; + pwdb_flag flag; + int retval, i; + + D(("called.")); + + /* obtain default user record */ + + retval = pwdb_locate("user", PWDB_DEFAULT, user, PWDB_ID_UNKNOWN, &pw); + if (retval == PWDB_PASS_PHRASE_REQD) { + retval = pwdb_set_entry(pw, "pass_phrase" + , pass_old, 1+strlen(pass_old) + , NULL, NULL, 0); + if (retval == PWDB_SUCCESS) + retval = pwdb_locate("user", pw->source, user + , PWDB_ID_UNKNOWN, &pw); + } + pass_old = NULL; + + if ( retval != PWDB_SUCCESS ) { + _log_err(LOG_ALERT, "cannot identify user %s (uid=%d)" + , user, getuid() ); + pass_new = NULL; + if (pw) + (void) pwdb_delete(&pw); + return PAM_USER_UNKNOWN; + } + + /* check that we can update all of the default databases */ + + retval = pwdb_flags("user", pw->source, &flag); + + if ( retval != PWDB_SUCCESS || ( pwdb_on(flag,PWDB_F_NOUPDATE) ) ) { + _log_err(LOG_ERR, "cannot update default database for user %s" + , user ); + pass_new = NULL; + if (pw) + (void) pwdb_delete(&pw); + return PAM_PERM_DENIED; + } + + /* If there was one, we delete the "last_change" entry */ + retval = pwdb_get_entry(pw, "last_change", &pwe); + if (retval == PWDB_SUCCESS) { + (void) pwdb_entry_delete(&pwe); + pwdb_set_entry(pw, "last_change", NULL, -1, NULL, NULL, 0); + } + + /* + * next check for pam.conf specified databases: shadow etc... [In + * other words, pam.conf indicates which database the password is + * to be subsequently placed in: this is password migration]. + */ + + if ( on(UNIX__SET_DB, ctrl) ) { + const char *db_token; + pwdb_type pt = _PWDB_MAX_TYPES; + + if ( on(UNIX_UNIX, ctrl) ) { + db_token = "U"; /* XXX - should be macro */ + pt = PWDB_UNIX; + } else if ( on(UNIX_SHADOW, ctrl) ) { + db_token = "x"; /* XXX - should be macro */ + pt = PWDB_SHADOW; + } else if ( on(UNIX_RADIUS, ctrl) ) { + db_token = "R"; /* XXX - is this ok? */ + pt = PWDB_RADIUS; + } else { + _log_err(LOG_ALERT + , "cannot determine database to use for authtok"); + pass_new = NULL; + if (pw) + (void) pwdb_delete(&pw); + return PAM_ABORT; /* we're in trouble */ + } + + /* + * Attempt to update the indicated database (only) + */ + + { + pwdb_type tpt[2]; + tpt[0] = pt; + tpt[1] = _PWDB_MAX_TYPES; + + /* Can we set entry in database? */ + retval = pwdb_flags("user", tpt, &flag); + if (retval == PWDB_SUCCESS && !pwdb_on(flag,PWDB_F_NOUPDATE)) { + /* YES. This database is available.. */ + + /* Only update if it is not already in the default list */ + for (i=0; pw->source[i] != _PWDB_MAX_TYPES + && pw->source[i] != pt ; ++i); + if (pw->source[i] == _PWDB_MAX_TYPES) { + const struct pwdb *tpw=NULL; + + /* copy database entry */ + if ((retval = pwdb_new(&tpw, 10)) != PWDB_SUCCESS + || (retval = pwdb_merge(tpw, pw, PWDB_TRUE)) + != PWDB_SUCCESS) { + _log_err(LOG_CRIT, "failed to obtain new pwdb: %s" + , pwdb_strerror(retval)); + retval = PAM_ABORT; + } else + retval = PAM_SUCCESS; + + /* set db_token */ + if (retval == PAM_SUCCESS) { + retval = pwdb_set_entry(tpw, "defer_pass", db_token + , 1+strlen(db_token) + , NULL, NULL, 0); + if (retval != PWDB_SUCCESS) { + _log_err(LOG_ALERT, "set defer_pass -> %s" + , pwdb_strerror(retval)); + retval = PAM_PERM_DENIED; + } else + retval = PAM_SUCCESS; + } + + /* update specific database */ + if (retval == PAM_SUCCESS) { + retval = pwdb_replace("user", tpt + , user, PWDB_ID_UNKNOWN, &tpw); + if (retval != PWDB_SUCCESS) { + const char *service=NULL; + (void) pam_get_item(pamh, PAM_SERVICE + , (const void **)&service); + _log_err(LOG_ALERT + , "(%s) specified database failed: %s" + , service + , pwdb_strerror(retval)); + retval = PAM_PERM_DENIED; + } else { + retval = PAM_SUCCESS; + } + } + + /* clean up temporary pwdb */ + if (tpw) + (void) pwdb_delete(&tpw); + } + + /* we can properly adopt new defer_pass */ + if (retval == PAM_SUCCESS) { + /* failing here will mean we go back to former + password location */ + (void) pwdb_set_entry(pw, "defer_pass", db_token + , 1+strlen(db_token), NULL, NULL, 0); + } + } + } + } + + /* + * the password will now be placed in appropriate (perhaps original) db + */ + + retval = pwdb_get_entry(pw, "uid", &pwe); + if (retval != PWDB_SUCCESS) { + _log_err(LOG_ALERT, "no uid!? (%s); %s", user, pwdb_strerror(retval)); + pass_new = NULL; + if (pw) + (void) pwdb_delete(&pw); + return PAM_USER_UNKNOWN; + } + + /* insert the passwd into the 'pw' structure */ + + retval = pwdb_set_entry(pw, "passwd", pass_new, 1+strlen(pass_new) + , NULL, NULL, 0); + pass_new = NULL; + if (retval != PWDB_SUCCESS) { + _log_err(LOG_ALERT, "set2 failed; %s", pwdb_strerror(retval)); + if (pw) + (void) pwdb_delete(&pw); + return PAM_AUTHTOK_LOCK_BUSY; + } + + retval = pwdb_replace("user", pw->source, user + , *((uid_t *)pwe->value), &pw); + if (retval != PWDB_SUCCESS) { + _log_err(LOG_ALERT, "user (%s/%d) update failed; %s" + , user, *((uid_t *)pwe->value), pwdb_strerror(retval)); + if (pw) + (void) pwdb_delete(&pw); + (void) pwdb_entry_delete(&pwe); + return PAM_ABORT; + } + + if (retval != PWDB_SUCCESS) { + + _log_err(LOG_ALERT, "user (%s/%d) update failed; %s" + , user, *((uid_t *)pwe->value), pwdb_strerror(retval)); + retval = PAM_ABORT; + + } else { + /* password updated */ + + _log_err(LOG_INFO, "password for (%s/%d) changed by (%s/%d)" + , user, *((uid_t *)pwe->value), getlogin(), getuid()); + retval = PAM_SUCCESS; + } + + /* tidy up */ + + (void) pwdb_entry_delete(&pwe); + if (pw) + (void) pwdb_delete(&pw); + + return retval; +} + +/* ****************************************************************** + * Copyright (c) Andrew Morgan <morgan@parc.power.net> 1996,1997. + * Copyright (c) Cristian Gafton, <gafton@redhat.com> 1996, 1997. + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_pwdb/pam_unix_sess.-c b/modules/pam_pwdb/pam_unix_sess.-c new file mode 100644 index 00000000..e1fe820b --- /dev/null +++ b/modules/pam_pwdb/pam_unix_sess.-c @@ -0,0 +1,118 @@ +/* + * $Id$ + * + * $Log$ + * Revision 1.1 2000/06/20 22:11:51 agmorgan + * Initial revision + * + * Revision 1.1.1.1 1998/07/12 05:17:16 morgan + * Linux PAM sources pre-0.66 + * + * Revision 1.4 1996/12/01 03:05:54 morgan + * debugging with _pam_macros.h + * + * Revision 1.3 1996/11/10 21:05:33 morgan + * pwdb conversion + * + * Revision 1.2 1996/09/05 06:49:02 morgan + * more informative logging + * + * Revision 1.1 1996/08/29 13:27:51 morgan + * Initial revision + * + * + * See end for Copyright information + */ + +static const char rcsid_sess[] = +"$Id$\n" +" - PAM_PWDB session management. morgan@parc.power.net"; + +/* Define internal functions */ + +static int _unix_open_session(pam_handle_t *pamh, unsigned int ctrl) +{ + int retval; + char *user_name, *service; + + D(("called.")); + + retval = pam_get_item( pamh, PAM_USER, (void *) &user_name ); + if ( user_name == NULL || retval != PAM_SUCCESS ) { + _log_err(LOG_CRIT, "open_session - error recovering username"); + return PAM_SESSION_ERR; + } + + retval = pam_get_item( pamh, PAM_SERVICE, (void*) &service ); + if ( service == NULL || retval != PAM_SUCCESS ) { + _log_err(LOG_CRIT, "open_session - error recovering service"); + return PAM_SESSION_ERR; + } + + _log_err(LOG_INFO, "(%s) session opened for user %s by %s(uid=%d)" + , service, user_name + , getlogin() == NULL ? "":getlogin(), getuid() ); + + return PAM_SUCCESS; +} + +static int _unix_close_session(pam_handle_t *pamh, unsigned int ctrl) +{ + int retval; + char *user_name, *service; + + D(("called.")); + + retval = pam_get_item( pamh, PAM_USER, (void*) &user_name ); + if ( user_name == NULL || retval != PAM_SUCCESS ) { + _log_err(LOG_CRIT, "close_session - error recovering username"); + return PAM_SESSION_ERR; + } + + retval = pam_get_item( pamh, PAM_SERVICE, (void*) &service ); + if ( service == NULL || retval != PAM_SUCCESS ) { + _log_err(LOG_CRIT, "close_session - error recovering service"); + return PAM_SESSION_ERR; + } + + _log_err(LOG_INFO, "(%s) session closed for user %s" + , service, user_name ); + + return PAM_SUCCESS; +} + +/* + * Copyright (c) Alexander O. Yuriev, 1996. All rights reserved. + * Copyright (c) Andrew G. Morgan, 1996, <morgan@parc.power.net> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_pwdb/pwdb_chkpwd.c b/modules/pam_pwdb/pwdb_chkpwd.c new file mode 100644 index 00000000..2c7ba29c --- /dev/null +++ b/modules/pam_pwdb/pwdb_chkpwd.c @@ -0,0 +1,204 @@ +/* + * $Id$ + * + * This program is designed to run setuid(root) or with sufficient + * privilege to read all of the unix password databases. It is designed + * to provide a mechanism for the current user (defined by this + * process' real uid) to verify their own password. + * + * The password is read from the standard input. The exit status of + * this program indicates whether the user is authenticated or not. + * + * Copyright information is located at the end of the file. + * + */ + +#define _BSD_SOURCE + +#ifdef linux +# define _GNU_SOURCE +# include <features.h> +#endif + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include <security/_pam_macros.h> + +#define MAXPASS 200 /* the maximum length of a password */ + +#define UNIX_PASSED (PWDB_SUCCESS) +#define UNIX_FAILED (PWDB_SUCCESS+1) + +#include <pwdb/pwdb_public.h> + +/* syslogging function for errors and other information */ + +static void _log_err(int err, const char *format, ...) +{ + va_list args; + + va_start(args, format); + openlog("pwdb_chkpwd", LOG_CONS|LOG_PID, LOG_AUTH); + vsyslog(err, format, args); + va_end(args); + closelog(); +} + +#define PWDB_NO_MD_COMPAT +#include "pam_unix_md.-c" + +static int _unix_verify_passwd(const char *salt, const char *p) +{ + char *pp=NULL; + int retval; + + if (p == NULL) { + if (*salt == '\0') { + retval = UNIX_PASSED; + } else { + retval = UNIX_FAILED; + } + } else { + pp = _pam_md(p, salt); + p = NULL; /* no longer needed here */ + + if ( strcmp( pp, salt ) == 0 ) { + retval = UNIX_PASSED; + } else { + retval = UNIX_FAILED; + } + } + + /* clean up */ + { + char *tp = pp; + if (pp != NULL) { + while(tp && *tp) + *tp++ = '\0'; + free(pp); + pp = tp = NULL; + } + } + + return retval; +} + +int main(void) +{ + const struct pwdb *pw=NULL; + const struct pwdb_entry *pwe=NULL; + char pass[MAXPASS+1]; + int npass; + int retval=UNIX_FAILED; + + /* + * we establish that this program is running with non-tty stdin. + * this is to discourage casual use. It does *NOT* prevent an + * intruder from repeatadly running this program to determine the + * password of the current user (brute force attack, but one for + * which the attacker must already have gained access to the user's + * account). + */ + + if ( isatty(STDIN_FILENO) ) { + _log_err(LOG_NOTICE + , "inappropriate use of PWDB helper binary [UID=%d]" + , getuid() ); + fprintf(stderr, + "This program is not designed for running in this way\n" + "-- the system administrator has been informed\n"); + exit(UNIX_FAILED); + } + + /* + * determine the current user's name: + */ + + retval = pwdb_start(); + if (retval != PWDB_SUCCESS) { + _log_err(LOG_ALERT, "failed to open pwdb"); + retval = UNIX_FAILED; + } + if (retval != UNIX_FAILED) { + retval = pwdb_locate("user", PWDB_DEFAULT, PWDB_NAME_UNKNOWN + , getuid(), &pw); + } + if (retval != PWDB_SUCCESS) { + _log_err(LOG_ALERT, "could not identify user"); + while (pwdb_end() != PWDB_SUCCESS); + exit(UNIX_FAILED); + } + + /* read the password from stdin (a pipe from the pam_pwdb module) */ + + npass = read(STDIN_FILENO, pass, MAXPASS); + + if (npass < 0) { /* is it a valid password? */ + _log_err(LOG_DEBUG, "no password supplied"); + retval = UNIX_FAILED; + } else if (npass >= MAXPASS-1) { + _log_err(LOG_DEBUG, "password too long"); + retval = UNIX_FAILED; + } else if (pwdb_get_entry(pw, "passwd", &pwe) != PWDB_SUCCESS) { + _log_err(LOG_WARNING, "password not found"); + retval = UNIX_FAILED; + } else { + if (npass <= 0) { + /* the password is NULL */ + + retval = _unix_verify_passwd((const char *)(pwe->value), NULL); + } else { + /* does pass agree with the official one? */ + + pass[npass] = '\0'; /* NUL terminate */ + retval = _unix_verify_passwd((const char *)(pwe->value), pass); + } + } + + memset(pass, '\0', MAXPASS); /* clear memory of the password */ + while (pwdb_end() != PWDB_SUCCESS); + + /* return pass or fail */ + + exit(retval); +} + +/* + * Copyright (c) Andrew G. Morgan, 1997. All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_pwdb/support.-c b/modules/pam_pwdb/support.-c new file mode 100644 index 00000000..2cbcb576 --- /dev/null +++ b/modules/pam_pwdb/support.-c @@ -0,0 +1,938 @@ +/* + * $Id$ + * + * Copyright information at end of file. + */ + +/* + * here is the string to inform the user that the new passwords they + * typed were not the same. + */ + +#define MISTYPED_PASS "Sorry, passwords do not match" + +/* type definition for the control options */ + +typedef struct { + const char *token; + unsigned int mask; /* shall assume 32 bits of flags */ + unsigned int flag; +} UNIX_Ctrls; + +/* + * macro to determine if a given flag is on + */ + +#define on(x,ctrl) (unix_args[x].flag & ctrl) + +/* + * macro to determine that a given flag is NOT on + */ + +#define off(x,ctrl) (!on(x,ctrl)) + +/* + * macro to turn on/off a ctrl flag manually + */ + +#define set(x,ctrl) (ctrl = ((ctrl)&unix_args[x].mask)|unix_args[x].flag) +#define unset(x,ctrl) (ctrl &= ~(unix_args[x].flag)) + +/* the generic mask */ + +#define _ALL_ON_ (~0U) + +/* end of macro definitions definitions for the control flags */ + +/* ****************************************************************** * + * ctrl flags proper.. + */ + +/* + * here are the various options recognized by the unix module. They + * are enumerated here and then defined below. Internal arguments are + * given NULL tokens. + */ + +#define UNIX__OLD_PASSWD 0 /* internal */ +#define UNIX__VERIFY_PASSWD 1 /* internal */ +#define UNIX__IAMROOT 2 /* internal */ + +#define UNIX_AUDIT 3 /* print more things than debug.. + some information may be sensitive */ +#define UNIX_USE_FIRST_PASS 4 +#define UNIX_TRY_FIRST_PASS 5 +#define UNIX_NOT_SET_PASS 6 /* don't set the AUTHTOK items */ + +#define UNIX__PRELIM 7 /* internal */ +#define UNIX__UPDATE 8 /* internal */ +#define UNIX__NONULL 9 /* internal */ +#define UNIX__QUIET 10 /* internal */ +#define UNIX_USE_AUTHTOK 11 /* insist on reading PAM_AUTHTOK */ +#define UNIX_SHADOW 12 /* signal shadow on */ +#define UNIX_MD5_PASS 13 /* force the use of MD5 passwords */ +#define UNIX__NULLOK 14 /* Null token ok */ +#define UNIX_RADIUS 15 /* wish to use RADIUS for password */ +#define UNIX__SET_DB 16 /* internal - signals redirect to db */ +#define UNIX_DEBUG 17 /* send more info to syslog(3) */ +#define UNIX_NODELAY 18 /* admin does not want a fail-delay */ +#define UNIX_UNIX 19 /* wish to use /etc/passwd for pwd */ +#define UNIX_BIGCRYPT 20 /* use DEC-C2 crypt()^x function */ +#define UNIX_LIKE_AUTH 21 /* need to auth for setcred to work */ +/* -------------- */ +#define UNIX_CTRLS_ 22 /* number of ctrl arguments defined */ + + +static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = { +/* symbol token name ctrl mask ctrl * + * ------------------ ------------------ -------------- ---------- */ + +/* UNIX__OLD_PASSWD */ { NULL, _ALL_ON_, 01 }, +/* UNIX__VERIFY_PASSWD */ { NULL, _ALL_ON_, 02 }, +/* UNIX__IAMROOT */ { NULL, _ALL_ON_, 04 }, +/* UNIX_AUDIT */ { "audit", _ALL_ON_, 010 }, +/* UNIX_USE_FIRST_PASS */ { "use_first_pass", _ALL_ON_^(060), 020 }, +/* UNIX_TRY_FIRST_PASS */ { "try_first_pass", _ALL_ON_^(060), 040 }, +/* UNIX_NOT_SET_PASS */ { "not_set_pass", _ALL_ON_, 0100 }, +/* UNIX__PRELIM */ { NULL, _ALL_ON_^(0600), 0200 }, +/* UNIX__UPDATE */ { NULL, _ALL_ON_^(0600), 0400 }, +/* UNIX__NONULL */ { NULL, _ALL_ON_, 01000 }, +/* UNIX__QUIET */ { NULL, _ALL_ON_, 02000 }, +/* UNIX_USE_AUTHTOK */ { "use_authtok", _ALL_ON_, 04000 }, +/* UNIX_SHADOW */ { "shadow", _ALL_ON_^(0140000), 010000 }, +/* UNIX_MD5_PASS */ { "md5", _ALL_ON_^(02000000), 020000 }, +/* UNIX__NULLOK */ { "nullok", _ALL_ON_^(01000), 0 }, +/* UNIX_RADIUS */ { "radius", _ALL_ON_^(0110000), 040000 }, +/* UNIX__SET_DB */ { NULL, _ALL_ON_, 0100000 }, +/* UNIX_DEBUG */ { "debug", _ALL_ON_, 0200000 }, +/* UNIX_NODELAY */ { "nodelay", _ALL_ON_, 0400000 }, +/* UNIX_UNIX */ { "unix", _ALL_ON_^(050000), 01000000 }, +/* UNIX_BIGCRYPT */ { "bigcrypt", _ALL_ON_^(020000), 02000000 }, +/* UNIX_LIKE_AUTH */ { "likeauth", _ALL_ON_, 04000000 }, +}; + +#define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag) + +/* syslogging function for errors and other information */ + +static void _log_err(int err, const char *format, ...) +{ + va_list args; + + va_start(args, format); + openlog("PAM_pwdb", LOG_CONS|LOG_PID, LOG_AUTH); + vsyslog(err, format, args); + va_end(args); + closelog(); +} + +/* this is a front-end for module-application conversations */ + +static int converse(pam_handle_t *pamh, int ctrl, int nargs + , struct pam_message **message + , struct pam_response **response) +{ + int retval; + struct pam_conv *conv; + + D(("begin to converse")); + + retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ; + if ( retval == PAM_SUCCESS ) { + + retval = conv->conv(nargs, ( const struct pam_message ** ) message + , response, conv->appdata_ptr); + + D(("returned from application's conversation function")); + + if (retval != PAM_SUCCESS && on(UNIX_DEBUG,ctrl) ) { + _log_err(LOG_DEBUG, "conversation failure [%s]" + , pam_strerror(pamh, retval)); + } + + } else if (retval != PAM_CONV_AGAIN) { + _log_err(LOG_ERR, "couldn't obtain coversation function [%s]" + , pam_strerror(pamh, retval)); + } + + D(("ready to return from module conversation")); + + return retval; /* propagate error status */ +} + +static int make_remark(pam_handle_t *pamh, unsigned int ctrl + , int type, const char *text) +{ + int retval=PAM_SUCCESS; + + if ( off(UNIX__QUIET, ctrl) ) { + struct pam_message *pmsg[1], msg[1]; + struct pam_response *resp; + + pmsg[0] = &msg[0]; + msg[0].msg = text; + msg[0].msg_style = type; + + resp = NULL; + retval = converse(pamh, ctrl, 1, pmsg, &resp); + + if (resp) { + _pam_drop_reply(resp, 1); + } + } + return retval; +} + +/* + * set the control flags for the UNIX module. + */ + +static int set_ctrl(int flags, int argc, const char **argv) +{ + unsigned int ctrl; + + D(("called.")); + + ctrl = UNIX_DEFAULTS; /* the default selection of options */ + + /* set some flags manually */ + + if ( getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK) ) { + set(UNIX__IAMROOT, ctrl); + } + if ( flags & PAM_UPDATE_AUTHTOK ) { + set(UNIX__UPDATE, ctrl); + } + if ( flags & PAM_PRELIM_CHECK ) { + set(UNIX__PRELIM, ctrl); + } + if ( flags & PAM_DISALLOW_NULL_AUTHTOK ) { + set(UNIX__NONULL, ctrl); + } + if ( flags & PAM_SILENT ) { + set(UNIX__QUIET, ctrl); + } + + /* now parse the arguments to this module */ + + while (argc-- > 0) { + int j; + + D(("pam_pwdb arg: %s",*argv)); + + for (j=0; j<UNIX_CTRLS_; ++j) { + if (unix_args[j].token + && ! strcmp(*argv, unix_args[j].token) ) { + break; + } + } + + if ( j >= UNIX_CTRLS_ ) { + _log_err(LOG_ERR, "unrecognized option [%s]",*argv); + } else { + ctrl &= unix_args[j].mask; /* for turning things off */ + ctrl |= unix_args[j].flag; /* for turning things on */ + } + + ++argv; /* step to next argument */ + } + + /* these are used for updating passwords in specific places */ + + if (on(UNIX_SHADOW,ctrl) || on(UNIX_RADIUS,ctrl) || on(UNIX_UNIX,ctrl)) { + set(UNIX__SET_DB, ctrl); + } + + /* auditing is a more sensitive version of debug */ + + if ( on(UNIX_AUDIT,ctrl) ) { + set(UNIX_DEBUG, ctrl); + } + + /* return the set of flags */ + + D(("done.")); + return ctrl; +} + +/* use this to free strings. ESPECIALLY password strings */ + +static char *_pam_delete(register char *xx) +{ + _pam_overwrite(xx); + _pam_drop(xx); + return NULL; +} + +static void _cleanup(pam_handle_t *pamh, void *x, int error_status) +{ + x = _pam_delete( (char *) x ); +} + +/* ************************************************************** * + * Useful non-trivial functions * + * ************************************************************** */ + +#include "pam_unix_md.-c" + +/* + * the following is used to keep track of the number of times a user fails + * to authenticate themself. + */ + +#define FAIL_PREFIX "-UN*X-FAIL-" +#define UNIX_MAX_RETRIES 3 + +struct _pam_failed_auth { + char *user; /* user that's failed to be authenticated */ + char *name; /* attempt from user with name */ + int id; /* uid of name'd user */ + int count; /* number of failures so far */ +}; + +#ifndef PAM_DATA_REPLACE +#error "Need to get an updated libpam 0.52 or better" +#endif + +static void _cleanup_failures(pam_handle_t *pamh, void *fl, int err) +{ + int quiet; + const char *service=NULL; + struct _pam_failed_auth *failure; + + D(("called")); + + quiet = err & PAM_DATA_SILENT; /* should we log something? */ + err &= PAM_DATA_REPLACE; /* are we just replacing data? */ + failure = (struct _pam_failed_auth *) fl; + + if ( failure != NULL ) { + + if ( !quiet && !err ) { /* under advisement from Sun,may go away */ + + /* log the number of authentication failures */ + if ( failure->count > 1 ) { + (void) pam_get_item(pamh, PAM_SERVICE + , (const void **)&service); + _log_err(LOG_NOTICE + , "%d more authentication failure%s; %s(uid=%d) -> " + "%s for %s service" + , failure->count-1, failure->count==2 ? "":"s" + , failure->name + , failure->id + , failure->user + , service == NULL ? "**unknown**":service + ); + if ( failure->count > UNIX_MAX_RETRIES ) { + _log_err(LOG_ALERT + , "service(%s) ignoring max retries; %d > %d" + , service == NULL ? "**unknown**":service + , failure->count + , UNIX_MAX_RETRIES ); + } + } + } + failure->user = _pam_delete(failure->user); /* tidy up */ + failure->name = _pam_delete(failure->name); /* tidy up */ + free(failure); + } +} + +/* + * verify the password of a user + */ + +#include <sys/types.h> +#include <sys/wait.h> + +static int pwdb_run_helper_binary(pam_handle_t *pamh, const char *passwd) +{ + int retval, child, fds[2]; + + D(("called.")); + /* create a pipe for the password */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + return PAM_AUTH_ERR; + } + + /* fork */ + child = fork(); + if (child == 0) { + static char *args[] = { NULL, NULL }; + static char *envp[] = { NULL }; + + /* XXX - should really tidy up PAM here too */ + while (pwdb_end() == PWDB_SUCCESS); + + /* reopen stdin as pipe */ + close(fds[1]); + dup2(fds[0], STDIN_FILENO); + + /* exec binary helper */ + args[0] = x_strdup(CHKPWD_HELPER); + execve(CHKPWD_HELPER, args, envp); + + /* should not get here: exit with error */ + D(("helper binary is not available")); + exit(PWDB_SUCCESS+1); + } else if (child > 0) { + /* wait for child */ + close(fds[0]); + if (passwd != NULL) { /* send the password to the child */ + write(fds[1], passwd, strlen(passwd)+1); + passwd = NULL; + } else { + write(fds[1], "", 1); /* blank password */ + } + close(fds[1]); + (void) waitpid(child, &retval, 0); /* wait for helper to complete */ + retval = (retval == PWDB_SUCCESS) ? PAM_SUCCESS:PAM_AUTH_ERR; + } else { + D(("fork failed")); + retval = PAM_AUTH_ERR; + } + + D(("returning %d", retval)); + return retval; +} + +static int _unix_verify_password(pam_handle_t *pamh, const char *name + , const char *p, unsigned int ctrl) +{ + const struct pwdb *pw=NULL; + const struct pwdb_entry *pwe=NULL; + + const char *salt; + char *pp; + char *data_name; + int retval; + int verify_result; + + D(("called")); + +#ifdef HAVE_PAM_FAIL_DELAY + if ( off(UNIX_NODELAY, ctrl) ) { + D(("setting delay")); + (void) pam_fail_delay(pamh, 1000000); /* 1 sec delay for on failure */ + } +#endif + + /* locate the entry for this user */ + + D(("locating user's record")); + retval = pwdb_locate("user", PWDB_DEFAULT, name, PWDB_ID_UNKNOWN, &pw); + if (retval == PWDB_PASS_PHRASE_REQD) { + /* + * give the password to the pwdb library. It may be needed to + * access the database + */ + + retval = pwdb_set_entry( pw, "pass_phrase", p, 1+strlen(p) + , NULL, NULL, 0); + if (retval != PWDB_SUCCESS) { + _log_err(LOG_ALERT, "find pass; %s", pwdb_strerror(retval)); + (void) pwdb_delete(&pw); + p = NULL; + return PAM_CRED_INSUFFICIENT; + } + + retval = pwdb_locate("user", pw->source, name, PWDB_ID_UNKNOWN, &pw); + } + + if (retval != PWDB_SUCCESS) { + D(("user's record unavailable")); + if ( on(UNIX_AUDIT, ctrl) ) { + /* this might be a typo and the user has given a password + instead of a username. Careful with this. */ + _log_err(LOG_ALERT, "check pass; user (%s) unknown", name); + } else { + _log_err(LOG_ALERT, "check pass; user unknown"); + } + (void) pwdb_delete(&pw); + p = NULL; + return PAM_USER_UNKNOWN; + } + + /* + * courtesy of PWDB the password for the user is stored in + * encrypted form in the "passwd" entry of pw. + */ + + retval = pwdb_get_entry(pw, "passwd", &pwe); + if (retval != PWDB_SUCCESS) { + if (geteuid()) { + /* we are not root perhaps this is the reason? Run helper */ + D(("running helper binary")); + retval = pwdb_run_helper_binary(pamh, p); + } else { + retval = PAM_AUTHINFO_UNAVAIL; + _log_err(LOG_ALERT, "get passwd; %s", pwdb_strerror(retval)); + } + (void) pwdb_delete(&pw); + p = NULL; + return retval; + } + salt = (const char *) pwe->value; + + /* + * XXX: Cristian, the above is not the case for RADIUS(?) Some + * lines should be added for RADIUS to verify the password in + * clear text... + */ + + data_name = (char *) malloc(sizeof(FAIL_PREFIX)+strlen(name)); + if ( data_name == NULL ) { + _log_err(LOG_CRIT, "no memory for data-name"); + } + strcpy(data_name, FAIL_PREFIX); + strcpy(data_name + sizeof(FAIL_PREFIX)-1, name); + + if ( !( (salt && *salt) || (p && *p) ) ) { + + D(("two null passwords to compare")); + + /* the stored password is NULL */ + pp = NULL; + if ( off(UNIX__NONULL, ctrl ) ) { /* this means we've succeeded */ + verify_result = PAM_SUCCESS; + } else { + verify_result = PAM_AUTH_ERR; + } + + } else if ( !( salt && p ) ) { + + D(("one of the two to compare are NULL")); + + pp = NULL; + verify_result = PAM_AUTH_ERR; + + } else { + + pp = _pam_md(p, salt); + + /* the moment of truth -- do we agree with the password? */ + D(("comparing state of pp[%s] and salt[%s]", pp, salt)); + + if ( strcmp( pp, salt ) == 0 ) { + verify_result = PAM_SUCCESS; + } else { + _pam_delete(pp); + pp = _pam_md_compat(p, salt); + if ( strcmp( pp, salt ) == 0 ) { + verify_result = PAM_SUCCESS; + } else { + verify_result = PAM_AUTH_ERR; + } + } + + p = NULL; /* no longer needed here */ + + } + + if ( verify_result == PAM_SUCCESS ) { + + retval = PAM_SUCCESS; + if (data_name) { /* reset failures */ + pam_set_data(pamh, data_name, NULL, _cleanup_failures); + } + + } else { + + retval = PAM_AUTH_ERR; + if (data_name != NULL) { + struct _pam_failed_auth *new=NULL; + const struct _pam_failed_auth *old=NULL; + + /* get a failure recorder */ + + new = (struct _pam_failed_auth *) + malloc(sizeof(struct _pam_failed_auth)); + + if (new != NULL) { + + new->user = x_strdup(name); + new->id = getuid(); + new->name = x_strdup(getlogin() ? getlogin():"" ); + + /* any previous failures for this user ? */ + pam_get_data(pamh, data_name, (const void **)&old ); + + if (old != NULL) { + new->count = old->count +1; + if (new->count >= UNIX_MAX_RETRIES) { + retval = PAM_MAXTRIES; + } + } else { + const char *service=NULL; + (void) pam_get_item(pamh, PAM_SERVICE + , (const void **)&service); + _log_err(LOG_NOTICE + , "authentication failure; %s(uid=%d) -> " + "%s for %s service" + , new->name + , new->id + , new->user + , service == NULL ? "**unknown**":service + ); + new->count = 1; + } + + pam_set_data(pamh, data_name, new, _cleanup_failures); + + } else { + _log_err(LOG_CRIT, "no memory for failure recorder"); + } + } + + } + + (void) pwdb_entry_delete(&pwe); + (void) pwdb_delete(&pw); + salt = NULL; + _pam_delete(data_name); + _pam_delete(pp); + + D(("done [%d].", retval)); + + return retval; +} + +/* + * this function obtains the name of the current user and ensures + * that the PAM_USER item is set to this value + */ + +static int _unix_get_user(pam_handle_t *pamh, unsigned int ctrl + , const char *prompt, const char **user) +{ + int retval; + + D(("called")); + + retval = pam_get_user(pamh, user, prompt); + if (retval != PAM_SUCCESS) { + D(("trouble reading username")); + return retval; + } + + /* + * Various libraries at various times have had bugs related to + * '+' or '-' as the first character of a user name. Don't take + * any chances here. Require that the username starts with an + * alphanumeric character. + */ + + if (*user == NULL || !isalnum(**user)) { + D(("bad username")); + if (on(UNIX_DEBUG,ctrl)) { + _log_err(LOG_ERR, "bad username [%s]", *user); + } + return PAM_USER_UNKNOWN; + } + + if (retval == PAM_SUCCESS && on(UNIX_DEBUG,ctrl)) { + _log_err(LOG_DEBUG, "username [%s] obtained", *user); + } + + return retval; +} + +/* + * _unix_blankpasswd() is a quick check for a blank password + * + * returns TRUE if user does not have a password + * - to avoid prompting for one in such cases (CG) + */ + +static int _unix_blankpasswd(unsigned int ctrl, const char *name) +{ + const struct pwdb *pw=NULL; + const struct pwdb_entry *pwe=NULL; + int retval; + + D(("called")); + + /* + * This function does not have to be too smart if something goes + * wrong, return FALSE and let this case to be treated somewhere + * else (CG) + */ + + if ( on(UNIX__NONULL, ctrl) ) + return 0; /* will fail but don't let on yet */ + + /* find the user's database entry */ + + retval = pwdb_locate("user", PWDB_DEFAULT, name, PWDB_ID_UNKNOWN, &pw); + if (retval != PWDB_SUCCESS || pw == NULL ) { + + retval = 0; + + } else { + + /* Does this user have a password? */ + + retval = pwdb_get_entry(pw, "passwd", &pwe); + if ( retval != PWDB_SUCCESS || pwe == NULL ) + retval = 0; + else if ( pwe->value == NULL || ((char *)pwe->value)[0] == '\0' ) + retval = 1; + else + retval = 0; + + } + + /* tidy up */ + + if ( pw ) { + (void) pwdb_delete(&pw); + if ( pwe ) + (void) pwdb_entry_delete(&pwe); + } + + return retval; +} + +/* + * obtain a password from the user + */ + +static int _unix_read_password( pam_handle_t *pamh + , unsigned int ctrl + , const char *comment + , const char *prompt1 + , const char *prompt2 + , const char *data_name + , const char **pass ) +{ + int authtok_flag; + int retval; + const char *item; + char *token; + + D(("called")); + + /* + * make sure nothing inappropriate gets returned + */ + + *pass = token = NULL; + + /* + * which authentication token are we getting? + */ + + authtok_flag = on(UNIX__OLD_PASSWD,ctrl) ? PAM_OLDAUTHTOK:PAM_AUTHTOK ; + + /* + * should we obtain the password from a PAM item ? + */ + + if ( on(UNIX_TRY_FIRST_PASS,ctrl) || on(UNIX_USE_FIRST_PASS,ctrl) ) { + retval = pam_get_item(pamh, authtok_flag, (const void **) &item); + if (retval != PAM_SUCCESS ) { + /* very strange. */ + _log_err(LOG_ALERT + , "pam_get_item returned error to unix-read-password" + ); + return retval; + } else if (item != NULL) { /* we have a password! */ + *pass = item; + item = NULL; + return PAM_SUCCESS; + } else if (on(UNIX_USE_FIRST_PASS,ctrl)) { + return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */ + } else if (on(UNIX_USE_AUTHTOK, ctrl) + && off(UNIX__OLD_PASSWD, ctrl)) { + return PAM_AUTHTOK_RECOVER_ERR; + } + } + + /* + * getting here implies we will have to get the password from the + * user directly. + */ + + { + struct pam_message msg[3],*pmsg[3]; + struct pam_response *resp; + int i, replies; + + /* prepare to converse */ + + if ( comment != NULL && off(UNIX__QUIET, ctrl) ) { + pmsg[0] = &msg[0]; + msg[0].msg_style = PAM_TEXT_INFO; + msg[0].msg = comment; + i = 1; + } else { + i = 0; + } + + pmsg[i] = &msg[i]; + msg[i].msg_style = PAM_PROMPT_ECHO_OFF; + msg[i++].msg = prompt1; + replies = 1; + + if ( prompt2 != NULL ) { + pmsg[i] = &msg[i]; + msg[i].msg_style = PAM_PROMPT_ECHO_OFF; + msg[i++].msg = prompt2; + ++replies; + } + + /* so call the conversation expecting i responses */ + resp = NULL; + retval = converse(pamh, ctrl, i, pmsg, &resp); + + if (resp != NULL) { + + /* interpret the response */ + + if (retval == PAM_SUCCESS) { /* a good conversation */ + + token = x_strdup(resp[i-replies].resp); + if (token != NULL) { + if (replies == 2) { + + /* verify that password entered correctly */ + if (!resp[i-1].resp + || strcmp(token,resp[i-1].resp)) { + token = _pam_delete(token); /* mistyped */ + retval = PAM_AUTHTOK_RECOVER_ERR; + make_remark(pamh, ctrl + , PAM_ERROR_MSG, MISTYPED_PASS); + } + } + + } else { + _log_err(LOG_NOTICE + , "could not recover authentication token"); + } + + } + + /* + * tidy up the conversation (resp_retcode) is ignored + * -- what is it for anyway? AGM + */ + + _pam_drop_reply(resp, i); + + } else { + retval = (retval == PAM_SUCCESS) + ? PAM_AUTHTOK_RECOVER_ERR:retval ; + } + } + + if (retval != PAM_SUCCESS) { + if ( on(UNIX_DEBUG,ctrl) ) + _log_err(LOG_DEBUG,"unable to obtain a password"); + return retval; + } + + /* 'token' is the entered password */ + + if ( off(UNIX_NOT_SET_PASS, ctrl) ) { + + /* we store this password as an item */ + + retval = pam_set_item(pamh, authtok_flag, token); + token = _pam_delete(token); /* clean it up */ + if ( retval != PAM_SUCCESS + || (retval = pam_get_item(pamh, authtok_flag + , (const void **)&item)) + != PAM_SUCCESS ) { + + _log_err(LOG_CRIT, "error manipulating password"); + return retval; + + } + + } else { + /* + * then store it as data specific to this module. pam_end() + * will arrange to clean it up. + */ + + retval = pam_set_data(pamh, data_name, (void *) token, _cleanup); + if (retval != PAM_SUCCESS) { + _log_err(LOG_CRIT, "error manipulating password data [%s]" + , pam_strerror(pamh, retval) ); + token = _pam_delete(token); + return retval; + } + item = token; + token = NULL; /* break link to password */ + } + + *pass = item; + item = NULL; /* break link to password */ + + return PAM_SUCCESS; +} + +static int _pam_unix_approve_pass(pam_handle_t *pamh + , unsigned int ctrl + , const char *pass_old + , const char *pass_new) +{ + D(("&new=%p, &old=%p",pass_old,pass_new)); + D(("new=[%s]",pass_new)); + D(("old=[%s]",pass_old)); + + if (pass_new == NULL || (pass_old && !strcmp(pass_old,pass_new))) { + if ( on(UNIX_DEBUG, ctrl) ) { + _log_err(LOG_DEBUG, "bad authentication token"); + } + make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ? + "No password supplied":"Password unchanged" ); + return PAM_AUTHTOK_ERR; + } + + /* + * if one wanted to hardwire authentication token strength + * checking this would be the place - AGM + */ + + return PAM_SUCCESS; +} + +/* ****************************************************************** * + * Copyright (c) Andrew G. Morgan 1996-8. + * Copyright (c) Alex O. Yuriev, 1996. + * Copyright (c) Cristian Gafton 1996. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + |