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