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