apr1.c revision 1.2 1 1.2 christos /* $NetBSD: apr1.c,v 1.2 2021/08/14 16:14:52 christos Exp $ */
2 1.1 tron
3 1.2 christos /* $OpenLDAP$ */
4 1.2 christos /*
5 1.2 christos * This file is derived from OpenLDAP Software. All of the modifications to
6 1.2 christos * OpenLDAP Software represented in the following file were developed by
7 1.2 christos * Devin J. Pohly <djpohly (at) gmail.com>. I have not assigned rights and/or
8 1.2 christos * interest in this work to any party.
9 1.2 christos *
10 1.2 christos * The extensions to OpenLDAP Software herein are subject to the following
11 1.2 christos * notice:
12 1.2 christos *
13 1.2 christos * Copyright 2011 Devin J. Pohly
14 1.2 christos * Portions Copyright 2011 Howard Chu
15 1.2 christos * Redistribution and use in source and binary forms, with or without
16 1.2 christos * modification, are permitted only as authorized by the OpenLDAP Public
17 1.2 christos * License.
18 1.2 christos *
19 1.2 christos * A portion of this code is used in accordance with the Beer-ware License,
20 1.2 christos * revision 42, as noted.
21 1.2 christos *
22 1.2 christos */
23 1.2 christos
24 1.2 christos #include <sys/cdefs.h>
25 1.2 christos __RCSID("$NetBSD: apr1.c,v 1.2 2021/08/14 16:14:52 christos Exp $");
26 1.2 christos
27 1.2 christos #include "portable.h"
28 1.2 christos
29 1.2 christos #include <lber.h>
30 1.2 christos #include <lber_pvt.h>
31 1.2 christos #include "lutil.h"
32 1.2 christos #include "lutil_md5.h"
33 1.2 christos #include <ac/string.h>
34 1.2 christos
35 1.2 christos #include <assert.h>
36 1.2 christos
37 1.2 christos /* the only difference between this and straight PHK is the magic */
38 1.2 christos static LUTIL_PASSWD_CHK_FUNC chk_apr1;
39 1.2 christos static LUTIL_PASSWD_HASH_FUNC hash_apr1;
40 1.2 christos static const struct berval scheme_apr1 = BER_BVC("{APR1}");
41 1.2 christos static const struct berval magic_apr1 = BER_BVC("$apr1$");
42 1.2 christos
43 1.2 christos static LUTIL_PASSWD_CHK_FUNC chk_bsdmd5;
44 1.2 christos static LUTIL_PASSWD_HASH_FUNC hash_bsdmd5;
45 1.2 christos static const struct berval scheme_bsdmd5 = BER_BVC("{BSDMD5}");
46 1.2 christos static const struct berval magic_bsdmd5 = BER_BVC("$1$");
47 1.2 christos
48 1.2 christos static const unsigned char apr64[] =
49 1.2 christos "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
50 1.2 christos
51 1.2 christos #define APR_SALT_SIZE 8
52 1.2 christos
53 1.2 christos /* The algorithm implemented in this function was created by Poul-Henning
54 1.2 christos * Kamp and released under the following license:
55 1.2 christos * ----------------------------------------------------------------------------
56 1.2 christos * "THE BEER-WARE LICENSE" (Revision 42):
57 1.2 christos * <phk (at) FreeBSD.ORG> wrote this file. As long as you retain this notice you
58 1.2 christos * can do whatever you want with this stuff. If we meet some day, and you think
59 1.2 christos * this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp
60 1.2 christos * ----------------------------------------------------------------------------
61 1.2 christos */
62 1.2 christos static void do_phk_hash(
63 1.2 christos const struct berval *passwd,
64 1.2 christos const struct berval *salt,
65 1.2 christos const struct berval *magic,
66 1.2 christos unsigned char *digest)
67 1.2 christos {
68 1.2 christos lutil_MD5_CTX ctx, ctx1;
69 1.2 christos int n;
70 1.2 christos
71 1.2 christos /* Start hashing */
72 1.2 christos lutil_MD5Init(&ctx);
73 1.2 christos lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, passwd->bv_len);
74 1.2 christos lutil_MD5Update(&ctx, (const unsigned char *) magic->bv_val, magic->bv_len);
75 1.2 christos lutil_MD5Update(&ctx, (const unsigned char *) salt->bv_val, salt->bv_len);
76 1.2 christos /* Inner hash */
77 1.2 christos lutil_MD5Init(&ctx1);
78 1.2 christos lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);
79 1.2 christos lutil_MD5Update(&ctx1, (const unsigned char *) salt->bv_val, salt->bv_len);
80 1.2 christos lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);
81 1.2 christos lutil_MD5Final(digest, &ctx1);
82 1.2 christos /* Nom start mixing things up */
83 1.2 christos for (n = passwd->bv_len; n > 0; n -= LUTIL_MD5_BYTES)
84 1.2 christos lutil_MD5Update(&ctx, digest,
85 1.2 christos (n > LUTIL_MD5_BYTES ? LUTIL_MD5_BYTES : n));
86 1.2 christos memset(digest, 0, LUTIL_MD5_BYTES);
87 1.2 christos /* Curiouser and curiouser... */
88 1.2 christos for (n = passwd->bv_len; n; n >>= 1)
89 1.2 christos if (n & 1)
90 1.2 christos lutil_MD5Update(&ctx, digest, 1);
91 1.2 christos else
92 1.2 christos lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, 1);
93 1.2 christos lutil_MD5Final(digest, &ctx);
94 1.2 christos /*
95 1.2 christos * Repeatedly hash things into the final value. This was originally
96 1.2 christos * intended to slow the algorithm down.
97 1.2 christos */
98 1.2 christos for (n = 0; n < 1000; n++) {
99 1.2 christos lutil_MD5Init(&ctx1);
100 1.2 christos if (n & 1)
101 1.2 christos lutil_MD5Update(&ctx1,
102 1.2 christos (const unsigned char *) passwd->bv_val, passwd->bv_len);
103 1.2 christos else
104 1.2 christos lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);
105 1.2 christos
106 1.2 christos if (n % 3)
107 1.2 christos lutil_MD5Update(&ctx1,
108 1.2 christos (const unsigned char *) salt->bv_val, salt->bv_len);
109 1.2 christos if (n % 7)
110 1.2 christos lutil_MD5Update(&ctx1,
111 1.2 christos (const unsigned char *) passwd->bv_val, passwd->bv_len);
112 1.2 christos
113 1.2 christos if (n & 1)
114 1.2 christos lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);
115 1.2 christos else
116 1.2 christos lutil_MD5Update(&ctx1,
117 1.2 christos (const unsigned char *) passwd->bv_val, passwd->bv_len);
118 1.2 christos lutil_MD5Final(digest, &ctx1);
119 1.2 christos }
120 1.2 christos }
121 1.2 christos
122 1.2 christos static int chk_phk(
123 1.2 christos const struct berval *magic,
124 1.2 christos const struct berval *passwd,
125 1.2 christos const struct berval *cred,
126 1.2 christos const char **text)
127 1.2 christos {
128 1.2 christos unsigned char digest[LUTIL_MD5_BYTES];
129 1.2 christos unsigned char *orig_pass;
130 1.2 christos int rc;
131 1.2 christos struct berval salt;
132 1.2 christos size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
133 1.2 christos
134 1.2 christos /* safety check */
135 1.2 christos if (decode_len <= sizeof(digest))
136 1.2 christos return LUTIL_PASSWD_ERR;
137 1.2 christos
138 1.2 christos /* base64 un-encode password hash */
139 1.2 christos orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
140 1.2 christos
141 1.2 christos if (orig_pass == NULL)
142 1.2 christos return LUTIL_PASSWD_ERR;
143 1.2 christos
144 1.2 christos rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
145 1.2 christos
146 1.2 christos if (rc <= (int) sizeof(digest)) {
147 1.2 christos ber_memfree(orig_pass);
148 1.2 christos return LUTIL_PASSWD_ERR;
149 1.2 christos }
150 1.2 christos
151 1.2 christos salt.bv_val = (char *) &orig_pass[sizeof(digest)];
152 1.2 christos salt.bv_len = rc - sizeof(digest);
153 1.2 christos
154 1.2 christos do_phk_hash(cred, &salt, magic, digest);
155 1.2 christos
156 1.2 christos if (text)
157 1.2 christos *text = NULL;
158 1.2 christos
159 1.2 christos /* compare */
160 1.2 christos rc = memcmp((char *) orig_pass, (char *) digest, sizeof(digest));
161 1.2 christos ber_memfree(orig_pass);
162 1.2 christos return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
163 1.2 christos }
164 1.2 christos
165 1.2 christos static int chk_apr1(
166 1.2 christos const struct berval *scheme,
167 1.2 christos const struct berval *passwd,
168 1.2 christos const struct berval *cred,
169 1.2 christos const char **text)
170 1.2 christos {
171 1.2 christos return chk_phk(&magic_apr1, passwd, cred, text);
172 1.2 christos }
173 1.2 christos
174 1.2 christos static int chk_bsdmd5(
175 1.2 christos const struct berval *scheme,
176 1.2 christos const struct berval *passwd,
177 1.2 christos const struct berval *cred,
178 1.2 christos const char **text)
179 1.2 christos {
180 1.2 christos return chk_phk(&magic_bsdmd5, passwd, cred, text);
181 1.2 christos }
182 1.2 christos
183 1.2 christos static int hash_phk(
184 1.2 christos const struct berval *scheme,
185 1.2 christos const struct berval *magic,
186 1.2 christos const struct berval *passwd,
187 1.2 christos struct berval *hash,
188 1.2 christos const char **text)
189 1.2 christos {
190 1.2 christos unsigned char digest_buf[LUTIL_MD5_BYTES];
191 1.2 christos char salt_buf[APR_SALT_SIZE];
192 1.2 christos struct berval digest;
193 1.2 christos struct berval salt;
194 1.2 christos int n;
195 1.2 christos
196 1.2 christos digest.bv_val = (char *) digest_buf;
197 1.2 christos digest.bv_len = sizeof(digest_buf);
198 1.2 christos salt.bv_val = salt_buf;
199 1.2 christos salt.bv_len = APR_SALT_SIZE;
200 1.2 christos
201 1.2 christos /* generate random salt */
202 1.2 christos if (lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0)
203 1.2 christos return LUTIL_PASSWD_ERR;
204 1.2 christos /* limit it to characters in the 64-char set */
205 1.2 christos for (n = 0; n < salt.bv_len; n++)
206 1.2 christos salt.bv_val[n] = apr64[salt.bv_val[n] % (sizeof(apr64) - 1)];
207 1.2 christos
208 1.2 christos do_phk_hash(passwd, &salt, magic, digest_buf);
209 1.2 christos
210 1.2 christos if (text)
211 1.2 christos *text = NULL;
212 1.2 christos
213 1.2 christos return lutil_passwd_string64(scheme, &digest, hash, &salt);
214 1.2 christos }
215 1.2 christos
216 1.2 christos static int hash_apr1(
217 1.2 christos const struct berval *scheme,
218 1.2 christos const struct berval *passwd,
219 1.2 christos struct berval *hash,
220 1.2 christos const char **text)
221 1.2 christos {
222 1.2 christos return hash_phk(scheme, &magic_apr1, passwd, hash, text);
223 1.2 christos }
224 1.2 christos
225 1.2 christos static int hash_bsdmd5(
226 1.2 christos const struct berval *scheme,
227 1.2 christos const struct berval *passwd,
228 1.2 christos struct berval *hash,
229 1.2 christos const char **text)
230 1.2 christos {
231 1.2 christos return hash_phk(scheme, &magic_bsdmd5, passwd, hash, text);
232 1.2 christos }
233 1.2 christos
234 1.2 christos int init_module(int argc, char *argv[]) {
235 1.2 christos int rc;
236 1.2 christos rc = lutil_passwd_add((struct berval *) &scheme_apr1, chk_apr1, hash_apr1);
237 1.2 christos if ( !rc )
238 1.2 christos rc = lutil_passwd_add((struct berval *) &scheme_bsdmd5,
239 1.2 christos chk_bsdmd5, hash_bsdmd5);
240 1.2 christos return rc;
241 1.2 christos }
242