GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / plugins / ntlm.c
1 /* NTLM SASL plugin
2  * Ken Murchison
3  * $Id: ntlm.c,v 1.30 2005/07/07 16:10:14 mel 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.30 2005/07/07 16:10:14 mel Exp $";
104
105 #ifdef WIN32
106 static ssize_t writev (SOCKET fd, const struct iovec *iov, size_t iovcnt);
107
108 ssize_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
137 typedef unsigned int uint16;
138 #elif USHRT_MAX == UINT16_MAX
139 typedef 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
149 typedef unsigned int uint32;
150 #elif ULONG_MAX == UINT32_MAX
151 typedef unsigned long uint32;
152 #elif USHRT_MAX == UINT32_MAX
153 typedef unsigned short uint32;
154 #else
155 #error dont know what to use for uint32
156 #endif
157
158 #define NTLM_SIGNATURE          "NTLMSSP"
159
160 enum {
161     NTLM_TYPE_REQUEST           = 1,
162     NTLM_TYPE_CHALLENGE         = 2,
163     NTLM_TYPE_RESPONSE          = 3
164 };
165
166 enum {
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
177 enum {
178     NTLM_NONCE_LENGTH           = 8,
179     NTLM_HASH_LENGTH            = 21,
180     NTLM_RESP_LENGTH            = 24,
181     NTLM_SESSKEY_LENGTH         = 16,
182 };
183
184 enum {
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 */
239 static 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) */
255 static 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 */
264 static 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 */
273 static 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 */
293 static 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  */
335 static 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
367 static 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, 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
384 static 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
400 static 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
412 static 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
420 static 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
468 typedef 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
487 typedef 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
501 typedef 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
516 typedef 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
530 typedef struct {
531     unsigned char andx_command;
532     unsigned char andx_reserved;
533     uint16 andx_offset;
534     uint16 action;
535 } SMB_SessionSetup_Resp;
536
537 enum {
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
575 static 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
591 static 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
608 static 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
625 static 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
643 static 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
660 static 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  */
675 static 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  */
732 static 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
759 static 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(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
787 static 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
943 static 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     int 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
1126 static 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     int i, n;
1141     int rc;
1142 #ifdef WIN32
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  */
1326 static 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
1359 static 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
1395 static 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
1461 static 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, clientin + NTLM_TYPE3_LMRESP_OFFSET,
1482                            (u_char **) &lm_resp, &lm_resp_len, 0,
1483                            clientin, clientinlen);
1484     if (result != SASL_OK) goto cleanup;
1485
1486     result = unload_buffer(sparams->utils, clientin + NTLM_TYPE3_NTRESP_OFFSET,
1487                            (u_char **) &nt_resp, &nt_resp_len, 0,
1488                            clientin, clientinlen);
1489     if (result != SASL_OK) goto cleanup;
1490
1491     result = unload_buffer(sparams->utils, clientin + NTLM_TYPE3_DOMAIN_OFFSET,
1492                            (u_char **) &domain, &domain_len,
1493                            text->flags & NTLM_USE_UNICODE,
1494                            clientin, clientinlen);
1495     if (result != SASL_OK) goto cleanup;
1496
1497     result = unload_buffer(sparams->utils, clientin + NTLM_TYPE3_USER_OFFSET,
1498                            (u_char **) &authid, &authid_len,
1499                            text->flags & NTLM_USE_UNICODE,
1500                            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 = (unsigned) pass_len;
1563         strncpy(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
1657 static 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
1692 static 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
1705 static 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
1726 int 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
1746 typedef 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  */
1765 static 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 = *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  */
1808 static 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 = *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
1857 static 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
1879 static 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
1908 static 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, 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 = 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
2061 static 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
2099 static 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
2111 static 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
2131 int 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 }