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