1/*
2 * Copyright (c) 2006 - 2007 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <config.h>
35
36RCSID("$Id: ntlm.c 22370 2007-12-28 16:12:01Z lha $");
37
38#include <stdio.h>
39#include <stdlib.h>
40#include <assert.h>
41#include <string.h>
42#include <ctype.h>
43#include <errno.h>
44#include <limits.h>
45
46#include <krb5.h>
47#include <roken.h>
48
49#include "krb5-types.h"
50#include "crypto-headers.h"
51
52#include <heimntlm.h>
53
54/*! \mainpage Heimdal NTLM library
55 *
56 * \section intro Introduction
57 *
58 * Heimdal libheimntlm library is a implementation of the NTLM
59 * protocol, both version 1 and 2. The GSS-API mech that uses this
60 * library adds support for transport encryption and integrity
61 * checking.
62 *
63 * NTLM is a protocol for mutual authentication, its still used in
64 * many protocol where Kerberos is not support, one example is
65 * EAP/X802.1x mechanism LEAP from Microsoft and Cisco.
66 *
67 * This is a support library for the core protocol, its used in
68 * Heimdal to implement and GSS-API mechanism. There is also support
69 * in the KDC to do remote digest authenticiation, this to allow
70 * services to authenticate users w/o direct access to the users ntlm
71 * hashes (same as Kerberos arcfour enctype hashes).
72 *
73 * More information about the NTLM protocol can found here
74 * http://davenport.sourceforge.net/ntlm.html .
75 *
76 * The Heimdal projects web page: http://www.h5l.org/
77 */
78
79/** @defgroup ntlm_core Heimdal NTLM library
80 *
81 * The NTLM core functions implement the string2key generation
82 * function, message encode and decode function, and the hash function
83 * functions.
84 */
85
86struct sec_buffer {
87    uint16_t length;
88    uint16_t allocated;
89    uint32_t offset;
90};
91
92static const unsigned char ntlmsigature[8] = "NTLMSSP\x00";
93
94/*
95 *
96 */
97
98#define CHECK(f, e)							\
99    do { ret = f ; if (ret != (e)) { ret = EINVAL; goto out; } } while(0)
100
101/**
102 * heim_ntlm_free_buf frees the ntlm buffer
103 *
104 * @param p buffer to be freed
105 *
106 * @ingroup ntlm_core
107 */
108
109void
110heim_ntlm_free_buf(struct ntlm_buf *p)
111{
112    if (p->data)
113	free(p->data);
114    p->data = NULL;
115    p->length = 0;
116}
117
118
119static int
120ascii2ucs2le(const char *string, int up, struct ntlm_buf *buf)
121{
122    unsigned char *p;
123    size_t len, i;
124
125    len = strlen(string);
126    if (len / 2 > UINT_MAX)
127	return ERANGE;
128
129    buf->length = len * 2;
130    buf->data = malloc(buf->length);
131    if (buf->data == NULL && len != 0) {
132	heim_ntlm_free_buf(buf);
133	return ENOMEM;
134    }
135
136    p = buf->data;
137    for (i = 0; i < len; i++) {
138	unsigned char t = (unsigned char)string[i];
139	if (t & 0x80) {
140	    heim_ntlm_free_buf(buf);
141	    return EINVAL;
142	}
143	if (up)
144	    t = toupper(t);
145	p[(i * 2) + 0] = t;
146	p[(i * 2) + 1] = 0;
147    }
148    return 0;
149}
150
151/*
152 *
153 */
154
155static krb5_error_code
156ret_sec_buffer(krb5_storage *sp, struct sec_buffer *buf)
157{
158    krb5_error_code ret;
159    CHECK(krb5_ret_uint16(sp, &buf->length), 0);
160    CHECK(krb5_ret_uint16(sp, &buf->allocated), 0);
161    CHECK(krb5_ret_uint32(sp, &buf->offset), 0);
162out:
163    return ret;
164}
165
166static krb5_error_code
167store_sec_buffer(krb5_storage *sp, const struct sec_buffer *buf)
168{
169    krb5_error_code ret;
170    CHECK(krb5_store_uint16(sp, buf->length), 0);
171    CHECK(krb5_store_uint16(sp, buf->allocated), 0);
172    CHECK(krb5_store_uint32(sp, buf->offset), 0);
173out:
174    return ret;
175}
176
177/*
178 * Strings are either OEM or UNICODE. The later is encoded as ucs2 on
179 * wire, but using utf8 in memory.
180 */
181
182static krb5_error_code
183len_string(int ucs2, const char *s)
184{
185    size_t len = strlen(s);
186    if (ucs2)
187	len *= 2;
188    return len;
189}
190
191static krb5_error_code
192ret_string(krb5_storage *sp, int ucs2, struct sec_buffer *desc, char **s)
193{
194    krb5_error_code ret;
195
196    *s = malloc(desc->length + 1);
197    CHECK(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
198    CHECK(krb5_storage_read(sp, *s, desc->length), desc->length);
199    (*s)[desc->length] = '\0';
200
201    if (ucs2) {
202	size_t i;
203	for (i = 0; i < desc->length / 2; i++) {
204	    (*s)[i] = (*s)[i * 2];
205	    if ((*s)[i * 2 + 1]) {
206		free(*s);
207		*s = NULL;
208		return EINVAL;
209	    }
210	}
211	(*s)[i] = '\0';
212    }
213    ret = 0;
214out:
215    return ret;
216
217    return 0;
218}
219
220static krb5_error_code
221put_string(krb5_storage *sp, int ucs2, const char *s)
222{
223    krb5_error_code ret;
224    struct ntlm_buf buf;
225
226    if (ucs2) {
227	ret = ascii2ucs2le(s, 0, &buf);
228	if (ret)
229	    return ret;
230    } else {
231	buf.data = rk_UNCONST(s);
232	buf.length = strlen(s);
233    }
234
235    CHECK(krb5_storage_write(sp, buf.data, buf.length), buf.length);
236    if (ucs2)
237	heim_ntlm_free_buf(&buf);
238    ret = 0;
239out:
240    return ret;
241}
242
243/*
244 *
245 */
246
247static krb5_error_code
248ret_buf(krb5_storage *sp, struct sec_buffer *desc, struct ntlm_buf *buf)
249{
250    krb5_error_code ret;
251
252    buf->data = malloc(desc->length);
253    buf->length = desc->length;
254    CHECK(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
255    CHECK(krb5_storage_read(sp, buf->data, buf->length), buf->length);
256    ret = 0;
257out:
258    return ret;
259}
260
261static krb5_error_code
262put_buf(krb5_storage *sp, const struct ntlm_buf *buf)
263{
264    krb5_error_code ret;
265    CHECK(krb5_storage_write(sp, buf->data, buf->length), buf->length);
266    ret = 0;
267out:
268    return ret;
269}
270
271/**
272 * Frees the ntlm_targetinfo message
273 *
274 * @param ti targetinfo to be freed
275 *
276 * @ingroup ntlm_core
277 */
278
279void
280heim_ntlm_free_targetinfo(struct ntlm_targetinfo *ti)
281{
282    free(ti->servername);
283    free(ti->domainname);
284    free(ti->dnsdomainname);
285    free(ti->dnsservername);
286    memset(ti, 0, sizeof(*ti));
287}
288
289static int
290encode_ti_blob(krb5_storage *out, uint16_t type, int ucs2, char *s)
291{
292    krb5_error_code ret;
293    CHECK(krb5_store_uint16(out, type), 0);
294    CHECK(krb5_store_uint16(out, len_string(ucs2, s)), 0);
295    CHECK(put_string(out, ucs2, s), 0);
296out:
297    return ret;
298}
299
300/**
301 * Encodes a ntlm_targetinfo message.
302 *
303 * @param ti the ntlm_targetinfo message to encode.
304 * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message).
305 * @param data is the return buffer with the encoded message, should be
306 * freed with heim_ntlm_free_buf().
307 *
308 * @return In case of success 0 is return, an errors, a errno in what
309 * went wrong.
310 *
311 * @ingroup ntlm_core
312 */
313
314int
315heim_ntlm_encode_targetinfo(const struct ntlm_targetinfo *ti,
316			    int ucs2,
317			    struct ntlm_buf *data)
318{
319    krb5_error_code ret;
320    krb5_storage *out;
321
322    data->data = NULL;
323    data->length = 0;
324
325    out = krb5_storage_emem();
326    if (out == NULL)
327	return ENOMEM;
328
329    if (ti->servername)
330	CHECK(encode_ti_blob(out, 1, ucs2, ti->servername), 0);
331    if (ti->domainname)
332	CHECK(encode_ti_blob(out, 2, ucs2, ti->domainname), 0);
333    if (ti->dnsservername)
334	CHECK(encode_ti_blob(out, 3, ucs2, ti->dnsservername), 0);
335    if (ti->dnsdomainname)
336	CHECK(encode_ti_blob(out, 4, ucs2, ti->dnsdomainname), 0);
337
338    /* end tag */
339    CHECK(krb5_store_int16(out, 0), 0);
340    CHECK(krb5_store_int16(out, 0), 0);
341
342    {
343	krb5_data d;
344	ret = krb5_storage_to_data(out, &d);
345	data->data = d.data;
346	data->length = d.length;
347    }
348out:
349    krb5_storage_free(out);
350    return ret;
351}
352
353/**
354 * Decodes an NTLM targetinfo message
355 *
356 * @param data input data buffer with the encode NTLM targetinfo message
357 * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message).
358 * @param ti the decoded target info, should be freed with heim_ntlm_free_targetinfo().
359 *
360 * @return In case of success 0 is return, an errors, a errno in what
361 * went wrong.
362 *
363 * @ingroup ntlm_core
364 */
365
366int
367heim_ntlm_decode_targetinfo(const struct ntlm_buf *data,
368			    int ucs2,
369			    struct ntlm_targetinfo *ti)
370{
371    memset(ti, 0, sizeof(*ti));
372    return 0;
373}
374
375/**
376 * Frees the ntlm_type1 message
377 *
378 * @param data message to be freed
379 *
380 * @ingroup ntlm_core
381 */
382
383void
384heim_ntlm_free_type1(struct ntlm_type1 *data)
385{
386    if (data->domain)
387	free(data->domain);
388    if (data->hostname)
389	free(data->hostname);
390    memset(data, 0, sizeof(*data));
391}
392
393int
394heim_ntlm_decode_type1(const struct ntlm_buf *buf, struct ntlm_type1 *data)
395{
396    krb5_error_code ret;
397    unsigned char sig[8];
398    uint32_t type;
399    struct sec_buffer domain, hostname;
400    krb5_storage *in;
401
402    memset(data, 0, sizeof(*data));
403
404    in = krb5_storage_from_readonly_mem(buf->data, buf->length);
405    if (in == NULL) {
406	ret = EINVAL;
407	goto out;
408    }
409    krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
410
411    CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
412    CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
413    CHECK(krb5_ret_uint32(in, &type), 0);
414    CHECK(type, 1);
415    CHECK(krb5_ret_uint32(in, &data->flags), 0);
416    if (data->flags & NTLM_SUPPLIED_DOMAIN)
417	CHECK(ret_sec_buffer(in, &domain), 0);
418    if (data->flags & NTLM_SUPPLIED_WORKSTAION)
419	CHECK(ret_sec_buffer(in, &hostname), 0);
420#if 0
421    if (domain.offset > 32) {
422	CHECK(krb5_ret_uint32(in, &data->os[0]), 0);
423	CHECK(krb5_ret_uint32(in, &data->os[1]), 0);
424    }
425#endif
426    if (data->flags & NTLM_SUPPLIED_DOMAIN)
427	CHECK(ret_string(in, 0, &domain, &data->domain), 0);
428    if (data->flags & NTLM_SUPPLIED_WORKSTAION)
429	CHECK(ret_string(in, 0, &hostname, &data->hostname), 0);
430
431out:
432    krb5_storage_free(in);
433    if (ret)
434	heim_ntlm_free_type1(data);
435
436    return ret;
437}
438
439/**
440 * Encodes an ntlm_type1 message.
441 *
442 * @param type1 the ntlm_type1 message to encode.
443 * @param data is the return buffer with the encoded message, should be
444 * freed with heim_ntlm_free_buf().
445 *
446 * @return In case of success 0 is return, an errors, a errno in what
447 * went wrong.
448 *
449 * @ingroup ntlm_core
450 */
451
452int
453heim_ntlm_encode_type1(const struct ntlm_type1 *type1, struct ntlm_buf *data)
454{
455    krb5_error_code ret;
456    struct sec_buffer domain, hostname;
457    krb5_storage *out;
458    uint32_t base, flags;
459
460    flags = type1->flags;
461    base = 16;
462
463    if (type1->domain) {
464	base += 8;
465	flags |= NTLM_SUPPLIED_DOMAIN;
466    }
467    if (type1->hostname) {
468	base += 8;
469	flags |= NTLM_SUPPLIED_WORKSTAION;
470    }
471    if (type1->os[0])
472	base += 8;
473
474    if (type1->domain) {
475	domain.offset = base;
476	domain.length = len_string(0, type1->domain);
477	domain.allocated = domain.length;
478    }
479    if (type1->hostname) {
480	hostname.offset = domain.allocated + domain.offset;
481	hostname.length = len_string(0, type1->hostname);
482	hostname.allocated = hostname.length;
483    }
484
485    out = krb5_storage_emem();
486    if (out == NULL)
487	return ENOMEM;
488
489    krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
490    CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
491	  sizeof(ntlmsigature));
492    CHECK(krb5_store_uint32(out, 1), 0);
493    CHECK(krb5_store_uint32(out, flags), 0);
494
495    if (type1->domain)
496	CHECK(store_sec_buffer(out, &domain), 0);
497    if (type1->hostname)
498	CHECK(store_sec_buffer(out, &hostname), 0);
499    if (type1->os[0]) {
500	CHECK(krb5_store_uint32(out, type1->os[0]), 0);
501	CHECK(krb5_store_uint32(out, type1->os[1]), 0);
502    }
503    if (type1->domain)
504	CHECK(put_string(out, 0, type1->domain), 0);
505    if (type1->hostname)
506	CHECK(put_string(out, 0, type1->hostname), 0);
507
508    {
509	krb5_data d;
510	ret = krb5_storage_to_data(out, &d);
511	data->data = d.data;
512	data->length = d.length;
513    }
514out:
515    krb5_storage_free(out);
516
517    return ret;
518}
519
520/**
521 * Frees the ntlm_type2 message
522 *
523 * @param data message to be freed
524 *
525 * @ingroup ntlm_core
526 */
527
528void
529heim_ntlm_free_type2(struct ntlm_type2 *data)
530{
531    if (data->targetname)
532	free(data->targetname);
533    heim_ntlm_free_buf(&data->targetinfo);
534    memset(data, 0, sizeof(*data));
535}
536
537int
538heim_ntlm_decode_type2(const struct ntlm_buf *buf, struct ntlm_type2 *type2)
539{
540    krb5_error_code ret;
541    unsigned char sig[8];
542    uint32_t type, ctx[2];
543    struct sec_buffer targetname, targetinfo;
544    krb5_storage *in;
545    int ucs2 = 0;
546
547    memset(type2, 0, sizeof(*type2));
548
549    in = krb5_storage_from_readonly_mem(buf->data, buf->length);
550    if (in == NULL) {
551	ret = EINVAL;
552	goto out;
553    }
554    krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
555
556    CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
557    CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
558    CHECK(krb5_ret_uint32(in, &type), 0);
559    CHECK(type, 2);
560
561    CHECK(ret_sec_buffer(in, &targetname), 0);
562    CHECK(krb5_ret_uint32(in, &type2->flags), 0);
563    if (type2->flags & NTLM_NEG_UNICODE)
564	ucs2 = 1;
565    CHECK(krb5_storage_read(in, type2->challange, sizeof(type2->challange)),
566	  sizeof(type2->challange));
567    CHECK(krb5_ret_uint32(in, &ctx[0]), 0); /* context */
568    CHECK(krb5_ret_uint32(in, &ctx[1]), 0);
569    CHECK(ret_sec_buffer(in, &targetinfo), 0);
570    /* os version */
571#if 0
572    CHECK(krb5_ret_uint32(in, &type2->os[0]), 0);
573    CHECK(krb5_ret_uint32(in, &type2->os[1]), 0);
574#endif
575
576    CHECK(ret_string(in, ucs2, &targetname, &type2->targetname), 0);
577    CHECK(ret_buf(in, &targetinfo, &type2->targetinfo), 0);
578    ret = 0;
579
580out:
581    krb5_storage_free(in);
582    if (ret)
583	heim_ntlm_free_type2(type2);
584
585    return ret;
586}
587
588/**
589 * Encodes an ntlm_type2 message.
590 *
591 * @param type2 the ntlm_type2 message to encode.
592 * @param data is the return buffer with the encoded message, should be
593 * freed with heim_ntlm_free_buf().
594 *
595 * @return In case of success 0 is return, an errors, a errno in what
596 * went wrong.
597 *
598 * @ingroup ntlm_core
599 */
600
601int
602heim_ntlm_encode_type2(const struct ntlm_type2 *type2, struct ntlm_buf *data)
603{
604    struct sec_buffer targetname, targetinfo;
605    krb5_error_code ret;
606    krb5_storage *out = NULL;
607    uint32_t base;
608    int ucs2 = 0;
609
610    if (type2->os[0])
611	base = 56;
612    else
613	base = 48;
614
615    if (type2->flags & NTLM_NEG_UNICODE)
616	ucs2 = 1;
617
618    targetname.offset = base;
619    targetname.length = len_string(ucs2, type2->targetname);
620    targetname.allocated = targetname.length;
621
622    targetinfo.offset = targetname.allocated + targetname.offset;
623    targetinfo.length = type2->targetinfo.length;
624    targetinfo.allocated = type2->targetinfo.length;
625
626    out = krb5_storage_emem();
627    if (out == NULL)
628	return ENOMEM;
629
630    krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
631    CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
632	  sizeof(ntlmsigature));
633    CHECK(krb5_store_uint32(out, 2), 0);
634    CHECK(store_sec_buffer(out, &targetname), 0);
635    CHECK(krb5_store_uint32(out, type2->flags), 0);
636    CHECK(krb5_storage_write(out, type2->challange, sizeof(type2->challange)),
637	  sizeof(type2->challange));
638    CHECK(krb5_store_uint32(out, 0), 0); /* context */
639    CHECK(krb5_store_uint32(out, 0), 0);
640    CHECK(store_sec_buffer(out, &targetinfo), 0);
641    /* os version */
642    if (type2->os[0]) {
643	CHECK(krb5_store_uint32(out, type2->os[0]), 0);
644	CHECK(krb5_store_uint32(out, type2->os[1]), 0);
645    }
646    CHECK(put_string(out, ucs2, type2->targetname), 0);
647    CHECK(krb5_storage_write(out, type2->targetinfo.data,
648			     type2->targetinfo.length),
649	  type2->targetinfo.length);
650
651    {
652	krb5_data d;
653	ret = krb5_storage_to_data(out, &d);
654	data->data = d.data;
655	data->length = d.length;
656    }
657
658out:
659    krb5_storage_free(out);
660
661    return ret;
662}
663
664/**
665 * Frees the ntlm_type3 message
666 *
667 * @param data message to be freed
668 *
669 * @ingroup ntlm_core
670 */
671
672void
673heim_ntlm_free_type3(struct ntlm_type3 *data)
674{
675    heim_ntlm_free_buf(&data->lm);
676    heim_ntlm_free_buf(&data->ntlm);
677    if (data->targetname)
678	free(data->targetname);
679    if (data->username)
680	free(data->username);
681    if (data->ws)
682	free(data->ws);
683    heim_ntlm_free_buf(&data->sessionkey);
684    memset(data, 0, sizeof(*data));
685}
686
687/*
688 *
689 */
690
691int
692heim_ntlm_decode_type3(const struct ntlm_buf *buf,
693		       int ucs2,
694		       struct ntlm_type3 *type3)
695{
696    krb5_error_code ret;
697    unsigned char sig[8];
698    uint32_t type;
699    krb5_storage *in;
700    struct sec_buffer lm, ntlm, target, username, sessionkey, ws;
701
702    memset(type3, 0, sizeof(*type3));
703    memset(&sessionkey, 0, sizeof(sessionkey));
704
705    in = krb5_storage_from_readonly_mem(buf->data, buf->length);
706    if (in == NULL) {
707	ret = EINVAL;
708	goto out;
709    }
710    krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
711
712    CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
713    CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
714    CHECK(krb5_ret_uint32(in, &type), 0);
715    CHECK(type, 3);
716    CHECK(ret_sec_buffer(in, &lm), 0);
717    CHECK(ret_sec_buffer(in, &ntlm), 0);
718    CHECK(ret_sec_buffer(in, &target), 0);
719    CHECK(ret_sec_buffer(in, &username), 0);
720    CHECK(ret_sec_buffer(in, &ws), 0);
721    if (lm.offset >= 60) {
722	CHECK(ret_sec_buffer(in, &sessionkey), 0);
723    }
724    if (lm.offset >= 64) {
725	CHECK(krb5_ret_uint32(in, &type3->flags), 0);
726    }
727    if (lm.offset >= 72) {
728	CHECK(krb5_ret_uint32(in, &type3->os[0]), 0);
729	CHECK(krb5_ret_uint32(in, &type3->os[1]), 0);
730    }
731    CHECK(ret_buf(in, &lm, &type3->lm), 0);
732    CHECK(ret_buf(in, &ntlm, &type3->ntlm), 0);
733    CHECK(ret_string(in, ucs2, &target, &type3->targetname), 0);
734    CHECK(ret_string(in, ucs2, &username, &type3->username), 0);
735    CHECK(ret_string(in, ucs2, &ws, &type3->ws), 0);
736    if (sessionkey.offset)
737	CHECK(ret_buf(in, &sessionkey, &type3->sessionkey), 0);
738
739out:
740    krb5_storage_free(in);
741    if (ret)
742	heim_ntlm_free_type3(type3);
743
744    return ret;
745}
746
747/**
748 * Encodes an ntlm_type3 message.
749 *
750 * @param type3 the ntlm_type3 message to encode.
751 * @param data is the return buffer with the encoded message, should be
752 * freed with heim_ntlm_free_buf().
753 *
754 * @return In case of success 0 is return, an errors, a errno in what
755 * went wrong.
756 *
757 * @ingroup ntlm_core
758 */
759
760int
761heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data)
762{
763    struct sec_buffer lm, ntlm, target, username, sessionkey, ws;
764    krb5_error_code ret;
765    krb5_storage *out = NULL;
766    uint32_t base;
767    int ucs2 = 0;
768
769    memset(&lm, 0, sizeof(lm));
770    memset(&ntlm, 0, sizeof(ntlm));
771    memset(&target, 0, sizeof(target));
772    memset(&username, 0, sizeof(username));
773    memset(&ws, 0, sizeof(ws));
774    memset(&sessionkey, 0, sizeof(sessionkey));
775
776    base = 52;
777    if (type3->sessionkey.length) {
778	base += 8; /* sessionkey sec buf */
779	base += 4; /* flags */
780    }
781    if (type3->os[0]) {
782	base += 8;
783    }
784
785    if (type3->flags & NTLM_NEG_UNICODE)
786	ucs2 = 1;
787
788    lm.offset = base;
789    lm.length = type3->lm.length;
790    lm.allocated = type3->lm.length;
791
792    ntlm.offset = lm.offset + lm.allocated;
793    ntlm.length = type3->ntlm.length;
794    ntlm.allocated = ntlm.length;
795
796    target.offset = ntlm.offset + ntlm.allocated;
797    target.length = len_string(ucs2, type3->targetname);
798    target.allocated = target.length;
799
800    username.offset = target.offset + target.allocated;
801    username.length = len_string(ucs2, type3->username);
802    username.allocated = username.length;
803
804    ws.offset = username.offset + username.allocated;
805    ws.length = len_string(ucs2, type3->ws);
806    ws.allocated = ws.length;
807
808    sessionkey.offset = ws.offset + ws.allocated;
809    sessionkey.length = type3->sessionkey.length;
810    sessionkey.allocated = type3->sessionkey.length;
811
812    out = krb5_storage_emem();
813    if (out == NULL)
814	return ENOMEM;
815
816    krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
817    CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
818	  sizeof(ntlmsigature));
819    CHECK(krb5_store_uint32(out, 3), 0);
820
821    CHECK(store_sec_buffer(out, &lm), 0);
822    CHECK(store_sec_buffer(out, &ntlm), 0);
823    CHECK(store_sec_buffer(out, &target), 0);
824    CHECK(store_sec_buffer(out, &username), 0);
825    CHECK(store_sec_buffer(out, &ws), 0);
826    /* optional */
827    if (type3->sessionkey.length) {
828	CHECK(store_sec_buffer(out, &sessionkey), 0);
829	CHECK(krb5_store_uint32(out, type3->flags), 0);
830    }
831#if 0
832    CHECK(krb5_store_uint32(out, 0), 0); /* os0 */
833    CHECK(krb5_store_uint32(out, 0), 0); /* os1 */
834#endif
835
836    CHECK(put_buf(out, &type3->lm), 0);
837    CHECK(put_buf(out, &type3->ntlm), 0);
838    CHECK(put_string(out, ucs2, type3->targetname), 0);
839    CHECK(put_string(out, ucs2, type3->username), 0);
840    CHECK(put_string(out, ucs2, type3->ws), 0);
841    CHECK(put_buf(out, &type3->sessionkey), 0);
842
843    {
844	krb5_data d;
845	ret = krb5_storage_to_data(out, &d);
846	data->data = d.data;
847	data->length = d.length;
848    }
849
850out:
851    krb5_storage_free(out);
852
853    return ret;
854}
855
856
857/*
858 *
859 */
860
861static void
862splitandenc(unsigned char *hash,
863	    unsigned char *challange,
864	    unsigned char *answer)
865{
866    DES_cblock key;
867    DES_key_schedule sched;
868
869    ((unsigned char*)key)[0] =  hash[0];
870    ((unsigned char*)key)[1] = (hash[0] << 7) | (hash[1] >> 1);
871    ((unsigned char*)key)[2] = (hash[1] << 6) | (hash[2] >> 2);
872    ((unsigned char*)key)[3] = (hash[2] << 5) | (hash[3] >> 3);
873    ((unsigned char*)key)[4] = (hash[3] << 4) | (hash[4] >> 4);
874    ((unsigned char*)key)[5] = (hash[4] << 3) | (hash[5] >> 5);
875    ((unsigned char*)key)[6] = (hash[5] << 2) | (hash[6] >> 6);
876    ((unsigned char*)key)[7] = (hash[6] << 1);
877
878    DES_set_odd_parity(&key);
879    DES_set_key(&key, &sched);
880    DES_ecb_encrypt((DES_cblock *)challange, (DES_cblock *)answer, &sched, 1);
881    memset(&sched, 0, sizeof(sched));
882    memset(key, 0, sizeof(key));
883}
884
885/**
886 * Calculate the NTLM key, the password is assumed to be in UTF8.
887 *
888 * @param password password to calcute the key for.
889 * @param key calcuted key, should be freed with heim_ntlm_free_buf().
890 *
891 * @return In case of success 0 is return, an errors, a errno in what
892 * went wrong.
893 *
894 * @ingroup ntlm_core
895 */
896
897int
898heim_ntlm_nt_key(const char *password, struct ntlm_buf *key)
899{
900    struct ntlm_buf buf;
901    MD4_CTX ctx;
902    int ret;
903
904    key->data = malloc(MD5_DIGEST_LENGTH);
905    if (key->data == NULL)
906	return ENOMEM;
907    key->length = MD5_DIGEST_LENGTH;
908
909    ret = ascii2ucs2le(password, 0, &buf);
910    if (ret) {
911	heim_ntlm_free_buf(key);
912	return ret;
913    }
914    MD4_Init(&ctx);
915    MD4_Update(&ctx, buf.data, buf.length);
916    MD4_Final(key->data, &ctx);
917    heim_ntlm_free_buf(&buf);
918    return 0;
919}
920
921/**
922 * Calculate NTLMv1 response hash
923 *
924 * @param key the ntlm v1 key
925 * @param len length of key
926 * @param challange sent by the server
927 * @param answer calculated answer, should be freed with heim_ntlm_free_buf().
928 *
929 * @return In case of success 0 is return, an errors, a errno in what
930 * went wrong.
931 *
932 * @ingroup ntlm_core
933 */
934
935int
936heim_ntlm_calculate_ntlm1(void *key, size_t len,
937			  unsigned char challange[8],
938			  struct ntlm_buf *answer)
939{
940    unsigned char res[21];
941
942    if (len != MD4_DIGEST_LENGTH)
943	return EINVAL;
944
945    memcpy(res, key, len);
946    memset(&res[MD4_DIGEST_LENGTH], 0, sizeof(res) - MD4_DIGEST_LENGTH);
947
948    answer->data = malloc(24);
949    if (answer->data == NULL)
950	return ENOMEM;
951    answer->length = 24;
952
953    splitandenc(&res[0],  challange, ((unsigned char *)answer->data) + 0);
954    splitandenc(&res[7],  challange, ((unsigned char *)answer->data) + 8);
955    splitandenc(&res[14], challange, ((unsigned char *)answer->data) + 16);
956
957    return 0;
958}
959
960/**
961 * Generates an NTLMv1 session random with assosited session master key.
962 *
963 * @param key the ntlm v1 key
964 * @param len length of key
965 * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
966 * @param master calculated session master key, should be freed with heim_ntlm_free_buf().
967 *
968 * @return In case of success 0 is return, an errors, a errno in what
969 * went wrong.
970 *
971 * @ingroup ntlm_core
972 */
973
974int
975heim_ntlm_build_ntlm1_master(void *key, size_t len,
976			     struct ntlm_buf *session,
977			     struct ntlm_buf *master)
978{
979    RC4_KEY rc4;
980
981    memset(master, 0, sizeof(*master));
982    memset(session, 0, sizeof(*session));
983
984    if (len != MD4_DIGEST_LENGTH)
985	return EINVAL;
986
987    session->length = MD4_DIGEST_LENGTH;
988    session->data = malloc(session->length);
989    if (session->data == NULL) {
990	session->length = 0;
991	return EINVAL;
992    }
993    master->length = MD4_DIGEST_LENGTH;
994    master->data = malloc(master->length);
995    if (master->data == NULL) {
996	heim_ntlm_free_buf(master);
997	heim_ntlm_free_buf(session);
998	return EINVAL;
999    }
1000
1001    {
1002	unsigned char sessionkey[MD4_DIGEST_LENGTH];
1003	MD4_CTX ctx;
1004
1005	MD4_Init(&ctx);
1006	MD4_Update(&ctx, key, len);
1007	MD4_Final(sessionkey, &ctx);
1008
1009	RC4_set_key(&rc4, sizeof(sessionkey), sessionkey);
1010    }
1011
1012    if (RAND_bytes(session->data, session->length) != 1) {
1013	heim_ntlm_free_buf(master);
1014	heim_ntlm_free_buf(session);
1015	return EINVAL;
1016    }
1017
1018    RC4(&rc4, master->length, session->data, master->data);
1019    memset(&rc4, 0, sizeof(rc4));
1020
1021    return 0;
1022}
1023
1024/**
1025 * Generates an NTLMv2 session key.
1026 *
1027 * @param key the ntlm key
1028 * @param len length of key
1029 * @param username name of the user, as sent in the message, assumed to be in UTF8.
1030 * @param target the name of the target, assumed to be in UTF8.
1031 * @param ntlmv2 the ntlmv2 session key
1032 *
1033 * @ingroup ntlm_core
1034 */
1035
1036void
1037heim_ntlm_ntlmv2_key(const void *key, size_t len,
1038		     const char *username,
1039		     const char *target,
1040		     unsigned char ntlmv2[16])
1041{
1042    unsigned int hmaclen;
1043    HMAC_CTX c;
1044
1045    HMAC_CTX_init(&c);
1046    HMAC_Init_ex(&c, key, len, EVP_md5(), NULL);
1047    {
1048	struct ntlm_buf buf;
1049	/* uppercase username and turn it inte ucs2-le */
1050	ascii2ucs2le(username, 1, &buf);
1051	HMAC_Update(&c, buf.data, buf.length);
1052	free(buf.data);
1053	/* uppercase target and turn into ucs2-le */
1054	ascii2ucs2le(target, 1, &buf);
1055	HMAC_Update(&c, buf.data, buf.length);
1056	free(buf.data);
1057    }
1058    HMAC_Final(&c, ntlmv2, &hmaclen);
1059    HMAC_CTX_cleanup(&c);
1060
1061}
1062
1063/*
1064 *
1065 */
1066
1067#define NTTIME_EPOCH 0x019DB1DED53E8000LL
1068
1069static uint64_t
1070unix2nttime(time_t unix_time)
1071{
1072    long long wt;
1073    wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
1074    return wt;
1075}
1076
1077static time_t
1078nt2unixtime(uint64_t t)
1079{
1080    t = ((t - (uint64_t)NTTIME_EPOCH) / (uint64_t)10000000);
1081    if (t > (((time_t)(~(uint64_t)0)) >> 1))
1082	return 0;
1083    return (time_t)t;
1084}
1085
1086
1087/**
1088 * Calculate NTLMv2 response
1089 *
1090 * @param key the ntlm key
1091 * @param len length of key
1092 * @param username name of the user, as sent in the message, assumed to be in UTF8.
1093 * @param target the name of the target, assumed to be in UTF8.
1094 * @param serverchallange challange as sent by the server in the type2 message.
1095 * @param infotarget infotarget as sent by the server in the type2 message.
1096 * @param ntlmv2 calculated session key
1097 * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
1098 *
1099 * @return In case of success 0 is return, an errors, a errno in what
1100 * went wrong.
1101 *
1102 * @ingroup ntlm_core
1103 */
1104
1105int
1106heim_ntlm_calculate_ntlm2(const void *key, size_t len,
1107			  const char *username,
1108			  const char *target,
1109			  const unsigned char serverchallange[8],
1110			  const struct ntlm_buf *infotarget,
1111			  unsigned char ntlmv2[16],
1112			  struct ntlm_buf *answer)
1113{
1114    krb5_error_code ret;
1115    krb5_data data;
1116    unsigned int hmaclen;
1117    unsigned char ntlmv2answer[16];
1118    krb5_storage *sp;
1119    unsigned char clientchallange[8];
1120    HMAC_CTX c;
1121    uint64_t t;
1122
1123    t = unix2nttime(time(NULL));
1124
1125    if (RAND_bytes(clientchallange, sizeof(clientchallange)) != 1)
1126	return EINVAL;
1127
1128    /* calculate ntlmv2 key */
1129
1130    heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2);
1131
1132    /* calculate and build ntlmv2 answer */
1133
1134    sp = krb5_storage_emem();
1135    if (sp == NULL)
1136	return ENOMEM;
1137    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1138
1139    CHECK(krb5_store_uint32(sp, 0x00000101), 0);
1140    CHECK(krb5_store_uint32(sp, 0), 0);
1141    /* timestamp le 64 bit ts */
1142    CHECK(krb5_store_uint32(sp, t & 0xffffffff), 0);
1143    CHECK(krb5_store_uint32(sp, t >> 32), 0);
1144
1145    CHECK(krb5_storage_write(sp, clientchallange, 8), 8);
1146
1147    CHECK(krb5_store_uint32(sp, 0), 0);  /* unknown but zero will work */
1148    CHECK(krb5_storage_write(sp, infotarget->data, infotarget->length),
1149	  infotarget->length);
1150    CHECK(krb5_store_uint32(sp, 0), 0); /* unknown but zero will work */
1151
1152    CHECK(krb5_storage_to_data(sp, &data), 0);
1153    krb5_storage_free(sp);
1154    sp = NULL;
1155
1156    HMAC_CTX_init(&c);
1157    HMAC_Init_ex(&c, ntlmv2, 16, EVP_md5(), NULL);
1158    HMAC_Update(&c, serverchallange, 8);
1159    HMAC_Update(&c, data.data, data.length);
1160    HMAC_Final(&c, ntlmv2answer, &hmaclen);
1161    HMAC_CTX_cleanup(&c);
1162
1163    sp = krb5_storage_emem();
1164    if (sp == NULL) {
1165	krb5_data_free(&data);
1166	return ENOMEM;
1167    }
1168
1169    CHECK(krb5_storage_write(sp, ntlmv2answer, 16), 16);
1170    CHECK(krb5_storage_write(sp, data.data, data.length), data.length);
1171    krb5_data_free(&data);
1172
1173    CHECK(krb5_storage_to_data(sp, &data), 0);
1174    krb5_storage_free(sp);
1175    sp = NULL;
1176
1177    answer->data = data.data;
1178    answer->length = data.length;
1179
1180    return 0;
1181out:
1182    if (sp)
1183	krb5_storage_free(sp);
1184    return ret;
1185}
1186
1187static const int authtimediff = 3600 * 2; /* 2 hours */
1188
1189/**
1190 * Verify NTLMv2 response.
1191 *
1192 * @param key the ntlm key
1193 * @param len length of key
1194 * @param username name of the user, as sent in the message, assumed to be in UTF8.
1195 * @param target the name of the target, assumed to be in UTF8.
1196 * @param now the time now (0 if the library should pick it up itself)
1197 * @param serverchallange challange as sent by the server in the type2 message.
1198 * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
1199 * @param infotarget infotarget as sent by the server in the type2 message.
1200 * @param ntlmv2 calculated session key
1201 *
1202 * @return In case of success 0 is return, an errors, a errno in what
1203 * went wrong.
1204 *
1205 * @ingroup ntlm_core
1206 */
1207
1208int
1209heim_ntlm_verify_ntlm2(const void *key, size_t len,
1210		       const char *username,
1211		       const char *target,
1212		       time_t now,
1213		       const unsigned char serverchallange[8],
1214		       const struct ntlm_buf *answer,
1215		       struct ntlm_buf *infotarget,
1216		       unsigned char ntlmv2[16])
1217{
1218    krb5_error_code ret;
1219    unsigned int hmaclen;
1220    unsigned char clientanswer[16];
1221    unsigned char clientnonce[8];
1222    unsigned char serveranswer[16];
1223    krb5_storage *sp;
1224    HMAC_CTX c;
1225    uint64_t t;
1226    time_t authtime;
1227    uint32_t temp;
1228
1229    infotarget->length = 0;
1230    infotarget->data = NULL;
1231
1232    if (answer->length < 16)
1233	return EINVAL;
1234
1235    if (now == 0)
1236	now = time(NULL);
1237
1238    /* calculate ntlmv2 key */
1239
1240    heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2);
1241
1242    /* calculate and build ntlmv2 answer */
1243
1244    sp = krb5_storage_from_readonly_mem(answer->data, answer->length);
1245    if (sp == NULL)
1246	return ENOMEM;
1247    krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
1248
1249    CHECK(krb5_storage_read(sp, clientanswer, 16), 16);
1250
1251    CHECK(krb5_ret_uint32(sp, &temp), 0);
1252    CHECK(temp, 0x00000101);
1253    CHECK(krb5_ret_uint32(sp, &temp), 0);
1254    CHECK(temp, 0);
1255    /* timestamp le 64 bit ts */
1256    CHECK(krb5_ret_uint32(sp, &temp), 0);
1257    t = temp;
1258    CHECK(krb5_ret_uint32(sp, &temp), 0);
1259    t |= ((uint64_t)temp)<< 32;
1260
1261    authtime = nt2unixtime(t);
1262
1263    if (abs((int)(authtime - now)) > authtimediff) {
1264	ret = EINVAL;
1265	goto out;
1266    }
1267
1268    /* client challange */
1269    CHECK(krb5_storage_read(sp, clientnonce, 8), 8);
1270
1271    CHECK(krb5_ret_uint32(sp, &temp), 0); /* unknown */
1272
1273    /* should really unparse the infotarget, but lets pick up everything */
1274    infotarget->length = answer->length - krb5_storage_seek(sp, 0, SEEK_CUR);
1275    infotarget->data = malloc(infotarget->length);
1276    if (infotarget->data == NULL) {
1277	ret = ENOMEM;
1278	goto out;
1279    }
1280    CHECK(krb5_storage_read(sp, infotarget->data, infotarget->length),
1281	  infotarget->length);
1282    /* XXX remove the unknown ?? */
1283    krb5_storage_free(sp);
1284    sp = NULL;
1285
1286    HMAC_CTX_init(&c);
1287    HMAC_Init_ex(&c, ntlmv2, 16, EVP_md5(), NULL);
1288    HMAC_Update(&c, serverchallange, 8);
1289    HMAC_Update(&c, ((unsigned char *)answer->data) + 16, answer->length - 16);
1290    HMAC_Final(&c, serveranswer, &hmaclen);
1291    HMAC_CTX_cleanup(&c);
1292
1293    if (memcmp(serveranswer, clientanswer, 16) != 0) {
1294	heim_ntlm_free_buf(infotarget);
1295	return EINVAL;
1296    }
1297
1298    return 0;
1299out:
1300    heim_ntlm_free_buf(infotarget);
1301    if (sp)
1302	krb5_storage_free(sp);
1303    return ret;
1304}
1305
1306
1307/*
1308 * Calculate the NTLM2 Session Response
1309 *
1310 * @param clnt_nonce client nonce
1311 * @param svr_chal server challage
1312 * @param ntlm2_hash ntlm hash
1313 * @param lm The LM response, should be freed with heim_ntlm_free_buf().
1314 * @param ntlm The NTLM response, should be freed with heim_ntlm_free_buf().
1315 *
1316 * @return In case of success 0 is return, an errors, a errno in what
1317 * went wrong.
1318 *
1319 * @ingroup ntlm_core
1320 */
1321
1322int
1323heim_ntlm_calculate_ntlm2_sess(const unsigned char clnt_nonce[8],
1324			       const unsigned char svr_chal[8],
1325			       const unsigned char ntlm_hash[16],
1326			       struct ntlm_buf *lm,
1327			       struct ntlm_buf *ntlm)
1328{
1329    unsigned char ntlm2_sess_hash[MD5_DIGEST_LENGTH];
1330    unsigned char res[21], *resp;
1331    MD5_CTX md5;
1332
1333    lm->data = malloc(24);
1334    if (lm->data == NULL)
1335	return ENOMEM;
1336    lm->length = 24;
1337
1338    ntlm->data = malloc(24);
1339    if (ntlm->data == NULL) {
1340	free(lm->data);
1341	lm->data = NULL;
1342	return ENOMEM;
1343    }
1344    ntlm->length = 24;
1345
1346    /* first setup the lm resp */
1347    memset(lm->data, 0, 24);
1348    memcpy(lm->data, clnt_nonce, 8);
1349
1350    MD5_Init(&md5);
1351    MD5_Update(&md5, svr_chal, 8); /* session nonce part 1 */
1352    MD5_Update(&md5, clnt_nonce, 8); /* session nonce part 2 */
1353    MD5_Final(ntlm2_sess_hash, &md5); /* will only use first 8 bytes */
1354
1355    memset(res, 0, sizeof(res));
1356    memcpy(res, ntlm_hash, 16);
1357
1358    resp = ntlm->data;
1359    splitandenc(&res[0], ntlm2_sess_hash, resp + 0);
1360    splitandenc(&res[7], ntlm2_sess_hash, resp + 8);
1361    splitandenc(&res[14], ntlm2_sess_hash, resp + 16);
1362
1363    return 0;
1364}
1365