]> git.sur5r.net Git - openldap/commitdiff
ITS#6826 from Devin J. Pohly
authorHoward Chu <hyc@openldap.org>
Mon, 7 Feb 2011 00:49:04 +0000 (00:49 +0000)
committerHoward Chu <hyc@openldap.org>
Mon, 7 Feb 2011 00:49:04 +0000 (00:49 +0000)
contrib/slapd-modules/passwd/apr1.c [new file with mode: 0644]

diff --git a/contrib/slapd-modules/passwd/apr1.c b/contrib/slapd-modules/passwd/apr1.c
new file mode 100644 (file)
index 0000000..51955ed
--- /dev/null
@@ -0,0 +1,239 @@
+/*\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