/* * This file is derived from OpenLDAP Software. All of the modifications to * OpenLDAP Software represented in the following file were developed by * Devin J. Pohly . I have not assigned rights and/or * interest in this work to any party. * * The extensions to OpenLDAP Software herein are subject to the following * notice: * * Copyright 2011 Devin J. Pohly * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP Public * License. * * A portion of this code is used in accordance with the Beer-ware License, * revision 42, as noted. */ #include #include #include "lutil.h" #include "lutil_md5.h" #include #include static LUTIL_PASSWD_CHK_FUNC chk_apr1; static LUTIL_PASSWD_HASH_FUNC hash_apr1; static const struct berval scheme = BER_BVC("{APR1}"); static const unsigned char apr64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; #define APR_SALT_SIZE 8 /* copied from liblutil/passwd.c */ static int pw_string64( const struct berval *sc, const struct berval *hash, struct berval *b64, const struct berval *salt ) { int rc; struct berval string; size_t b64len; if( salt ) { /* need to base64 combined string */ string.bv_len = hash->bv_len + salt->bv_len; string.bv_val = ber_memalloc( string.bv_len + 1 ); if( string.bv_val == NULL ) { return LUTIL_PASSWD_ERR; } AC_MEMCPY( string.bv_val, hash->bv_val, hash->bv_len ); AC_MEMCPY( &string.bv_val[hash->bv_len], salt->bv_val, salt->bv_len ); string.bv_val[string.bv_len] = '\0'; } else { string = *hash; } b64len = LUTIL_BASE64_ENCODE_LEN( string.bv_len ) + 1; b64->bv_len = b64len + sc->bv_len; b64->bv_val = ber_memalloc( b64->bv_len + 1 ); if( b64->bv_val == NULL ) { if( salt ) ber_memfree( string.bv_val ); return LUTIL_PASSWD_ERR; } AC_MEMCPY(b64->bv_val, sc->bv_val, sc->bv_len); rc = lutil_b64_ntop( (unsigned char *) string.bv_val, string.bv_len, &b64->bv_val[sc->bv_len], b64len ); if( salt ) ber_memfree( string.bv_val ); if( rc < 0 ) { return LUTIL_PASSWD_ERR; } /* recompute length */ b64->bv_len = sc->bv_len + rc; assert( strlen(b64->bv_val) == b64->bv_len ); return LUTIL_PASSWD_OK; } /* The algorithm implemented in this function was created by Poul-Henning * Kamp and released under the following license: * ---------------------------------------------------------------------------- * "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 * ---------------------------------------------------------------------------- */ static void do_apr_hash( const struct berval *passwd, const struct berval *salt, unsigned char *digest) { lutil_MD5_CTX ctx, ctx1; int n; /* Start hashing */ lutil_MD5Init(&ctx); lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, passwd->bv_len); lutil_MD5Update(&ctx, "$apr1$", 6); lutil_MD5Update(&ctx, (const unsigned char *) salt->bv_val, salt->bv_len); /* Inner hash */ lutil_MD5Init(&ctx1); lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len); lutil_MD5Update(&ctx1, (const unsigned char *) salt->bv_val, salt->bv_len); lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len); lutil_MD5Final(digest, &ctx1); /* Nom start mixing things up */ for (n = passwd->bv_len; n > 0; n -= LUTIL_MD5_BYTES) lutil_MD5Update(&ctx, digest, (n > LUTIL_MD5_BYTES ? LUTIL_MD5_BYTES : n)); memset(digest, 0, LUTIL_MD5_BYTES); /* Curiouser and curiouser... */ for (n = passwd->bv_len; n; n >>= 1) if (n & 1) lutil_MD5Update(&ctx, digest, 1); else lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, 1); lutil_MD5Final(digest, &ctx); /* * Repeatedly hash things into the final value. This was originally * intended to slow the algorithm down. */ for (n = 0; n < 1000; n++) { lutil_MD5Init(&ctx1); if (n & 1) lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len); else lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES); if (n % 3) lutil_MD5Update(&ctx1, (const unsigned char *) salt->bv_val, salt->bv_len); if (n % 7) lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len); if (n & 1) lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES); else lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len); lutil_MD5Final(digest, &ctx1); } } static int chk_apr1( const struct berval *scheme, const struct berval *passwd, const struct berval *cred, const char **text) { unsigned char digest[LUTIL_MD5_BYTES]; unsigned char *orig_pass; int rc, n; struct berval salt; /* safety check */ n = LUTIL_BASE64_DECODE_LEN(passwd->bv_len); if (n <= sizeof(digest)) return LUTIL_PASSWD_ERR; /* base64 un-encode password hash */ orig_pass = (unsigned char *) ber_memalloc((size_t) (n + 1)); if (orig_pass == NULL) return LUTIL_PASSWD_ERR; rc = lutil_b64_pton(passwd->bv_val, orig_pass, passwd->bv_len); if (rc <= (int) sizeof(digest)) { ber_memfree(orig_pass); return LUTIL_PASSWD_ERR; } salt.bv_val = (char *) &orig_pass[sizeof(digest)]; salt.bv_len = rc - sizeof(digest); /* the only difference between this and straight PHK is the magic */ do_apr_hash(cred, &salt, digest); if (text) *text = NULL; /* compare */ rc = memcmp((char *) orig_pass, (char *) digest, sizeof(digest)); ber_memfree(orig_pass); return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; } static int hash_apr1( const struct berval *scheme, const struct berval *passwd, struct berval *hash, const char **text) { unsigned char digest_buf[LUTIL_MD5_BYTES]; char salt_buf[APR_SALT_SIZE]; struct berval digest; struct berval salt; int n; digest.bv_val = (char *) digest_buf; digest.bv_len = sizeof(digest_buf); salt.bv_val = salt_buf; salt.bv_len = APR_SALT_SIZE; /* generate random salt */ if (lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0) return LUTIL_PASSWD_ERR; /* limit it to characters in the 64-char set */ for (n = 0; n < salt.bv_len; n++) salt.bv_val[n] = apr64[salt.bv_val[n] % (sizeof(apr64) - 1)]; /* the only difference between this and straight PHK is the magic */ do_apr_hash(passwd, &salt, digest_buf); if (text) *text = NULL; return pw_string64(scheme, &digest, hash, &salt); } int init_module(int argc, char *argv[]) { return lutil_passwd_add((struct berval *) &scheme, chk_apr1, hash_apr1); }