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