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