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