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