summaryrefslogtreecommitdiff
path: root/modules/pam_pwdb
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2000-06-20 22:10:38 +0000
committerAndrew G. Morgan <morgan@kernel.org>2000-06-20 22:10:38 +0000
commitea488580c42e8918445a945484de3c8a5addc761 (patch)
treec992f3ba699caafedfadc16af38e6359c3c24698 /modules/pam_pwdb
Initial revision
Diffstat (limited to 'modules/pam_pwdb')
-rw-r--r--modules/pam_pwdb/.cvsignore2
-rw-r--r--modules/pam_pwdb/BUGS14
-rw-r--r--modules/pam_pwdb/CHANGELOG10
-rw-r--r--modules/pam_pwdb/Makefile193
-rw-r--r--modules/pam_pwdb/README41
-rw-r--r--modules/pam_pwdb/TODO34
-rw-r--r--modules/pam_pwdb/bigcrypt.-c114
-rw-r--r--modules/pam_pwdb/md5.c270
-rw-r--r--modules/pam_pwdb/md5.h34
-rw-r--r--modules/pam_pwdb/md5_crypt.c153
-rw-r--r--modules/pam_pwdb/pam_pwdb.c256
-rw-r--r--modules/pam_pwdb/pam_unix_acct.-c298
-rw-r--r--modules/pam_pwdb/pam_unix_auth.-c131
-rw-r--r--modules/pam_pwdb/pam_unix_md.-c73
-rw-r--r--modules/pam_pwdb/pam_unix_passwd.-c404
-rw-r--r--modules/pam_pwdb/pam_unix_pwupd.-c260
-rw-r--r--modules/pam_pwdb/pam_unix_sess.-c118
-rw-r--r--modules/pam_pwdb/pwdb_chkpwd.c204
-rw-r--r--modules/pam_pwdb/support.-c938
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.
+ */
+