pax_global_header00006660000000000000000000000064122220305150014502gustar00rootroot0000000000000052 comment=8f0e412b48178c00abd023917dd2c9050ee89c18 libpam-pwdfile-1.0/000077500000000000000000000000001222203051500142365ustar00rootroot00000000000000libpam-pwdfile-1.0/INSTALL000066400000000000000000000004261222203051500152710ustar00rootroot00000000000000* install needed packages (if not installed already): * make * C compiler (e.g. gcc or clang) * libc development headers (package libc6-dev on Debian, glibc-headers on Hat) * PAM development headers (package libpam-dev on Debian, pam-devel on Hat) * make * make install libpam-pwdfile-1.0/Makefile000066400000000000000000000016571222203051500157070ustar00rootroot00000000000000PAM_LIB_DIR ?= /lib/security INSTALL ?= install CFLAGS ?= -O2 -g -Wall -Wformat-security CPPFLAGS += -DUSE_CRYPT_R CFLAGS += -fPIC -fvisibility=hidden LDFLAGS += -Wl,-x -shared TITLE = pam_pwdfile LIBSHARED = $(TITLE).so LDLIBS = -lcrypt -lpam LIBOBJ = $(TITLE).o md5_broken.o md5_crypt_broken.o bigcrypt.o CPPFLAGS_MD5_BROKEN = -DHIGHFIRST -D'MD5Name(x)=Broken\#\#x' all: $(LIBSHARED) $(LIBSHARED): $(LIBOBJ) $(CC) $(LDFLAGS) $(LIBOBJ) $(LDLIBS) -o $@ md5_broken.o: md5.c $(CC) -c $(CPPFLAGS) $(CPPFLAGS_MD5_BROKEN) $(CFLAGS) $< -o $@ md5_crypt_broken.o: md5_crypt.c $(CC) -c $(CPPFLAGS) $(CPPFLAGS_MD5_BROKEN) $(CFLAGS) $< -o $@ install: $(LIBSHARED) $(INSTALL) -m 0755 -d $(DESTDIR)$(PAM_LIB_DIR) $(INSTALL) -m 0755 $(LIBSHARED) $(DESTDIR)$(PAM_LIB_DIR) clean: $(RM) *.o *.so changelog-from-git: changelog { git log --decorate $(shell head -1 changelog | cut -d\ -f2).. | vipe; echo; cat changelog; } | sponge changelog libpam-pwdfile-1.0/README000066400000000000000000000041761222203051500151260ustar00rootroot00000000000000This pam module provides the authentication service using an own set of user/password pairs. CONFIGURATION ============= simple PAM config ----------------- Just add/change the config file for service to contain the line: auth required pam_pwdfile.so pwdfile=/path/to/passwd_file If your service does more with PAM than auth there will be a fallback to the service "other". If that is not what you want, you can use pam_permit.so or pam_deny.so for that: account required pam_permit.so session required pam_permit.so password required pam_deny.so options ------- * pwdfile= * debug: produce a bit of debug output * nodelay: don't tell the PAM stack to cause a delay on auth failure * flock: use a shared (read) advisory lock on pwdfile, you should better move new versions into place instead * legacy_crypt: see section LEGACY CRYPT PASSWORD FILE ============= The password file basically looks like passwd(5): one line for each user with two or more colon-separated fields. First field contains the username, the second the crypt()ed password. Other fields are optional. crypt()ed passwords in various formats can be generated with mkpasswd from the whois package. LEGACY CRYPT ============ There are two crypt types that are disabled by default: bigcrypt and broken md5_crypt. They are disabled because they use static buffers which is bad when doing PAM authentication using this module in a multithreaded server. All the other crypt types are checked via the systems crypt_r function if available, else with the normal crypt function and the same static-buffer-problem. bigcrypt was used on DEC systems to allow for longer passwords. You can check if your passwd file contains any of these with `cut -d: -f2 passwd-file | egrep '^[^$].{13}'`. Broken md5_crypt is a speciality of big-endian systems. An early implementation of md5_crypt got the byte order wrong here and produced different crypt outputs. You might have some of these crypt hashes in your passwd file only if you created them on a big-endian system. If an md5_crypt hash also worked on a little-endian system (up to and including libpam-pwdfile 0.99) it isn't broken md5_crypt. libpam-pwdfile-1.0/bigcrypt.c000066400000000000000000000045061222203051500162320ustar00rootroot00000000000000/* * 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 */ #define _XOPEN_SOURCE 700 #include #include #include "bigcrypt.h" /* * 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_SEGMENTS 16 #define SEGMENT_SIZE 8 #define SALT_SIZE 2 #define ESEGMENT_SIZE 11 char *bigcrypt(char const * key, char const * salt) { static char outbuf[MAX_SEGMENTS * ESEGMENT_SIZE + SALT_SIZE + 1]; /* static storage area */ unsigned char n_seg, seg; char * outptr; /* ensure NUL-termination */ memset(outbuf, 0, sizeof(outbuf)); if (strlen(salt) == (SALT_SIZE + ESEGMENT_SIZE)) /* conventional crypt */ n_seg = 1; else if (key[0] == '\0') n_seg = 1; else n_seg = (strnlen(key, MAX_SEGMENTS * SEGMENT_SIZE) + SEGMENT_SIZE - 1) / SEGMENT_SIZE; /* first block is special and just traditional crypt() */ outptr = outbuf; strncpy(outptr, crypt(key, salt), SALT_SIZE + ESEGMENT_SIZE); for (seg = 1, outptr += SALT_SIZE; seg < n_seg; ++seg) { /* subsequent blocks use the previous output block for salt input */ salt = outptr; key += SEGMENT_SIZE; outptr += ESEGMENT_SIZE; /* and omit the salt on output */ strncpy(outptr, crypt(key, salt) + SALT_SIZE, ESEGMENT_SIZE); } return outbuf; } libpam-pwdfile-1.0/bigcrypt.h000066400000000000000000000000721222203051500162310ustar00rootroot00000000000000extern char *bigcrypt(const char *key, const char *salt); libpam-pwdfile-1.0/changelog000066400000000000000000000204131222203051500161100ustar00rootroot00000000000000commit 6946f4bd3102d677d9ce43d4c48ed6bec2b13a31 Author: Timo Weingärtner Date: 2013-09-29 15:50:21 +0200 update copyright also fix spelling error "GNU Public License" commit 183200793e5aec74f73b21bd96d46b447da64f11 Author: Timo Weingärtner Date: 2013-09-28 16:35:58 +0200 Makefile: add target changelog-from-git needs moreutils commit 5e5588cd1edfbab1889537b93ae13b159f76caff Author: Timo Weingärtner Date: 2013-09-26 16:29:26 +0200 README: describe legacy_crypt in more detail commit 235369330e1c1366e5c7cb31561cdb673703ad58 Author: Timo Weingärtner Date: 2013-09-26 16:29:11 +0200 fix typo in README commit 090a8585370c7857a193467fc057828b82ec358f Author: Timo Weingärtner Date: 2013-06-01 00:15:00 +0200 move opening brace out of ifdef to make code folding work commit 4964a685e75c75d662bb0d4f1bb3fd126f49eca2 Author: Timo Weingärtner Date: 2013-06-01 00:14:07 +0200 don't include features.h commit 201e799f7a2fcba3af2c9214f25545460ef9b08a (tag: v0.100) Author: Timo Weingärtner Date: 2013-05-27 21:10:18 +0200 rework documentation remove stuff in contrib, it is outdated or explained in the service's documentation commit da52bf9630a077d90e1338d818a3e179367058c4 Author: Timo Weingärtner Date: 2013-05-27 21:08:02 +0200 separate DESTDIR and PAM_LIB_DIR commit e493c1467bbaebfbaf2a9a6b1da3398b76232ce5 Author: Timo Weingärtner Date: 2013-05-14 20:22:36 +0200 remove CVS $Id line and static version number commit caea065f12f3d358948cd0ca760ebd7c27cb6c80 Author: Timo Weingärtner Date: 2013-05-14 20:08:06 +0200 overhaul bigcrypt.c * drop unnessesary variables * rename variables and define's to be more desciptive * rotate pointer updates to front of loop * don't copy key there was no point in using crypt_r() here, we return our result in a static buffer ourselves commit 495461432ca4034d49ee37cb398c6bd253d6f66d Author: Timo Weingärtner Date: 2013-05-11 19:34:44 +0200 md5.c: fix compiler warnings commit be53f76279d158aa3e5fb2960f9ae4da52201857 Author: Timo Weingärtner Date: 2013-05-11 01:43:50 +0200 replace self-defined uint32 with uint32_t from stdint.h unsigned int is not guaranteed to have 32 bits commit ce9367b3202477b3cc914cabfe0cb2a856f3a51d Author: Timo Weingärtner Date: 2013-05-10 21:30:05 +0200 major overhaul * merge fgetpwnam into pam_sm_authenticate * handle empty password field * fix a fd and memory leak if pwdfile opening succeeds but locking fails * use crypt_r (enabled via USE_CRYPT_R) * rely on crypt() to handle newer crypt variants (including "good" md5 crypt) * make bigcrypt and broken md5 crypt optional * add some const's commit 88dd2b1a22cd06fc401a8ddadd41114cebe159d5 Author: Timo Weingärtner Date: 2013-05-10 21:27:56 +0200 include proper headers for crypt() this also prepares for crypt_r() commit 138c589dd4cdf68659bfa643e5659fa1200f6081 Author: Timo Weingärtner Date: 2013-04-29 13:06:26 +0200 rework pwdfile reading * drop rewind(), we read the file just once * use getline() to get rid of the fixed-size buffer * let strsep() also handle the newline * stop at the first line containing the user instead of using the last commit 0437f4656f1d5a541b4ab951c457fae19f8deee4 Author: Timo Weingärtner Date: 2013-04-28 17:15:15 +0200 use pam_get_authtok this also gets it right with use_first_pass also use the default prompt for the username commit 86c95423b2908869ee42f9f40896a0bb0b773cf4 Author: Timo Weingärtner Date: 2013-04-27 23:55:20 +0200 rework argument parsing * don't copy pwdfile argument, we don't need to modify it * replace sizeof() with strlen() as that is easier to understand and the compiler can also optimize it away * expand DEFINE's so we can get rid of the comments commit 64707e82165bb32db5763b38bf550b538bcd4eec Author: Timo Weingärtner Date: 2013-04-27 18:07:22 +0200 make Brokencrypt_md5 also broken on little-endian otherwise broken hashes from big-endian systems won't work also remove ASM_MD5 #ifndef's, we don't have assembler code here commit 629c03d7775e1f4b5c0fdee358c6773f70e91961 Author: Timo Weingärtner Date: 2013-04-26 12:57:56 +0200 add debug module option and use pam_syslog also: * remove some unnessesary comments * add vim settings for unusual indentation commit fbce1a480fda4c97b21c87fb39096d23db6eedfb Author: Timo Weingärtner Date: 2013-04-25 14:27:07 +0200 apply visibility patch by Peter Palfrader commit 0148de59cdcea4013d694fc04db3174ce06c60b1 Author: Timo Weingärtner Date: 2013-04-25 14:22:49 +0200 rework Makefile for standalone building if this is ever integrated into pam it will be autotools anyways use ?= and += to better work with distribution's build systems commit 5dbeed06ae0b0f168158920c59dcfb0cc822dee6 Author: Charl Botha Date: 2009-08-16 19:57:51 +0000 Added note about contrib directory. ===================================== old (pre-git) changelog: 0.99 : Sat Dec 20 20:30:37 CET 2003 * added micro howto by Warwick Duncan (in contrib/ dir) explaining how to setup Cyrus IMAPD + pam_pwdfile so that one does not have to create system accounts for imapd users * added Makefile.standalone by Gerald Richter to the contrib files. * integrated patch by Greg Norris for better handling of bigcrypt/crypt switcheroos. Greg Norris in the current Debian maintainer. * The 't' key on my home linux workstation is going. 0.98 : Mon Jun 10 23:49:46 CEST 2002 * added contrib directory with spec file and Makefile by Jason F. McBrayer . * -lcrypt is now used again (because it is necessary in some cases, doh); Problem reported by Charles Vejnar * That damn Mozilla beat us to 1.0! 0.97 : Sat May 11 16:40:19 CEST 2002 * added md5.h, md5.c, md5_crypt.c and bigcrypt.c from the pam_unix.c module. This fixes the bug where crypt() would not be able to do md5 crypting if an SSL library was linked into the calling application. Several users reported this, but the report (and possible patch, which I chose not to use, opting instead to emulate pam_unix) of Yu Guanghui was most useful. * Small tweaks to documentation. 0.95 : Sat Jul 14 22:38:16 CEST 2001 * added features.h, _BSD_SOURCE now defined by Makefile (for vsyslog a.o.) * integrated patch by Ethan Benson that enables a two second PAM delay when an incorrect password is supplied. * yes, if this version floats for long enough, we can do the 1.0 thing. 0.9: Fri Jun 15 23:23:31 CEST 2001 * integrated patch by Stephen Darragh to fix problems with short MD5 crypts and trailing newlines. In fact, his patch just makes things a tad more correct, short MD5 crypts or not. 0.8: Sat May 12 11:57:18 CEST 2001 * the MrKen (aka MJ Turner) release. Removed unnecessary include that was breaking the debian builds. 0.7: Tue Apr 17 23:13:34 CEST 2001 * changed error message if user is not found in password file * updated to work with > Linux-PAM-0.75 (thanks to Kelly Corbin for reporting the problem) 0.6: Sat Nov 11 23:51:32 CET 2000 * fixed a stupid bug I introduced when integrating Warwick's patch; tested fine with both DES and MD5 crypts 0.5: Wed Nov 8 01:39:22 CET 2000 * added patch by Warwick Duncan to support MD5 crypted passwords as well 0.4: Tue Aug 29 09:23:23 SAST 2000 * fixed typo in INSTALL (thanks to Quynh Nguyen Anh ) 0.3: Mon Aug 28 09:49:27 SAST 2000 * INSTALL file created. This explains how to get pam_pwdfile compiled from scratch. 0.2: Mon May 22 22:41:30 SAST 2000 * integrated patch by Jacob Schroeder to implement flock()-based password file locking * minor code tweaks (indentation, unnecessary variables removed) * minor README file updates * this file 0.1: ? * Initial release libpam-pwdfile-1.0/md5.c000066400000000000000000000166671222203051500151070ustar00rootroot00000000000000/* * 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. * */ #include #include #include "md5.h" #ifndef HIGHFIRST #define byteReverse(buf, len) /* Nothing */ #else static void byteReverse(unsigned char *buf, unsigned longs) { for (; longs; --longs, buf +=4) *((uint32_t *) buf) = bswap_32(*((uint32_t *) buf)); } #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 t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((uint32_t) 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_t *) 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_t *) 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_t *) 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 */ memcpy(ctx->in + 56, ctx->bits, 8); MD5Name(MD5Transform)(ctx->buf, (uint32_t *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); memcpy(digest, ctx->buf, 16); memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* 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<>(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_t buf[4], uint32_t const in[16]) { register uint32_t 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; } libpam-pwdfile-1.0/md5.h000066400000000000000000000015501222203051500150750ustar00rootroot00000000000000 #ifndef MD5_H #define MD5_H #include struct MD5Context { uint32_t buf[4]; uint32_t 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_t buf[4], uint32_t 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_t buf[4], uint32_t 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 */ libpam-pwdfile-1.0/md5_crypt.c000066400000000000000000000077341222203051500163230ustar00rootroot00000000000000/* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * 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 * */ #include #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; } libpam-pwdfile-1.0/pam_pwdfile.c000066400000000000000000000171411222203051500166750ustar00rootroot00000000000000/* * pam authentication module that can be pointed at any username/crypted * text file so that pam using application can use an alternate set of * passwords than specified in system password database * * Copyright (c) 1999-2003 Charl P. Botha * Copyright (c) 2012-2013 Timo Weingärtner * * 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 General 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. */ #ifdef USE_CRYPT_R #define _GNU_SOURCE #include #else #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 700 #endif #ifndef _BSD_SOURCE #define _BSD_SOURCE #endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #define PAM_SM_AUTH #include #include #include "md5.h" #include "bigcrypt.h" static int lock_fd(int fd) { int delay; for (delay = 5; delay <= 40; delay *= 2) { if (flock(fd, LOCK_SH | LOCK_NB) == -1) { /* failed */ if (errno != EWOULDBLOCK) goto failed; sleep(delay); }else{ return 0; } } if (flock(fd, LOCK_SH | LOCK_NB) != -1) return 0; failed: return -1; } /* expected hook for auth service */ __attribute__((visibility("default"))) PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { int i; const char *name; char const * password; char const * pwdfilename = NULL; char const * stored_crypted_password = NULL; char const * crypted_password; FILE *pwdfile; int use_flock = 0; int use_delay = 1; int legacy_crypt = 0; int debug = 0; char * linebuf = NULL; size_t linebuflen; #ifdef USE_CRYPT_R struct crypt_data crypt_buf; #endif /* we require the pwdfile switch and argument to be present, else we don't work */ for (i = 0; i < argc; ++i) { if (!strcmp(argv[i], "pwdfile") && i + 1 < argc) pwdfilename = argv[++i]; else if (!strncmp(argv[i], "pwdfile=", strlen("pwdfile="))) pwdfilename = argv[i] + strlen("pwdfile="); else if (!strcmp(argv[i], "flock")) use_flock = 1; else if (!strcmp(argv[i], "noflock")) use_flock = 0; else if (!strcmp(argv[i], "nodelay")) use_delay = 0; else if (!strcmp(argv[i], "debug")) debug = 1; else if (!strcmp(argv[i], "legacy_crypt")) legacy_crypt = 1; } #ifdef HAVE_PAM_FAIL_DELAY if (use_delay) { if (debug) pam_syslog(pamh, LOG_DEBUG, "setting fail delay"); (void) pam_fail_delay(pamh, 2000000); /* 2 sec */ } #endif if (!pwdfilename) { pam_syslog(pamh, LOG_ERR, "password file name not specified"); return PAM_AUTHINFO_UNAVAIL; } if (pam_get_user(pamh, &name, NULL) != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "couldn't get username from PAM stack"); return PAM_AUTH_ERR; } if (debug) pam_syslog(pamh, LOG_DEBUG, "username is %s", name); if (!(pwdfile = fopen(pwdfilename, "r"))) { pam_syslog(pamh, LOG_ALERT, "couldn't open password file %s", pwdfilename); return PAM_AUTHINFO_UNAVAIL; } if (use_flock && lock_fd(fileno(pwdfile)) == -1) { pam_syslog(pamh, LOG_ALERT, "couldn't lock password file %s", pwdfilename); fclose(pwdfile); return PAM_AUTHINFO_UNAVAIL; } /* get the crypted password corresponding to this user out of pwdfile */ while (getline(&linebuf, &linebuflen, pwdfile) > 0) { /* strsep changes its argument, make a copy */ char * nexttok = linebuf; /* first field: username */ char * curtok = strsep(&nexttok, ":"); /* skip non-matching usernames */ if (strcmp(curtok, name)) continue; /* second field: password (until next colon or newline) */ if ((curtok = strsep(&nexttok, ":\n"))) { stored_crypted_password = curtok; break; } } fclose(pwdfile); /* we keep linebuf (allocated by getline), stored_crypted_password is pointing into it */ if (!stored_crypted_password) if (debug) pam_syslog(pamh, LOG_ERR, "user not found in password database"); if (stored_crypted_password && !strlen(stored_crypted_password)) { if (debug) pam_syslog(pamh, LOG_DEBUG, "user has empty password field"); free(linebuf); return flags & PAM_DISALLOW_NULL_AUTHTOK ? PAM_AUTH_ERR : PAM_SUCCESS; } if (pam_get_authtok(pamh, PAM_AUTHTOK, &password, NULL) != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "couldn't get password from PAM stack"); free(linebuf); return PAM_AUTH_ERR; } if (!stored_crypted_password) { free(linebuf); return PAM_USER_UNKNOWN; } if (debug) pam_syslog(pamh, LOG_DEBUG, "got crypted password == '%s'", stored_crypted_password); #ifdef USE_CRYPT_R crypt_buf.initialized = 0; if (!(crypted_password = crypt_r(password, stored_crypted_password, &crypt_buf))) #else if (!(crypted_password = crypt(password, stored_crypted_password))) #endif { pam_syslog(pamh, LOG_ERR, "crypt() failed"); free(linebuf); return PAM_AUTH_ERR; } if (legacy_crypt && strcmp(crypted_password, stored_crypted_password)) { if (!strncmp(stored_crypted_password, "$1$", 3)) crypted_password = Brokencrypt_md5(password, stored_crypted_password); else crypted_password = bigcrypt(password, stored_crypted_password); } if (strcmp(crypted_password, stored_crypted_password)) { pam_syslog(pamh, LOG_NOTICE, "wrong password for user %s", name); free(linebuf); return PAM_AUTH_ERR; } if (debug) pam_syslog(pamh, LOG_DEBUG, "passwords match"); free(linebuf); return PAM_SUCCESS; } /* another expected hook */ __attribute__((visibility("default"))) PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; } #ifdef PAM_STATIC struct pam_module _pam_listfile_modstruct = { "pam_pwdfile", pam_sm_authenticate, pam_sm_setcred, NULL, NULL, NULL, NULL, }; #endif /* vim:set ts=8 sw=4: */