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