]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/passwd/apr1.c
Use lutil_passwd_string64 instead of pw_string64
[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 /* The algorithm implemented in this function was created by Poul-Henning\r
36  * Kamp and released under the following license:\r
37  * ----------------------------------------------------------------------------\r
38  * "THE BEER-WARE LICENSE" (Revision 42):\r
39  * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you\r
40  * can do whatever you want with this stuff. If we meet some day, and you think\r
41  * this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp\r
42  * ----------------------------------------------------------------------------\r
43  */\r
44 static void do_apr_hash(\r
45         const struct berval *passwd,\r
46         const struct berval *salt,\r
47         unsigned char *digest)\r
48 {\r
49         lutil_MD5_CTX ctx, ctx1;\r
50         int n;\r
51 \r
52         /* Start hashing */\r
53         lutil_MD5Init(&ctx);\r
54         lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, passwd->bv_len);\r
55         lutil_MD5Update(&ctx, "$apr1$", 6);\r
56         lutil_MD5Update(&ctx, (const unsigned char *) salt->bv_val, salt->bv_len);\r
57         /* Inner hash */\r
58         lutil_MD5Init(&ctx1);\r
59         lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);\r
60         lutil_MD5Update(&ctx1, (const unsigned char *) salt->bv_val, salt->bv_len);\r
61         lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);\r
62         lutil_MD5Final(digest, &ctx1);\r
63         /* Nom start mixing things up */\r
64         for (n = passwd->bv_len; n > 0; n -= LUTIL_MD5_BYTES)\r
65                 lutil_MD5Update(&ctx, digest,\r
66                                 (n > LUTIL_MD5_BYTES ? LUTIL_MD5_BYTES : n));\r
67         memset(digest, 0, LUTIL_MD5_BYTES);\r
68         /* Curiouser and curiouser... */\r
69         for (n = passwd->bv_len; n; n >>= 1)\r
70                 if (n & 1)\r
71                         lutil_MD5Update(&ctx, digest, 1);\r
72                 else\r
73                         lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, 1);\r
74         lutil_MD5Final(digest, &ctx);\r
75         /*\r
76          * Repeatedly hash things into the final value. This was originally\r
77          * intended to slow the algorithm down.\r
78          */\r
79         for (n = 0; n < 1000; n++) {\r
80                 lutil_MD5Init(&ctx1);\r
81                 if (n & 1)\r
82                         lutil_MD5Update(&ctx1,\r
83                                 (const unsigned char *) passwd->bv_val, passwd->bv_len);\r
84                 else\r
85                         lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);\r
86 \r
87                 if (n % 3)\r
88                         lutil_MD5Update(&ctx1,\r
89                                 (const unsigned char *) salt->bv_val, salt->bv_len);\r
90                 if (n % 7)\r
91                         lutil_MD5Update(&ctx1,\r
92                                 (const unsigned char *) passwd->bv_val, passwd->bv_len);\r
93 \r
94                 if (n & 1)\r
95                         lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);\r
96                 else\r
97                         lutil_MD5Update(&ctx1,\r
98                                 (const unsigned char *) passwd->bv_val, passwd->bv_len);\r
99                 lutil_MD5Final(digest, &ctx1);\r
100         }\r
101 }\r
102 \r
103 static int chk_apr1(\r
104         const struct berval *scheme,\r
105         const struct berval *passwd,\r
106         const struct berval *cred,\r
107         const char **text)\r
108 {\r
109         unsigned char digest[LUTIL_MD5_BYTES];\r
110         unsigned char *orig_pass;\r
111         int rc, n;\r
112         struct berval salt;\r
113 \r
114         /* safety check */\r
115         n = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);\r
116         if (n <= sizeof(digest))\r
117                 return LUTIL_PASSWD_ERR;\r
118 \r
119         /* base64 un-encode password hash */\r
120         orig_pass = (unsigned char *) ber_memalloc((size_t) (n + 1));\r
121 \r
122         if (orig_pass == NULL)\r
123                 return LUTIL_PASSWD_ERR;\r
124 \r
125         rc = lutil_b64_pton(passwd->bv_val, orig_pass, passwd->bv_len);\r
126 \r
127         if (rc <= (int) sizeof(digest)) {\r
128                 ber_memfree(orig_pass);\r
129                 return LUTIL_PASSWD_ERR;\r
130         }\r
131 \r
132         salt.bv_val = (char *) &orig_pass[sizeof(digest)];\r
133         salt.bv_len = rc - sizeof(digest);\r
134 \r
135         /* the only difference between this and straight PHK is the magic */\r
136         do_apr_hash(cred, &salt, digest);\r
137 \r
138         if (text)\r
139                 *text = NULL;\r
140 \r
141         /* compare */\r
142         rc = memcmp((char *) orig_pass, (char *) digest, sizeof(digest));\r
143         ber_memfree(orig_pass);\r
144         return rc ?  LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;\r
145 }\r
146 \r
147 static int hash_apr1(\r
148         const struct berval *scheme,\r
149         const struct berval *passwd,\r
150         struct berval *hash,\r
151         const char **text)\r
152 {\r
153         unsigned char digest_buf[LUTIL_MD5_BYTES];\r
154         char salt_buf[APR_SALT_SIZE];\r
155         struct berval digest;\r
156         struct berval salt;\r
157         int n;\r
158 \r
159         digest.bv_val = (char *) digest_buf;\r
160         digest.bv_len = sizeof(digest_buf);\r
161         salt.bv_val = salt_buf;\r
162         salt.bv_len = APR_SALT_SIZE;\r
163 \r
164         /* generate random salt */\r
165         if (lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0)\r
166                 return LUTIL_PASSWD_ERR; \r
167         /* limit it to characters in the 64-char set */\r
168         for (n = 0; n < salt.bv_len; n++)\r
169                 salt.bv_val[n] = apr64[salt.bv_val[n] % (sizeof(apr64) - 1)];\r
170 \r
171         /* the only difference between this and straight PHK is the magic */\r
172         do_apr_hash(passwd, &salt, digest_buf);\r
173 \r
174         if (text)\r
175                 *text = NULL;\r
176 \r
177         return lutil_passwd_string64(scheme, &digest, hash, &salt);\r
178 }\r
179 \r
180 int init_module(int argc, char *argv[]) {\r
181         return lutil_passwd_add((struct berval *) &scheme, chk_apr1, hash_apr1);\r
182 }\r