1/* 2 * Copyright (c) 2010 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include <sys/types.h> 37#include <stdio.h> 38#include <unistd.h> 39#include <CommonCrypto/CommonDigest.h> 40#include <CommonCrypto/CommonHMAC.h> 41#include <roken.h> 42#include <hex.h> 43#include "heim-auth.h" 44#include "ntlm_err.h" 45 46char * 47heim_generate_challenge(const char *hostname) 48{ 49 char host[MAXHOSTNAMELEN], *str = NULL; 50 uint32_t num, t; 51 52 if (hostname == NULL) { 53 if (gethostname(host, sizeof(host))) 54 return NULL; 55 hostname = host; 56 } 57 58 t = (uint32_t)time(NULL); 59 num = rk_random(); 60 61 asprintf(&str, "<%lu%lu@%s>", (unsigned long)t, 62 (unsigned long)num, hostname); 63 64 return str; 65} 66 67char * 68heim_apop_create(const char *challenge, const char *password) 69{ 70 char *str = NULL; 71 uint8_t hash[CC_MD5_DIGEST_LENGTH]; 72 CC_MD5_CTX ctx; 73 74 CC_MD5_Init(&ctx); 75 CC_MD5_Update(&ctx, challenge, (CC_LONG)strlen(challenge)); 76 CC_MD5_Update(&ctx, password, (CC_LONG)strlen(password)); 77 78 CC_MD5_Final(hash, &ctx); 79 80 hex_encode(hash, sizeof(hash), &str); 81 if (str) 82 strlwr(str); 83 84 return str; 85} 86 87int 88heim_apop_verify(const char *challenge, const char *password, const char *response) 89{ 90 char *str; 91 int res; 92 93 str = heim_apop_create(challenge, password); 94 if (str == NULL) 95 return ENOMEM; 96 97 res = (strcasecmp(str, response) != 0); 98 free(str); 99 100 if (res) 101 return HNTLM_ERR_INVALID_APOP; 102 return 0; 103} 104 105struct heim_cram_md5_data { 106 CC_MD5_CTX ipad; 107 CC_MD5_CTX opad; 108}; 109 110 111void 112heim_cram_md5_export(const char *password, heim_CRAM_MD5_STATE *state) 113{ 114 size_t keylen = strlen(password); 115 uint8_t key[CC_MD5_BLOCK_BYTES]; 116 uint8_t pad[CC_MD5_BLOCK_BYTES]; 117 struct heim_cram_md5_data ctx; 118 size_t n; 119 120 memset(&ctx, 0, sizeof(ctx)); 121 122 if (keylen > CC_MD5_BLOCK_BYTES) { 123 CC_MD5(password, (CC_LONG)keylen, key); 124 keylen = sizeof(keylen); 125 } else { 126 memcpy(key, password, keylen); 127 } 128 129 memset(pad, 0x36, sizeof(pad)); 130 for (n = 0; n < keylen; n++) 131 pad[n] ^= key[n]; 132 133 CC_MD5_Init(&ctx.ipad); 134 CC_MD5_Init(&ctx.opad); 135 136 CC_MD5_Update(&ctx.ipad, pad, sizeof(pad)); 137 138 memset(pad, 0x5c, sizeof(pad)); 139 for (n = 0; n < keylen; n++) 140 pad[n] ^= key[n]; 141 142 CC_MD5_Update(&ctx.opad, pad, sizeof(pad)); 143 144 memset(pad, 0, sizeof(pad)); 145 memset(key, 0, sizeof(key)); 146 147 state->istate[0] = htonl(ctx.ipad.A); 148 state->istate[1] = htonl(ctx.ipad.B); 149 state->istate[2] = htonl(ctx.ipad.C); 150 state->istate[3] = htonl(ctx.ipad.D); 151 152 state->ostate[0] = htonl(ctx.opad.A); 153 state->ostate[1] = htonl(ctx.opad.B); 154 state->ostate[2] = htonl(ctx.opad.C); 155 state->ostate[3] = htonl(ctx.opad.D); 156 157 memset(&ctx, 0, sizeof(ctx)); 158} 159 160 161heim_cram_md5 162heim_cram_md5_import(void *data, size_t len) 163{ 164 heim_CRAM_MD5_STATE state; 165 heim_cram_md5 ctx; 166 167 if (len != sizeof(state)) 168 return NULL; 169 170 ctx = calloc(1, sizeof(*ctx)); 171 if (ctx == NULL) 172 return NULL; 173 174 memcpy(&state, data, sizeof(state)); 175 176 ctx->ipad.A = ntohl(state.istate[0]); 177 ctx->ipad.B = ntohl(state.istate[1]); 178 ctx->ipad.C = ntohl(state.istate[2]); 179 ctx->ipad.D = ntohl(state.istate[3]); 180 181 ctx->opad.A = ntohl(state.ostate[0]); 182 ctx->opad.B = ntohl(state.ostate[1]); 183 ctx->opad.C = ntohl(state.ostate[2]); 184 ctx->opad.D = ntohl(state.ostate[3]); 185 186 ctx->ipad.Nl = ctx->opad.Nl = 512; 187 ctx->ipad.Nh = ctx->opad.Nh = 0; 188 ctx->ipad.num = ctx->opad.num = 0; 189 190 return ctx; 191} 192 193int 194heim_cram_md5_verify_ctx(heim_cram_md5 ctx, const char *challenge, const char *response) 195{ 196 uint8_t hash[CC_MD5_DIGEST_LENGTH]; 197 char *str = NULL; 198 int res; 199 200 CC_MD5_Update(&ctx->ipad, challenge, (CC_LONG)strlen(challenge)); 201 CC_MD5_Final(hash, &ctx->ipad); 202 203 CC_MD5_Update(&ctx->opad, hash, sizeof(hash)); 204 CC_MD5_Final(hash, &ctx->opad); 205 206 hex_encode(hash, sizeof(hash), &str); 207 if (str == NULL) 208 return ENOMEM; 209 210 res = (strcasecmp(str, response) != 0); 211 free(str); 212 213 if (res) 214 return HNTLM_ERR_INVALID_CRAM_MD5; 215 return 0; 216} 217 218void 219heim_cram_md5_free(heim_cram_md5 ctx) 220{ 221 memset(ctx, 0, sizeof(*ctx)); 222 free(ctx); 223} 224 225 226char * 227heim_cram_md5_create(const char *challenge, const char *password) 228{ 229 CCHmacContext ctx; 230 uint8_t hash[CC_MD5_DIGEST_LENGTH]; 231 char *str = NULL; 232 233 CCHmacInit(&ctx, kCCHmacAlgMD5, password, strlen(password)); 234 CCHmacUpdate(&ctx, challenge, strlen(challenge)); 235 CCHmacFinal(&ctx, hash); 236 237 memset(&ctx, 0, sizeof(ctx)); 238 239 hex_encode(hash, sizeof(hash), &str); 240 if (str) 241 strlwr(str); 242 243 return str; 244} 245 246 int 247heim_cram_md5_verify(const char *challenge, const char *password, const char *response) 248{ 249 char *str; 250 int res; 251 252 str = heim_cram_md5_create(challenge, password); 253 if (str == NULL) 254 return ENOMEM; 255 256 res = (strcasecmp(str, response) != 0); 257 free(str); 258 259 if (res) 260 return HNTLM_ERR_INVALID_CRAM_MD5; 261 return 0; 262} 263 264