1/*
2 * Copyright (c) 2006 - 2008 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 <config.h>
37
38#ifdef ENABLE_NTLM
39
40#include <stdio.h>
41#include <stdlib.h>
42#include <assert.h>
43#include <string.h>
44#include <ctype.h>
45#include <errno.h>
46#include <limits.h>
47
48#include <roken.h>
49
50#include <wind.h>
51#include <parse_units.h>
52#include <krb5.h>
53
54
55#define HC_DEPRECATED_CRYPTO
56
57#include "krb5-types.h"
58#include "crypto-headers.h"
59
60#include <heimntlm.h>
61
62/*! \mainpage Heimdal NTLM library
63 *
64 * \section intro Introduction
65 *
66 * Heimdal libheimntlm library is a implementation of the NTLM
67 * protocol, both version 1 and 2. The GSS-API mech that uses this
68 * library adds support for transport encryption and integrity
69 * checking.
70 *
71 * NTLM is a protocol for mutual authentication, its still used in
72 * many protocol where Kerberos is not support, one example is
73 * EAP/X802.1x mechanism LEAP from Microsoft and Cisco.
74 *
75 * This is a support library for the core protocol, its used in
76 * Heimdal to implement and GSS-API mechanism. There is also support
77 * in the KDC to do remote digest authenticiation, this to allow
78 * services to authenticate users w/o direct access to the users ntlm
79 * hashes (same as Kerberos arcfour enctype keys).
80 *
81 * More information about the NTLM protocol can found here
82 * http://davenport.sourceforge.net/ntlm.html .
83 *
84 * The Heimdal projects web page: http://www.h5l.org/
85 *
86 * @section ntlm_example NTLM Example
87 *
88 * Example to to use @ref test_ntlm.c .
89 *
90 * @example test_ntlm.c
91 *
92 * Example how to use the NTLM primitives.
93 *
94 */
95
96/** @defgroup ntlm_core Heimdal NTLM library
97 *
98 * The NTLM core functions implement the string2key generation
99 * function, message encode and decode function, and the hash function
100 * functions.
101 */
102
103struct sec_buffer {
104    uint16_t length;
105    uint16_t allocated;
106    uint32_t offset;
107};
108
109static const unsigned char ntlmsigature[8] = "NTLMSSP\x00";
110
111time_t heim_ntlm_time_skew = 300;
112
113/*
114 *
115 */
116
117#define CHECK(f, e)							\
118    do {								\
119	ret = f;							\
120	if (ret != (ssize_t)(e)) {					\
121	    ret = HNTLM_ERR_DECODE;					\
122	    goto out;							\
123	}								\
124    } while(/*CONSTCOND*/0)
125
126#define CHECK_SIZE(f, e)						\
127    do {								\
128	ssize_t sret = f;						\
129	if (sret != (ssize_t)(e)) {					\
130	    ret = HNTLM_ERR_DECODE;					\
131	    goto out;							\
132	}								\
133    } while(/*CONSTCOND*/0)
134
135#define CHECK_OFFSET(f, e)						\
136    do {								\
137	off_t sret = f;							\
138	if (sret != (e)) {						\
139	    ret = HNTLM_ERR_DECODE;					\
140	    goto out;							\
141	}								\
142    } while(/*CONSTCOND*/0)
143
144
145static struct units ntlm_flag_units[] = {
146#define ntlm_flag(x) { #x, NTLM_##x }
147    ntlm_flag(ENC_56),
148    ntlm_flag(NEG_KEYEX),
149    ntlm_flag(ENC_128),
150    ntlm_flag(MBZ1),
151    ntlm_flag(MBZ2),
152    ntlm_flag(MBZ3),
153    ntlm_flag(NEG_VERSION),
154    ntlm_flag(MBZ4),
155    ntlm_flag(NEG_TARGET_INFO),
156    ntlm_flag(NON_NT_SESSION_KEY),
157    ntlm_flag(MBZ5),
158    ntlm_flag(NEG_IDENTIFY),
159    ntlm_flag(NEG_NTLM2),
160    ntlm_flag(TARGET_SHARE),
161    ntlm_flag(TARGET_SERVER),
162    ntlm_flag(TARGET_DOMAIN),
163    ntlm_flag(NEG_ALWAYS_SIGN),
164    ntlm_flag(MBZ6),
165    ntlm_flag(OEM_SUPPLIED_WORKSTATION),
166    ntlm_flag(OEM_SUPPLIED_DOMAIN),
167    ntlm_flag(NEG_ANONYMOUS),
168    ntlm_flag(NEG_NT_ONLY),
169    ntlm_flag(NEG_NTLM),
170    ntlm_flag(MBZ8),
171    ntlm_flag(NEG_LM_KEY),
172    ntlm_flag(NEG_DATAGRAM),
173    ntlm_flag(NEG_SEAL),
174    ntlm_flag(NEG_SIGN),
175    ntlm_flag(MBZ9),
176    ntlm_flag(NEG_TARGET),
177    ntlm_flag(NEG_OEM),
178    ntlm_flag(NEG_UNICODE),
179#undef ntlm_flag
180    {NULL, 0}
181};
182
183size_t
184heim_ntlm_unparse_flags(uint32_t flags, char *s, size_t len)
185{
186    return unparse_flags(flags, ntlm_flag_units, s, len);
187}
188
189
190/**
191 * heim_ntlm_free_buf frees the ntlm buffer
192 *
193 * @param p buffer to be freed
194 *
195 * @ingroup ntlm_core
196 */
197
198void
199heim_ntlm_free_buf(struct ntlm_buf *p)
200{
201    if (p->data)
202	free(p->data);
203    p->data = NULL;
204    p->length = 0;
205}
206
207
208static int
209ascii2ucs2le(const char *string, int up, struct ntlm_buf *buf)
210{
211    uint16_t *data;
212    size_t len, n;
213    uint8_t *p;
214    int ret;
215
216    ret = wind_utf8ucs2_length(string, &len);
217    if (ret)
218	return ret;
219    if (len > UINT_MAX / sizeof(data[0]))
220	return ERANGE;
221
222    data = malloc(len * sizeof(data[0]));
223    if (data == NULL)
224	return ENOMEM;
225
226    ret = wind_utf8ucs2(string, data, &len);
227    if (ret) {
228	free(data);
229	return ret;
230    }
231
232    if (len == 0) {
233	free(data);
234	buf->data = NULL;
235	buf->length = 0;
236	return 0;
237    }
238
239    /* uppercase string, only handle ascii right now */
240    if (up) {
241	for (n = 0; n < len ; n++) {
242	    if (data[n] < 128)
243		data[n] = toupper((int)data[n]);
244	}
245    }
246
247    buf->length = len * 2;
248    p = buf->data = malloc(buf->length);
249    if (buf->data == NULL && len != 0) {
250	free(data);
251	heim_ntlm_free_buf(buf);
252	return ENOMEM;
253    }
254
255    for (n = 0; n < len ; n++) {
256	p[(n * 2) + 0] = (data[n]     ) & 0xff;
257	p[(n * 2) + 1] = (data[n] >> 8) & 0xff;
258    }
259    memset(data, 0, sizeof(data[0]) * len);
260    free(data);
261
262    return 0;
263}
264
265/*
266 * Sizes in bytes
267 */
268
269#define SIZE_SEC_BUFFER		(2+2+4)
270#define SIZE_OS_VERSION		(8)
271
272/*
273 *
274 */
275
276static krb5_error_code
277ret_sec_buffer(krb5_storage *sp, struct sec_buffer *buf)
278{
279    krb5_error_code ret;
280    CHECK(krb5_ret_uint16(sp, &buf->length), 0);
281    CHECK(krb5_ret_uint16(sp, &buf->allocated), 0);
282    CHECK(krb5_ret_uint32(sp, &buf->offset), 0);
283out:
284    return ret;
285}
286
287static krb5_error_code
288store_sec_buffer(krb5_storage *sp, const struct sec_buffer *buf)
289{
290    krb5_error_code ret;
291    CHECK(krb5_store_uint16(sp, buf->length), 0);
292    CHECK(krb5_store_uint16(sp, buf->allocated), 0);
293    CHECK(krb5_store_uint32(sp, buf->offset), 0);
294out:
295    return ret;
296}
297
298/*
299 * Strings are either OEM or UNICODE. The later is encoded as ucs2 on
300 * wire, but using utf8 in memory.
301 */
302
303static size_t
304len_string(int ucs2, const char *s)
305{
306    if (ucs2) {
307	size_t len;
308	int ret;
309
310	ret = wind_utf8ucs2_length(s, &len);
311	if (ret == 0)
312	    return len * 2;
313	return strlen(s) * 5 * 2;
314    } else {
315	return strlen(s);
316    }
317}
318
319/*
320 *
321 */
322
323static krb5_error_code
324ret_string(krb5_storage *sp, int ucs2, size_t len, char **s)
325{
326    krb5_error_code ret;
327    uint16_t *data = NULL;
328
329    *s = malloc(len + 1);
330    if (*s == NULL)
331	return ENOMEM;
332    CHECK_SIZE(krb5_storage_read(sp, *s, len), len);
333
334    (*s)[len] = '\0';
335
336    if (ucs2) {
337	unsigned int flags = WIND_RW_LE;
338	size_t utf16len = len / 2;
339	size_t utf8len;
340
341	data = malloc(utf16len * sizeof(data[0]));
342	if (data == NULL) {
343	    free(*s); *s = NULL;
344	    ret = ENOMEM;
345	    goto out;
346	}
347
348	ret = wind_ucs2read(*s, len, &flags, data, &utf16len);
349	free(*s); *s = NULL;
350	if (ret) {
351	    goto out;
352	}
353
354	CHECK(wind_ucs2utf8_length(data, utf16len, &utf8len), 0);
355
356	utf8len += 1;
357
358	*s = malloc(utf8len);
359	if (s == NULL) {
360	    ret = ENOMEM;
361	    goto out;
362	}
363
364	CHECK(wind_ucs2utf8(data, utf16len, *s, &utf8len), 0);
365    }
366    ret = 0;
367 out:
368    if (data)
369	free(data);
370
371    return ret;
372}
373
374
375
376static krb5_error_code
377ret_sec_string(krb5_storage *sp, int ucs2, struct sec_buffer *desc, char **s)
378{
379    krb5_error_code ret = 0;
380    CHECK_OFFSET(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
381    CHECK(ret_string(sp, ucs2, desc->length, s), 0);
382 out:
383    return ret;
384}
385
386static krb5_error_code
387put_string(krb5_storage *sp, int ucs2, const char *s)
388{
389    krb5_error_code ret;
390    struct ntlm_buf buf;
391
392    if (ucs2) {
393	ret = ascii2ucs2le(s, 0, &buf);
394	if (ret)
395	    return ret;
396    } else {
397	buf.data = rk_UNCONST(s);
398	buf.length = strlen(s);
399    }
400
401    CHECK_SIZE(krb5_storage_write(sp, buf.data, buf.length), buf.length);
402    if (ucs2)
403	heim_ntlm_free_buf(&buf);
404    ret = 0;
405out:
406    return ret;
407}
408
409/*
410 *
411 */
412
413static krb5_error_code
414ret_buf(krb5_storage *sp, struct sec_buffer *desc, struct ntlm_buf *buf)
415{
416    krb5_error_code ret;
417
418    buf->data = malloc(desc->length);
419    buf->length = desc->length;
420    CHECK_OFFSET(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
421    CHECK_SIZE(krb5_storage_read(sp, buf->data, buf->length), buf->length);
422    ret = 0;
423out:
424    return ret;
425}
426
427static krb5_error_code
428put_buf(krb5_storage *sp, const struct ntlm_buf *buf)
429{
430    krb5_error_code ret;
431    CHECK_SIZE(krb5_storage_write(sp, buf->data, buf->length), buf->length);
432    ret = 0;
433out:
434    return ret;
435}
436
437/**
438 * Frees the ntlm_targetinfo message
439 *
440 * @param ti targetinfo to be freed
441 *
442 * @ingroup ntlm_core
443 */
444
445void
446heim_ntlm_free_targetinfo(struct ntlm_targetinfo *ti)
447{
448    free(ti->servername);
449    free(ti->domainname);
450    free(ti->dnsdomainname);
451    free(ti->dnsservername);
452    free(ti->dnstreename);
453    free(ti->targetname);
454    heim_ntlm_free_buf(&ti->channel_bindings);
455    memset(ti, 0, sizeof(*ti));
456}
457
458static int
459encode_ti_string(krb5_storage *out, uint16_t type, int ucs2, char *s)
460{
461    krb5_error_code ret;
462    CHECK(krb5_store_uint16(out, type), 0);
463    CHECK(krb5_store_uint16(out, len_string(ucs2, s)), 0);
464    CHECK(put_string(out, ucs2, s), 0);
465out:
466    return ret;
467}
468
469/**
470 * Encodes a ntlm_targetinfo message.
471 *
472 * @param ti the ntlm_targetinfo message to encode.
473 * @param ucs2 ignored
474 * @param data is the return buffer with the encoded message, should be
475 * freed with heim_ntlm_free_buf().
476 *
477 * @return In case of success 0 is return, an errors, a errno in what
478 * went wrong.
479 *
480 * @ingroup ntlm_core
481 */
482
483int
484heim_ntlm_encode_targetinfo(const struct ntlm_targetinfo *ti,
485			    int ucs2,
486			    struct ntlm_buf *data)
487{
488    krb5_error_code ret;
489    krb5_storage *out;
490
491    data->data = NULL;
492    data->length = 0;
493
494    out = krb5_storage_emem();
495    if (out == NULL)
496	return ENOMEM;
497
498    krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
499
500    if (ti->servername)
501	CHECK(encode_ti_string(out, 1, ucs2, ti->servername), 0);
502    if (ti->domainname)
503	CHECK(encode_ti_string(out, 2, ucs2, ti->domainname), 0);
504    if (ti->dnsservername)
505	CHECK(encode_ti_string(out, 3, ucs2, ti->dnsservername), 0);
506    if (ti->dnsdomainname)
507	CHECK(encode_ti_string(out, 4, ucs2, ti->dnsdomainname), 0);
508    if (ti->dnstreename)
509	CHECK(encode_ti_string(out, 5, ucs2, ti->dnstreename), 0);
510    if (ti->avflags) {
511	CHECK(krb5_store_uint16(out, 6), 0);
512	CHECK(krb5_store_uint16(out, 4), 0);
513	CHECK(krb5_store_uint32(out, ti->avflags), 0);
514    }
515    if (ti->timestamp) {
516	CHECK(krb5_store_uint16(out, 7), 0);
517	CHECK(krb5_store_uint16(out, 8), 0);
518	CHECK(krb5_store_uint32(out, ti->timestamp & 0xffffffff), 0);
519	CHECK(krb5_store_uint32(out, (ti->timestamp >> 32) & 0xffffffff), 0);
520    }
521    if (ti->targetname) {
522	CHECK(encode_ti_string(out, 9, ucs2, ti->targetname), 0);
523    }
524    if (ti->channel_bindings.length) {
525	CHECK(krb5_store_uint16(out, 10), 0);
526	CHECK(krb5_store_uint16(out, ti->channel_bindings.length), 0);
527	CHECK_SIZE(krb5_storage_write(out, ti->channel_bindings.data, ti->channel_bindings.length), ti->channel_bindings.length);
528    }
529
530    /* end tag */
531    CHECK(krb5_store_int16(out, 0), 0);
532    CHECK(krb5_store_int16(out, 0), 0);
533
534    {
535	krb5_data d;
536	ret = krb5_storage_to_data(out, &d);
537	data->data = d.data;
538	data->length = d.length;
539    }
540out:
541    krb5_storage_free(out);
542    return ret;
543}
544
545/**
546 * Decodes an NTLM targetinfo message
547 *
548 * @param data input data buffer with the encode NTLM targetinfo message
549 * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message).
550 * @param ti the decoded target info, should be freed with heim_ntlm_free_targetinfo().
551 *
552 * @return In case of success 0 is return, an errors, a errno in what
553 * went wrong.
554 *
555 * @ingroup ntlm_core
556 */
557
558int
559heim_ntlm_decode_targetinfo(const struct ntlm_buf *data,
560			    int ucs2,
561			    struct ntlm_targetinfo *ti)
562{
563    uint16_t type, len;
564    krb5_storage *in;
565    int ret = 0, done = 0;
566
567    memset(ti, 0, sizeof(*ti));
568
569    if (data->length == 0)
570	return 0;
571
572    in = krb5_storage_from_readonly_mem(data->data, data->length);
573    if (in == NULL)
574	return ENOMEM;
575    krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
576
577    while (!done) {
578	CHECK(krb5_ret_uint16(in, &type), 0);
579	CHECK(krb5_ret_uint16(in, &len), 0);
580
581	switch (type) {
582	case 0:
583	    done = 1;
584	    break;
585	case 1:
586	    CHECK(ret_string(in, ucs2, len, &ti->servername), 0);
587	    break;
588	case 2:
589	    CHECK(ret_string(in, ucs2, len, &ti->domainname), 0);
590	    break;
591	case 3:
592	    CHECK(ret_string(in, ucs2, len, &ti->dnsservername), 0);
593	    break;
594	case 4:
595	    CHECK(ret_string(in, ucs2, len, &ti->dnsdomainname), 0);
596	    break;
597	case 5:
598	    CHECK(ret_string(in, ucs2, len, &ti->dnstreename), 0);
599	    break;
600	case 6:
601	    CHECK(krb5_ret_uint32(in, &ti->avflags), 0);
602	    break;
603	case 7: {
604	    uint32_t tmp;
605	    CHECK(krb5_ret_uint32(in, &tmp), 0);
606	    ti->timestamp = tmp;
607	    CHECK(krb5_ret_uint32(in, &tmp), 0);
608	    ti->timestamp |= ((uint64_t)tmp) << 32;
609	    break;
610	}
611	case 9:
612	    CHECK(ret_string(in, 1, len, &ti->targetname), 0);
613	    break;
614	case 10:
615	    ti->channel_bindings.data = malloc(len);
616	    if (ti->channel_bindings.data == NULL) {
617		ret = ENOMEM;
618		goto out;
619	    }
620	    ti->channel_bindings.length = len;
621	    CHECK_SIZE(krb5_storage_read(in, ti->channel_bindings.data, len), len);
622	    break;
623	default:
624	    krb5_storage_seek(in, len, SEEK_CUR);
625	    break;
626	}
627    }
628 out:
629    if (in)
630	krb5_storage_free(in);
631    return ret;
632}
633
634static krb5_error_code
635encode_os_version(krb5_storage *out)
636{
637    krb5_error_code ret;
638    CHECK(krb5_store_uint8(out, 0x06), 0);
639    CHECK(krb5_store_uint8(out, 0x01), 0);
640    CHECK(krb5_store_uint16(out, 0x1db0), 0);
641    CHECK(krb5_store_uint8(out, 0x0f), 0); /* ntlm version 15 */
642    CHECK(krb5_store_uint8(out, 0x00), 0);
643    CHECK(krb5_store_uint8(out, 0x00), 0);
644    CHECK(krb5_store_uint8(out, 0x00), 0);
645 out:
646    return ret;
647}
648
649/**
650 * Frees the ntlm_type1 message
651 *
652 * @param data message to be freed
653 *
654 * @ingroup ntlm_core
655 */
656
657void
658heim_ntlm_free_type1(struct ntlm_type1 *data)
659{
660    if (data->domain)
661	free(data->domain);
662    if (data->hostname)
663	free(data->hostname);
664    memset(data, 0, sizeof(*data));
665}
666
667int
668heim_ntlm_decode_type1(const struct ntlm_buf *buf, struct ntlm_type1 *data)
669{
670    krb5_error_code ret;
671    unsigned char sig[8];
672    uint32_t type;
673    struct sec_buffer domain, hostname;
674    krb5_storage *in;
675    int ucs2;
676
677    memset(data, 0, sizeof(*data));
678
679    in = krb5_storage_from_readonly_mem(buf->data, buf->length);
680    if (in == NULL) {
681	ret = ENOMEM;
682	goto out;
683    }
684    krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
685
686    CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
687    CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
688    CHECK(krb5_ret_uint32(in, &type), 0);
689    CHECK(type, 1);
690    CHECK(krb5_ret_uint32(in, &data->flags), 0);
691
692    ucs2 = !!(data->flags & NTLM_NEG_UNICODE);
693
694    /*
695     * domain and hostname are unconditionally encoded regardless of
696     * NTLMSSP_NEGOTIATE_OEM_{HOSTNAME,WORKSTATION}_SUPPLIED flag
697     */
698    CHECK(ret_sec_buffer(in, &domain), 0);
699    CHECK(ret_sec_buffer(in, &hostname), 0);
700
701    if (data->flags & NTLM_NEG_VERSION) {
702	CHECK(krb5_ret_uint32(in, &data->os[0]), 0);
703	CHECK(krb5_ret_uint32(in, &data->os[1]), 0);
704    }
705
706    if (data->flags & NTLM_OEM_SUPPLIED_DOMAIN)
707	CHECK(ret_sec_string(in, ucs2, &domain, &data->domain), 0);
708    if (data->flags & NTLM_OEM_SUPPLIED_WORKSTATION)
709	CHECK(ret_sec_string(in, ucs2, &hostname, &data->hostname), 0);
710
711out:
712    if (in)
713	krb5_storage_free(in);
714    if (ret)
715	heim_ntlm_free_type1(data);
716
717    return ret;
718}
719
720/**
721 * Encodes an ntlm_type1 message.
722 *
723 * @param type1 the ntlm_type1 message to encode.
724 * @param data is the return buffer with the encoded message, should be
725 * freed with heim_ntlm_free_buf().
726 *
727 * @return In case of success 0 is return, an errors, a errno in what
728 * went wrong.
729 *
730 * @ingroup ntlm_core
731 */
732
733int
734heim_ntlm_encode_type1(const struct ntlm_type1 *type1, struct ntlm_buf *data)
735{
736    krb5_error_code ret;
737    struct sec_buffer domain, hostname;
738    krb5_storage *out;
739    uint32_t base, flags;
740    int ucs2 = 0;
741
742    flags = type1->flags;
743    base = 16;
744
745    if (flags & NTLM_NEG_UNICODE)
746	ucs2 = 1;
747
748    if (type1->domain) {
749	base += SIZE_SEC_BUFFER;
750	flags |= NTLM_OEM_SUPPLIED_DOMAIN;
751    }
752    if (type1->hostname) {
753	base += SIZE_SEC_BUFFER;
754	flags |= NTLM_OEM_SUPPLIED_WORKSTATION;
755    }
756    if (flags & NTLM_NEG_VERSION)
757	base += SIZE_OS_VERSION; /* os */
758
759    if (type1->domain) {
760	domain.offset = base;
761	domain.length = len_string(ucs2, type1->domain);
762	domain.allocated = domain.length;
763    } else {
764	domain.offset = 0;
765	domain.length = 0;
766	domain.allocated = 0;
767    }
768
769    if (type1->hostname) {
770	hostname.offset = domain.allocated + domain.offset;
771	hostname.length = len_string(ucs2, type1->hostname);
772	hostname.allocated = hostname.length;
773    } else {
774	hostname.offset = 0;
775	hostname.length = 0;
776	hostname.allocated = 0;
777    }
778
779    out = krb5_storage_emem();
780    if (out == NULL)
781	return ENOMEM;
782
783    krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
784    CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
785	  sizeof(ntlmsigature));
786    CHECK(krb5_store_uint32(out, 1), 0);
787    CHECK(krb5_store_uint32(out, flags), 0);
788
789    CHECK(store_sec_buffer(out, &domain), 0);
790    CHECK(store_sec_buffer(out, &hostname), 0);
791
792    if (flags & NTLM_NEG_VERSION) {
793	CHECK(encode_os_version(out), 0);
794    }
795    if (type1->domain)
796	CHECK(put_string(out, ucs2, type1->domain), 0);
797    if (type1->hostname)
798	CHECK(put_string(out, ucs2, type1->hostname), 0);
799
800    {
801	krb5_data d;
802	ret = krb5_storage_to_data(out, &d);
803	data->data = d.data;
804	data->length = d.length;
805    }
806out:
807    krb5_storage_free(out);
808
809    return ret;
810}
811
812/**
813 * Frees the ntlm_type2 message
814 *
815 * @param data message to be freed
816 *
817 * @ingroup ntlm_core
818 */
819
820void
821heim_ntlm_free_type2(struct ntlm_type2 *data)
822{
823    if (data->targetname)
824	free(data->targetname);
825    heim_ntlm_free_buf(&data->targetinfo);
826    memset(data, 0, sizeof(*data));
827}
828
829int
830heim_ntlm_decode_type2(const struct ntlm_buf *buf, struct ntlm_type2 *type2)
831{
832    krb5_error_code ret;
833    unsigned char sig[8];
834    uint32_t type, ctx[2];
835    struct sec_buffer targetname, targetinfo;
836    krb5_storage *in;
837    int ucs2 = 0;
838
839    memset(type2, 0, sizeof(*type2));
840
841    in = krb5_storage_from_readonly_mem(buf->data, buf->length);
842    if (in == NULL) {
843	ret = ENOMEM;
844	goto out;
845    }
846    krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
847
848    CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
849    CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
850    CHECK(krb5_ret_uint32(in, &type), 0);
851    CHECK(type, 2);
852
853    CHECK(ret_sec_buffer(in, &targetname), 0);
854    CHECK(krb5_ret_uint32(in, &type2->flags), 0);
855    if (type2->flags & NTLM_NEG_UNICODE)
856	ucs2 = 1;
857    CHECK_SIZE(krb5_storage_read(in, type2->challenge, sizeof(type2->challenge)),
858	  sizeof(type2->challenge));
859    CHECK(krb5_ret_uint32(in, &ctx[0]), 0); /* context */
860    CHECK(krb5_ret_uint32(in, &ctx[1]), 0);
861    CHECK(ret_sec_buffer(in, &targetinfo), 0);
862    /* os version */
863    if (type2->flags & NTLM_NEG_VERSION) {
864	CHECK(krb5_ret_uint32(in, &type2->os[0]), 0);
865	CHECK(krb5_ret_uint32(in, &type2->os[1]), 0);
866    }
867
868    CHECK(ret_sec_string(in, ucs2, &targetname, &type2->targetname), 0);
869    CHECK(ret_buf(in, &targetinfo, &type2->targetinfo), 0);
870    ret = 0;
871
872out:
873    if (in)
874	krb5_storage_free(in);
875    if (ret)
876	heim_ntlm_free_type2(type2);
877
878    return ret;
879}
880
881/**
882 * Encodes an ntlm_type2 message.
883 *
884 * @param type2 the ntlm_type2 message to encode.
885 * @param data is the return buffer with the encoded message, should be
886 * freed with heim_ntlm_free_buf().
887 *
888 * @return In case of success 0 is return, an errors, a errno in what
889 * went wrong.
890 *
891 * @ingroup ntlm_core
892 */
893
894int
895heim_ntlm_encode_type2(const struct ntlm_type2 *type2, struct ntlm_buf *data)
896{
897    struct sec_buffer targetname, targetinfo;
898    krb5_error_code ret;
899    krb5_storage *out = NULL;
900    uint32_t base;
901    int ucs2 = 0;
902
903    base = 48;
904
905    if (type2->flags & NTLM_NEG_VERSION)
906	base += SIZE_OS_VERSION;
907
908    if (type2->flags & NTLM_NEG_UNICODE)
909	ucs2 = 1;
910
911    targetname.offset = base;
912    targetname.length = len_string(ucs2, type2->targetname);
913    targetname.allocated = targetname.length;
914
915    targetinfo.offset = targetname.allocated + targetname.offset;
916    targetinfo.length = type2->targetinfo.length;
917    targetinfo.allocated = type2->targetinfo.length;
918
919    out = krb5_storage_emem();
920    if (out == NULL)
921	return ENOMEM;
922
923    krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
924    CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
925	  sizeof(ntlmsigature));
926    CHECK(krb5_store_uint32(out, 2), 0);
927    CHECK(store_sec_buffer(out, &targetname), 0);
928    CHECK(krb5_store_uint32(out, type2->flags), 0);
929    CHECK_SIZE(krb5_storage_write(out, type2->challenge, sizeof(type2->challenge)),
930	  sizeof(type2->challenge));
931    CHECK(krb5_store_uint32(out, 0), 0); /* context */
932    CHECK(krb5_store_uint32(out, 0), 0);
933    CHECK(store_sec_buffer(out, &targetinfo), 0);
934    /* os version */
935    if (type2->flags & NTLM_NEG_VERSION) {
936	CHECK(encode_os_version(out), 0);
937    }
938    CHECK(put_string(out, ucs2, type2->targetname), 0);
939    CHECK_SIZE(krb5_storage_write(out, type2->targetinfo.data,
940			     type2->targetinfo.length),
941	  type2->targetinfo.length);
942
943    {
944	krb5_data d;
945	ret = krb5_storage_to_data(out, &d);
946	data->data = d.data;
947	data->length = d.length;
948    }
949
950out:
951    krb5_storage_free(out);
952
953    return ret;
954}
955
956/**
957 * Frees the ntlm_type3 message
958 *
959 * @param data message to be freed
960 *
961 * @ingroup ntlm_core
962 */
963
964void
965heim_ntlm_free_type3(struct ntlm_type3 *data)
966{
967    heim_ntlm_free_buf(&data->lm);
968    heim_ntlm_free_buf(&data->ntlm);
969    if (data->targetname)
970	free(data->targetname);
971    if (data->username)
972	free(data->username);
973    if (data->ws)
974	free(data->ws);
975    heim_ntlm_free_buf(&data->sessionkey);
976    memset(data, 0, sizeof(*data));
977}
978
979/*
980 *
981 */
982
983int
984heim_ntlm_decode_type3(const struct ntlm_buf *buf,
985		       int ucs2,
986		       struct ntlm_type3 *type3)
987{
988    krb5_error_code ret;
989    unsigned char sig[8];
990    uint32_t type;
991    krb5_storage *in;
992    struct sec_buffer lm, ntlm, target, username, sessionkey, ws;
993    uint32_t min_offset = 0xffffffff;
994
995    memset(type3, 0, sizeof(*type3));
996    memset(&sessionkey, 0, sizeof(sessionkey));
997
998    in = krb5_storage_from_readonly_mem(buf->data, buf->length);
999    if (in == NULL) {
1000	ret = ENOMEM;
1001	goto out;
1002    }
1003    krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
1004
1005    CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
1006    CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
1007    CHECK(krb5_ret_uint32(in, &type), 0);
1008    CHECK(type, 3);
1009    CHECK(ret_sec_buffer(in, &lm), 0);
1010    if (lm.allocated)
1011	min_offset = MIN(min_offset, lm.offset);
1012    CHECK(ret_sec_buffer(in, &ntlm), 0);
1013    if (ntlm.allocated)
1014	min_offset = MIN(min_offset, ntlm.offset);
1015    CHECK(ret_sec_buffer(in, &target), 0);
1016    min_offset = MIN(min_offset, target.offset);
1017    CHECK(ret_sec_buffer(in, &username), 0);
1018    min_offset = MIN(min_offset, username.offset);
1019    CHECK(ret_sec_buffer(in, &ws), 0);
1020    if (ws.allocated)
1021	min_offset = MIN(min_offset, ws.offset);
1022
1023    if (min_offset >= 52) {
1024	CHECK(ret_sec_buffer(in, &sessionkey), 0);
1025	min_offset = MIN(min_offset, sessionkey.offset);
1026	CHECK(krb5_ret_uint32(in, &type3->flags), 0);
1027    }
1028    if (min_offset >= 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION) {
1029	CHECK(krb5_ret_uint32(in, &type3->os[0]), 0);
1030	CHECK(krb5_ret_uint32(in, &type3->os[1]), 0);
1031    }
1032    if (min_offset >= 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION + 16) {
1033	type3->mic_offset = 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION;
1034	CHECK_SIZE(krb5_storage_read(in, type3->mic, sizeof(type3->mic)), sizeof(type3->mic));
1035    } else
1036	type3->mic_offset = 0;
1037    CHECK(ret_buf(in, &lm, &type3->lm), 0);
1038    CHECK(ret_buf(in, &ntlm, &type3->ntlm), 0);
1039    CHECK(ret_sec_string(in, ucs2, &target, &type3->targetname), 0);
1040    CHECK(ret_sec_string(in, ucs2, &username, &type3->username), 0);
1041    CHECK(ret_sec_string(in, ucs2, &ws, &type3->ws), 0);
1042    if (sessionkey.offset)
1043	CHECK(ret_buf(in, &sessionkey, &type3->sessionkey), 0);
1044
1045out:
1046    if (in)
1047	krb5_storage_free(in);
1048    if (ret)
1049	heim_ntlm_free_type3(type3);
1050
1051    return ret;
1052}
1053
1054/**
1055 * Encodes an ntlm_type3 message.
1056 *
1057 * @param type3 the ntlm_type3 message to encode.
1058 * @param data is the return buffer with the encoded message, should be
1059 * freed with heim_ntlm_free_buf().
1060 *
1061 * @return In case of success 0 is return, an errors, a errno in what
1062 * went wrong.
1063 *
1064 * @ingroup ntlm_core
1065 */
1066
1067int
1068heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data, size_t *mic_offset)
1069{
1070    struct sec_buffer lm, ntlm, target, username, sessionkey, ws;
1071    krb5_error_code ret;
1072    krb5_storage *out = NULL;
1073    uint32_t base;
1074    int ucs2 = 0;
1075
1076    memset(&lm, 0, sizeof(lm));
1077    memset(&ntlm, 0, sizeof(ntlm));
1078    memset(&target, 0, sizeof(target));
1079    memset(&username, 0, sizeof(username));
1080    memset(&ws, 0, sizeof(ws));
1081    memset(&sessionkey, 0, sizeof(sessionkey));
1082
1083    base = 52;
1084
1085    base += 8; /* sessionkey sec buf */
1086    base += 4; /* flags */
1087    if (type3->flags & NTLM_NEG_VERSION)
1088	base += SIZE_OS_VERSION; /* os flags */
1089
1090    if (mic_offset) {
1091	*mic_offset = base;
1092	base += 16;
1093    }
1094
1095    if (type3->flags & NTLM_NEG_UNICODE)
1096	ucs2 = 1;
1097
1098    target.offset = base;
1099    target.length = len_string(ucs2, type3->targetname);
1100    target.allocated = target.length;
1101
1102    username.offset = target.offset + target.allocated;
1103    username.length = len_string(ucs2, type3->username);
1104    username.allocated = username.length;
1105
1106    ws.offset = username.offset + username.allocated;
1107    ws.length = len_string(ucs2, type3->ws);
1108    ws.allocated = ws.length;
1109
1110    lm.offset = ws.offset + ws.allocated;
1111    lm.length = type3->lm.length;
1112    lm.allocated = type3->lm.length;
1113
1114    ntlm.offset = lm.offset + lm.allocated;
1115    ntlm.length = type3->ntlm.length;
1116    ntlm.allocated = ntlm.length;
1117
1118    sessionkey.offset = ntlm.offset + ntlm.allocated;
1119    sessionkey.length = type3->sessionkey.length;
1120    sessionkey.allocated = type3->sessionkey.length;
1121
1122    out = krb5_storage_emem();
1123    if (out == NULL)
1124	return ENOMEM;
1125
1126    krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
1127    CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
1128	  sizeof(ntlmsigature));
1129    CHECK(krb5_store_uint32(out, 3), 0);
1130
1131    CHECK(store_sec_buffer(out, &lm), 0);
1132    CHECK(store_sec_buffer(out, &ntlm), 0);
1133    CHECK(store_sec_buffer(out, &target), 0);
1134    CHECK(store_sec_buffer(out, &username), 0);
1135    CHECK(store_sec_buffer(out, &ws), 0);
1136    CHECK(store_sec_buffer(out, &sessionkey), 0);
1137    CHECK(krb5_store_uint32(out, type3->flags), 0);
1138
1139    /* os version */
1140    if (type3->flags & NTLM_NEG_VERSION) {
1141	CHECK(encode_os_version(out), 0);
1142    }
1143
1144    if (mic_offset) {
1145	static const uint8_t buf[16] = { 0 };
1146	CHECK_SIZE(krb5_storage_write(out, buf, sizeof(buf)), sizeof(buf));
1147    }
1148
1149    CHECK(put_string(out, ucs2, type3->targetname), 0);
1150    CHECK(put_string(out, ucs2, type3->username), 0);
1151    CHECK(put_string(out, ucs2, type3->ws), 0);
1152    CHECK(put_buf(out, &type3->lm), 0);
1153    CHECK(put_buf(out, &type3->ntlm), 0);
1154    CHECK(put_buf(out, &type3->sessionkey), 0);
1155
1156    {
1157	krb5_data d;
1158	ret = krb5_storage_to_data(out, &d);
1159	data->data = d.data;
1160	data->length = d.length;
1161    }
1162
1163out:
1164    krb5_storage_free(out);
1165
1166    return ret;
1167}
1168
1169
1170/*
1171 *
1172 */
1173
1174static void
1175splitandenc(unsigned char *hash,
1176	    unsigned char *challenge,
1177	    unsigned char *answer)
1178{
1179    EVP_CIPHER_CTX ctx;
1180    unsigned char key[8];
1181
1182    key[0] =  hash[0];
1183    key[1] = (hash[0] << 7) | (hash[1] >> 1);
1184    key[2] = (hash[1] << 6) | (hash[2] >> 2);
1185    key[3] = (hash[2] << 5) | (hash[3] >> 3);
1186    key[4] = (hash[3] << 4) | (hash[4] >> 4);
1187    key[5] = (hash[4] << 3) | (hash[5] >> 5);
1188    key[6] = (hash[5] << 2) | (hash[6] >> 6);
1189    key[7] = (hash[6] << 1);
1190
1191    EVP_CIPHER_CTX_init(&ctx);
1192
1193    EVP_CipherInit_ex(&ctx, EVP_des_cbc(), NULL, key, NULL, 1);
1194    EVP_Cipher(&ctx, answer, challenge, 8);
1195    EVP_CIPHER_CTX_cleanup(&ctx);
1196    memset(key, 0, sizeof(key));
1197}
1198
1199/**
1200 * Calculate the NTLM key, the password is assumed to be in UTF8.
1201 *
1202 * @param password password to calcute the key for.
1203 * @param key calcuted key, should be freed with heim_ntlm_free_buf().
1204 *
1205 * @return In case of success 0 is return, an errors, a errno in what
1206 * went wrong.
1207 *
1208 * @ingroup ntlm_core
1209 */
1210
1211int
1212heim_ntlm_nt_key(const char *password, struct ntlm_buf *key)
1213{
1214    struct ntlm_buf buf;
1215    CCDigestRef m;
1216    int ret;
1217
1218    key->data = malloc(CC_MD4_DIGEST_LENGTH);
1219    if (key->data == NULL)
1220	return ENOMEM;
1221    key->length = CC_MD4_DIGEST_LENGTH;
1222
1223    ret = ascii2ucs2le(password, 0, &buf);
1224    if (ret) {
1225	heim_ntlm_free_buf(key);
1226	return ret;
1227    }
1228
1229    m = CCDigestCreate(kCCDigestMD4);
1230    if (m == NULL) {
1231	heim_ntlm_free_buf(key);
1232	heim_ntlm_free_buf(&buf);
1233	return ENOMEM;
1234    }
1235
1236    CCDigestUpdate(m, buf.data, buf.length);
1237    CCDigestFinal(m, key->data);
1238    CCDigestDestroy(m);
1239
1240    heim_ntlm_free_buf(&buf);
1241    return 0;
1242}
1243
1244/**
1245 * Calculate NTLMv1 response hash
1246 *
1247 * @param key the ntlm v1 key
1248 * @param len length of key
1249 * @param challenge sent by the server
1250 * @param answer calculated answer, should be freed with heim_ntlm_free_buf().
1251 *
1252 * @return In case of success 0 is return, an errors, a errno in what
1253 * went wrong.
1254 *
1255 * @ingroup ntlm_core
1256 */
1257
1258int
1259heim_ntlm_calculate_ntlm1(void *key, size_t len,
1260			  unsigned char challenge[8],
1261			  struct ntlm_buf *answer)
1262{
1263    unsigned char res[21];
1264
1265    if (len != CC_MD4_DIGEST_LENGTH)
1266	return HNTLM_ERR_INVALID_LENGTH;
1267
1268    memcpy(res, key, len);
1269    memset(&res[CC_MD4_DIGEST_LENGTH], 0, sizeof(res) - CC_MD4_DIGEST_LENGTH);
1270
1271    answer->data = malloc(24);
1272    if (answer->data == NULL)
1273	return ENOMEM;
1274    answer->length = 24;
1275
1276    splitandenc(&res[0],  challenge, ((unsigned char *)answer->data) + 0);
1277    splitandenc(&res[7],  challenge, ((unsigned char *)answer->data) + 8);
1278    splitandenc(&res[14], challenge, ((unsigned char *)answer->data) + 16);
1279
1280    return 0;
1281}
1282
1283int
1284heim_ntlm_v1_base_session(void *key, size_t len,
1285			  struct ntlm_buf *session)
1286{
1287    CCDigestRef m;
1288
1289    session->length = CC_MD4_DIGEST_LENGTH;
1290    session->data = malloc(session->length);
1291    if (session->data == NULL) {
1292	session->length = 0;
1293	return ENOMEM;
1294    }
1295
1296    m = CCDigestCreate(kCCDigestMD4);
1297    if (m == NULL) {
1298	heim_ntlm_free_buf(session);
1299	return ENOMEM;
1300    }
1301    CCDigestUpdate(m, key, len);
1302    CCDigestFinal(m, session->data);
1303    CCDigestDestroy(m);
1304
1305    return 0;
1306}
1307
1308int
1309heim_ntlm_v2_base_session(void *key, size_t len,
1310			  struct ntlm_buf *ntlmResponse,
1311			  struct ntlm_buf *session)
1312{
1313    if (ntlmResponse->length <= 16)
1314        return HNTLM_ERR_INVALID_LENGTH;
1315
1316    session->data = malloc(16);
1317    if (session->data == NULL)
1318	return ENOMEM;
1319    session->length = 16;
1320
1321    /* Note: key is the NTLMv2 key */
1322    CCHmac(kCCHmacAlgMD5, key, len, ntlmResponse->data, 16, session->data);
1323
1324    return 0;
1325}
1326
1327
1328int
1329heim_ntlm_keyex_wrap(struct ntlm_buf *base_session,
1330		     struct ntlm_buf *session,
1331		     struct ntlm_buf *encryptedSession)
1332{
1333    EVP_CIPHER_CTX c;
1334    int ret;
1335
1336    if (base_session->length != CC_MD4_DIGEST_LENGTH)
1337	return HNTLM_ERR_INVALID_LENGTH;
1338
1339    session->length = CC_MD4_DIGEST_LENGTH;
1340    session->data = malloc(session->length);
1341    if (session->data == NULL) {
1342	session->length = 0;
1343	return ENOMEM;
1344    }
1345    encryptedSession->length = CC_MD4_DIGEST_LENGTH;
1346    encryptedSession->data = malloc(encryptedSession->length);
1347    if (encryptedSession->data == NULL) {
1348	heim_ntlm_free_buf(session);
1349	encryptedSession->length = 0;
1350	return ENOMEM;
1351    }
1352
1353    EVP_CIPHER_CTX_init(&c);
1354
1355    ret = EVP_CipherInit_ex(&c, EVP_rc4(), NULL, base_session->data, NULL, 1);
1356    if (ret != 1) {
1357	EVP_CIPHER_CTX_cleanup(&c);
1358	heim_ntlm_free_buf(encryptedSession);
1359	heim_ntlm_free_buf(session);
1360	return HNTLM_ERR_CRYPTO;
1361    }
1362
1363    if (CCRandomCopyBytes(kCCRandomDefault, session->data, session->length)) {
1364	EVP_CIPHER_CTX_cleanup(&c);
1365	heim_ntlm_free_buf(encryptedSession);
1366	heim_ntlm_free_buf(session);
1367	return HNTLM_ERR_RAND;
1368    }
1369
1370    EVP_Cipher(&c, encryptedSession->data, session->data, encryptedSession->length);
1371    EVP_CIPHER_CTX_cleanup(&c);
1372
1373    return 0;
1374
1375
1376
1377}
1378
1379/**
1380 * Generates an NTLMv1 session random with assosited session master key.
1381 *
1382 * @param key the ntlm v1 key
1383 * @param len length of key
1384 * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
1385 * @param master calculated session master key, should be freed with heim_ntlm_free_buf().
1386 *
1387 * @return In case of success 0 is return, an errors, a errno in what
1388 * went wrong.
1389 *
1390 * @ingroup ntlm_core
1391 */
1392
1393int
1394heim_ntlm_build_ntlm1_master(void *key, size_t len,
1395			     struct ntlm_buf *session,
1396			     struct ntlm_buf *master)
1397{
1398    struct ntlm_buf sess;
1399    int ret;
1400
1401    ret = heim_ntlm_v1_base_session(key, len, &sess);
1402    if (ret)
1403	return ret;
1404
1405    ret = heim_ntlm_keyex_wrap(&sess, session, master);
1406    heim_ntlm_free_buf(&sess);
1407
1408    return ret;
1409}
1410
1411/**
1412 * Generates an NTLMv2 session random with associated session master key.
1413 *
1414 * @param key the NTLMv2 key
1415 * @param len length of key
1416 * @param blob the NTLMv2 "blob"
1417 * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
1418 * @param master calculated session master key, should be freed with heim_ntlm_free_buf().
1419 *
1420 * @return In case of success 0 is return, an errors, a errno in what
1421 * went wrong.
1422 *
1423 * @ingroup ntlm_core
1424 */
1425
1426
1427int
1428heim_ntlm_build_ntlm2_master(void *key, size_t len,
1429			     struct ntlm_buf *blob,
1430			     struct ntlm_buf *session,
1431			     struct ntlm_buf *master)
1432{
1433    struct ntlm_buf sess;
1434    int ret;
1435
1436    ret = heim_ntlm_v2_base_session(key, len, blob, &sess);
1437    if (ret)
1438	return ret;
1439
1440    ret = heim_ntlm_keyex_wrap(&sess, session, master);
1441    heim_ntlm_free_buf(&sess);
1442
1443    return ret;
1444}
1445
1446/**
1447 * Given a key and encrypted session, unwrap the session key
1448 *
1449 * @param baseKey the sessionBaseKey
1450 * @param encryptedSession encrypted session, type3.session field.
1451 * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
1452 *
1453 * @return In case of success 0 is return, an errors, a errno in what
1454 * went wrong.
1455 *
1456 * @ingroup ntlm_core
1457 */
1458
1459int
1460heim_ntlm_keyex_unwrap(struct ntlm_buf *baseKey,
1461		       struct ntlm_buf *encryptedSession,
1462		       struct ntlm_buf *session)
1463{
1464    EVP_CIPHER_CTX c;
1465
1466    memset(session, 0, sizeof(*session));
1467
1468    if (encryptedSession->length != CC_MD4_DIGEST_LENGTH)
1469	return HNTLM_ERR_INVALID_LENGTH;
1470    if (baseKey->length != CC_MD4_DIGEST_LENGTH)
1471	return HNTLM_ERR_INVALID_LENGTH;
1472
1473    session->length = CC_MD4_DIGEST_LENGTH;
1474    session->data = malloc(session->length);
1475    if (session->data == NULL) {
1476	session->length = 0;
1477	return ENOMEM;
1478    }
1479    EVP_CIPHER_CTX_init(&c);
1480
1481    if (EVP_CipherInit_ex(&c, EVP_rc4(), NULL, baseKey->data, NULL, 0) != 1) {
1482	EVP_CIPHER_CTX_cleanup(&c);
1483	heim_ntlm_free_buf(session);
1484	return HNTLM_ERR_CRYPTO;
1485    }
1486
1487    EVP_Cipher(&c, session->data, encryptedSession->data, session->length);
1488    EVP_CIPHER_CTX_cleanup(&c);
1489
1490    return 0;
1491}
1492
1493
1494/**
1495 * Generates an NTLMv2 session key.
1496 *
1497 * @param key the ntlm key
1498 * @param len length of key
1499 * @param username name of the user, as sent in the message, assumed to be in UTF8.
1500 * @param target the name of the target, assumed to be in UTF8.
1501 * @param upper_case_target upper case the target, should not be used only for legacy systems
1502 * @param ntlmv2 the ntlmv2 session key
1503 *
1504 * @return 0 on success, or an error code on failure.
1505 *
1506 * @ingroup ntlm_core
1507 */
1508
1509int
1510heim_ntlm_ntlmv2_key(const void *key, size_t len,
1511		     const char *username,
1512		     const char *target,
1513		     int upper_case_target,
1514		     unsigned char ntlmv2[16])
1515{
1516    int ret;
1517    CCHmacContext c;
1518
1519    CCHmacInit(&c, kCCHmacAlgMD5, key, len);
1520    {
1521	struct ntlm_buf buf;
1522	/* uppercase username and turn it into ucs2-le */
1523	ret = ascii2ucs2le(username, 1, &buf);
1524	if (ret)
1525	    goto out;
1526	CCHmacUpdate(&c, buf.data, buf.length);
1527	free(buf.data);
1528	/* turn target into ucs2-le */
1529	ret = ascii2ucs2le(target, upper_case_target, &buf);
1530	if (ret)
1531	    goto out;
1532	CCHmacUpdate(&c, buf.data, buf.length);
1533	free(buf.data);
1534    }
1535    CCHmacFinal(&c, ntlmv2);
1536 out:
1537    memset(&c, 0, sizeof(c));
1538
1539    return ret;
1540}
1541
1542/*
1543 *
1544 */
1545
1546#define NTTIME_EPOCH 0x019DB1DED53E8000LL
1547
1548uint64_t
1549heim_ntlm_unix2ts_time(time_t unix_time)
1550{
1551    long long wt;
1552    wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
1553    return wt;
1554}
1555
1556time_t
1557heim_ntlm_ts2unixtime(uint64_t t)
1558{
1559    t = ((t - (uint64_t)NTTIME_EPOCH) / (uint64_t)10000000);
1560    if (t > (((uint64_t)(time_t)(~(uint64_t)0)) >> 1))
1561	return 0;
1562    return (time_t)t;
1563}
1564
1565/**
1566 * Calculate LMv2 response
1567 *
1568 * @param key the ntlm key
1569 * @param len length of key
1570 * @param username name of the user, as sent in the message, assumed to be in UTF8.
1571 * @param target the name of the target, assumed to be in UTF8.
1572 * @param serverchallenge challenge as sent by the server in the type2 message.
1573 * @param ntlmv2 calculated session key
1574 * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
1575 *
1576 * @return In case of success 0 is return, an errors, a errno in what
1577 * went wrong.
1578 *
1579 * @ingroup ntlm_core
1580 */
1581
1582int
1583heim_ntlm_calculate_lm2(const void *key, size_t len,
1584			const char *username,
1585			const char *target,
1586			const unsigned char serverchallenge[8],
1587			unsigned char ntlmv2[16],
1588			struct ntlm_buf *answer)
1589{
1590    unsigned char clientchallenge[8];
1591
1592    if (CCRandomCopyBytes(kCCRandomDefault, clientchallenge, sizeof(clientchallenge)))
1593	return HNTLM_ERR_RAND;
1594
1595    /* calculate ntlmv2 key */
1596
1597    heim_ntlm_ntlmv2_key(key, len, username, target, 0, ntlmv2);
1598
1599    answer->data = malloc(24);
1600    if (answer->data == NULL)
1601        return ENOMEM;
1602    answer->length = 24;
1603
1604    heim_ntlm_derive_ntlm2_sess(ntlmv2, clientchallenge, 8,
1605				serverchallenge, answer->data);
1606
1607    memcpy((uint8_t *)answer->data + 16, clientchallenge, 8);
1608
1609    return 0;
1610}
1611
1612
1613/**
1614 * Calculate NTLMv2 response
1615 *
1616 * @param key the ntlm key
1617 * @param len length of key
1618 * @param username name of the user, as sent in the message, assumed to be in UTF8.
1619 * @param target the name of the target, assumed to be in UTF8.
1620 * @param serverchallenge challenge as sent by the server in the type2 message.
1621 * @param infotarget infotarget as sent by the server in the type2 message.
1622 * @param ntlmv2 calculated session key
1623 * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
1624 *
1625 * @return In case of success 0 is return, an errors, a errno in what
1626 * went wrong.
1627 *
1628 * @ingroup ntlm_core
1629 */
1630
1631int
1632heim_ntlm_calculate_ntlm2(const void *key, size_t len,
1633			  const char *username,
1634			  const char *target,
1635			  const unsigned char serverchallenge[8],
1636			  const struct ntlm_buf *infotarget,
1637			  unsigned char ntlmv2[16],
1638			  struct ntlm_buf *answer)
1639{
1640    krb5_error_code ret;
1641    krb5_data data;
1642    unsigned char ntlmv2answer[16];
1643    krb5_storage *sp;
1644    unsigned char clientchallenge[8];
1645    uint64_t t;
1646
1647    t = heim_ntlm_unix2ts_time(time(NULL));
1648
1649    if (CCRandomCopyBytes(kCCRandomDefault, clientchallenge, sizeof(clientchallenge)))
1650	return HNTLM_ERR_RAND;
1651
1652    /* calculate ntlmv2 key */
1653
1654    heim_ntlm_ntlmv2_key(key, len, username, target, 0, ntlmv2);
1655
1656    /* calculate and build ntlmv2 answer */
1657
1658    sp = krb5_storage_emem();
1659    if (sp == NULL)
1660	return ENOMEM;
1661    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1662
1663    CHECK(krb5_store_uint32(sp, 0x00000101), 0);
1664    CHECK(krb5_store_uint32(sp, 0), 0);
1665    /* timestamp le 64 bit ts */
1666    CHECK(krb5_store_uint32(sp, t & 0xffffffff), 0);
1667    CHECK(krb5_store_uint32(sp, t >> 32), 0);
1668
1669    CHECK_SIZE(krb5_storage_write(sp, clientchallenge, 8), 8);
1670
1671    CHECK(krb5_store_uint32(sp, 0), 0);  /* Z(4) */
1672    CHECK_SIZE(krb5_storage_write(sp, infotarget->data, infotarget->length),
1673	  infotarget->length);
1674
1675    /*
1676     * These last 4 bytes(Z(4)) are not documented by MicroSoft and
1677     * SnowLeopard doesn't send them, Lion expected them to be there,
1678     * so we have to continue to send them. That is ok, since everyone
1679     * else (except Snow) seems to do that too.
1680     */
1681    CHECK(krb5_store_uint32(sp, 0), 0); /* Z(4) */
1682
1683    CHECK(krb5_storage_to_data(sp, &data), 0);
1684    krb5_storage_free(sp);
1685    sp = NULL;
1686
1687    heim_ntlm_derive_ntlm2_sess(ntlmv2, data.data, data.length, serverchallenge, ntlmv2answer);
1688
1689    sp = krb5_storage_emem();
1690    if (sp == NULL) {
1691	krb5_data_free(&data);
1692	return ENOMEM;
1693    }
1694
1695    CHECK_SIZE(krb5_storage_write(sp, ntlmv2answer, 16), 16);
1696    CHECK_SIZE(krb5_storage_write(sp, data.data, data.length), data.length);
1697    krb5_data_free(&data);
1698
1699    CHECK(krb5_storage_to_data(sp, &data), 0);
1700    krb5_storage_free(sp);
1701    sp = NULL;
1702
1703    answer->data = data.data;
1704    answer->length = data.length;
1705
1706    return 0;
1707out:
1708    if (sp)
1709	krb5_storage_free(sp);
1710    return ret;
1711}
1712
1713static const int authtimediff = 3600 * 2; /* 2 hours */
1714
1715static int
1716verify_ntlm2(const void *key, size_t len,
1717	     const char *username,
1718	     const char *target,
1719	     int upper_case_target,
1720	     time_t now,
1721	     const unsigned char serverchallenge[8],
1722	     const struct ntlm_buf *answer,
1723	     struct ntlm_buf *infotarget,
1724	     unsigned char ntlmv2[16])
1725{
1726    krb5_error_code ret;
1727    unsigned char clientanswer[16];
1728    unsigned char clientnonce[8];
1729    unsigned char serveranswer[16];
1730    krb5_storage *sp;
1731    uint64_t t;
1732    time_t authtime;
1733    uint32_t temp;
1734
1735    infotarget->length = 0;
1736    infotarget->data = NULL;
1737
1738    if (answer->length < 16)
1739	return HNTLM_ERR_INVALID_LENGTH;
1740
1741    if (now == 0)
1742	now = time(NULL);
1743
1744    /* calculate ntlmv2 key */
1745
1746    heim_ntlm_ntlmv2_key(key, len, username, target, upper_case_target, ntlmv2);
1747
1748    /* calculate and build ntlmv2 answer */
1749
1750    sp = krb5_storage_from_readonly_mem(answer->data, answer->length);
1751    if (sp == NULL)
1752	return ENOMEM;
1753    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1754
1755    CHECK_SIZE(krb5_storage_read(sp, clientanswer, 16), 16);
1756
1757    CHECK(krb5_ret_uint32(sp, &temp), 0);
1758    CHECK(temp, 0x00000101);
1759    CHECK(krb5_ret_uint32(sp, &temp), 0);
1760    CHECK(temp, 0);
1761    /* timestamp le 64 bit ts */
1762    CHECK(krb5_ret_uint32(sp, &temp), 0);
1763    t = temp;
1764    CHECK(krb5_ret_uint32(sp, &temp), 0);
1765    t |= ((uint64_t)temp)<< 32;
1766
1767    authtime = heim_ntlm_ts2unixtime(t);
1768
1769    if (abs((int)(authtime - now)) > authtimediff) {
1770	ret = HNTLM_ERR_TIME_SKEW;
1771	goto out;
1772    }
1773
1774    /* client challenge */
1775    CHECK_SIZE(krb5_storage_read(sp, clientnonce, 8), 8);
1776
1777    CHECK(krb5_ret_uint32(sp, &temp), 0); /* Z(4) */
1778
1779    /* let pick up targetinfo */
1780    infotarget->length = answer->length - (size_t)krb5_storage_seek(sp, 0, SEEK_CUR);
1781    if (infotarget->length < 4) {
1782	ret = HNTLM_ERR_INVALID_LENGTH;
1783	goto out;
1784    }
1785    infotarget->data = malloc(infotarget->length);
1786    if (infotarget->data == NULL) {
1787	ret = ENOMEM;
1788	goto out;
1789    }
1790    CHECK_SIZE(krb5_storage_read(sp, infotarget->data, infotarget->length),
1791	  infotarget->length);
1792
1793    krb5_storage_free(sp);
1794    sp = NULL;
1795
1796    if (answer->length < 16) {
1797	ret = HNTLM_ERR_INVALID_LENGTH;
1798	goto out;
1799    }
1800
1801    heim_ntlm_derive_ntlm2_sess(ntlmv2,
1802				((unsigned char *)answer->data) + 16, answer->length - 16,
1803				serverchallenge,
1804				serveranswer);
1805
1806    if (memcmp(serveranswer, clientanswer, 16) != 0) {
1807	heim_ntlm_free_buf(infotarget);
1808	return HNTLM_ERR_AUTH;
1809    }
1810
1811    return 0;
1812out:
1813    heim_ntlm_free_buf(infotarget);
1814    if (sp)
1815	krb5_storage_free(sp);
1816    return ret;
1817}
1818
1819/**
1820 * Verify NTLMv2 response.
1821 *
1822 * @param key the ntlm key
1823 * @param len length of key
1824 * @param username name of the user, as sent in the message, assumed to be in UTF8.
1825 * @param target the name of the target, assumed to be in UTF8.
1826 * @param now the time now (0 if the library should pick it up itself)
1827 * @param serverchallenge challenge as sent by the server in the type2 message.
1828 * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
1829 * @param infotarget infotarget as sent by the server in the type2 message.
1830 * @param ntlmv2 calculated session key
1831 *
1832 * @return In case of success 0 is return, an errors, a errno in what
1833 * went wrong.
1834 *
1835 * @ingroup ntlm_core
1836 */
1837
1838int
1839heim_ntlm_verify_ntlm2(const void *key, size_t len,
1840		       const char *username,
1841		       const char *target,
1842		       time_t now,
1843		       const unsigned char serverchallenge[8],
1844		       const struct ntlm_buf *answer,
1845		       struct ntlm_buf *infotarget,
1846		       unsigned char ntlmv2[16])
1847{
1848    int ret;
1849
1850    /**
1851     * First check with the domain as the client passed it to the function.
1852     */
1853
1854    ret = verify_ntlm2(key, len, username, target, 0, now,
1855		       serverchallenge, answer, infotarget, ntlmv2);
1856
1857    /**
1858     * Second check with domain uppercased.
1859     */
1860
1861    if (ret)
1862	ret = verify_ntlm2(key, len, username, target, 1, now,
1863			   serverchallenge, answer, infotarget, ntlmv2);
1864
1865    /**
1866     * Third check with empty domain.
1867     */
1868    if (ret)
1869	ret = verify_ntlm2(key, len, username, "", 0, now,
1870			   serverchallenge, answer, infotarget, ntlmv2);
1871    return ret;
1872}
1873
1874/*
1875 * Calculate the NTLM2 Session Response
1876 *
1877 * @param clnt_nonce client nonce
1878 * @param svr_chal server challage
1879 * @param ntlm2_hash ntlm hash
1880 * @param lm The LM response, should be freed with heim_ntlm_free_buf().
1881 * @param ntlm The NTLM response, should be freed with heim_ntlm_free_buf().
1882 *
1883 * @return In case of success 0 is return, an errors, a errno in what
1884 * went wrong.
1885 *
1886 * @ingroup ntlm_core
1887 */
1888
1889int
1890heim_ntlm_calculate_ntlm2_sess(const unsigned char clnt_nonce[8],
1891			       const unsigned char svr_chal[8],
1892			       const unsigned char ntlm_hash[16],
1893			       struct ntlm_buf *lm,
1894			       struct ntlm_buf *ntlm)
1895{
1896    unsigned char ntlm2_sess_hash[8];
1897    unsigned char res[21], *resp;
1898    int code;
1899
1900    code = heim_ntlm_calculate_ntlm2_sess_hash(clnt_nonce, svr_chal,
1901					       ntlm2_sess_hash);
1902    if (code) {
1903	return code;
1904    }
1905
1906    lm->data = malloc(24);
1907    if (lm->data == NULL) {
1908	return ENOMEM;
1909    }
1910    lm->length = 24;
1911
1912    ntlm->data = malloc(24);
1913    if (ntlm->data == NULL) {
1914	free(lm->data);
1915	lm->data = NULL;
1916	return ENOMEM;
1917    }
1918    ntlm->length = 24;
1919
1920    /* first setup the lm resp */
1921    memset(lm->data, 0, 24);
1922    memcpy(lm->data, clnt_nonce, 8);
1923
1924    memset(res, 0, sizeof(res));
1925    memcpy(res, ntlm_hash, 16);
1926
1927    resp = ntlm->data;
1928    splitandenc(&res[0], ntlm2_sess_hash, resp + 0);
1929    splitandenc(&res[7], ntlm2_sess_hash, resp + 8);
1930    splitandenc(&res[14], ntlm2_sess_hash, resp + 16);
1931
1932    return 0;
1933}
1934
1935
1936/*
1937 * Calculate the NTLM2 Session "Verifier"
1938 *
1939 * @param clnt_nonce client nonce
1940 * @param svr_chal server challage
1941 * @param hash The NTLM session verifier
1942 *
1943 * @return In case of success 0 is return, an errors, a errno in what
1944 * went wrong.
1945 *
1946 * @ingroup ntlm_core
1947 */
1948
1949int
1950heim_ntlm_calculate_ntlm2_sess_hash(const unsigned char clnt_nonce[8],
1951				    const unsigned char svr_chal[8],
1952				    unsigned char verifier[8])
1953{
1954    unsigned char ntlm2_sess_hash[CC_MD5_DIGEST_LENGTH];
1955    CCDigestRef m;
1956
1957    m = CCDigestCreate(kCCDigestMD5);
1958    if (m == NULL)
1959	return ENOMEM;
1960
1961    CCDigestUpdate(m, svr_chal, 8); /* session nonce part 1 */
1962    CCDigestUpdate(m, clnt_nonce, 8); /* session nonce part 2 */
1963    CCDigestFinal(m, ntlm2_sess_hash); /* will only use first 8 bytes */
1964    CCDigestDestroy(m);
1965
1966    memcpy(verifier, ntlm2_sess_hash, 8);
1967
1968    return 0;
1969}
1970
1971
1972/*
1973 * Derive a NTLM2 session key
1974 *
1975 * @param sessionkey session key from domain controller
1976 * @param clnt_nonce client nonce
1977 * @param svr_chal server challenge
1978 * @param derivedkey salted session key
1979 *
1980 * @return In case of success 0 is return, an errors, a errno in what
1981 * went wrong.
1982 *
1983 * @ingroup ntlm_core
1984 */
1985
1986void
1987heim_ntlm_derive_ntlm2_sess(const unsigned char sessionkey[16],
1988			    const unsigned char *clnt_nonce, size_t clnt_nonce_length,
1989			    const unsigned char svr_chal[8],
1990			    unsigned char derivedkey[16])
1991{
1992    CCHmacContext c;
1993
1994    /* HMAC(Ksession, serverchallenge || clientchallenge) */
1995    CCHmacInit(&c, kCCHmacAlgMD5, sessionkey, 16);
1996    CCHmacUpdate(&c, svr_chal, 8);
1997    CCHmacUpdate(&c, clnt_nonce, clnt_nonce_length);
1998    CCHmacFinal(&c, derivedkey);
1999    memset(&c, 0, sizeof(c));
2000}
2001
2002/*
2003 *
2004 */
2005
2006static const uint8_t shapad1[40] =
2007    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
2008    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
2009static const uint8_t shapad2[40] =
2010    "\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2"
2011    "\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2";
2012
2013static const uint8_t Magic1[27] =
2014   {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
2015    0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
2016    0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79};
2017
2018static const uint8_t Magic2[84] =
2019    {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
2020     0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
2021     0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
2022     0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
2023     0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
2024     0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
2025     0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
2026     0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
2027     0x6b, 0x65, 0x79, 0x2e};
2028
2029static const uint8_t Magic3[84] = {
2030    0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
2031    0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
2032    0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
2033    0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
2034    0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
2035    0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
2036    0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
2037    0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
2038    0x6b, 0x65, 0x79, 0x2e
2039};
2040
2041/**
2042 *
2043 * @ingroup ntlm_core
2044 */
2045
2046int
2047heim_ntlm_mppe_getmasterkey(const uint8_t ntlmHashHash[16],
2048			    const uint8_t ntlmResponse[24],
2049			    uint8_t masterKey[16])
2050{
2051    CCDigestRef m;
2052
2053    m = CCDigestCreate(kCCDigestSHA1);
2054    if (m == NULL)
2055	return ENOMEM;
2056
2057    CCDigestUpdate(m, ntlmHashHash, 16);
2058    CCDigestUpdate(m, ntlmResponse, 24);
2059    CCDigestUpdate(m, Magic1, sizeof(Magic1));
2060
2061    CCDigestFinal(m, masterKey);
2062    CCDigestDestroy(m);
2063
2064    return 0;
2065}
2066
2067/**
2068 *
2069 * @ingroup ntlm_core
2070 */
2071
2072int
2073heim_ntlm_mppe_getasymmetricstartkey(const uint8_t MasterKey[16], uint8_t *MasterXKey, size_t len,
2074				     int isSend, int isServer)
2075{
2076    const uint8_t *s =  (!!isSend) ^ (!!isSend) ? Magic2 : Magic3;
2077
2078    unsigned char res[CC_SHA1_DIGEST_LENGTH];
2079    CCDigestRef m;
2080
2081    if (sizeof(res) < len)
2082	return EINVAL;
2083
2084    m = CCDigestCreate(kCCDigestSHA1);
2085    if (m == NULL)
2086	return ENOMEM;
2087
2088    CCDigestUpdate(m, MasterKey, 16);
2089    CCDigestUpdate(m, shapad1, sizeof(shapad1));
2090    CCDigestUpdate(m, s, 84);
2091    CCDigestUpdate(m, shapad2, sizeof(shapad2));
2092    CCDigestFinal(m, res);
2093    CCDigestDestroy(m);
2094
2095    memcpy(MasterXKey, res, len);
2096
2097    return 0;
2098}
2099
2100int
2101heim_ntlm_mppe_getsessionkey(const uint8_t ntlmHash[16],
2102			     const uint8_t ntlmResponse[24],
2103			     int isServer,
2104			     size_t len, uint8_t *sendKey, uint8_t *recvKey)
2105{
2106    uint8_t hashHash[CC_MD4_DIGEST_LENGTH];
2107    uint8_t masterKey[CC_SHA1_DIGEST_LENGTH];
2108    int ret;
2109
2110    CC_MD4(ntlmHash, 16, hashHash);
2111    ret = heim_ntlm_mppe_getmasterkey(hashHash, ntlmResponse, masterKey);
2112    memset(hashHash, 0, sizeof(hashHash));
2113    if (ret)
2114	return ret;
2115
2116    ret = heim_ntlm_mppe_getasymmetricstartkey(masterKey, sendKey, len, TRUE, isServer);
2117    if (ret == 0)
2118	ret = heim_ntlm_mppe_getasymmetricstartkey(masterKey, recvKey, len, FALSE, isServer);
2119    memset(masterKey, 0, sizeof(masterKey));
2120
2121    return ret;
2122}
2123
2124
2125#endif /* ENABLE_NTLM */
2126