This patch adds --with-openssl to Shadow, and currently only supports DES and MD5. lib/openssl-md5crypt.c should probably use OpenSSL's EVP_DigestInit. I made this patch by example, and I have no examples for sha512. I hope that this patch can be taken over by someone who know what they're doing, so more algorithms can be added. This patch is fairly conservative and I believe it is safe to use. robert @ lfs Index: configure.in =================================================================== --- configure.in (revision 2270) +++ configure.in (working copy) @@ -235,7 +235,14 @@ AC_ARG_WITH(sha-crypt, [AC_HELP_STRING([--with-sha-crypt], [allow the SHA256 and SHA512 password encryption algorithms @<:@default=yes@:>@])], [with_sha_crypt=$withval], [with_sha_crypt=yes]) +AC_ARG_WITH(openssl, + [AC_HELP_STRING([--with-openssl], [use OpenSSL libcrypto instead of libcrypt. This option disables --with-sha-crypt. Currently only MD5 and DES work. @<:@default=no@:>@])], + [with_openssl=$withval], [with_openssl=no]) +if test "x$with_openssl" = "xyes"; then + with_sha_crypt="no" +fi + AM_CONDITIONAL(USE_SHA_CRYPT, test "x$with_sha_crypt" = "xyes") if test "$with_sha_crypt" = "yes"; then AC_DEFINE(USE_SHA_CRYPT, 1, [Define to allow the SHA256 and SHA512 password encryption algorithms]) @@ -272,8 +279,17 @@ AM_CONDITIONAL(ENABLE_REGENERATE_MAN, test "x$enable_man" != "xno") AC_SUBST(LIBCRYPT) -AC_CHECK_LIB(crypt, crypt, [LIBCRYPT=-lcrypt], - [AC_MSG_ERROR([crypt() not found])]) +if test "x$with_openssl" = "xyes"; then + AC_CHECK_LIB(crypto, DES_crypt, + [LIBCRYPT=-lcrypto AC_DEFINE(USE_OPENSSL, 1, [Defined to use OpenSSL.])], + [AC_MSG_ERROR([DES_crypt() not found])]) + AC_CHECK_HEADER([openssl/des.h],,[AC_MSG_ERROR([openssl/des.h not found])]) + AC_CHECK_HEADER([openssl/md5.h],,[AC_MSG_ERROR([openssl/md5.h not found])]) + AC_CHECK_HEADER([openssl/buffer.h],,[AC_MSG_ERROR([openssl/buffer.h not found])]) + else + AC_CHECK_LIB(crypt, crypt, [LIBCRYPT=-lcrypt], + [AC_MSG_ERROR([crypt() not found])]) +fi AC_SUBST(LIBAUDIT) if test "$with_audit" != "no"; then @@ -457,4 +473,5 @@ echo " shadow group support: $enable_shadowgrp" echo " S/Key support: $with_skey" echo " SHA passwords encryption: $with_sha_crypt" +echo " OpenSSL support: $with_openssl" echo Index: lib/defines.h =================================================================== --- lib/defines.h (revision 2270) +++ lib/defines.h (working copy) @@ -350,4 +350,10 @@ # define unused #endif +#ifdef USE_OPENSSL +# define CRYPT openssl_crypt +#else +# define CRYPT crypt +#endif + #endif /* _DEFINES_H_ */ Index: lib/encrypt.c =================================================================== --- lib/encrypt.c (revision 2270) +++ lib/encrypt.c (working copy) @@ -45,7 +45,7 @@ static char cipher[128]; char *cp; - cp = crypt (clear, salt); + cp = CRYPT (clear, salt); if (!cp) { /* * Single Unix Spec: crypt() may return a null pointer, Index: lib/openssl-md5crypt.c =================================================================== --- lib/openssl-md5crypt.c (revision 0) +++ lib/openssl-md5crypt.c (revision 0) @@ -0,0 +1,153 @@ +/* + * ---------------------------------------------------------------------------- + * "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 + * ---------------------------------------------------------------------------- + * + * from NetBSD: md5crypt.c,v 1.9 2007/01/17 23:24:22 hubertf Exp + * via FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp + * via OpenBSD: md5crypt.c,v 1.9 1997/07/23 20:58:27 kstailey Exp + * + */ + +#include + +#ifdef USE_OPENSSL + +#include +#include +#include +#include +#include /* For BUF_strlcat/strlcpy */ + +#include "prototypes.h" +#include "defines.h" + +#define MD5_MAGIC "$1$" +#define MD5_MAGIC_LEN 3 + +static const unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +void +__crypt_to64(char *s, u_int32_t v, int n) +{ + + while (--n >= 0) { + *s++ = itoa64[v & 0x3f]; + v >>= 6; + } +} + +/* + * MD5 password encryption. + */ +char * +openssl_md5crypt(const char *pw, const char *salt) +{ + static char passwd[120], *p; + const char *sp, *ep; + unsigned char final[MD5_DIGEST_LENGTH]; + unsigned int i, sl, pwl; + MD5_CTX ctx, ctx1; + u_int32_t l; + int pl; + + pwl = strlen(pw); + + /* Refine the salt first */ + sp = salt; + + /* If it starts with the magic string, then skip that */ + if (strncmp(sp, MD5_MAGIC, MD5_MAGIC_LEN) == 0) + sp += MD5_MAGIC_LEN; + + /* It stops at the first '$', max 8 chars */ + for (ep = sp; *ep != '\0' && *ep != '$' && ep < (sp + 8); ep++) + continue; + + /* get the length of the true salt */ + sl = ep - sp; + + MD5_Init(&ctx); + + /* The password first, since that is what is most unknown */ + MD5_Update(&ctx, (const unsigned char *)pw, pwl); + + /* Then our magic string */ + MD5_Update(&ctx, (const unsigned char *)MD5_MAGIC, MD5_MAGIC_LEN); + + /* Then the raw salt */ + MD5_Update(&ctx, (const unsigned char *)sp, sl); + + /* Then just as many characters of the MD5(pw,salt,pw) */ + MD5_Init(&ctx1); + MD5_Update(&ctx1, (const unsigned char *)pw, pwl); + MD5_Update(&ctx1, (const unsigned char *)sp, sl); + MD5_Update(&ctx1, (const unsigned char *)pw, pwl); + MD5_Final(final, &ctx1); + + for (pl = pwl; pl > 0; pl -= 16) + MD5_Update(&ctx, final, (unsigned int)(pl > 16 ? 16 : pl)); + + /* Don't leave anything around in vm they could use. */ + memset(final, 0, sizeof(final)); + + /* Then something really weird... */ + for (i = pwl; i != 0; i >>= 1) + if ((i & 1) != 0) + MD5_Update(&ctx, final, 1); + else + MD5_Update(&ctx, (const unsigned char *)pw, 1); + + /* Now make the output string */ + memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN); + BUF_strlcpy(passwd + MD5_MAGIC_LEN, sp, sl + 1); + BUF_strlcat(passwd, "$", sizeof(passwd)); + + MD5_Final(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++) { + MD5_Init(&ctx1); + + if ((i & 1) != 0) + MD5_Update(&ctx1, (const unsigned char *)pw, pwl); + else + MD5_Update(&ctx1, final, 16); + + if ((i % 3) != 0) + MD5_Update(&ctx1, (const unsigned char *)sp, sl); + + if ((i % 7) != 0) + MD5_Update(&ctx1, (const unsigned char *)pw, pwl); + + if ((i & 1) != 0) + MD5_Update(&ctx1, final, 16); + else + MD5_Update(&ctx1, (const unsigned char *)pw, pwl); + + MD5_Final(final, &ctx1); + } + + p = passwd + sl + MD5_MAGIC_LEN + 1; + + l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; __crypt_to64(p,l,4); p += 4; + l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; __crypt_to64(p,l,4); p += 4; + l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; __crypt_to64(p,l,4); p += 4; + l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; __crypt_to64(p,l,4); p += 4; + l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; __crypt_to64(p,l,4); p += 4; + l = final[11] ; __crypt_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); +} +#endif /* USE_OPENSSL */ Index: lib/openssl-crypt.c =================================================================== --- lib/openssl-crypt.c (revision 0) +++ lib/openssl-crypt.c (revision 0) @@ -0,0 +1,37 @@ +/* If I wrote this it would be public domain, but I didn't. - robert */ + +#include + +#ifdef USE_OPENSSL + +#include +#include + +#include "prototypes.h" +#include "defines.h" + +/* From Glibc-2.7 crypt/crypt-entry.c */ +static const char md5_salt_prefix[] = "$1$"; +#if 0 +static const char sha256_salt_prefix[] = "$5$"; +static const char sha512_salt_prefix[] = "$6$"; +#endif + +char * +openssl_crypt (const char *key, const char *salt) +{ +if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0) + return (char *)openssl_md5crypt (key, salt); + +#if 0 +if (strncmp (sha256_salt_prefix, salt, sizeof (sha256_salt_prefix) - 1) == 0) + return (char *)openssl_sha256crypt (key, salt); + +if (strncmp (sha512_salt_prefix, salt, sizeof (sha512_salt_prefix) - 1) == 0) + return (char *)openssl_sha512crypt (key, salt); +#endif + +/* Else, default to DES. */ +return (char *)DES_crypt (key, salt); +} +#endif /* USE_OPENSSL */ Index: lib/Makefile.am =================================================================== --- lib/Makefile.am (revision 2270) +++ lib/Makefile.am (working copy) @@ -24,6 +24,8 @@ lockpw.c \ nscd.c \ nscd.h \ + openssl-crypt.c \ + openssl-md5crypt.c \ pam_defs.h \ port.c \ port.h \ Index: lib/prototypes.h =================================================================== --- lib/prototypes.h (revision 2270) +++ lib/prototypes.h (working copy) @@ -285,4 +285,5 @@ /* yesno.c */ extern bool yes_or_no (bool read_only); +extern char * openssl_crypt (const char *key, const char *salt); #endif /* _PROTOTYPES_H */