1/* NTLM SASL plugin
2 * Ken Murchison
3 * $Id: ntlm.c,v 1.6 2006/01/24 20:37:26 snsimon Exp $
4 *
5 * References:
6 *   http://www.innovation.ch/java/ntlm.html
7 *   http://www.opengroup.org/comsource/techref2/NCH1222X.HTM
8 *   http://www.ubiqx.org/cifs/rfc-draft/draft-leach-cifs-v1-spec-02.html
9 */
10/*
11 * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 * 1. Redistributions of source code must retain the above copyright
18 *    notice, this list of conditions and the following disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above copyright
21 *    notice, this list of conditions and the following disclaimer in
22 *    the documentation and/or other materials provided with the
23 *    distribution.
24 *
25 * 3. The name "Carnegie Mellon University" must not be used to
26 *    endorse or promote products derived from this software without
27 *    prior written permission. For permission or any other legal
28 *    details, please contact
29 *      Office of Technology Transfer
30 *      Carnegie Mellon University
31 *      5000 Forbes Avenue
32 *      Pittsburgh, PA  15213-3890
33 *      (412) 268-4387, fax: (412) 268-7395
34 *      tech-transfer@andrew.cmu.edu
35 *
36 * 4. Redistributions of any form whatsoever must retain the following
37 *    acknowledgment:
38 *    "This product includes software developed by Computing Services
39 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
40 *
41 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
42 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
43 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
44 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
45 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
46 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
47 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
48 */
49
50#include <config.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <ctype.h>
54#include <errno.h>
55#include <limits.h>
56
57#ifdef WIN32
58# include <process.h>	    /* for getpid */
59  typedef int pid_t;
60#else
61# include <unistd.h>
62# include <sys/types.h>
63# include <sys/socket.h>
64# include <sys/utsname.h>
65# include <netdb.h>
66
67#ifndef SYS_NMLN
68  struct utsname dummy;
69# define SYS_NMLN sizeof(dummy.sysname)
70#endif
71
72# define closesocket(sock)   close(sock)
73  typedef int SOCKET;
74#endif /* WIN32 */
75
76#include <openssl/md4.h>
77#include <openssl/md5.h>
78#include <openssl/hmac.h>
79#include <openssl/des.h>
80#include <openssl/opensslv.h>
81#if (OPENSSL_VERSION_NUMBER >= 0x0090700f) && \
82     !defined(OPENSSL_ENABLE_OLD_DES_SUPPORT)
83# define des_cblock DES_cblock
84# define des_key_schedule DES_key_schedule
85# define des_set_odd_parity(k) \
86	 DES_set_odd_parity((k))
87# define des_set_key(k,ks) \
88	 DES_set_key((k),&(ks))
89# define des_key_sched(k,ks) \
90         DES_key_sched((k),&(ks))
91# define des_ecb_encrypt(i,o,k,e) \
92	 DES_ecb_encrypt((i),(o),&(k),(e))
93#endif /* OpenSSL 0.9.7+ w/o old DES support */
94
95#include <sasl.h>
96#define MD5_H  /* suppress internal MD5 */
97#include <saslplug.h>
98
99#include "plugin_common.h"
100
101/*****************************  Common Section  *****************************/
102
103//static const char plugin_id[] = "$Id: ntlm.c,v 1.6 2006/01/24 20:37:26 snsimon Exp $";
104
105#ifdef WIN32
106static ssize_t writev (SOCKET fd, const struct iovec *iov, size_t iovcnt);
107
108ssize_t writev (SOCKET fd, const struct iovec *iov, size_t iovcnt)
109{
110    ssize_t nwritten;		/* amount written */
111    ssize_t nbytes;
112    size_t i;
113
114    nbytes = 0;
115
116    for (i = 0; i < iovcnt; i++) {
117	if ((nwritten = send (fd, iov[i].iov_base, iov[i].iov_len, 0)) == SOCKET_ERROR) {
118/* Unless socket is nonblocking, we should always write everything */
119	    return (-1);
120	}
121
122	nbytes += nwritten;
123
124	if (nwritten < iov[i].iov_len) {
125	    break;
126	}
127    }
128    return (nbytes);
129}
130#endif /* WIN32 */
131
132#ifndef UINT16_MAX
133#define UINT16_MAX 65535U
134#endif
135
136#if UINT_MAX == UINT16_MAX
137typedef unsigned int uint16;
138#elif USHRT_MAX == UINT16_MAX
139typedef unsigned short uint16;
140#else
141#error dont know what to use for uint16
142#endif
143
144#ifndef UINT32_MAX
145#define UINT32_MAX 4294967295U
146#endif
147
148#if UINT_MAX == UINT32_MAX
149typedef unsigned int uint32;
150#elif ULONG_MAX == UINT32_MAX
151typedef unsigned long uint32;
152#elif USHRT_MAX == UINT32_MAX
153typedef unsigned short uint32;
154#else
155#error dont know what to use for uint32
156#endif
157
158#define NTLM_SIGNATURE		"NTLMSSP"
159
160enum {
161    NTLM_TYPE_REQUEST		= 1,
162    NTLM_TYPE_CHALLENGE		= 2,
163    NTLM_TYPE_RESPONSE		= 3
164};
165
166enum {
167    NTLM_USE_UNICODE		= 0x00001,
168    NTLM_USE_ASCII		= 0x00002,
169    NTLM_ASK_TARGET		= 0x00004,
170    NTLM_AUTH_NTLM		= 0x00200,
171    NTLM_ALWAYS_SIGN		= 0x08000,
172    NTLM_TARGET_IS_DOMAIN	= 0x10000,
173    NTLM_TARGET_IS_SERVER	= 0x20000,
174    NTLM_FLAGS_MASK		= 0x0ffff
175};
176
177enum {
178    NTLM_NONCE_LENGTH		= 8,
179    NTLM_HASH_LENGTH		= 21,
180    NTLM_RESP_LENGTH		= 24,
181    NTLM_SESSKEY_LENGTH		= 16,
182};
183
184enum {
185    NTLM_SIG_OFFSET		= 0,
186    NTLM_TYPE_OFFSET		= 8,
187
188    NTLM_TYPE1_FLAGS_OFFSET	= 12,
189    NTLM_TYPE1_DOMAIN_OFFSET	= 16,
190    NTLM_TYPE1_WORKSTN_OFFSET	= 24,
191    NTLM_TYPE1_DATA_OFFSET	= 32,
192    NTLM_TYPE1_MINSIZE		= 16,
193
194    NTLM_TYPE2_TARGET_OFFSET	= 12,
195    NTLM_TYPE2_FLAGS_OFFSET	= 20,
196    NTLM_TYPE2_CHALLENGE_OFFSET	= 24,
197    NTLM_TYPE2_CONTEXT_OFFSET	= 32,
198    NTLM_TYPE2_TARGETINFO_OFFSET= 40,
199    NTLM_TYPE2_DATA_OFFSET	= 48,
200    NTLM_TYPE2_MINSIZE		= 32,
201
202    NTLM_TYPE3_LMRESP_OFFSET	= 12,
203    NTLM_TYPE3_NTRESP_OFFSET	= 20,
204    NTLM_TYPE3_DOMAIN_OFFSET	= 28,
205    NTLM_TYPE3_USER_OFFSET	= 36,
206    NTLM_TYPE3_WORKSTN_OFFSET	= 44,
207    NTLM_TYPE3_SESSIONKEY_OFFSET= 52,
208    NTLM_TYPE3_FLAGS_OFFSET	= 60,
209    NTLM_TYPE3_DATA_OFFSET	= 64,
210    NTLM_TYPE3_MINSIZE		= 52,
211
212    NTLM_BUFFER_LEN_OFFSET	= 0,
213    NTLM_BUFFER_MAXLEN_OFFSET	= 2,
214    NTLM_BUFFER_OFFSET_OFFSET	= 4,
215    NTLM_BUFFER_SIZE		= 8
216};
217
218/* return the length of a string (even if it is NULL) */
219#define xstrlen(s) (s ? strlen(s) : 0)
220
221/* machine-independent routines to convert to/from Intel byte-order */
222#define htois(is, hs) \
223    (is)[0] = hs & 0xff; \
224    (is)[1] = hs >> 8
225
226#define itohs(is) \
227    ((is)[0] | ((is)[1] << 8))
228
229#define htoil(il, hl) \
230    (il)[0] = hl & 0xff; \
231    (il)[1] = (hl >> 8) & 0xff; \
232    (il)[2] = (hl >> 16) & 0xff; \
233    (il)[3] = hl >> 24
234
235#define itohl(il) \
236    ((il)[0] | ((il)[1] << 8) | ((il)[2] << 16) | ((il)[3] << 24))
237
238/* convert string to all upper case */
239static const char *ucase(const char *str, size_t len)
240{
241    char *cp = (char *) str;
242
243    if (!len) len = xstrlen(str);
244
245    while (len && cp && *cp) {
246	*cp = toupper((int) *cp);
247	cp++;
248	len--;
249    }
250
251    return (str);
252}
253
254/* copy src to dst as unicode (in Intel byte-order) */
255static void to_unicode(u_char *dst, const char *src, int len)
256{
257    for (; len; len--) {
258	*dst++ = *src++;
259	*dst++ = 0;
260    }
261}
262
263/* copy unicode src (in Intel byte-order) to dst */
264static void from_unicode(char *dst, u_char *src, int len)
265{
266    for (; len; len--) {
267	*dst++ = *src & 0x7f;
268	src += 2;
269    }
270}
271
272/* load a string into an NTLM buffer */
273static void load_buffer(u_char *buf, const u_char *str, uint16 len,
274			int unicode, u_char *base, uint32 *offset)
275{
276    if (len) {
277	if (unicode) {
278	    to_unicode(base + *offset, str, len);
279	    len *= 2;
280	}
281	else {
282	    memcpy(base + *offset, str, len);
283	}
284    }
285
286    htois(buf + NTLM_BUFFER_LEN_OFFSET, len);
287    htois(buf + NTLM_BUFFER_MAXLEN_OFFSET, len);
288    htoil(buf + NTLM_BUFFER_OFFSET_OFFSET, *offset);
289    *offset += len;
290}
291
292/* unload a string from an NTLM buffer */
293static int unload_buffer(const sasl_utils_t *utils, const u_char *buf,
294			 u_char **str, unsigned *outlen,
295			 int unicode, const u_char *base, unsigned msglen)
296{
297    uint16 len = itohs(buf + NTLM_BUFFER_LEN_OFFSET);
298
299    if (len) {
300	uint32 offset;
301
302	*str = utils->malloc(len + 1); /* add 1 for NUL */
303	if (*str == NULL) {
304	    MEMERROR(utils);
305	    return SASL_NOMEM;
306	}
307
308	offset = itohl(buf + NTLM_BUFFER_OFFSET_OFFSET);
309
310	/* sanity check */
311	if (offset > msglen || len > (msglen - offset)) return SASL_BADPROT;
312
313	if (unicode) {
314	    len /= 2;
315	    from_unicode((char *) *str, (u_char *) base + offset, len);
316	}
317	else
318	    memcpy(*str, base + offset, len);
319
320	(*str)[len] = '\0'; /* add NUL */
321    }
322    else {
323	*str = NULL;
324    }
325
326    if (outlen) *outlen = len;
327
328    return SASL_OK;
329}
330
331/*
332 * NTLM encryption/authentication routines per section 2.10 of
333 * draft-leach-cifs-v1-spec-02
334 */
335static void E(unsigned char *out, unsigned char *K, unsigned Klen,
336	      unsigned char *D, unsigned Dlen)
337
338{
339    unsigned k, d;
340    des_cblock K64;
341    des_key_schedule ks;
342    unsigned char *Dp;
343#define KEY_SIZE   7
344#define BLOCK_SIZE 8
345
346    for (k = 0; k < Klen; k += KEY_SIZE, K += KEY_SIZE) {
347	/* convert 56-bit key to 64-bit */
348	K64[0] = K[0];
349	K64[1] = ((K[0] << 7) & 0xFF) | (K[1] >> 1);
350	K64[2] = ((K[1] << 6) & 0xFF) | (K[2] >> 2);
351	K64[3] = ((K[2] << 5) & 0xFF) | (K[3] >> 3);
352	K64[4] = ((K[3] << 4) & 0xFF) | (K[4] >> 4);
353	K64[5] = ((K[4] << 3) & 0xFF) | (K[5] >> 5);
354	K64[6] = ((K[5] << 2) & 0xFF) | (K[6] >> 6);
355	K64[7] =  (K[6] << 1) & 0xFF;
356
357 	des_set_odd_parity(&K64); /* XXX is this necessary? */
358 	des_set_key(&K64, ks);
359
360	for (d = 0, Dp = D; d < Dlen;
361	     d += BLOCK_SIZE, Dp += BLOCK_SIZE, out += BLOCK_SIZE) {
362 	    des_ecb_encrypt((void *) Dp, (void *) out, ks, DES_ENCRYPT);
363	}
364    }
365}
366
367static unsigned char *P16_lm(unsigned char *P16, sasl_secret_t *passwd,
368			     const sasl_utils_t *utils __attribute__((unused)),
369			     char **buf __attribute__((unused)),
370			     unsigned *buflen __attribute__((unused)),
371			     int *result)
372{
373    char P14[14];
374    unsigned char S8[] = { 0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 };
375
376    strncpy(P14, (char *)passwd->data, sizeof(P14));
377    ucase(P14, sizeof(P14));
378
379    E(P16, P14, sizeof(P14), S8, sizeof(S8));
380    *result = SASL_OK;
381    return P16;
382}
383
384static unsigned char *P16_nt(unsigned char *P16, sasl_secret_t *passwd,
385			     const sasl_utils_t *utils,
386			     char **buf, unsigned *buflen, int *result)
387{
388    if (_plug_buf_alloc(utils, buf, buflen, 2 * passwd->len) != SASL_OK) {
389	SETERROR(utils, "cannot allocate P16_nt unicode buffer");
390	*result = SASL_NOMEM;
391    }
392    else {
393	to_unicode(*buf, passwd->data, passwd->len);
394	MD4(*buf, 2 * passwd->len, P16);
395	*result = SASL_OK;
396    }
397    return P16;
398}
399
400static unsigned char *P21(unsigned char *P21, sasl_secret_t *passwd,
401			  unsigned char * (*P16)(unsigned char *,
402						 sasl_secret_t *,
403						 const sasl_utils_t *,
404						 char **, unsigned *, int *),
405			  const sasl_utils_t *utils,
406			  char **buf, unsigned *buflen, int *result)
407{
408    memset(P16(P21, passwd, utils, buf, buflen, result) + 16, 0, 5);
409    return P21;
410}
411
412static unsigned char *P24(unsigned char *P24, unsigned char *P21,
413			  unsigned char *C8)
414
415{
416    E(P24, P21, NTLM_HASH_LENGTH, C8, NTLM_NONCE_LENGTH);
417    return P24;
418}
419
420static unsigned char *V2(unsigned char *V2, sasl_secret_t *passwd,
421			 const char *authid, const char *target,
422			 const unsigned char *challenge,
423			 const unsigned char *blob, unsigned bloblen,
424			 const sasl_utils_t *utils,
425			 char **buf, unsigned *buflen, int *result)
426{
427    HMAC_CTX ctx;
428    unsigned char hash[EVP_MAX_MD_SIZE];
429    char *upper;
430    int len;
431
432    /* Allocate enough space for the unicode target */
433    len = (int) (strlen(authid) + xstrlen(target));
434    if (_plug_buf_alloc(utils, buf, buflen, 2 * len + 1) != SASL_OK) {
435	SETERROR(utils, "cannot allocate NTLMv2 hash");
436	*result = SASL_NOMEM;
437    }
438    else {
439	/* NTLMv2hash = HMAC-MD5(NTLMhash, unicode(ucase(authid + domain))) */
440	P16_nt(hash, passwd, utils, buf, buflen, result);
441
442	/* Use the tail end of the buffer for ucase() conversion */
443	upper = *buf + len;
444	strcpy(upper, authid);
445	if (target) strcat(upper, target);
446	ucase(upper, len);
447	to_unicode(*buf, upper, len);
448
449	HMAC(EVP_md5(), hash, MD4_DIGEST_LENGTH, *buf, 2 * len, hash, &len);
450
451	/* V2 = HMAC-MD5(NTLMv2hash, challenge + blob) + blob */
452	HMAC_Init(&ctx, hash, len, EVP_md5());
453	HMAC_Update(&ctx, challenge, NTLM_NONCE_LENGTH);
454	HMAC_Update(&ctx, blob, bloblen);
455	HMAC_Final(&ctx, V2, &len);
456	HMAC_cleanup(&ctx);
457
458	/* the blob is concatenated outside of this function */
459
460	*result = SASL_OK;
461    }
462
463    return V2;
464}
465
466/*****************************  Server Section  *****************************/
467
468typedef struct server_context {
469    int state;
470
471    uint32 flags;
472    unsigned char nonce[NTLM_NONCE_LENGTH];
473
474    /* per-step mem management */
475    char *out_buf;
476    unsigned out_buf_len;
477
478    /* socket to remote authentication host */
479    SOCKET sock;
480
481} server_context_t;
482
483#define	N(a)			(sizeof (a) / sizeof (a[0]))
484
485#define SMB_HDR_PROTOCOL	"\xffSMB"
486
487typedef struct {
488    unsigned char protocol[4];
489    unsigned char command;
490    uint32 status;
491    unsigned char flags;
492    uint16 flags2;
493    uint16 PidHigh;
494    unsigned char extra[10];
495    uint16 tid;
496    uint16 pid;
497    uint16 uid;
498    uint16 mid;
499} SMB_Header;
500
501typedef struct {
502    uint16 dialect_index;
503    unsigned char security_mode;
504    uint16 max_mpx_count;
505    uint16 max_number_vcs;
506    uint32 max_buffer_size;
507    uint32 max_raw_size;
508    uint32 session_key;
509    uint32 capabilities;
510    uint32 system_time_low;
511    uint32 system_time_high;
512    uint16 server_time_zone;
513    unsigned char encryption_key_length;
514} SMB_NegProt_Resp;
515
516typedef struct {
517    unsigned char andx_command;
518    unsigned char andx_reserved;
519    uint16 andx_offset;
520    uint16 max_buffer_size;
521    uint16 max_mpx_count;
522    uint16 vc_number;
523    uint32 session_key;
524    uint16 case_insensitive_passwd_len;
525    uint16 case_sensitive_passwd_len;
526    uint32 reserved;
527    uint32 capabilities;
528} SMB_SessionSetup;
529
530typedef struct {
531    unsigned char andx_command;
532    unsigned char andx_reserved;
533    uint16 andx_offset;
534    uint16 action;
535} SMB_SessionSetup_Resp;
536
537enum {
538    NBT_SESSION_REQUEST		= 0x81,
539    NBT_POSITIVE_SESSION_RESP	= 0x82,
540    NBT_NEGATIVE_SESSION_RESP	= 0x83,
541    NBT_ERR_NO_LISTEN_CALLED	= 0x80,
542    NBT_ERR_NO_LISTEN_CALLING	= 0x81,
543    NBT_ERR_CALLED_NOT_PRESENT	= 0x82,
544    NBT_ERR_INSUFFICIENT_RESRC	= 0x83,
545    NBT_ERR_UNSPECIFIED		= 0x8F,
546
547    SMB_HDR_SIZE		= 32,
548
549    SMB_COM_NEGOTIATE_PROTOCOL	= 0x72,
550    SMB_COM_SESSION_SETUP_ANDX	= 0x73,
551    SMB_COM_NONE		= 0xFF,
552
553    SMB_FLAGS_SERVER_TO_REDIR	= 0x80,
554
555    SMB_FLAGS2_ERR_STATUS	= 0x4000,
556    SMB_FLAGS2_UNICODE		= 0x8000,
557
558    SMB_NEGPROT_RESP_SIZE	= 34,
559
560    SMB_SECURITY_MODE_USER	= 0x1,
561    SMB_SECURITY_MODE_ENCRYPT	= 0x2,
562    SMB_SECURITY_MODE_SIGN	= 0x4,
563    SMB_SECURITY_MODE_SIGN_REQ	= 0x8,
564
565    SMB_CAP_UNICODE		= 0x0004,
566    SMB_CAP_STATUS32		= 0x0040,
567    SMB_CAP_EXTENDED_SECURITY	= 0x80000000,
568
569    SMB_SESSION_SETUP_SIZE	= 26,
570    SMB_SESSION_SETUP_RESP_SIZE	= 6,
571
572    SMB_REQUEST_MODE_GUEST	= 0x1
573};
574
575static const char *SMB_DIALECTS[] = {
576#if 0
577    "\x02PC NETWORK PROGRAM 1.0",
578    "\x02PCLAN1.0",
579    "\x02MICROSOFT NETWORKS 1.03",
580    "\x02MICROSOFT NETWORKS 3.0",
581    "\x02LANMAN1.0",
582    "\x02Windows for Workgroups 3.1a",
583    "\x02LM1.2X002",
584    "\x02DOS LM1.2X002",
585    "\x02DOS LANLAM2.1",
586    "\x02LANMAN2.1",
587#endif
588    "\x02NT LM 0.12"
589};
590
591static void load_smb_header(unsigned char buf[], SMB_Header *hdr)
592{
593    unsigned char *p = buf;
594
595    memcpy(p, SMB_HDR_PROTOCOL, 4); p += 4;
596    *p++ = hdr->command;
597    htoil(p, hdr->status); p += 4;
598    *p++ = hdr->flags;
599    htois(p, hdr->flags2); p += 2;
600    htois(p, hdr->PidHigh); p += 2;
601    memcpy(p, hdr->extra, 10); p += 10;
602    htois(p, hdr->tid); p += 2;
603    htois(p, hdr->pid); p += 2;
604    htois(p, hdr->uid); p += 2;
605    htois(p, hdr->mid);
606}
607
608static void unload_smb_header(unsigned char buf[], SMB_Header *hdr)
609{
610    unsigned char *p = buf;
611
612    memcpy(hdr->protocol, p, 4); p += 4;
613    hdr->command = *p++;
614    hdr->status = itohl(p); p += 4;
615    hdr->flags = *p++;
616    hdr->flags2 = itohs(p); p += 2;
617    hdr->PidHigh = itohs(p); p += 2;
618    memcpy(hdr->extra, p, 10); p += 10;
619    hdr->tid = itohs(p); p += 2;
620    hdr->pid = itohs(p); p += 2;
621    hdr->uid = itohs(p); p += 2;
622    hdr->mid = itohs(p);
623}
624
625static void unload_negprot_resp(unsigned char buf[], SMB_NegProt_Resp *resp)
626{
627    unsigned char *p = buf;
628
629    resp->dialect_index = itohs(p); p += 2;
630    resp->security_mode = *p++;
631    resp->max_mpx_count = itohs(p); p += 2;
632    resp->max_number_vcs = itohs(p); p += 2;
633    resp->max_buffer_size = itohl(p); p += 4;
634    resp->max_raw_size = itohl(p); p += 4;
635    resp->session_key = itohl(p); p += 4;
636    resp->capabilities = itohl(p); p += 4;
637    resp->system_time_low = itohl(p); p += 4;
638    resp->system_time_high = itohl(p); p += 4;
639    resp->server_time_zone = itohs(p); p += 2;
640    resp->encryption_key_length = *p;
641}
642
643static void load_session_setup(unsigned char buf[], SMB_SessionSetup *setup)
644{
645    unsigned char *p = buf;
646
647    *p++ = setup->andx_command;
648    *p++ = setup->andx_reserved;
649    htois(p, setup->andx_offset); p += 2;
650    htois(p, setup->max_buffer_size); p += 2;
651    htois(p, setup->max_mpx_count); p += 2;
652    htois(p, setup->vc_number); p += 2;
653    htoil(p, setup->session_key); p += 4;
654    htois(p, setup->case_insensitive_passwd_len); p += 2;
655    htois(p, setup->case_sensitive_passwd_len); p += 2;
656    htoil(p, setup->reserved); p += 4;
657    htoil(p, setup->capabilities); p += 4;
658}
659
660static void unload_session_setup_resp(unsigned char buf[],
661				      SMB_SessionSetup_Resp *resp)
662{
663    unsigned char *p = buf;
664
665    resp->andx_command = *p++;
666    resp->andx_reserved = *p++;
667    resp->andx_offset = itohs(p); p += 2;
668    resp->action = itohs(p);
669}
670
671/*
672 * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
673 * until all the data is written out or an error occurs.
674 */
675static int retry_writev(SOCKET fd, struct iovec *iov, int iovcnt)
676{
677    int n;
678    int i;
679    int written = 0;
680    static int iov_max =
681#ifdef MAXIOV
682	MAXIOV
683#else
684#ifdef IOV_MAX
685	IOV_MAX
686#else
687	8192
688#endif
689#endif
690	;
691
692    for (;;) {
693	while (iovcnt && iov[0].iov_len == 0) {
694	    iov++;
695	    iovcnt--;
696	}
697
698	if (!iovcnt) return written;
699
700	n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
701	if (n == -1) {
702#ifndef WIN32
703	    if (errno == EINVAL && iov_max > 10) {
704		iov_max /= 2;
705		continue;
706	    }
707	    if (errno == EINTR) continue;
708#endif
709	    return -1;
710	}
711
712	written += n;
713
714	for (i = 0; i < iovcnt; i++) {
715	    if ((int) iov[i].iov_len > n) {
716		iov[i].iov_base = (char *) iov[i].iov_base + n;
717		iov[i].iov_len -= n;
718		break;
719	    }
720	    n -= iov[i].iov_len;
721	    iov[i].iov_len = 0;
722	}
723
724	if (i == iovcnt) return written;
725    }
726}
727
728/*
729 * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
730 * until all the data is read in or an error occurs.
731 */
732static int retry_read(SOCKET fd, char *buf0, unsigned nbyte)
733{
734    int n;
735    int nread = 0;
736    char *buf = buf0;
737
738    if (nbyte == 0) return 0;
739
740    for (;;) {
741/* Can't use read() on sockets on Windows, but recv works on all platforms */
742	n = recv (fd, buf, nbyte, 0);
743	if (n == -1 || n == 0) {
744#ifndef WIN32
745	    if (errno == EINTR || errno == EAGAIN) continue;
746#endif
747	    return -1;
748	}
749
750	nread += n;
751
752	if (n >= (int) nbyte) return nread;
753
754	buf += n;
755	nbyte -= n;
756    }
757}
758
759static void make_netbios_name(const char *in, unsigned char out[])
760{
761    size_t i, j = 0, n;
762
763    /* create a NetBIOS name from the DNS name
764     *
765     * - use up to the first 16 chars of the first part of the hostname
766     * - convert to all uppercase
767     * - use the tail end of the output buffer as temp space
768     */
769    n = strcspn(in, ".");
770    if (n > 16) n = 16;
771    strncpy((char *)out+18, in, n);
772    in = out+18;
773    ucase(in, n);
774
775    out[j++] = 0x20;
776    for (i = 0; i < n; i++) {
777	out[j++] = ((in[i] >> 4) & 0xf) + 0x41;
778	out[j++] = (in[i] & 0xf) + 0x41;
779    }
780    for (; i < 16; i++) {
781	out[j++] = ((0x20 >> 4) & 0xf) + 0x41;
782	out[j++] = (0x20 & 0xf) + 0x41;
783    }
784    out[j] = 0;
785}
786
787static SOCKET smb_connect_server(const sasl_utils_t *utils, const char *client,
788			      const char *server)
789{
790    struct addrinfo hints;
791    struct addrinfo *ai = NULL, *r;
792    SOCKET s = (SOCKET) -1;
793    int err;
794    char * error_str;
795#ifdef WIN32
796    DWORD saved_errno;
797#else
798    int saved_errno;
799#endif
800    int niflags;
801    char *port = "139";
802    char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
803
804    unsigned char called[34];
805    unsigned char calling[34];
806    struct iovec iov[3];
807    uint32 pkt;
808    int rc;
809
810    memset(&hints, 0, sizeof(hints));
811    hints.ai_family = PF_UNSPEC;
812    hints.ai_socktype = SOCK_STREAM;
813    hints.ai_flags = AI_CANONNAME;
814    if ((err = getaddrinfo(server, port, &hints, &ai)) != 0) {
815	utils->log(NULL, SASL_LOG_ERR,
816		   "NTLM: getaddrinfo %s/%s: %s",
817		   server, port, gai_strerror(err));
818	return -1;
819    }
820
821    /* Make sure we have AF_INET or AF_INET6 addresses. */
822    if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
823	utils->log(NULL, SASL_LOG_ERR, "NTLM: no IP address info for %s",
824		   ai->ai_canonname ? ai->ai_canonname : server);
825	freeaddrinfo(ai);
826	return -1;
827    }
828
829    /* establish connection to authentication server */
830    for (r = ai; r; r = r->ai_next) {
831	s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
832	if (s < 0)
833	    continue;
834	if (connect(s, r->ai_addr, r->ai_addrlen) >= 0)
835	    break;
836#ifdef WIN32
837	saved_errno = WSAGetLastError();
838#else
839	saved_errno = errno;
840#endif
841	closesocket (s);
842	s = -1;
843	niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
844#ifdef NI_WITHSCOPEID
845	if (r->ai_family == AF_INET6)
846	    niflags |= NI_WITHSCOPEID;
847#endif
848	if (getnameinfo(r->ai_addr, r->ai_addrlen, hbuf, sizeof(hbuf),
849			pbuf, sizeof(pbuf), niflags) != 0) {
850	    strcpy(hbuf, "unknown");
851	    strcpy(pbuf, "unknown");
852	}
853
854        /* Can't use errno (and %m), as it doesn't contain
855         * the socket error on Windows */
856	error_str = _plug_get_error_message (utils, saved_errno);
857	utils->log(NULL, SASL_LOG_WARN, "NTLM: connect %s[%s]/%s: %s",
858		   ai->ai_canonname ? ai->ai_canonname : server,
859		   hbuf,
860		   pbuf,
861		   error_str);
862	utils->free (error_str);
863    }
864    if (s < 0) {
865	if (getnameinfo(ai->ai_addr, ai->ai_addrlen, NULL, 0,
866			pbuf, sizeof(pbuf), NI_NUMERICSERV) != 0) {
867		strcpy(pbuf, "unknown");
868	}
869	utils->log(NULL, SASL_LOG_ERR, "NTLM: couldn't connect to %s/%s",
870		   ai->ai_canonname ? ai->ai_canonname : server, pbuf);
871	freeaddrinfo(ai);
872	return -1;
873    }
874
875    freeaddrinfo(ai);
876
877    /*** send NetBIOS session request ***/
878
879    /* get length of data */
880    pkt = sizeof(called) + sizeof(calling);
881
882    /* make sure length is less than 17 bits */
883    if (pkt >= (1 << 17)) {
884	closesocket(s);
885	return -1;
886    }
887
888    /* prepend the packet type */
889    pkt |= (NBT_SESSION_REQUEST << 24);
890    pkt = htonl(pkt);
891
892    /* XXX should determine the real NetBIOS name */
893    make_netbios_name(server, called);
894    make_netbios_name(client, calling);
895
896    iov[0].iov_base = (void *) &pkt;
897    iov[0].iov_len = sizeof(pkt);
898    iov[1].iov_base = called;
899    iov[1].iov_len = sizeof(called);
900    iov[2].iov_base = calling;
901    iov[2].iov_len = sizeof(calling);
902
903    rc = retry_writev(s, iov, N(iov));
904    if (rc == -1) {
905	utils->log(NULL, SASL_LOG_ERR,
906		   "NTLM: error sending NetBIOS session request");
907	closesocket(s);
908	return -1;
909    }
910
911    rc = retry_read(s, (char *) &pkt, sizeof(pkt));
912    pkt = ntohl(pkt);
913    if (rc == -1 || pkt != (uint32) (NBT_POSITIVE_SESSION_RESP << 24)) {
914	unsigned char ec = NBT_ERR_UNSPECIFIED;
915	char *errstr;
916
917	retry_read(s, (char *) &ec, sizeof(ec));
918	switch (ec) {
919	case NBT_ERR_NO_LISTEN_CALLED:
920	    errstr = "Not listening on called name";
921	    break;
922	case NBT_ERR_NO_LISTEN_CALLING:
923	    errstr = "Not listening for calling name";
924	    break;
925	case NBT_ERR_CALLED_NOT_PRESENT:
926	    errstr = "Called name not present";
927	    break;
928	case NBT_ERR_INSUFFICIENT_RESRC:
929	    errstr = "Called name present, but insufficient resources";
930	    break;
931	default:
932	    errstr = "Unspecified error";
933	}
934	utils->log(NULL, SASL_LOG_ERR,
935		   "NTLM: negative NetBIOS session response: %s", errstr);
936	closesocket(s);
937	return -1;
938    }
939
940    return s;
941}
942
943static int smb_negotiate_protocol(const sasl_utils_t *utils,
944				  server_context_t *text, char **domain)
945{
946    SMB_Header hdr;
947    SMB_NegProt_Resp resp;
948    unsigned char hbuf[SMB_HDR_SIZE], *p;
949    unsigned char wordcount = 0;
950    unsigned char bc[sizeof(uint16)];
951    uint16 bytecount;
952    uint32 len, nl;
953    int n_dialects = N(SMB_DIALECTS);
954    struct iovec iov[4+N(SMB_DIALECTS)];
955    size_t i, n;
956    int rc;
957    pid_t current_pid;
958
959    /*** create a negotiate protocol request ***/
960
961    /* create a header */
962    memset(&hdr, 0, sizeof(hdr));
963    hdr.command = SMB_COM_NEGOTIATE_PROTOCOL;
964#if 0
965    hdr.flags2 = SMB_FLAGS2_ERR_STATUS;
966    if (text->flags & NTLM_USE_UNICODE) hdr.flags2 |= SMB_FLAGS2_UNICODE;
967#endif
968    current_pid = getpid();
969    if (sizeof(current_pid) <= 2) {
970	hdr.pid = (uint16) current_pid;
971	hdr.PidHigh = 0;
972    } else {
973	hdr.pid = (uint16) (((uint32) current_pid) & 0xFFFF);
974	hdr.PidHigh = (uint16) (((uint32) current_pid) >> 16);
975    }
976
977    load_smb_header(hbuf, &hdr);
978
979    /* put together all of the pieces of the request */
980    n = 0;
981    iov[n].iov_base = (void *) &nl;
982    iov[n++].iov_len = sizeof(len);
983    iov[n].iov_base = hbuf;
984    iov[n++].iov_len = SMB_HDR_SIZE;
985    iov[n].iov_base = &wordcount;
986    iov[n++].iov_len = sizeof(wordcount);
987    iov[n].iov_base = (void *) &bc;
988    iov[n++].iov_len = sizeof(bc);
989
990    /* add our supported dialects */
991    for (i = 0; i < n_dialects; i++) {
992	iov[n].iov_base = (char *) SMB_DIALECTS[i];
993	iov[n++].iov_len = (long) strlen(SMB_DIALECTS[i]) + 1;
994    }
995
996    /* total up the lengths */
997    len = bytecount = 0;
998    for (i = 1; i < 4; i++) len += iov[i].iov_len;
999    for (i = 4; i < n; i++) bytecount += (uint16) iov[i].iov_len;
1000    len += bytecount;
1001    nl = htonl(len);
1002    htois((char *) &bc, bytecount);
1003
1004    /* send it */
1005    rc = retry_writev(text->sock, iov, n);
1006    if (rc == -1) {
1007	utils->log(NULL, SASL_LOG_ERR,
1008		   "NTLM: error sending NEGPROT request");
1009	return SASL_FAIL;
1010    }
1011
1012    /*** read the negotiate protocol response ***/
1013
1014    /* read the total length */
1015    rc = retry_read(text->sock, (char *) &nl, sizeof(nl));
1016    if (rc < (int) sizeof(nl)) {
1017	utils->log(NULL, SASL_LOG_ERR,
1018		   "NTLM: error reading NEGPROT response length");
1019	return SASL_FAIL;
1020    }
1021
1022    /* read the data */
1023    len = ntohl(nl);
1024    if (_plug_buf_alloc(utils, &text->out_buf, &text->out_buf_len,
1025			len) != SASL_OK) {
1026	SETERROR(utils, "cannot allocate NTLM NEGPROT response buffer");
1027	return SASL_NOMEM;
1028    }
1029
1030    rc = retry_read(text->sock, text->out_buf, len);
1031    if (rc < (int) len) {
1032	utils->log(NULL, SASL_LOG_ERR,
1033		   "NTLM: error reading NEGPROT response");
1034	return SASL_FAIL;
1035    }
1036    p = text->out_buf;
1037
1038    /* parse the header */
1039    if (len < SMB_HDR_SIZE) {
1040	utils->log(NULL, SASL_LOG_ERR,
1041		   "NTLM: not enough data for NEGPROT response header");
1042	return SASL_FAIL;
1043    }
1044    unload_smb_header(p, &hdr);
1045    p += SMB_HDR_SIZE;
1046    len -= SMB_HDR_SIZE;
1047
1048    /* sanity check the header */
1049    if (memcmp(hdr.protocol, SMB_HDR_PROTOCOL, 4)	 /* correct protocol */
1050	|| hdr.command != SMB_COM_NEGOTIATE_PROTOCOL /* correct command */
1051	|| hdr.status				 /* no errors */
1052	|| !(hdr.flags & SMB_FLAGS_SERVER_TO_REDIR)) { /* response */
1053	utils->log(NULL, SASL_LOG_ERR,
1054		   "NTLM: error in NEGPROT response header: %ld",
1055		   hdr.status);
1056	return SASL_FAIL;
1057    }
1058
1059    /* get the wordcount */
1060    if (len < 1) {
1061	utils->log(NULL, SASL_LOG_ERR,
1062		   "NTLM: not enough data for NEGPROT response wordcount");
1063	return SASL_FAIL;
1064    }
1065    wordcount = *p++;
1066    len--;
1067
1068    /* parse the parameters */
1069    if (wordcount != SMB_NEGPROT_RESP_SIZE / sizeof(uint16)) {
1070	utils->log(NULL, SASL_LOG_ERR,
1071		   "NTLM: incorrect NEGPROT wordcount for NT LM 0.12");
1072	return SASL_FAIL;
1073    }
1074    unload_negprot_resp(p, &resp);
1075    p += SMB_NEGPROT_RESP_SIZE;
1076    len -= SMB_NEGPROT_RESP_SIZE;
1077
1078    /* sanity check the parameters */
1079    if (resp.dialect_index != 0
1080	|| !(resp.security_mode & SMB_SECURITY_MODE_USER)
1081	|| !(resp.security_mode & SMB_SECURITY_MODE_ENCRYPT)
1082	|| resp.security_mode & SMB_SECURITY_MODE_SIGN_REQ
1083	|| resp.capabilities & SMB_CAP_EXTENDED_SECURITY
1084	|| resp.encryption_key_length != NTLM_NONCE_LENGTH) {
1085	utils->log(NULL, SASL_LOG_ERR,
1086		   "NTLM: error in NEGPROT response parameters");
1087	return SASL_FAIL;
1088    }
1089
1090    /* get the bytecount */
1091    if (len < 2) {
1092	utils->log(NULL, SASL_LOG_ERR,
1093		   "NTLM: not enough data for NEGPROT response bytecount");
1094	return SASL_FAIL;
1095    }
1096    bytecount = itohs(p);
1097    p += 2;
1098    len -= 2;
1099    if (len != bytecount) {
1100	utils->log(NULL, SASL_LOG_ERR,
1101		   "NTLM: incorrect bytecount for NEGPROT response data");
1102	return SASL_FAIL;
1103    }
1104
1105    /* parse the data */
1106    memcpy(text->nonce, p, resp.encryption_key_length);
1107    p += resp.encryption_key_length;
1108    len -= resp.encryption_key_length;
1109
1110    /* if client asked for target, send domain */
1111    if (text->flags & NTLM_ASK_TARGET) {
1112	*domain = utils->malloc(len);
1113	if (domain == NULL) {
1114	    MEMERROR(utils);
1115	    return SASL_NOMEM;
1116	}
1117	memcpy(*domain, p, len);
1118	from_unicode(*domain, *domain, len);
1119
1120	text->flags |= NTLM_TARGET_IS_DOMAIN;
1121    }
1122
1123    return SASL_OK;
1124}
1125
1126static int smb_session_setup(const sasl_utils_t *utils, server_context_t *text,
1127			     const char *authid, char *domain,
1128			     unsigned char *lm_resp, unsigned lm_resp_len,
1129			     unsigned char *nt_resp, unsigned nt_resp_len)
1130{
1131    SMB_Header hdr;
1132    SMB_SessionSetup setup;
1133    SMB_SessionSetup_Resp resp;
1134    unsigned char hbuf[SMB_HDR_SIZE], sbuf[SMB_SESSION_SETUP_SIZE], *p;
1135    unsigned char wordcount = SMB_SESSION_SETUP_SIZE / sizeof(uint16);
1136    unsigned char bc[sizeof(uint16)];
1137    uint16 bytecount;
1138    uint32 len, nl;
1139    struct iovec iov[12];
1140    size_t i, n;
1141    int rc;
1142#if (defined(WIN32) || defined(__APPLE__))
1143    char osbuf[80];
1144#else
1145    char osbuf[2*SYS_NMLN+2];
1146#endif
1147    char lanman[20];
1148    pid_t current_pid;
1149
1150    /*** create a session setup request ***/
1151
1152    /* create a header */
1153    memset(&hdr, 0, sizeof(hdr));
1154    hdr.command = SMB_COM_SESSION_SETUP_ANDX;
1155#if 0
1156    hdr.flags2 = SMB_FLAGS2_ERR_STATUS;
1157    if (text->flags & NTLM_USE_UNICODE) hdr.flags2 |= SMB_FLAGS2_UNICODE;
1158#endif
1159    current_pid = getpid();
1160    if (sizeof(current_pid) <= 2) {
1161	hdr.pid = (uint16) current_pid;
1162	hdr.PidHigh = 0;
1163    } else {
1164	hdr.pid = (uint16) (((uint32) current_pid) & 0xFFFF);
1165	hdr.PidHigh = (uint16) (((uint32) current_pid) >> 16);
1166    }
1167
1168    load_smb_header(hbuf, &hdr);
1169
1170    /* create a the setup parameters */
1171    memset(&setup, 0, sizeof(setup));
1172    setup.andx_command = SMB_COM_NONE;
1173    setup.max_buffer_size = 0xFFFF;
1174    if (lm_resp) setup.case_insensitive_passwd_len = lm_resp_len;
1175    if (nt_resp) setup.case_sensitive_passwd_len = nt_resp_len;
1176#if 0
1177    if (text->flags & NTLM_USE_UNICODE)
1178	setup.capabilities = SMB_CAP_UNICODE;
1179#endif
1180    load_session_setup(sbuf, &setup);
1181
1182    _plug_snprintf_os_info (osbuf, sizeof(osbuf));
1183
1184    snprintf(lanman, sizeof(lanman), "Cyrus SASL %u.%u.%u",
1185	     SASL_VERSION_MAJOR, SASL_VERSION_MINOR,
1186	     SASL_VERSION_STEP);
1187
1188    /* put together all of the pieces of the request */
1189    n = 0;
1190    iov[n].iov_base = (void *) &nl;
1191    iov[n++].iov_len = sizeof(len);
1192    iov[n].iov_base = hbuf;
1193    iov[n++].iov_len = SMB_HDR_SIZE;
1194    iov[n].iov_base = &wordcount;
1195    iov[n++].iov_len = sizeof(wordcount);
1196    iov[n].iov_base = sbuf;
1197    iov[n++].iov_len = SMB_SESSION_SETUP_SIZE;
1198    iov[n].iov_base = (void *) &bc;
1199    iov[n++].iov_len = sizeof(bc);
1200    if (lm_resp) {
1201	iov[n].iov_base = lm_resp;
1202	iov[n++].iov_len = NTLM_RESP_LENGTH;
1203    }
1204    if (nt_resp) {
1205	iov[n].iov_base = nt_resp;
1206	iov[n++].iov_len = NTLM_RESP_LENGTH;
1207    }
1208    iov[n].iov_base = (char*) authid;
1209    iov[n++].iov_len = (long) strlen(authid) + 1;
1210    if (!domain) domain = "";
1211    iov[n].iov_base = domain;
1212    iov[n++].iov_len = (long) strlen(domain) + 1;
1213    iov[n].iov_base = osbuf;
1214    iov[n++].iov_len = (long) strlen(osbuf) + 1;
1215    iov[n].iov_base = lanman;
1216    iov[n++].iov_len = (long) strlen(lanman) + 1;
1217
1218    /* total up the lengths */
1219    len = bytecount = 0;
1220    for (i = 1; i < 5; i++) len += iov[i].iov_len;
1221    for (i = 5; i < n; i++) bytecount += (uint16) iov[i].iov_len;
1222    len += bytecount;
1223    nl = htonl(len);
1224    htois((char *) &bc, bytecount);
1225
1226    /* send it */
1227    rc = retry_writev(text->sock, iov, n);
1228    if (rc == -1) {
1229	utils->log(NULL, SASL_LOG_ERR,
1230		   "NTLM: error sending SESSIONSETUP request");
1231	return SASL_FAIL;
1232    }
1233
1234    /*** read the session setup response ***/
1235
1236    /* read the total length */
1237    rc = retry_read(text->sock, (char *) &nl, sizeof(nl));
1238    if (rc < (int) sizeof(nl)) {
1239	utils->log(NULL, SASL_LOG_ERR,
1240		   "NTLM: error reading SESSIONSETUP response length");
1241	return SASL_FAIL;
1242    }
1243
1244    /* read the data */
1245    len = ntohl(nl);
1246    if (_plug_buf_alloc(utils, &text->out_buf, &text->out_buf_len,
1247			len) != SASL_OK) {
1248	SETERROR(utils,
1249		 "cannot allocate NTLM SESSIONSETUP response buffer");
1250	return SASL_NOMEM;
1251    }
1252
1253    rc = retry_read(text->sock, text->out_buf, len);
1254    if (rc < (int) len) {
1255	utils->log(NULL, SASL_LOG_ERR,
1256		   "NTLM: error reading SESSIONSETUP response");
1257	return SASL_FAIL;
1258    }
1259    p = text->out_buf;
1260
1261    /* parse the header */
1262    if (len < SMB_HDR_SIZE) {
1263	utils->log(NULL, SASL_LOG_ERR,
1264		   "NTLM: not enough data for SESSIONSETUP response header");
1265	return SASL_FAIL;
1266    }
1267    unload_smb_header(p, &hdr);
1268    p += SMB_HDR_SIZE;
1269    len -= SMB_HDR_SIZE;
1270
1271    /* sanity check the header */
1272    if (memcmp(hdr.protocol, SMB_HDR_PROTOCOL, 4)	/* correct protocol */
1273	|| hdr.command != SMB_COM_SESSION_SETUP_ANDX	/* correct command */
1274	|| !(hdr.flags & SMB_FLAGS_SERVER_TO_REDIR)) {	/* response */
1275	utils->log(NULL, SASL_LOG_ERR,
1276		   "NTLM: error in SESSIONSETUP response header");
1277	return SASL_FAIL;
1278    }
1279
1280    /* check auth success */
1281    if (hdr.status) {
1282	utils->log(NULL, SASL_LOG_ERR,
1283		   "NTLM: auth failure: %ld", hdr.status);
1284	return SASL_BADAUTH;
1285    }
1286
1287    /* get the wordcount */
1288    if (len < 1) {
1289	utils->log(NULL, SASL_LOG_ERR,
1290		   "NTLM: not enough data for SESSIONSETUP response wordcount");
1291	return SASL_FAIL;
1292    }
1293    wordcount = *p++;
1294    len--;
1295
1296    /* parse the parameters */
1297    if (wordcount < SMB_SESSION_SETUP_RESP_SIZE / sizeof(uint16)) {
1298	utils->log(NULL, SASL_LOG_ERR,
1299		   "NTLM: incorrect SESSIONSETUP wordcount");
1300	return SASL_FAIL;
1301    }
1302    unload_session_setup_resp(p, &resp);
1303
1304    /* check auth success */
1305    if (resp.action & SMB_REQUEST_MODE_GUEST) {
1306	utils->log(NULL, SASL_LOG_ERR,
1307		   "NTLM: authenticated as guest");
1308	return SASL_BADAUTH;
1309    }
1310
1311    return SASL_OK;
1312}
1313
1314/*
1315 * Create a server challenge message (type 2) consisting of:
1316 *
1317 * signature (8 bytes)
1318 * message type (uint32)
1319 * target name (buffer)
1320 * flags (uint32)
1321 * challenge (8 bytes)
1322 * context (8 bytes)
1323 * target info (buffer)
1324 * data
1325 */
1326static int create_challenge(const sasl_utils_t *utils,
1327			    char **buf, unsigned *buflen,
1328			    const char *target, uint32 flags,
1329			    const u_char *nonce, unsigned *outlen)
1330{
1331    uint32 offset = NTLM_TYPE2_DATA_OFFSET;
1332    u_char *base;
1333
1334    if (!nonce) {
1335	SETERROR(utils, "need nonce for NTLM challenge");
1336	return SASL_FAIL;
1337    }
1338
1339    *outlen = offset + 2 * xstrlen(target);
1340
1341    if (_plug_buf_alloc(utils, buf, buflen, *outlen) != SASL_OK) {
1342	SETERROR(utils, "cannot allocate NTLM challenge");
1343	return SASL_NOMEM;
1344    }
1345
1346    base = *buf;
1347    memset(base, 0, *outlen);
1348    memcpy(base + NTLM_SIG_OFFSET, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
1349    htoil(base + NTLM_TYPE_OFFSET, NTLM_TYPE_CHALLENGE);
1350    load_buffer(base + NTLM_TYPE2_TARGET_OFFSET,
1351		ucase(target, 0), (uint16) xstrlen(target), flags & NTLM_USE_UNICODE,
1352		base, &offset);
1353    htoil(base + NTLM_TYPE2_FLAGS_OFFSET, flags);
1354    memcpy(base + NTLM_TYPE2_CHALLENGE_OFFSET, nonce, NTLM_NONCE_LENGTH);
1355
1356    return SASL_OK;
1357}
1358
1359static int ntlm_server_mech_new(void *glob_context __attribute__((unused)),
1360				sasl_server_params_t *sparams,
1361				const char *challenge __attribute__((unused)),
1362				unsigned challen __attribute__((unused)),
1363				void **conn_context)
1364{
1365    server_context_t *text;
1366    const char *serv;
1367    unsigned int len;
1368    SOCKET sock = (SOCKET) -1;
1369
1370    sparams->utils->getopt(sparams->utils->getopt_context,
1371			   "NTLM", "ntlm_server", &serv, &len);
1372    if (serv) {
1373	/* try to start a NetBIOS session with the server */
1374	sock = smb_connect_server(sparams->utils, sparams->serverFQDN, serv);
1375	if (sock == (SOCKET) -1) return SASL_UNAVAIL;
1376    }
1377
1378    /* holds state are in */
1379    text = sparams->utils->malloc(sizeof(server_context_t));
1380    if (text == NULL) {
1381	MEMERROR( sparams->utils );
1382	return SASL_NOMEM;
1383    }
1384
1385    memset(text, 0, sizeof(server_context_t));
1386
1387    text->state = 1;
1388    text->sock = sock;
1389
1390    *conn_context = text;
1391
1392    return SASL_OK;
1393}
1394
1395static int ntlm_server_mech_step1(server_context_t *text,
1396				  sasl_server_params_t *sparams,
1397				  const char *clientin,
1398				  unsigned clientinlen,
1399				  const char **serverout,
1400				  unsigned *serveroutlen,
1401				  sasl_out_params_t *oparams __attribute__((unused)))
1402{
1403    char *domain = NULL;
1404    int result;
1405
1406    if (!clientin || clientinlen < NTLM_TYPE1_MINSIZE ||
1407	memcmp(clientin, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) ||
1408	itohl(clientin + NTLM_TYPE_OFFSET) != NTLM_TYPE_REQUEST) {
1409	SETERROR(sparams->utils, "client didn't issue valid NTLM request");
1410	return SASL_BADPROT;
1411    }
1412
1413    text->flags = itohl(clientin + NTLM_TYPE1_FLAGS_OFFSET);
1414    sparams->utils->log(NULL, SASL_LOG_DEBUG,
1415			"client flags: %x", text->flags);
1416
1417    text->flags &= NTLM_FLAGS_MASK; /* mask off the bits we don't support */
1418
1419    /* if client can do Unicode, turn off ASCII */
1420    if (text->flags & NTLM_USE_UNICODE) text->flags &= ~NTLM_USE_ASCII;
1421
1422    if (text->sock == -1) {
1423	/* generate challenge internally */
1424
1425	/* if client asked for target, use FQDN as server target */
1426	if (text->flags & NTLM_ASK_TARGET) {
1427	    result = _plug_strdup(sparams->utils, sparams->serverFQDN,
1428			      &domain, NULL);
1429	    if (result != SASL_OK) return result;
1430
1431	    text->flags |= NTLM_TARGET_IS_SERVER;
1432	}
1433
1434	/* generate a nonce */
1435	sparams->utils->rand(sparams->utils->rpool,
1436			     (char *) text->nonce, NTLM_NONCE_LENGTH);
1437    }
1438    else {
1439	/* proxy the response/challenge */
1440	result = smb_negotiate_protocol(sparams->utils, text, &domain);
1441	if (result != SASL_OK) goto cleanup;
1442    }
1443
1444    result = create_challenge(sparams->utils,
1445			      &text->out_buf, &text->out_buf_len,
1446			      domain, text->flags, text->nonce, serveroutlen);
1447    if (result != SASL_OK) goto cleanup;
1448
1449    *serverout = text->out_buf;
1450
1451    text->state = 2;
1452
1453    result = SASL_CONTINUE;
1454
1455  cleanup:
1456    if (domain) sparams->utils->free(domain);
1457
1458    return result;
1459}
1460
1461static int ntlm_server_mech_step2(server_context_t *text,
1462				  sasl_server_params_t *sparams,
1463				  const char *clientin,
1464				  unsigned clientinlen,
1465				  const char **serverout __attribute__((unused)),
1466				  unsigned *serveroutlen __attribute__((unused)),
1467				  sasl_out_params_t *oparams)
1468{
1469    unsigned char *lm_resp = NULL, *nt_resp = NULL;
1470    char *domain = NULL, *authid = NULL;
1471    unsigned lm_resp_len, nt_resp_len, domain_len, authid_len;
1472    int result;
1473
1474    if (!clientin || clientinlen < NTLM_TYPE3_MINSIZE ||
1475	memcmp(clientin, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) ||
1476	itohl(clientin + NTLM_TYPE_OFFSET) != NTLM_TYPE_RESPONSE) {
1477	SETERROR(sparams->utils, "client didn't issue valid NTLM response");
1478	return SASL_BADPROT;
1479    }
1480
1481    result = unload_buffer(sparams->utils, (const u_char *)clientin + NTLM_TYPE3_LMRESP_OFFSET,
1482			   (u_char **) &lm_resp, &lm_resp_len, 0,
1483			   (const u_char *)clientin, clientinlen);
1484    if (result != SASL_OK) goto cleanup;
1485
1486    result = unload_buffer(sparams->utils, (const u_char *)clientin + NTLM_TYPE3_NTRESP_OFFSET,
1487			   (u_char **) &nt_resp, &nt_resp_len, 0,
1488			   (const u_char *)clientin, clientinlen);
1489    if (result != SASL_OK) goto cleanup;
1490
1491    result = unload_buffer(sparams->utils, (const u_char *)clientin + NTLM_TYPE3_DOMAIN_OFFSET,
1492			   (u_char **) &domain, &domain_len,
1493			   text->flags & NTLM_USE_UNICODE,
1494			   (const u_char *)clientin, clientinlen);
1495    if (result != SASL_OK) goto cleanup;
1496
1497    result = unload_buffer(sparams->utils, (const u_char *)clientin + NTLM_TYPE3_USER_OFFSET,
1498			   (u_char **) &authid, &authid_len,
1499			   text->flags & NTLM_USE_UNICODE,
1500			   (const u_char *)clientin, clientinlen);
1501    if (result != SASL_OK) goto cleanup;
1502
1503    /* require at least one response and an authid */
1504    if ((!lm_resp && !nt_resp) ||
1505	(lm_resp && lm_resp_len < NTLM_RESP_LENGTH) ||
1506	(nt_resp && nt_resp_len < NTLM_RESP_LENGTH) ||
1507	!authid) {
1508	SETERROR(sparams->utils, "client issued incorrect/nonexistent responses");
1509	result = SASL_BADPROT;
1510	goto cleanup;
1511    }
1512
1513    sparams->utils->log(NULL, SASL_LOG_DEBUG,
1514			"client user: %s", authid);
1515    if (domain) sparams->utils->log(NULL, SASL_LOG_DEBUG,
1516				    "client domain: %s", domain);
1517
1518    if (text->sock == -1) {
1519	/* verify the response internally */
1520
1521	sasl_secret_t *password = NULL;
1522	size_t pass_len;
1523	const char *password_request[] = { SASL_AUX_PASSWORD,
1524				       NULL };
1525	struct propval auxprop_values[2];
1526	unsigned char hash[NTLM_HASH_LENGTH];
1527	unsigned char resp[NTLM_RESP_LENGTH];
1528
1529	/* fetch user's password */
1530	result = sparams->utils->prop_request(sparams->propctx, password_request);
1531	if (result != SASL_OK) goto cleanup;
1532
1533	/* this will trigger the getting of the aux properties */
1534	result = sparams->canon_user(sparams->utils->conn, authid, authid_len,
1535				     SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
1536	if (result != SASL_OK) goto cleanup;
1537
1538	result = sparams->utils->prop_getnames(sparams->propctx,
1539					       password_request,
1540					       auxprop_values);
1541	if (result < 0 ||
1542	    (!auxprop_values[0].name || !auxprop_values[0].values)) {
1543	    /* We didn't find this username */
1544	    SETERROR(sparams->utils, "no secret in database");
1545	    result = sparams->transition ? SASL_TRANS : SASL_NOUSER;
1546	    goto cleanup;
1547	}
1548
1549	pass_len = strlen(auxprop_values[0].values[0]);
1550	if (pass_len == 0) {
1551	    SETERROR(sparams->utils, "empty secret");
1552	    result = SASL_FAIL;
1553	    goto cleanup;
1554	}
1555
1556	password = sparams->utils->malloc(sizeof(sasl_secret_t) + pass_len);
1557	if (!password) {
1558	    result = SASL_NOMEM;
1559	    goto cleanup;
1560	}
1561
1562	password->len = pass_len;
1563	strncpy((char *)password->data, auxprop_values[0].values[0], pass_len + 1);
1564
1565	/* erase the plaintext password */
1566	sparams->utils->prop_erase(sparams->propctx, password_request[0]);
1567
1568	/* calculate our own response(s) and compare with client's */
1569	result = SASL_OK;
1570	if (nt_resp && (nt_resp_len > NTLM_RESP_LENGTH)) {
1571	    /* Try NTv2 response */
1572	    sparams->utils->log(NULL, SASL_LOG_DEBUG,
1573				"calculating NTv2 response");
1574	    V2(resp, password, authid, domain, text->nonce,
1575	       lm_resp + MD5_DIGEST_LENGTH, nt_resp_len - MD5_DIGEST_LENGTH,
1576	       sparams->utils, &text->out_buf, &text->out_buf_len,
1577	       &result);
1578
1579	    /* No need to compare the blob */
1580	    if (memcmp(nt_resp, resp, MD5_DIGEST_LENGTH)) {
1581		SETERROR(sparams->utils, "incorrect NTLMv2 response");
1582		result = SASL_BADAUTH;
1583	    }
1584	}
1585	else if (nt_resp) {
1586	    /* Try NT response */
1587	    sparams->utils->log(NULL, SASL_LOG_DEBUG,
1588				"calculating NT response");
1589	    P24(resp, P21(hash, password, P16_nt, sparams->utils,
1590			  &text->out_buf, &text->out_buf_len, &result),
1591		text->nonce);
1592	    if (memcmp(nt_resp, resp, NTLM_RESP_LENGTH)) {
1593		SETERROR(sparams->utils, "incorrect NTLM response");
1594		result = SASL_BADAUTH;
1595	    }
1596	}
1597	else if (lm_resp) {
1598	    /* Try LMv2 response */
1599	    sparams->utils->log(NULL, SASL_LOG_DEBUG,
1600				"calculating LMv2 response");
1601	    V2(resp, password, authid, domain, text->nonce,
1602	       lm_resp + MD5_DIGEST_LENGTH, lm_resp_len - MD5_DIGEST_LENGTH,
1603	       sparams->utils, &text->out_buf, &text->out_buf_len,
1604	       &result);
1605
1606	    /* No need to compare the blob */
1607	    if (memcmp(lm_resp, resp, MD5_DIGEST_LENGTH)) {
1608		/* Try LM response */
1609		sparams->utils->log(NULL, SASL_LOG_DEBUG,
1610				    "calculating LM response");
1611		P24(resp, P21(hash, password, P16_lm, sparams->utils,
1612			      &text->out_buf, &text->out_buf_len, &result),
1613		    text->nonce);
1614		if (memcmp(lm_resp, resp, NTLM_RESP_LENGTH)) {
1615		    SETERROR(sparams->utils, "incorrect LMv1/v2 response");
1616		    result = SASL_BADAUTH;
1617		}
1618	    }
1619	}
1620
1621	_plug_free_secret(sparams->utils, &password);
1622
1623	if (result != SASL_OK) goto cleanup;
1624    }
1625    else {
1626	/* proxy the response */
1627	result = smb_session_setup(sparams->utils, text, authid, domain,
1628				   lm_resp, lm_resp_len, nt_resp, nt_resp_len);
1629	if (result != SASL_OK) goto cleanup;
1630
1631	result = sparams->canon_user(sparams->utils->conn, authid, authid_len,
1632				     SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
1633	if (result != SASL_OK) goto cleanup;
1634    }
1635
1636    /* set oparams */
1637    oparams->doneflag = 1;
1638    oparams->mech_ssf = 0;
1639    oparams->maxoutbuf = 0;
1640    oparams->encode_context = NULL;
1641    oparams->encode = NULL;
1642    oparams->decode_context = NULL;
1643    oparams->decode = NULL;
1644    oparams->param_version = 0;
1645
1646    result = SASL_OK;
1647
1648  cleanup:
1649    if (lm_resp) sparams->utils->free(lm_resp);
1650    if (nt_resp) sparams->utils->free(nt_resp);
1651    if (domain) sparams->utils->free(domain);
1652    if (authid) sparams->utils->free(authid);
1653
1654    return result;
1655}
1656
1657static int ntlm_server_mech_step(void *conn_context,
1658				 sasl_server_params_t *sparams,
1659				 const char *clientin,
1660				 unsigned clientinlen,
1661				 const char **serverout,
1662				 unsigned *serveroutlen,
1663				 sasl_out_params_t *oparams)
1664{
1665    server_context_t *text = (server_context_t *) conn_context;
1666
1667    *serverout = NULL;
1668    *serveroutlen = 0;
1669
1670    sparams->utils->log(NULL, SASL_LOG_DEBUG,
1671		       "NTLM server step %d\n", text->state);
1672
1673    switch (text->state) {
1674
1675    case 1:
1676	return ntlm_server_mech_step1(text, sparams, clientin, clientinlen,
1677				      serverout, serveroutlen, oparams);
1678
1679    case 2:
1680	return ntlm_server_mech_step2(text, sparams, clientin, clientinlen,
1681				      serverout, serveroutlen, oparams);
1682
1683    default:
1684	sparams->utils->log(NULL, SASL_LOG_ERR,
1685			   "Invalid NTLM server step %d\n", text->state);
1686	return SASL_FAIL;
1687    }
1688
1689    return SASL_FAIL; /* should never get here */
1690}
1691
1692static void ntlm_server_mech_dispose(void *conn_context,
1693				     const sasl_utils_t *utils)
1694{
1695    server_context_t *text = (server_context_t *) conn_context;
1696
1697    if (!text) return;
1698
1699    if (text->out_buf) utils->free(text->out_buf);
1700    if (text->sock != -1) closesocket(text->sock);
1701
1702    utils->free(text);
1703}
1704
1705static sasl_server_plug_t ntlm_server_plugins[] =
1706{
1707    {
1708	"NTLM",				/* mech_name */
1709	0,				/* max_ssf */
1710	SASL_SEC_NOPLAINTEXT
1711	| SASL_SEC_NOANONYMOUS,		/* security_flags */
1712	SASL_FEAT_WANT_CLIENT_FIRST,	/* features */
1713	NULL,				/* glob_context */
1714	&ntlm_server_mech_new,		/* mech_new */
1715	&ntlm_server_mech_step,		/* mech_step */
1716	&ntlm_server_mech_dispose,	/* mech_dispose */
1717	NULL,				/* mech_free */
1718	NULL,				/* setpass */
1719	NULL,				/* user_query */
1720	NULL,				/* idle */
1721	NULL,				/* mech_avail */
1722	NULL				/* spare */
1723    }
1724};
1725
1726int ntlm_server_plug_init(sasl_utils_t *utils,
1727			  int maxversion,
1728			  int *out_version,
1729			  sasl_server_plug_t **pluglist,
1730			  int *plugcount)
1731{
1732    if (maxversion < SASL_SERVER_PLUG_VERSION) {
1733	SETERROR(utils, "NTLM version mismatch");
1734	return SASL_BADVERS;
1735    }
1736
1737    *out_version = SASL_SERVER_PLUG_VERSION;
1738    *pluglist = ntlm_server_plugins;
1739    *plugcount = 1;
1740
1741    return SASL_OK;
1742}
1743
1744/*****************************  Client Section  *****************************/
1745
1746typedef struct client_context {
1747    int state;
1748
1749    /* per-step mem management */
1750    char *out_buf;
1751    unsigned out_buf_len;
1752
1753} client_context_t;
1754
1755/*
1756 * Create a client request (type 1) consisting of:
1757 *
1758 * signature (8 bytes)
1759 * message type (uint32)
1760 * flags (uint32)
1761 * domain (buffer)
1762 * workstation (buffer)
1763 * data
1764 */
1765static int create_request(const sasl_utils_t *utils,
1766			  char **buf, unsigned *buflen,
1767			  const char *domain, const char *wkstn,
1768			  unsigned *outlen)
1769{
1770    uint32 flags = ( NTLM_USE_UNICODE | NTLM_USE_ASCII |
1771		     NTLM_ASK_TARGET | NTLM_AUTH_NTLM );
1772    uint32 offset = NTLM_TYPE1_DATA_OFFSET;
1773    u_char *base;
1774
1775    *outlen = offset + xstrlen(domain) + xstrlen(wkstn);
1776    if (_plug_buf_alloc(utils, buf, buflen, *outlen) != SASL_OK) {
1777	SETERROR(utils, "cannot allocate NTLM request");
1778	return SASL_NOMEM;
1779    }
1780
1781    base = (u_char *)*buf;
1782    memset(base, 0, *outlen);
1783    memcpy(base + NTLM_SIG_OFFSET, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
1784    htoil(base + NTLM_TYPE_OFFSET, NTLM_TYPE_REQUEST);
1785    htoil(base + NTLM_TYPE1_FLAGS_OFFSET, flags);
1786    load_buffer(base + NTLM_TYPE1_DOMAIN_OFFSET,
1787		domain, (uint16) xstrlen(domain), 0, base, &offset);
1788    load_buffer(base + NTLM_TYPE1_WORKSTN_OFFSET,
1789		wkstn, (uint16) xstrlen(wkstn), 0, base, &offset);
1790
1791    return SASL_OK;
1792}
1793
1794/*
1795 * Create a client response (type 3) consisting of:
1796 *
1797 * signature (8 bytes)
1798 * message type (uint32)
1799 * LM/LMv2 response (buffer)
1800 * NTLM/NTLMv2 response (buffer)
1801 * domain (buffer)
1802 * user name (buffer)
1803 * workstation (buffer)
1804 * session key (buffer)
1805 * flags (uint32)
1806 * data
1807 */
1808static int create_response(const sasl_utils_t *utils,
1809			   char **buf, unsigned *buflen,
1810			   const u_char *lm_resp, const u_char *nt_resp,
1811			   const char *domain, const char *user,
1812			   const char *wkstn, const u_char *key,
1813			   uint32 flags, unsigned *outlen)
1814{
1815    uint32 offset = NTLM_TYPE3_DATA_OFFSET;
1816    u_char *base;
1817
1818    if (!lm_resp && !nt_resp) {
1819	SETERROR(utils, "need at least one NT/LM response");
1820	return SASL_FAIL;
1821    }
1822
1823    *outlen = offset + (flags & NTLM_USE_UNICODE ? 2 : 1) *
1824	(xstrlen(domain) + xstrlen(user) + xstrlen(wkstn));
1825    if (lm_resp) *outlen += NTLM_RESP_LENGTH;
1826    if (nt_resp) *outlen += NTLM_RESP_LENGTH;
1827    if (key) *outlen += NTLM_SESSKEY_LENGTH;
1828
1829    if (_plug_buf_alloc(utils, buf, buflen, *outlen) != SASL_OK) {
1830	SETERROR(utils, "cannot allocate NTLM response");
1831	return SASL_NOMEM;
1832    }
1833
1834    base = (u_char *)*buf;
1835    memset(base, 0, *outlen);
1836    memcpy(base + NTLM_SIG_OFFSET, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
1837    htoil(base + NTLM_TYPE_OFFSET, NTLM_TYPE_RESPONSE);
1838    load_buffer(base + NTLM_TYPE3_LMRESP_OFFSET,
1839		lm_resp, lm_resp ? NTLM_RESP_LENGTH : 0, 0, base, &offset);
1840    load_buffer(base + NTLM_TYPE3_NTRESP_OFFSET,
1841		nt_resp, nt_resp ? NTLM_RESP_LENGTH : 0, 0, base, &offset);
1842    load_buffer(base + NTLM_TYPE3_DOMAIN_OFFSET,
1843		ucase(domain, 0), (uint16) xstrlen(domain), flags & NTLM_USE_UNICODE,
1844		base, &offset);
1845    load_buffer(base + NTLM_TYPE3_USER_OFFSET,
1846		user, (uint16) xstrlen(user), flags & NTLM_USE_UNICODE, base, &offset);
1847    load_buffer(base + NTLM_TYPE3_WORKSTN_OFFSET,
1848		ucase(wkstn, 0), (uint16) xstrlen(wkstn), flags & NTLM_USE_UNICODE,
1849		base, &offset);
1850    load_buffer(base + NTLM_TYPE3_SESSIONKEY_OFFSET,
1851		key, key ? NTLM_SESSKEY_LENGTH : 0, 0, base, &offset);
1852    htoil(base + NTLM_TYPE3_FLAGS_OFFSET, flags);
1853
1854    return SASL_OK;
1855}
1856
1857static int ntlm_client_mech_new(void *glob_context __attribute__((unused)),
1858			       sasl_client_params_t *params,
1859			       void **conn_context)
1860{
1861    client_context_t *text;
1862
1863    /* holds state are in */
1864    text = params->utils->malloc(sizeof(client_context_t));
1865    if (text == NULL) {
1866	MEMERROR( params->utils );
1867	return SASL_NOMEM;
1868    }
1869
1870    memset(text, 0, sizeof(client_context_t));
1871
1872    text->state = 1;
1873
1874    *conn_context = text;
1875
1876    return SASL_OK;
1877}
1878
1879static int ntlm_client_mech_step1(client_context_t *text,
1880				  sasl_client_params_t *params,
1881				  const char *serverin __attribute__((unused)),
1882				  unsigned serverinlen __attribute__((unused)),
1883				  sasl_interact_t **prompt_need __attribute__((unused)),
1884				  const char **clientout,
1885				  unsigned *clientoutlen,
1886				  sasl_out_params_t *oparams __attribute__((unused)))
1887{
1888    int result;
1889
1890    /* check if sec layer strong enough */
1891    if (params->props.min_ssf > params->external_ssf) {
1892	SETERROR(params->utils, "SSF requested of NTLM plugin");
1893	return SASL_TOOWEAK;
1894    }
1895
1896    /* we don't care about domain or wkstn */
1897    result = create_request(params->utils, &text->out_buf, &text->out_buf_len,
1898			    NULL, NULL, clientoutlen);
1899    if (result != SASL_OK) return result;
1900
1901    *clientout = text->out_buf;
1902
1903    text->state = 2;
1904
1905    return SASL_CONTINUE;
1906}
1907
1908static int ntlm_client_mech_step2(client_context_t *text,
1909				  sasl_client_params_t *params,
1910				  const char *serverin,
1911				  unsigned serverinlen,
1912				  sasl_interact_t **prompt_need,
1913				  const char **clientout,
1914				  unsigned *clientoutlen,
1915				  sasl_out_params_t *oparams)
1916{
1917    const char *authid = NULL;
1918    sasl_secret_t *password = NULL;
1919    unsigned int free_password; /* set if we need to free password */
1920    char *domain = NULL;
1921    int auth_result = SASL_OK;
1922    int pass_result = SASL_OK;
1923    uint32 flags = 0;
1924    unsigned char hash[NTLM_HASH_LENGTH];
1925    unsigned char resp[NTLM_RESP_LENGTH], *lm_resp = NULL, *nt_resp = NULL;
1926    int result;
1927    const char *sendv2;
1928
1929    if (!serverin || serverinlen < NTLM_TYPE2_MINSIZE ||
1930	memcmp(serverin, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) ||
1931	itohl(serverin + NTLM_TYPE_OFFSET) != NTLM_TYPE_CHALLENGE) {
1932	SETERROR(params->utils, "server didn't issue valid NTLM challenge");
1933	return SASL_BADPROT;
1934    }
1935
1936    /* try to get the authid */
1937    if (oparams->authid == NULL) {
1938	auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1939
1940	if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
1941	    return auth_result;
1942    }
1943
1944    /* try to get the password */
1945    if (password == NULL) {
1946	pass_result = _plug_get_password(params->utils, &password,
1947					 &free_password, prompt_need);
1948
1949	if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
1950	    return pass_result;
1951    }
1952
1953    /* free prompts we got */
1954    if (prompt_need && *prompt_need) {
1955	params->utils->free(*prompt_need);
1956	*prompt_need = NULL;
1957    }
1958
1959    /* if there are prompts not filled in */
1960    if ((auth_result == SASL_INTERACT) || (pass_result == SASL_INTERACT)) {
1961	/* make the prompt list */
1962	result =
1963	    _plug_make_prompts(params->utils, prompt_need,
1964			       NULL, NULL,
1965			       auth_result == SASL_INTERACT ?
1966			       "Please enter your authentication name" : NULL,
1967			       NULL,
1968			       pass_result == SASL_INTERACT ?
1969			       "Please enter your password" : NULL, NULL,
1970			       NULL, NULL, NULL,
1971			       NULL, NULL, NULL);
1972	if (result != SASL_OK) goto cleanup;
1973
1974	return SASL_INTERACT;
1975    }
1976
1977    result = params->canon_user(params->utils->conn, authid, 0,
1978				SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
1979    if (result != SASL_OK) goto cleanup;
1980
1981    flags = itohl(serverin + NTLM_TYPE2_FLAGS_OFFSET);
1982    params->utils->log(NULL, SASL_LOG_DEBUG,
1983		       "server flags: %x", flags);
1984
1985    flags &= NTLM_FLAGS_MASK; /* mask off the bits we don't support */
1986
1987    result = unload_buffer(params->utils, (const u_char *)serverin + NTLM_TYPE2_TARGET_OFFSET,
1988			   (u_char **) &domain, NULL,
1989			   flags & NTLM_USE_UNICODE,
1990			   (u_char *) serverin, serverinlen);
1991    if (result != SASL_OK) goto cleanup;
1992    params->utils->log(NULL, SASL_LOG_DEBUG,
1993		       "server domain: %s", domain);
1994
1995    /* should we send a NTLMv2 response? */
1996    params->utils->getopt(params->utils->getopt_context,
1997			  "NTLM", "ntlm_v2", &sendv2, NULL);
1998    if (sendv2 &&
1999	(*sendv2 == '1' || *sendv2 == 'y' ||
2000	 (*sendv2 == 'o' && *sendv2 == 'n') || *sendv2 == 't')) {
2001
2002	/* put the cnonce in place after the LMv2 HMAC */
2003	char *cnonce = (char *)(resp + MD5_DIGEST_LENGTH);
2004
2005	params->utils->log(NULL, SASL_LOG_DEBUG,
2006			   "calculating LMv2 response");
2007
2008	params->utils->rand(params->utils->rpool, cnonce, NTLM_NONCE_LENGTH);
2009
2010	V2(resp, password, oparams->authid, domain,
2011	   serverin + NTLM_TYPE2_CHALLENGE_OFFSET, cnonce, NTLM_NONCE_LENGTH,
2012	   params->utils, &text->out_buf, &text->out_buf_len, &result);
2013
2014	lm_resp = resp;
2015    }
2016    else if (flags & NTLM_AUTH_NTLM) {
2017	params->utils->log(NULL, SASL_LOG_DEBUG,
2018			   "calculating NT response");
2019	P24(resp, P21(hash, password, P16_nt, params->utils,
2020		      &text->out_buf, &text->out_buf_len, &result),
2021	    (unsigned char *) serverin + NTLM_TYPE2_CHALLENGE_OFFSET);
2022	nt_resp = resp;
2023    }
2024    else {
2025	params->utils->log(NULL, SASL_LOG_DEBUG,
2026			   "calculating LM response");
2027	P24(resp, P21(hash, password, P16_lm, params->utils,
2028		      &text->out_buf, &text->out_buf_len, &result),
2029	    (unsigned char *) serverin + NTLM_TYPE2_CHALLENGE_OFFSET);
2030	lm_resp = resp;
2031    }
2032    if (result != SASL_OK) goto cleanup;
2033
2034    /* we don't care about workstn or session key */
2035    result = create_response(params->utils, &text->out_buf, &text->out_buf_len,
2036			     lm_resp, nt_resp, domain, oparams->authid,
2037			     NULL, NULL, flags, clientoutlen);
2038    if (result != SASL_OK) goto cleanup;
2039
2040    *clientout = text->out_buf;
2041
2042    /* set oparams */
2043    oparams->doneflag = 1;
2044    oparams->mech_ssf = 0;
2045    oparams->maxoutbuf = 0;
2046    oparams->encode_context = NULL;
2047    oparams->encode = NULL;
2048    oparams->decode_context = NULL;
2049    oparams->decode = NULL;
2050    oparams->param_version = 0;
2051
2052    result = SASL_OK;
2053
2054  cleanup:
2055    if (domain) params->utils->free(domain);
2056    if (free_password) _plug_free_secret(params->utils, &password);
2057
2058    return result;
2059}
2060
2061static int ntlm_client_mech_step(void *conn_context,
2062				sasl_client_params_t *params,
2063				const char *serverin,
2064				unsigned serverinlen,
2065				sasl_interact_t **prompt_need,
2066				const char **clientout,
2067				unsigned *clientoutlen,
2068				sasl_out_params_t *oparams)
2069{
2070    client_context_t *text = (client_context_t *) conn_context;
2071
2072    *clientout = NULL;
2073    *clientoutlen = 0;
2074
2075    params->utils->log(NULL, SASL_LOG_DEBUG,
2076		       "NTLM client step %d\n", text->state);
2077
2078    switch (text->state) {
2079
2080    case 1:
2081	return ntlm_client_mech_step1(text, params, serverin, serverinlen,
2082				      prompt_need, clientout, clientoutlen,
2083				      oparams);
2084
2085    case 2:
2086	return ntlm_client_mech_step2(text, params, serverin, serverinlen,
2087				      prompt_need, clientout, clientoutlen,
2088				      oparams);
2089
2090    default:
2091	params->utils->log(NULL, SASL_LOG_ERR,
2092			   "Invalid NTLM client step %d\n", text->state);
2093	return SASL_FAIL;
2094    }
2095
2096    return SASL_FAIL; /* should never get here */
2097}
2098
2099static void ntlm_client_mech_dispose(void *conn_context,
2100				    const sasl_utils_t *utils)
2101{
2102    client_context_t *text = (client_context_t *) conn_context;
2103
2104    if (!text) return;
2105
2106    if (text->out_buf) utils->free(text->out_buf);
2107
2108    utils->free(text);
2109}
2110
2111static sasl_client_plug_t ntlm_client_plugins[] =
2112{
2113    {
2114	"NTLM",				/* mech_name */
2115	0,				/* max_ssf */
2116	SASL_SEC_NOPLAINTEXT
2117	| SASL_SEC_NOANONYMOUS,		/* security_flags */
2118	SASL_FEAT_WANT_CLIENT_FIRST,	/* features */
2119	NULL,				/* required_prompts */
2120	NULL,				/* glob_context */
2121	&ntlm_client_mech_new,		/* mech_new */
2122	&ntlm_client_mech_step,		/* mech_step */
2123	&ntlm_client_mech_dispose,	/* mech_dispose */
2124	NULL,				/* mech_free */
2125	NULL,				/* idle */
2126	NULL,				/* spare */
2127	NULL				/* spare */
2128    }
2129};
2130
2131int ntlm_client_plug_init(sasl_utils_t *utils,
2132			 int maxversion,
2133			 int *out_version,
2134			 sasl_client_plug_t **pluglist,
2135			 int *plugcount)
2136{
2137    if (maxversion < SASL_CLIENT_PLUG_VERSION) {
2138	SETERROR(utils, "NTLM version mismatch");
2139	return SASL_BADVERS;
2140    }
2141
2142    *out_version = SASL_CLIENT_PLUG_VERSION;
2143    *pluglist = ntlm_client_plugins;
2144    *plugcount = 1;
2145
2146    return SASL_OK;
2147}
2148