]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/passwd/apr1.c
ITS#6826 from Devin J. Pohly
[openldap] / contrib / slapd-modules / passwd / apr1.c
1 /*\r
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
6  *\r
7  * The extensions to OpenLDAP Software herein are subject to the following\r
8  * notice:\r
9  *\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
13  * License. \r
14  *\r
15  * A portion of this code is used in accordance with the Beer-ware License,\r
16  * revision 42, as noted.\r
17  */\r
18 #include <lber.h>\r
19 #include <lber_pvt.h>\r
20 #include "lutil.h"\r
21 #include "lutil_md5.h"\r
22 #include <ac/string.h>\r
23 \r
24 #include <assert.h>\r
25 \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
29 \r
30 static const unsigned char apr64[] =\r
31         "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";\r
32 \r
33 #define APR_SALT_SIZE   8\r
34 \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
39         struct berval *b64,\r
40         const struct berval *salt )\r
41 {\r
42         int rc;\r
43         struct berval string;\r
44         size_t b64len;\r
45 \r
46         if( salt ) {\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
50 \r
51                 if( string.bv_val == NULL ) {\r
52                         return LUTIL_PASSWD_ERR;\r
53                 }\r
54 \r
55                 AC_MEMCPY( string.bv_val, hash->bv_val,\r
56                         hash->bv_len );\r
57                 AC_MEMCPY( &string.bv_val[hash->bv_len], salt->bv_val,\r
58                         salt->bv_len );\r
59                 string.bv_val[string.bv_len] = '\0';\r
60 \r
61         } else {\r
62                 string = *hash;\r
63         }\r
64 \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
68 \r
69         if( b64->bv_val == NULL ) {\r
70                 if( salt ) ber_memfree( string.bv_val );\r
71                 return LUTIL_PASSWD_ERR;\r
72         }\r
73 \r
74         AC_MEMCPY(b64->bv_val, sc->bv_val, sc->bv_len);\r
75 \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
79 \r
80         if( salt ) ber_memfree( string.bv_val );\r
81         \r
82         if( rc < 0 ) {\r
83                 return LUTIL_PASSWD_ERR;\r
84         }\r
85 \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
90 }\r
91 \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
100  */\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
105 {\r
106         lutil_MD5_CTX ctx, ctx1;\r
107         int n;\r
108 \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
114         /* Inner hash */\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
127                 if (n & 1)\r
128                         lutil_MD5Update(&ctx, digest, 1);\r
129                 else\r
130                         lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, 1);\r
131         lutil_MD5Final(digest, &ctx);\r
132         /*\r
133          * Repeatedly hash things into the final value. This was originally\r
134          * intended to slow the algorithm down.\r
135          */\r
136         for (n = 0; n < 1000; n++) {\r
137                 lutil_MD5Init(&ctx1);\r
138                 if (n & 1)\r
139                         lutil_MD5Update(&ctx1,\r
140                                 (const unsigned char *) passwd->bv_val, passwd->bv_len);\r
141                 else\r
142                         lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);\r
143 \r
144                 if (n % 3)\r
145                         lutil_MD5Update(&ctx1,\r
146                                 (const unsigned char *) salt->bv_val, salt->bv_len);\r
147                 if (n % 7)\r
148                         lutil_MD5Update(&ctx1,\r
149                                 (const unsigned char *) passwd->bv_val, passwd->bv_len);\r
150 \r
151                 if (n & 1)\r
152                         lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);\r
153                 else\r
154                         lutil_MD5Update(&ctx1,\r
155                                 (const unsigned char *) passwd->bv_val, passwd->bv_len);\r
156                 lutil_MD5Final(digest, &ctx1);\r
157         }\r
158 }\r
159 \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
164         const char **text)\r
165 {\r
166         unsigned char digest[LUTIL_MD5_BYTES];\r
167         unsigned char *orig_pass;\r
168         int rc, n;\r
169         struct berval salt;\r
170 \r
171         /* safety check */\r
172         n = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);\r
173         if (n <= sizeof(digest))\r
174                 return LUTIL_PASSWD_ERR;\r
175 \r
176         /* base64 un-encode password hash */\r
177         orig_pass = (unsigned char *) ber_memalloc((size_t) (n + 1));\r
178 \r
179         if (orig_pass == NULL)\r
180                 return LUTIL_PASSWD_ERR;\r
181 \r
182         rc = lutil_b64_pton(passwd->bv_val, orig_pass, passwd->bv_len);\r
183 \r
184         if (rc <= (int) sizeof(digest)) {\r
185                 ber_memfree(orig_pass);\r
186                 return LUTIL_PASSWD_ERR;\r
187         }\r
188 \r
189         salt.bv_val = (char *) &orig_pass[sizeof(digest)];\r
190         salt.bv_len = rc - sizeof(digest);\r
191 \r
192         /* the only difference between this and straight PHK is the magic */\r
193         do_apr_hash(cred, &salt, digest);\r
194 \r
195         if (text)\r
196                 *text = NULL;\r
197 \r
198         /* compare */\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
202 }\r
203 \r
204 static int hash_apr1(\r
205         const struct berval *scheme,\r
206         const struct berval *passwd,\r
207         struct berval *hash,\r
208         const char **text)\r
209 {\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
214         int n;\r
215 \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
220 \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
227 \r
228         /* the only difference between this and straight PHK is the magic */\r
229         do_apr_hash(passwd, &salt, digest_buf);\r
230 \r
231         if (text)\r
232                 *text = NULL;\r
233 \r
234         return pw_string64(scheme, &digest, hash, &salt);\r
235 }\r
236 \r
237 int init_module(int argc, char *argv[]) {\r
238         return lutil_passwd_add((struct berval *) &scheme, chk_apr1, hash_apr1);\r
239 }