2 * This file is derived from OpenLDAP Software. All of the modifications to
\r
3 * OpenLDAP Software represented in the following file were developed by
\r
4 * Devin J. Pohly <djpohly@gmail.com>. I have not assigned rights and/or
\r
5 * interest in this work to any party.
\r
7 * The extensions to OpenLDAP Software herein are subject to the following
\r
10 * Copyright 2011 Devin J. Pohly
\r
11 * Redistribution and use in source and binary forms, with or without
\r
12 * modification, are permitted only as authorized by the OpenLDAP Public
\r
15 * A portion of this code is used in accordance with the Beer-ware License,
\r
16 * revision 42, as noted.
\r
19 #include <lber_pvt.h>
\r
21 #include "lutil_md5.h"
\r
22 #include <ac/string.h>
\r
26 static LUTIL_PASSWD_CHK_FUNC chk_apr1;
\r
27 static LUTIL_PASSWD_HASH_FUNC hash_apr1;
\r
28 static const struct berval scheme = BER_BVC("{APR1}");
\r
30 static const unsigned char apr64[] =
\r
31 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
\r
33 #define APR_SALT_SIZE 8
\r
35 /* copied from liblutil/passwd.c */
\r
36 static int pw_string64(
\r
37 const struct berval *sc,
\r
38 const struct berval *hash,
\r
40 const struct berval *salt )
\r
43 struct berval string;
\r
47 /* need to base64 combined string */
\r
48 string.bv_len = hash->bv_len + salt->bv_len;
\r
49 string.bv_val = ber_memalloc( string.bv_len + 1 );
\r
51 if( string.bv_val == NULL ) {
\r
52 return LUTIL_PASSWD_ERR;
\r
55 AC_MEMCPY( string.bv_val, hash->bv_val,
\r
57 AC_MEMCPY( &string.bv_val[hash->bv_len], salt->bv_val,
\r
59 string.bv_val[string.bv_len] = '\0';
\r
65 b64len = LUTIL_BASE64_ENCODE_LEN( string.bv_len ) + 1;
\r
66 b64->bv_len = b64len + sc->bv_len;
\r
67 b64->bv_val = ber_memalloc( b64->bv_len + 1 );
\r
69 if( b64->bv_val == NULL ) {
\r
70 if( salt ) ber_memfree( string.bv_val );
\r
71 return LUTIL_PASSWD_ERR;
\r
74 AC_MEMCPY(b64->bv_val, sc->bv_val, sc->bv_len);
\r
76 rc = lutil_b64_ntop(
\r
77 (unsigned char *) string.bv_val, string.bv_len,
\r
78 &b64->bv_val[sc->bv_len], b64len );
\r
80 if( salt ) ber_memfree( string.bv_val );
\r
83 return LUTIL_PASSWD_ERR;
\r
86 /* recompute length */
\r
87 b64->bv_len = sc->bv_len + rc;
\r
88 assert( strlen(b64->bv_val) == b64->bv_len );
\r
89 return LUTIL_PASSWD_OK;
\r
92 /* The algorithm implemented in this function was created by Poul-Henning
\r
93 * Kamp and released under the following license:
\r
94 * ----------------------------------------------------------------------------
\r
95 * "THE BEER-WARE LICENSE" (Revision 42):
\r
96 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
\r
97 * can do whatever you want with this stuff. If we meet some day, and you think
\r
98 * this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp
\r
99 * ----------------------------------------------------------------------------
\r
101 static void do_apr_hash(
\r
102 const struct berval *passwd,
\r
103 const struct berval *salt,
\r
104 unsigned char *digest)
\r
106 lutil_MD5_CTX ctx, ctx1;
\r
109 /* Start hashing */
\r
110 lutil_MD5Init(&ctx);
\r
111 lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, passwd->bv_len);
\r
112 lutil_MD5Update(&ctx, "$apr1$", 6);
\r
113 lutil_MD5Update(&ctx, (const unsigned char *) salt->bv_val, salt->bv_len);
\r
115 lutil_MD5Init(&ctx1);
\r
116 lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);
\r
117 lutil_MD5Update(&ctx1, (const unsigned char *) salt->bv_val, salt->bv_len);
\r
118 lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);
\r
119 lutil_MD5Final(digest, &ctx1);
\r
120 /* Nom start mixing things up */
\r
121 for (n = passwd->bv_len; n > 0; n -= LUTIL_MD5_BYTES)
\r
122 lutil_MD5Update(&ctx, digest,
\r
123 (n > LUTIL_MD5_BYTES ? LUTIL_MD5_BYTES : n));
\r
124 memset(digest, 0, LUTIL_MD5_BYTES);
\r
125 /* Curiouser and curiouser... */
\r
126 for (n = passwd->bv_len; n; n >>= 1)
\r
128 lutil_MD5Update(&ctx, digest, 1);
\r
130 lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, 1);
\r
131 lutil_MD5Final(digest, &ctx);
\r
133 * Repeatedly hash things into the final value. This was originally
\r
134 * intended to slow the algorithm down.
\r
136 for (n = 0; n < 1000; n++) {
\r
137 lutil_MD5Init(&ctx1);
\r
139 lutil_MD5Update(&ctx1,
\r
140 (const unsigned char *) passwd->bv_val, passwd->bv_len);
\r
142 lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);
\r
145 lutil_MD5Update(&ctx1,
\r
146 (const unsigned char *) salt->bv_val, salt->bv_len);
\r
148 lutil_MD5Update(&ctx1,
\r
149 (const unsigned char *) passwd->bv_val, passwd->bv_len);
\r
152 lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);
\r
154 lutil_MD5Update(&ctx1,
\r
155 (const unsigned char *) passwd->bv_val, passwd->bv_len);
\r
156 lutil_MD5Final(digest, &ctx1);
\r
160 static int chk_apr1(
\r
161 const struct berval *scheme,
\r
162 const struct berval *passwd,
\r
163 const struct berval *cred,
\r
166 unsigned char digest[LUTIL_MD5_BYTES];
\r
167 unsigned char *orig_pass;
\r
169 struct berval salt;
\r
172 n = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
\r
173 if (n <= sizeof(digest))
\r
174 return LUTIL_PASSWD_ERR;
\r
176 /* base64 un-encode password hash */
\r
177 orig_pass = (unsigned char *) ber_memalloc((size_t) (n + 1));
\r
179 if (orig_pass == NULL)
\r
180 return LUTIL_PASSWD_ERR;
\r
182 rc = lutil_b64_pton(passwd->bv_val, orig_pass, passwd->bv_len);
\r
184 if (rc <= (int) sizeof(digest)) {
\r
185 ber_memfree(orig_pass);
\r
186 return LUTIL_PASSWD_ERR;
\r
189 salt.bv_val = (char *) &orig_pass[sizeof(digest)];
\r
190 salt.bv_len = rc - sizeof(digest);
\r
192 /* the only difference between this and straight PHK is the magic */
\r
193 do_apr_hash(cred, &salt, digest);
\r
199 rc = memcmp((char *) orig_pass, (char *) digest, sizeof(digest));
\r
200 ber_memfree(orig_pass);
\r
201 return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
\r
204 static int hash_apr1(
\r
205 const struct berval *scheme,
\r
206 const struct berval *passwd,
\r
207 struct berval *hash,
\r
210 unsigned char digest_buf[LUTIL_MD5_BYTES];
\r
211 char salt_buf[APR_SALT_SIZE];
\r
212 struct berval digest;
\r
213 struct berval salt;
\r
216 digest.bv_val = (char *) digest_buf;
\r
217 digest.bv_len = sizeof(digest_buf);
\r
218 salt.bv_val = salt_buf;
\r
219 salt.bv_len = APR_SALT_SIZE;
\r
221 /* generate random salt */
\r
222 if (lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0)
\r
223 return LUTIL_PASSWD_ERR;
\r
224 /* limit it to characters in the 64-char set */
\r
225 for (n = 0; n < salt.bv_len; n++)
\r
226 salt.bv_val[n] = apr64[salt.bv_val[n] % (sizeof(apr64) - 1)];
\r
228 /* the only difference between this and straight PHK is the magic */
\r
229 do_apr_hash(passwd, &salt, digest_buf);
\r
234 return pw_string64(scheme, &digest, hash, &salt);
\r
237 int init_module(int argc, char *argv[]) {
\r
238 return lutil_passwd_add((struct berval *) &scheme, chk_apr1, hash_apr1);
\r