1 /* $NetBSD: apop.c,v 1.1.1.2 2014/04/24 12:45:51 pettai 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 *
heim_generate_challenge(const char * hostname)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 *
heim_apop_create(const char * challenge,const char * password)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
heim_apop_verify(const char * challenge,const char * password,const char * response)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
heim_cram_md5_export(const char * password,heim_CRAM_MD5_STATE * state)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
heim_cram_md5_import(void * data,size_t len)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
heim_cram_md5_verify_ctx(heim_cram_md5 ctx,const char * challenge,const char * response)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
heim_cram_md5_free(heim_cram_md5 ctx)222 heim_cram_md5_free(heim_cram_md5 ctx)
223 {
224 memset(ctx, 0, sizeof(*ctx));
225 free(ctx);
226 }
227
228
229 char *
heim_cram_md5_create(const char * challenge,const char * password)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
heim_cram_md5_verify(const char * challenge,const char * password,const char * response)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