3 * $Id: ntlm.c,v 1.30 2005/07/07 16:10:14 mel Exp $
6 * http://www.innovation.ch/java/ntlm.html
7 * http://www.opengroup.org/comsource/techref2/NCH1222X.HTM
8 * http://www.ubiqx.org/cifs/rfc-draft/draft-leach-cifs-v1-spec-02.html
11 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
25 * 3. The name "Carnegie Mellon University" must not be used to
26 * endorse or promote products derived from this software without
27 * prior written permission. For permission or any other legal
28 * details, please contact
29 * Office of Technology Transfer
30 * Carnegie Mellon University
32 * Pittsburgh, PA 15213-3890
33 * (412) 268-4387, fax: (412) 268-7395
34 * tech-transfer@andrew.cmu.edu
36 * 4. Redistributions of any form whatsoever must retain the following
38 * "This product includes software developed by Computing Services
39 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
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.
58 # include <process.h> /* for getpid */
62 # include <sys/types.h>
63 # include <sys/socket.h>
64 # include <sys/utsname.h>
69 # define SYS_NMLN sizeof(dummy.sysname)
72 # define closesocket(sock) close(sock)
76 #include <openssl/md4.h>
77 #include <openssl/md5.h>
78 #include <openssl/hmac.h>
79 #include <openssl/des.h>
80 #include <openssl/opensslv.h>
81 #if (OPENSSL_VERSION_NUMBER >= 0x0090700f) && \
82 !defined(OPENSSL_ENABLE_OLD_DES_SUPPORT)
83 # define des_cblock DES_cblock
84 # define des_key_schedule DES_key_schedule
85 # define des_set_odd_parity(k) \
86 DES_set_odd_parity((k))
87 # define des_set_key(k,ks) \
88 DES_set_key((k),&(ks))
89 # define des_key_sched(k,ks) \
90 DES_key_sched((k),&(ks))
91 # define des_ecb_encrypt(i,o,k,e) \
92 DES_ecb_encrypt((i),(o),&(k),(e))
93 #endif /* OpenSSL 0.9.7+ w/o old DES support */
96 #define MD5_H /* suppress internal MD5 */
99 #include "plugin_common.h"
101 /***************************** Common Section *****************************/
103 static const char plugin_id[] = "$Id: ntlm.c,v 1.30 2005/07/07 16:10:14 mel Exp $";
106 static ssize_t writev (SOCKET fd, const struct iovec *iov, size_t iovcnt);
108 ssize_t writev (SOCKET fd, const struct iovec *iov, size_t iovcnt)
110 ssize_t nwritten; /* amount written */
116 for (i = 0; i < iovcnt; i++) {
117 if ((nwritten = send (fd, iov[i].iov_base, iov[i].iov_len, 0)) == SOCKET_ERROR) {
118 /* Unless socket is nonblocking, we should always write everything */
124 if (nwritten < iov[i].iov_len) {
133 #define UINT16_MAX 65535U
136 #if UINT_MAX == UINT16_MAX
137 typedef unsigned int uint16;
138 #elif USHRT_MAX == UINT16_MAX
139 typedef unsigned short uint16;
141 #error dont know what to use for uint16
145 #define UINT32_MAX 4294967295U
148 #if UINT_MAX == UINT32_MAX
149 typedef unsigned int uint32;
150 #elif ULONG_MAX == UINT32_MAX
151 typedef unsigned long uint32;
152 #elif USHRT_MAX == UINT32_MAX
153 typedef unsigned short uint32;
155 #error dont know what to use for uint32
158 #define NTLM_SIGNATURE "NTLMSSP"
161 NTLM_TYPE_REQUEST = 1,
162 NTLM_TYPE_CHALLENGE = 2,
163 NTLM_TYPE_RESPONSE = 3
167 NTLM_USE_UNICODE = 0x00001,
168 NTLM_USE_ASCII = 0x00002,
169 NTLM_ASK_TARGET = 0x00004,
170 NTLM_AUTH_NTLM = 0x00200,
171 NTLM_ALWAYS_SIGN = 0x08000,
172 NTLM_TARGET_IS_DOMAIN = 0x10000,
173 NTLM_TARGET_IS_SERVER = 0x20000,
174 NTLM_FLAGS_MASK = 0x0ffff
178 NTLM_NONCE_LENGTH = 8,
179 NTLM_HASH_LENGTH = 21,
180 NTLM_RESP_LENGTH = 24,
181 NTLM_SESSKEY_LENGTH = 16,
186 NTLM_TYPE_OFFSET = 8,
188 NTLM_TYPE1_FLAGS_OFFSET = 12,
189 NTLM_TYPE1_DOMAIN_OFFSET = 16,
190 NTLM_TYPE1_WORKSTN_OFFSET = 24,
191 NTLM_TYPE1_DATA_OFFSET = 32,
192 NTLM_TYPE1_MINSIZE = 16,
194 NTLM_TYPE2_TARGET_OFFSET = 12,
195 NTLM_TYPE2_FLAGS_OFFSET = 20,
196 NTLM_TYPE2_CHALLENGE_OFFSET = 24,
197 NTLM_TYPE2_CONTEXT_OFFSET = 32,
198 NTLM_TYPE2_TARGETINFO_OFFSET= 40,
199 NTLM_TYPE2_DATA_OFFSET = 48,
200 NTLM_TYPE2_MINSIZE = 32,
202 NTLM_TYPE3_LMRESP_OFFSET = 12,
203 NTLM_TYPE3_NTRESP_OFFSET = 20,
204 NTLM_TYPE3_DOMAIN_OFFSET = 28,
205 NTLM_TYPE3_USER_OFFSET = 36,
206 NTLM_TYPE3_WORKSTN_OFFSET = 44,
207 NTLM_TYPE3_SESSIONKEY_OFFSET= 52,
208 NTLM_TYPE3_FLAGS_OFFSET = 60,
209 NTLM_TYPE3_DATA_OFFSET = 64,
210 NTLM_TYPE3_MINSIZE = 52,
212 NTLM_BUFFER_LEN_OFFSET = 0,
213 NTLM_BUFFER_MAXLEN_OFFSET = 2,
214 NTLM_BUFFER_OFFSET_OFFSET = 4,
218 /* return the length of a string (even if it is NULL) */
219 #define xstrlen(s) (s ? strlen(s) : 0)
221 /* machine-independent routines to convert to/from Intel byte-order */
222 #define htois(is, hs) \
223 (is)[0] = hs & 0xff; \
227 ((is)[0] | ((is)[1] << 8))
229 #define htoil(il, hl) \
230 (il)[0] = hl & 0xff; \
231 (il)[1] = (hl >> 8) & 0xff; \
232 (il)[2] = (hl >> 16) & 0xff; \
236 ((il)[0] | ((il)[1] << 8) | ((il)[2] << 16) | ((il)[3] << 24))
238 /* convert string to all upper case */
239 static const char *ucase(const char *str, size_t len)
241 char *cp = (char *) str;
243 if (!len) len = xstrlen(str);
245 while (len && cp && *cp) {
246 *cp = toupper((int) *cp);
254 /* copy src to dst as unicode (in Intel byte-order) */
255 static void to_unicode(u_char *dst, const char *src, int len)
263 /* copy unicode src (in Intel byte-order) to dst */
264 static void from_unicode(char *dst, u_char *src, int len)
267 *dst++ = *src & 0x7f;
272 /* load a string into an NTLM buffer */
273 static void load_buffer(u_char *buf, const u_char *str, uint16 len,
274 int unicode, u_char *base, uint32 *offset)
278 to_unicode(base + *offset, str, len);
282 memcpy(base + *offset, str, len);
286 htois(buf + NTLM_BUFFER_LEN_OFFSET, len);
287 htois(buf + NTLM_BUFFER_MAXLEN_OFFSET, len);
288 htoil(buf + NTLM_BUFFER_OFFSET_OFFSET, *offset);
292 /* unload a string from an NTLM buffer */
293 static int unload_buffer(const sasl_utils_t *utils, const u_char *buf,
294 u_char **str, unsigned *outlen,
295 int unicode, const u_char *base, unsigned msglen)
297 uint16 len = itohs(buf + NTLM_BUFFER_LEN_OFFSET);
302 *str = utils->malloc(len + 1); /* add 1 for NUL */
308 offset = itohl(buf + NTLM_BUFFER_OFFSET_OFFSET);
311 if (offset > msglen || len > (msglen - offset)) return SASL_BADPROT;
315 from_unicode((char *) *str, (u_char *) base + offset, len);
318 memcpy(*str, base + offset, len);
320 (*str)[len] = '\0'; /* add NUL */
326 if (outlen) *outlen = len;
332 * NTLM encryption/authentication routines per section 2.10 of
333 * draft-leach-cifs-v1-spec-02
335 static void E(unsigned char *out, unsigned char *K, unsigned Klen,
336 unsigned char *D, unsigned Dlen)
346 for (k = 0; k < Klen; k += KEY_SIZE, K += KEY_SIZE) {
347 /* convert 56-bit key to 64-bit */
349 K64[1] = ((K[0] << 7) & 0xFF) | (K[1] >> 1);
350 K64[2] = ((K[1] << 6) & 0xFF) | (K[2] >> 2);
351 K64[3] = ((K[2] << 5) & 0xFF) | (K[3] >> 3);
352 K64[4] = ((K[3] << 4) & 0xFF) | (K[4] >> 4);
353 K64[5] = ((K[4] << 3) & 0xFF) | (K[5] >> 5);
354 K64[6] = ((K[5] << 2) & 0xFF) | (K[6] >> 6);
355 K64[7] = (K[6] << 1) & 0xFF;
357 des_set_odd_parity(&K64); /* XXX is this necessary? */
358 des_set_key(&K64, ks);
360 for (d = 0, Dp = D; d < Dlen;
361 d += BLOCK_SIZE, Dp += BLOCK_SIZE, out += BLOCK_SIZE) {
362 des_ecb_encrypt((void *) Dp, (void *) out, ks, DES_ENCRYPT);
367 static unsigned char *P16_lm(unsigned char *P16, sasl_secret_t *passwd,
368 const sasl_utils_t *utils __attribute__((unused)),
369 char **buf __attribute__((unused)),
370 unsigned *buflen __attribute__((unused)),
374 unsigned char S8[] = { 0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 };
376 strncpy(P14, passwd->data, sizeof(P14));
377 ucase(P14, sizeof(P14));
379 E(P16, P14, sizeof(P14), S8, sizeof(S8));
384 static unsigned char *P16_nt(unsigned char *P16, sasl_secret_t *passwd,
385 const sasl_utils_t *utils,
386 char **buf, unsigned *buflen, int *result)
388 if (_plug_buf_alloc(utils, buf, buflen, 2 * passwd->len) != SASL_OK) {
389 SETERROR(utils, "cannot allocate P16_nt unicode buffer");
390 *result = SASL_NOMEM;
393 to_unicode(*buf, passwd->data, passwd->len);
394 MD4(*buf, 2 * passwd->len, P16);
400 static unsigned char *P21(unsigned char *P21, sasl_secret_t *passwd,
401 unsigned char * (*P16)(unsigned char *,
403 const sasl_utils_t *,
404 char **, unsigned *, int *),
405 const sasl_utils_t *utils,
406 char **buf, unsigned *buflen, int *result)
408 memset(P16(P21, passwd, utils, buf, buflen, result) + 16, 0, 5);
412 static unsigned char *P24(unsigned char *P24, unsigned char *P21,
416 E(P24, P21, NTLM_HASH_LENGTH, C8, NTLM_NONCE_LENGTH);
420 static unsigned char *V2(unsigned char *V2, sasl_secret_t *passwd,
421 const char *authid, const char *target,
422 const unsigned char *challenge,
423 const unsigned char *blob, unsigned bloblen,
424 const sasl_utils_t *utils,
425 char **buf, unsigned *buflen, int *result)
428 unsigned char hash[EVP_MAX_MD_SIZE];
432 /* Allocate enough space for the unicode target */
433 len = (int) (strlen(authid) + xstrlen(target));
434 if (_plug_buf_alloc(utils, buf, buflen, 2 * len + 1) != SASL_OK) {
435 SETERROR(utils, "cannot allocate NTLMv2 hash");
436 *result = SASL_NOMEM;
439 /* NTLMv2hash = HMAC-MD5(NTLMhash, unicode(ucase(authid + domain))) */
440 P16_nt(hash, passwd, utils, buf, buflen, result);
442 /* Use the tail end of the buffer for ucase() conversion */
444 strcpy(upper, authid);
445 if (target) strcat(upper, target);
447 to_unicode(*buf, upper, len);
449 HMAC(EVP_md5(), hash, MD4_DIGEST_LENGTH, *buf, 2 * len, hash, &len);
451 /* V2 = HMAC-MD5(NTLMv2hash, challenge + blob) + blob */
452 HMAC_Init(&ctx, hash, len, EVP_md5());
453 HMAC_Update(&ctx, challenge, NTLM_NONCE_LENGTH);
454 HMAC_Update(&ctx, blob, bloblen);
455 HMAC_Final(&ctx, V2, &len);
458 /* the blob is concatenated outside of this function */
466 /***************************** Server Section *****************************/
468 typedef struct server_context {
472 unsigned char nonce[NTLM_NONCE_LENGTH];
474 /* per-step mem management */
476 unsigned out_buf_len;
478 /* socket to remote authentication host */
483 #define N(a) (sizeof (a) / sizeof (a[0]))
485 #define SMB_HDR_PROTOCOL "\xffSMB"
488 unsigned char protocol[4];
489 unsigned char command;
494 unsigned char extra[10];
502 uint16 dialect_index;
503 unsigned char security_mode;
504 uint16 max_mpx_count;
505 uint16 max_number_vcs;
506 uint32 max_buffer_size;
510 uint32 system_time_low;
511 uint32 system_time_high;
512 uint16 server_time_zone;
513 unsigned char encryption_key_length;
517 unsigned char andx_command;
518 unsigned char andx_reserved;
520 uint16 max_buffer_size;
521 uint16 max_mpx_count;
524 uint16 case_insensitive_passwd_len;
525 uint16 case_sensitive_passwd_len;
531 unsigned char andx_command;
532 unsigned char andx_reserved;
535 } SMB_SessionSetup_Resp;
538 NBT_SESSION_REQUEST = 0x81,
539 NBT_POSITIVE_SESSION_RESP = 0x82,
540 NBT_NEGATIVE_SESSION_RESP = 0x83,
541 NBT_ERR_NO_LISTEN_CALLED = 0x80,
542 NBT_ERR_NO_LISTEN_CALLING = 0x81,
543 NBT_ERR_CALLED_NOT_PRESENT = 0x82,
544 NBT_ERR_INSUFFICIENT_RESRC = 0x83,
545 NBT_ERR_UNSPECIFIED = 0x8F,
549 SMB_COM_NEGOTIATE_PROTOCOL = 0x72,
550 SMB_COM_SESSION_SETUP_ANDX = 0x73,
553 SMB_FLAGS_SERVER_TO_REDIR = 0x80,
555 SMB_FLAGS2_ERR_STATUS = 0x4000,
556 SMB_FLAGS2_UNICODE = 0x8000,
558 SMB_NEGPROT_RESP_SIZE = 34,
560 SMB_SECURITY_MODE_USER = 0x1,
561 SMB_SECURITY_MODE_ENCRYPT = 0x2,
562 SMB_SECURITY_MODE_SIGN = 0x4,
563 SMB_SECURITY_MODE_SIGN_REQ = 0x8,
565 SMB_CAP_UNICODE = 0x0004,
566 SMB_CAP_STATUS32 = 0x0040,
567 SMB_CAP_EXTENDED_SECURITY = 0x80000000,
569 SMB_SESSION_SETUP_SIZE = 26,
570 SMB_SESSION_SETUP_RESP_SIZE = 6,
572 SMB_REQUEST_MODE_GUEST = 0x1
575 static const char *SMB_DIALECTS[] = {
577 "\x02PC NETWORK PROGRAM 1.0",
579 "\x02MICROSOFT NETWORKS 1.03",
580 "\x02MICROSOFT NETWORKS 3.0",
582 "\x02Windows for Workgroups 3.1a",
591 static void load_smb_header(unsigned char buf[], SMB_Header *hdr)
593 unsigned char *p = buf;
595 memcpy(p, SMB_HDR_PROTOCOL, 4); p += 4;
597 htoil(p, hdr->status); p += 4;
599 htois(p, hdr->flags2); p += 2;
600 htois(p, hdr->PidHigh); p += 2;
601 memcpy(p, hdr->extra, 10); p += 10;
602 htois(p, hdr->tid); p += 2;
603 htois(p, hdr->pid); p += 2;
604 htois(p, hdr->uid); p += 2;
608 static void unload_smb_header(unsigned char buf[], SMB_Header *hdr)
610 unsigned char *p = buf;
612 memcpy(hdr->protocol, p, 4); p += 4;
614 hdr->status = itohl(p); p += 4;
616 hdr->flags2 = itohs(p); p += 2;
617 hdr->PidHigh = itohs(p); p += 2;
618 memcpy(hdr->extra, p, 10); p += 10;
619 hdr->tid = itohs(p); p += 2;
620 hdr->pid = itohs(p); p += 2;
621 hdr->uid = itohs(p); p += 2;
625 static void unload_negprot_resp(unsigned char buf[], SMB_NegProt_Resp *resp)
627 unsigned char *p = buf;
629 resp->dialect_index = itohs(p); p += 2;
630 resp->security_mode = *p++;
631 resp->max_mpx_count = itohs(p); p += 2;
632 resp->max_number_vcs = itohs(p); p += 2;
633 resp->max_buffer_size = itohl(p); p += 4;
634 resp->max_raw_size = itohl(p); p += 4;
635 resp->session_key = itohl(p); p += 4;
636 resp->capabilities = itohl(p); p += 4;
637 resp->system_time_low = itohl(p); p += 4;
638 resp->system_time_high = itohl(p); p += 4;
639 resp->server_time_zone = itohs(p); p += 2;
640 resp->encryption_key_length = *p;
643 static void load_session_setup(unsigned char buf[], SMB_SessionSetup *setup)
645 unsigned char *p = buf;
647 *p++ = setup->andx_command;
648 *p++ = setup->andx_reserved;
649 htois(p, setup->andx_offset); p += 2;
650 htois(p, setup->max_buffer_size); p += 2;
651 htois(p, setup->max_mpx_count); p += 2;
652 htois(p, setup->vc_number); p += 2;
653 htoil(p, setup->session_key); p += 4;
654 htois(p, setup->case_insensitive_passwd_len); p += 2;
655 htois(p, setup->case_sensitive_passwd_len); p += 2;
656 htoil(p, setup->reserved); p += 4;
657 htoil(p, setup->capabilities); p += 4;
660 static void unload_session_setup_resp(unsigned char buf[],
661 SMB_SessionSetup_Resp *resp)
663 unsigned char *p = buf;
665 resp->andx_command = *p++;
666 resp->andx_reserved = *p++;
667 resp->andx_offset = itohs(p); p += 2;
668 resp->action = itohs(p);
672 * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
673 * until all the data is written out or an error occurs.
675 static int retry_writev(SOCKET fd, struct iovec *iov, int iovcnt)
693 while (iovcnt && iov[0].iov_len == 0) {
698 if (!iovcnt) return written;
700 n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
703 if (errno == EINVAL && iov_max > 10) {
707 if (errno == EINTR) continue;
714 for (i = 0; i < iovcnt; i++) {
715 if ((int) iov[i].iov_len > n) {
716 iov[i].iov_base = (char *) iov[i].iov_base + n;
724 if (i == iovcnt) return written;
729 * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
730 * until all the data is read in or an error occurs.
732 static int retry_read(SOCKET fd, char *buf0, unsigned nbyte)
738 if (nbyte == 0) return 0;
741 /* Can't use read() on sockets on Windows, but recv works on all platforms */
742 n = recv (fd, buf, nbyte, 0);
743 if (n == -1 || n == 0) {
745 if (errno == EINTR || errno == EAGAIN) continue;
752 if (n >= (int) nbyte) return nread;
759 static void make_netbios_name(const char *in, unsigned char out[])
763 /* create a NetBIOS name from the DNS name
765 * - use up to the first 16 chars of the first part of the hostname
766 * - convert to all uppercase
767 * - use the tail end of the output buffer as temp space
769 n = strcspn(in, ".");
771 strncpy(out+18, in, n);
776 for (i = 0; i < n; i++) {
777 out[j++] = ((in[i] >> 4) & 0xf) + 0x41;
778 out[j++] = (in[i] & 0xf) + 0x41;
780 for (; i < 16; i++) {
781 out[j++] = ((0x20 >> 4) & 0xf) + 0x41;
782 out[j++] = (0x20 & 0xf) + 0x41;
787 static SOCKET smb_connect_server(const sasl_utils_t *utils, const char *client,
790 struct addrinfo hints;
791 struct addrinfo *ai = NULL, *r;
792 SOCKET s = (SOCKET) -1;
802 char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
804 unsigned char called[34];
805 unsigned char calling[34];
810 memset(&hints, 0, sizeof(hints));
811 hints.ai_family = PF_UNSPEC;
812 hints.ai_socktype = SOCK_STREAM;
813 hints.ai_flags = AI_CANONNAME;
814 if ((err = getaddrinfo(server, port, &hints, &ai)) != 0) {
815 utils->log(NULL, SASL_LOG_ERR,
816 "NTLM: getaddrinfo %s/%s: %s",
817 server, port, gai_strerror(err));
821 /* Make sure we have AF_INET or AF_INET6 addresses. */
822 if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
823 utils->log(NULL, SASL_LOG_ERR, "NTLM: no IP address info for %s",
824 ai->ai_canonname ? ai->ai_canonname : server);
829 /* establish connection to authentication server */
830 for (r = ai; r; r = r->ai_next) {
831 s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
834 if (connect(s, r->ai_addr, r->ai_addrlen) >= 0)
837 saved_errno = WSAGetLastError();
843 niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
844 #ifdef NI_WITHSCOPEID
845 if (r->ai_family == AF_INET6)
846 niflags |= NI_WITHSCOPEID;
848 if (getnameinfo(r->ai_addr, r->ai_addrlen, hbuf, sizeof(hbuf),
849 pbuf, sizeof(pbuf), niflags) != 0) {
850 strcpy(hbuf, "unknown");
851 strcpy(pbuf, "unknown");
854 /* Can't use errno (and %m), as it doesn't contain
855 * the socket error on Windows */
856 error_str = _plug_get_error_message (utils, saved_errno);
857 utils->log(NULL, SASL_LOG_WARN, "NTLM: connect %s[%s]/%s: %s",
858 ai->ai_canonname ? ai->ai_canonname : server,
862 utils->free (error_str);
865 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, NULL, 0,
866 pbuf, sizeof(pbuf), NI_NUMERICSERV) != 0) {
867 strcpy(pbuf, "unknown");
869 utils->log(NULL, SASL_LOG_ERR, "NTLM: couldn't connect to %s/%s",
870 ai->ai_canonname ? ai->ai_canonname : server, pbuf);
877 /*** send NetBIOS session request ***/
879 /* get length of data */
880 pkt = sizeof(called) + sizeof(calling);
882 /* make sure length is less than 17 bits */
883 if (pkt >= (1 << 17)) {
888 /* prepend the packet type */
889 pkt |= (NBT_SESSION_REQUEST << 24);
892 /* XXX should determine the real NetBIOS name */
893 make_netbios_name(server, called);
894 make_netbios_name(client, calling);
896 iov[0].iov_base = (void *) &pkt;
897 iov[0].iov_len = sizeof(pkt);
898 iov[1].iov_base = called;
899 iov[1].iov_len = sizeof(called);
900 iov[2].iov_base = calling;
901 iov[2].iov_len = sizeof(calling);
903 rc = retry_writev(s, iov, N(iov));
905 utils->log(NULL, SASL_LOG_ERR,
906 "NTLM: error sending NetBIOS session request");
911 rc = retry_read(s, (char *) &pkt, sizeof(pkt));
913 if (rc == -1 || pkt != (uint32) (NBT_POSITIVE_SESSION_RESP << 24)) {
914 unsigned char ec = NBT_ERR_UNSPECIFIED;
917 retry_read(s, (char *) &ec, sizeof(ec));
919 case NBT_ERR_NO_LISTEN_CALLED:
920 errstr = "Not listening on called name";
922 case NBT_ERR_NO_LISTEN_CALLING:
923 errstr = "Not listening for calling name";
925 case NBT_ERR_CALLED_NOT_PRESENT:
926 errstr = "Called name not present";
928 case NBT_ERR_INSUFFICIENT_RESRC:
929 errstr = "Called name present, but insufficient resources";
932 errstr = "Unspecified error";
934 utils->log(NULL, SASL_LOG_ERR,
935 "NTLM: negative NetBIOS session response: %s", errstr);
943 static int smb_negotiate_protocol(const sasl_utils_t *utils,
944 server_context_t *text, char **domain)
947 SMB_NegProt_Resp resp;
948 unsigned char hbuf[SMB_HDR_SIZE], *p;
949 unsigned char wordcount = 0;
950 unsigned char bc[sizeof(uint16)];
953 int n_dialects = N(SMB_DIALECTS);
954 struct iovec iov[4+N(SMB_DIALECTS)];
959 /*** create a negotiate protocol request ***/
961 /* create a header */
962 memset(&hdr, 0, sizeof(hdr));
963 hdr.command = SMB_COM_NEGOTIATE_PROTOCOL;
965 hdr.flags2 = SMB_FLAGS2_ERR_STATUS;
966 if (text->flags & NTLM_USE_UNICODE) hdr.flags2 |= SMB_FLAGS2_UNICODE;
968 current_pid = getpid();
969 if (sizeof(current_pid) <= 2) {
970 hdr.pid = (uint16) current_pid;
973 hdr.pid = (uint16) (((uint32) current_pid) & 0xFFFF);
974 hdr.PidHigh = (uint16) (((uint32) current_pid) >> 16);
977 load_smb_header(hbuf, &hdr);
979 /* put together all of the pieces of the request */
981 iov[n].iov_base = (void *) &nl;
982 iov[n++].iov_len = sizeof(len);
983 iov[n].iov_base = hbuf;
984 iov[n++].iov_len = SMB_HDR_SIZE;
985 iov[n].iov_base = &wordcount;
986 iov[n++].iov_len = sizeof(wordcount);
987 iov[n].iov_base = (void *) &bc;
988 iov[n++].iov_len = sizeof(bc);
990 /* add our supported dialects */
991 for (i = 0; i < n_dialects; i++) {
992 iov[n].iov_base = (char *) SMB_DIALECTS[i];
993 iov[n++].iov_len = (long) strlen(SMB_DIALECTS[i]) + 1;
996 /* total up the lengths */
998 for (i = 1; i < 4; i++) len += iov[i].iov_len;
999 for (i = 4; i < n; i++) bytecount += (uint16) iov[i].iov_len;
1002 htois((char *) &bc, bytecount);
1005 rc = retry_writev(text->sock, iov, n);
1007 utils->log(NULL, SASL_LOG_ERR,
1008 "NTLM: error sending NEGPROT request");
1012 /*** read the negotiate protocol response ***/
1014 /* read the total length */
1015 rc = retry_read(text->sock, (char *) &nl, sizeof(nl));
1016 if (rc < (int) sizeof(nl)) {
1017 utils->log(NULL, SASL_LOG_ERR,
1018 "NTLM: error reading NEGPROT response length");
1024 if (_plug_buf_alloc(utils, &text->out_buf, &text->out_buf_len,
1026 SETERROR(utils, "cannot allocate NTLM NEGPROT response buffer");
1030 rc = retry_read(text->sock, text->out_buf, len);
1031 if (rc < (int) len) {
1032 utils->log(NULL, SASL_LOG_ERR,
1033 "NTLM: error reading NEGPROT response");
1038 /* parse the header */
1039 if (len < SMB_HDR_SIZE) {
1040 utils->log(NULL, SASL_LOG_ERR,
1041 "NTLM: not enough data for NEGPROT response header");
1044 unload_smb_header(p, &hdr);
1046 len -= SMB_HDR_SIZE;
1048 /* sanity check the header */
1049 if (memcmp(hdr.protocol, SMB_HDR_PROTOCOL, 4) /* correct protocol */
1050 || hdr.command != SMB_COM_NEGOTIATE_PROTOCOL /* correct command */
1051 || hdr.status /* no errors */
1052 || !(hdr.flags & SMB_FLAGS_SERVER_TO_REDIR)) { /* response */
1053 utils->log(NULL, SASL_LOG_ERR,
1054 "NTLM: error in NEGPROT response header: %ld",
1059 /* get the wordcount */
1061 utils->log(NULL, SASL_LOG_ERR,
1062 "NTLM: not enough data for NEGPROT response wordcount");
1068 /* parse the parameters */
1069 if (wordcount != SMB_NEGPROT_RESP_SIZE / sizeof(uint16)) {
1070 utils->log(NULL, SASL_LOG_ERR,
1071 "NTLM: incorrect NEGPROT wordcount for NT LM 0.12");
1074 unload_negprot_resp(p, &resp);
1075 p += SMB_NEGPROT_RESP_SIZE;
1076 len -= SMB_NEGPROT_RESP_SIZE;
1078 /* sanity check the parameters */
1079 if (resp.dialect_index != 0
1080 || !(resp.security_mode & SMB_SECURITY_MODE_USER)
1081 || !(resp.security_mode & SMB_SECURITY_MODE_ENCRYPT)
1082 || resp.security_mode & SMB_SECURITY_MODE_SIGN_REQ
1083 || resp.capabilities & SMB_CAP_EXTENDED_SECURITY
1084 || resp.encryption_key_length != NTLM_NONCE_LENGTH) {
1085 utils->log(NULL, SASL_LOG_ERR,
1086 "NTLM: error in NEGPROT response parameters");
1090 /* get the bytecount */
1092 utils->log(NULL, SASL_LOG_ERR,
1093 "NTLM: not enough data for NEGPROT response bytecount");
1096 bytecount = itohs(p);
1099 if (len != bytecount) {
1100 utils->log(NULL, SASL_LOG_ERR,
1101 "NTLM: incorrect bytecount for NEGPROT response data");
1105 /* parse the data */
1106 memcpy(text->nonce, p, resp.encryption_key_length);
1107 p += resp.encryption_key_length;
1108 len -= resp.encryption_key_length;
1110 /* if client asked for target, send domain */
1111 if (text->flags & NTLM_ASK_TARGET) {
1112 *domain = utils->malloc(len);
1113 if (domain == NULL) {
1117 memcpy(*domain, p, len);
1118 from_unicode(*domain, *domain, len);
1120 text->flags |= NTLM_TARGET_IS_DOMAIN;
1126 static int smb_session_setup(const sasl_utils_t *utils, server_context_t *text,
1127 const char *authid, char *domain,
1128 unsigned char *lm_resp, unsigned lm_resp_len,
1129 unsigned char *nt_resp, unsigned nt_resp_len)
1132 SMB_SessionSetup setup;
1133 SMB_SessionSetup_Resp resp;
1134 unsigned char hbuf[SMB_HDR_SIZE], sbuf[SMB_SESSION_SETUP_SIZE], *p;
1135 unsigned char wordcount = SMB_SESSION_SETUP_SIZE / sizeof(uint16);
1136 unsigned char bc[sizeof(uint16)];
1139 struct iovec iov[12];
1145 char osbuf[2*SYS_NMLN+2];
1150 /*** create a session setup request ***/
1152 /* create a header */
1153 memset(&hdr, 0, sizeof(hdr));
1154 hdr.command = SMB_COM_SESSION_SETUP_ANDX;
1156 hdr.flags2 = SMB_FLAGS2_ERR_STATUS;
1157 if (text->flags & NTLM_USE_UNICODE) hdr.flags2 |= SMB_FLAGS2_UNICODE;
1159 current_pid = getpid();
1160 if (sizeof(current_pid) <= 2) {
1161 hdr.pid = (uint16) current_pid;
1164 hdr.pid = (uint16) (((uint32) current_pid) & 0xFFFF);
1165 hdr.PidHigh = (uint16) (((uint32) current_pid) >> 16);
1168 load_smb_header(hbuf, &hdr);
1170 /* create a the setup parameters */
1171 memset(&setup, 0, sizeof(setup));
1172 setup.andx_command = SMB_COM_NONE;
1173 setup.max_buffer_size = 0xFFFF;
1174 if (lm_resp) setup.case_insensitive_passwd_len = lm_resp_len;
1175 if (nt_resp) setup.case_sensitive_passwd_len = nt_resp_len;
1177 if (text->flags & NTLM_USE_UNICODE)
1178 setup.capabilities = SMB_CAP_UNICODE;
1180 load_session_setup(sbuf, &setup);
1182 _plug_snprintf_os_info (osbuf, sizeof(osbuf));
1184 snprintf(lanman, sizeof(lanman), "Cyrus SASL %u.%u.%u",
1185 SASL_VERSION_MAJOR, SASL_VERSION_MINOR,
1188 /* put together all of the pieces of the request */
1190 iov[n].iov_base = (void *) &nl;
1191 iov[n++].iov_len = sizeof(len);
1192 iov[n].iov_base = hbuf;
1193 iov[n++].iov_len = SMB_HDR_SIZE;
1194 iov[n].iov_base = &wordcount;
1195 iov[n++].iov_len = sizeof(wordcount);
1196 iov[n].iov_base = sbuf;
1197 iov[n++].iov_len = SMB_SESSION_SETUP_SIZE;
1198 iov[n].iov_base = (void *) &bc;
1199 iov[n++].iov_len = sizeof(bc);
1201 iov[n].iov_base = lm_resp;
1202 iov[n++].iov_len = NTLM_RESP_LENGTH;
1205 iov[n].iov_base = nt_resp;
1206 iov[n++].iov_len = NTLM_RESP_LENGTH;
1208 iov[n].iov_base = (char*) authid;
1209 iov[n++].iov_len = (long) strlen(authid) + 1;
1210 if (!domain) domain = "";
1211 iov[n].iov_base = domain;
1212 iov[n++].iov_len = (long) strlen(domain) + 1;
1213 iov[n].iov_base = osbuf;
1214 iov[n++].iov_len = (long) strlen(osbuf) + 1;
1215 iov[n].iov_base = lanman;
1216 iov[n++].iov_len = (long) strlen(lanman) + 1;
1218 /* total up the lengths */
1219 len = bytecount = 0;
1220 for (i = 1; i < 5; i++) len += iov[i].iov_len;
1221 for (i = 5; i < n; i++) bytecount += (uint16) iov[i].iov_len;
1224 htois((char *) &bc, bytecount);
1227 rc = retry_writev(text->sock, iov, n);
1229 utils->log(NULL, SASL_LOG_ERR,
1230 "NTLM: error sending SESSIONSETUP request");
1234 /*** read the session setup response ***/
1236 /* read the total length */
1237 rc = retry_read(text->sock, (char *) &nl, sizeof(nl));
1238 if (rc < (int) sizeof(nl)) {
1239 utils->log(NULL, SASL_LOG_ERR,
1240 "NTLM: error reading SESSIONSETUP response length");
1246 if (_plug_buf_alloc(utils, &text->out_buf, &text->out_buf_len,
1249 "cannot allocate NTLM SESSIONSETUP response buffer");
1253 rc = retry_read(text->sock, text->out_buf, len);
1254 if (rc < (int) len) {
1255 utils->log(NULL, SASL_LOG_ERR,
1256 "NTLM: error reading SESSIONSETUP response");
1261 /* parse the header */
1262 if (len < SMB_HDR_SIZE) {
1263 utils->log(NULL, SASL_LOG_ERR,
1264 "NTLM: not enough data for SESSIONSETUP response header");
1267 unload_smb_header(p, &hdr);
1269 len -= SMB_HDR_SIZE;
1271 /* sanity check the header */
1272 if (memcmp(hdr.protocol, SMB_HDR_PROTOCOL, 4) /* correct protocol */
1273 || hdr.command != SMB_COM_SESSION_SETUP_ANDX /* correct command */
1274 || !(hdr.flags & SMB_FLAGS_SERVER_TO_REDIR)) { /* response */
1275 utils->log(NULL, SASL_LOG_ERR,
1276 "NTLM: error in SESSIONSETUP response header");
1280 /* check auth success */
1282 utils->log(NULL, SASL_LOG_ERR,
1283 "NTLM: auth failure: %ld", hdr.status);
1284 return SASL_BADAUTH;
1287 /* get the wordcount */
1289 utils->log(NULL, SASL_LOG_ERR,
1290 "NTLM: not enough data for SESSIONSETUP response wordcount");
1296 /* parse the parameters */
1297 if (wordcount < SMB_SESSION_SETUP_RESP_SIZE / sizeof(uint16)) {
1298 utils->log(NULL, SASL_LOG_ERR,
1299 "NTLM: incorrect SESSIONSETUP wordcount");
1302 unload_session_setup_resp(p, &resp);
1304 /* check auth success */
1305 if (resp.action & SMB_REQUEST_MODE_GUEST) {
1306 utils->log(NULL, SASL_LOG_ERR,
1307 "NTLM: authenticated as guest");
1308 return SASL_BADAUTH;
1315 * Create a server challenge message (type 2) consisting of:
1317 * signature (8 bytes)
1318 * message type (uint32)
1319 * target name (buffer)
1321 * challenge (8 bytes)
1323 * target info (buffer)
1326 static int create_challenge(const sasl_utils_t *utils,
1327 char **buf, unsigned *buflen,
1328 const char *target, uint32 flags,
1329 const u_char *nonce, unsigned *outlen)
1331 uint32 offset = NTLM_TYPE2_DATA_OFFSET;
1335 SETERROR(utils, "need nonce for NTLM challenge");
1339 *outlen = offset + 2 * xstrlen(target);
1341 if (_plug_buf_alloc(utils, buf, buflen, *outlen) != SASL_OK) {
1342 SETERROR(utils, "cannot allocate NTLM challenge");
1347 memset(base, 0, *outlen);
1348 memcpy(base + NTLM_SIG_OFFSET, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
1349 htoil(base + NTLM_TYPE_OFFSET, NTLM_TYPE_CHALLENGE);
1350 load_buffer(base + NTLM_TYPE2_TARGET_OFFSET,
1351 ucase(target, 0), (uint16) xstrlen(target), flags & NTLM_USE_UNICODE,
1353 htoil(base + NTLM_TYPE2_FLAGS_OFFSET, flags);
1354 memcpy(base + NTLM_TYPE2_CHALLENGE_OFFSET, nonce, NTLM_NONCE_LENGTH);
1359 static int ntlm_server_mech_new(void *glob_context __attribute__((unused)),
1360 sasl_server_params_t *sparams,
1361 const char *challenge __attribute__((unused)),
1362 unsigned challen __attribute__((unused)),
1363 void **conn_context)
1365 server_context_t *text;
1368 SOCKET sock = (SOCKET) -1;
1370 sparams->utils->getopt(sparams->utils->getopt_context,
1371 "NTLM", "ntlm_server", &serv, &len);
1373 /* try to start a NetBIOS session with the server */
1374 sock = smb_connect_server(sparams->utils, sparams->serverFQDN, serv);
1375 if (sock == (SOCKET) -1) return SASL_UNAVAIL;
1378 /* holds state are in */
1379 text = sparams->utils->malloc(sizeof(server_context_t));
1381 MEMERROR( sparams->utils );
1385 memset(text, 0, sizeof(server_context_t));
1390 *conn_context = text;
1395 static int ntlm_server_mech_step1(server_context_t *text,
1396 sasl_server_params_t *sparams,
1397 const char *clientin,
1398 unsigned clientinlen,
1399 const char **serverout,
1400 unsigned *serveroutlen,
1401 sasl_out_params_t *oparams __attribute__((unused)))
1403 char *domain = NULL;
1406 if (!clientin || clientinlen < NTLM_TYPE1_MINSIZE ||
1407 memcmp(clientin, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) ||
1408 itohl(clientin + NTLM_TYPE_OFFSET) != NTLM_TYPE_REQUEST) {
1409 SETERROR(sparams->utils, "client didn't issue valid NTLM request");
1410 return SASL_BADPROT;
1413 text->flags = itohl(clientin + NTLM_TYPE1_FLAGS_OFFSET);
1414 sparams->utils->log(NULL, SASL_LOG_DEBUG,
1415 "client flags: %x", text->flags);
1417 text->flags &= NTLM_FLAGS_MASK; /* mask off the bits we don't support */
1419 /* if client can do Unicode, turn off ASCII */
1420 if (text->flags & NTLM_USE_UNICODE) text->flags &= ~NTLM_USE_ASCII;
1422 if (text->sock == -1) {
1423 /* generate challenge internally */
1425 /* if client asked for target, use FQDN as server target */
1426 if (text->flags & NTLM_ASK_TARGET) {
1427 result = _plug_strdup(sparams->utils, sparams->serverFQDN,
1429 if (result != SASL_OK) return result;
1431 text->flags |= NTLM_TARGET_IS_SERVER;
1434 /* generate a nonce */
1435 sparams->utils->rand(sparams->utils->rpool,
1436 (char *) text->nonce, NTLM_NONCE_LENGTH);
1439 /* proxy the response/challenge */
1440 result = smb_negotiate_protocol(sparams->utils, text, &domain);
1441 if (result != SASL_OK) goto cleanup;
1444 result = create_challenge(sparams->utils,
1445 &text->out_buf, &text->out_buf_len,
1446 domain, text->flags, text->nonce, serveroutlen);
1447 if (result != SASL_OK) goto cleanup;
1449 *serverout = text->out_buf;
1453 result = SASL_CONTINUE;
1456 if (domain) sparams->utils->free(domain);
1461 static int ntlm_server_mech_step2(server_context_t *text,
1462 sasl_server_params_t *sparams,
1463 const char *clientin,
1464 unsigned clientinlen,
1465 const char **serverout __attribute__((unused)),
1466 unsigned *serveroutlen __attribute__((unused)),
1467 sasl_out_params_t *oparams)
1469 unsigned char *lm_resp = NULL, *nt_resp = NULL;
1470 char *domain = NULL, *authid = NULL;
1471 unsigned lm_resp_len, nt_resp_len, domain_len, authid_len;
1474 if (!clientin || clientinlen < NTLM_TYPE3_MINSIZE ||
1475 memcmp(clientin, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) ||
1476 itohl(clientin + NTLM_TYPE_OFFSET) != NTLM_TYPE_RESPONSE) {
1477 SETERROR(sparams->utils, "client didn't issue valid NTLM response");
1478 return SASL_BADPROT;
1481 result = unload_buffer(sparams->utils, clientin + NTLM_TYPE3_LMRESP_OFFSET,
1482 (u_char **) &lm_resp, &lm_resp_len, 0,
1483 clientin, clientinlen);
1484 if (result != SASL_OK) goto cleanup;
1486 result = unload_buffer(sparams->utils, clientin + NTLM_TYPE3_NTRESP_OFFSET,
1487 (u_char **) &nt_resp, &nt_resp_len, 0,
1488 clientin, clientinlen);
1489 if (result != SASL_OK) goto cleanup;
1491 result = unload_buffer(sparams->utils, clientin + NTLM_TYPE3_DOMAIN_OFFSET,
1492 (u_char **) &domain, &domain_len,
1493 text->flags & NTLM_USE_UNICODE,
1494 clientin, clientinlen);
1495 if (result != SASL_OK) goto cleanup;
1497 result = unload_buffer(sparams->utils, clientin + NTLM_TYPE3_USER_OFFSET,
1498 (u_char **) &authid, &authid_len,
1499 text->flags & NTLM_USE_UNICODE,
1500 clientin, clientinlen);
1501 if (result != SASL_OK) goto cleanup;
1503 /* require at least one response and an authid */
1504 if ((!lm_resp && !nt_resp) ||
1505 (lm_resp && lm_resp_len < NTLM_RESP_LENGTH) ||
1506 (nt_resp && nt_resp_len < NTLM_RESP_LENGTH) ||
1508 SETERROR(sparams->utils, "client issued incorrect/nonexistent responses");
1509 result = SASL_BADPROT;
1513 sparams->utils->log(NULL, SASL_LOG_DEBUG,
1514 "client user: %s", authid);
1515 if (domain) sparams->utils->log(NULL, SASL_LOG_DEBUG,
1516 "client domain: %s", domain);
1518 if (text->sock == -1) {
1519 /* verify the response internally */
1521 sasl_secret_t *password = NULL;
1523 const char *password_request[] = { SASL_AUX_PASSWORD,
1525 struct propval auxprop_values[2];
1526 unsigned char hash[NTLM_HASH_LENGTH];
1527 unsigned char resp[NTLM_RESP_LENGTH];
1529 /* fetch user's password */
1530 result = sparams->utils->prop_request(sparams->propctx, password_request);
1531 if (result != SASL_OK) goto cleanup;
1533 /* this will trigger the getting of the aux properties */
1534 result = sparams->canon_user(sparams->utils->conn, authid, authid_len,
1535 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
1536 if (result != SASL_OK) goto cleanup;
1538 result = sparams->utils->prop_getnames(sparams->propctx,
1542 (!auxprop_values[0].name || !auxprop_values[0].values)) {
1543 /* We didn't find this username */
1544 SETERROR(sparams->utils, "no secret in database");
1545 result = sparams->transition ? SASL_TRANS : SASL_NOUSER;
1549 pass_len = strlen(auxprop_values[0].values[0]);
1550 if (pass_len == 0) {
1551 SETERROR(sparams->utils, "empty secret");
1556 password = sparams->utils->malloc(sizeof(sasl_secret_t) + pass_len);
1558 result = SASL_NOMEM;
1562 password->len = (unsigned) pass_len;
1563 strncpy(password->data, auxprop_values[0].values[0], pass_len + 1);
1565 /* erase the plaintext password */
1566 sparams->utils->prop_erase(sparams->propctx, password_request[0]);
1568 /* calculate our own response(s) and compare with client's */
1570 if (nt_resp && (nt_resp_len > NTLM_RESP_LENGTH)) {
1571 /* Try NTv2 response */
1572 sparams->utils->log(NULL, SASL_LOG_DEBUG,
1573 "calculating NTv2 response");
1574 V2(resp, password, authid, domain, text->nonce,
1575 lm_resp + MD5_DIGEST_LENGTH, nt_resp_len - MD5_DIGEST_LENGTH,
1576 sparams->utils, &text->out_buf, &text->out_buf_len,
1579 /* No need to compare the blob */
1580 if (memcmp(nt_resp, resp, MD5_DIGEST_LENGTH)) {
1581 SETERROR(sparams->utils, "incorrect NTLMv2 response");
1582 result = SASL_BADAUTH;
1586 /* Try NT response */
1587 sparams->utils->log(NULL, SASL_LOG_DEBUG,
1588 "calculating NT response");
1589 P24(resp, P21(hash, password, P16_nt, sparams->utils,
1590 &text->out_buf, &text->out_buf_len, &result),
1592 if (memcmp(nt_resp, resp, NTLM_RESP_LENGTH)) {
1593 SETERROR(sparams->utils, "incorrect NTLM response");
1594 result = SASL_BADAUTH;
1598 /* Try LMv2 response */
1599 sparams->utils->log(NULL, SASL_LOG_DEBUG,
1600 "calculating LMv2 response");
1601 V2(resp, password, authid, domain, text->nonce,
1602 lm_resp + MD5_DIGEST_LENGTH, lm_resp_len - MD5_DIGEST_LENGTH,
1603 sparams->utils, &text->out_buf, &text->out_buf_len,
1606 /* No need to compare the blob */
1607 if (memcmp(lm_resp, resp, MD5_DIGEST_LENGTH)) {
1608 /* Try LM response */
1609 sparams->utils->log(NULL, SASL_LOG_DEBUG,
1610 "calculating LM response");
1611 P24(resp, P21(hash, password, P16_lm, sparams->utils,
1612 &text->out_buf, &text->out_buf_len, &result),
1614 if (memcmp(lm_resp, resp, NTLM_RESP_LENGTH)) {
1615 SETERROR(sparams->utils, "incorrect LMv1/v2 response");
1616 result = SASL_BADAUTH;
1621 _plug_free_secret(sparams->utils, &password);
1623 if (result != SASL_OK) goto cleanup;
1626 /* proxy the response */
1627 result = smb_session_setup(sparams->utils, text, authid, domain,
1628 lm_resp, lm_resp_len, nt_resp, nt_resp_len);
1629 if (result != SASL_OK) goto cleanup;
1631 result = sparams->canon_user(sparams->utils->conn, authid, authid_len,
1632 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
1633 if (result != SASL_OK) goto cleanup;
1637 oparams->doneflag = 1;
1638 oparams->mech_ssf = 0;
1639 oparams->maxoutbuf = 0;
1640 oparams->encode_context = NULL;
1641 oparams->encode = NULL;
1642 oparams->decode_context = NULL;
1643 oparams->decode = NULL;
1644 oparams->param_version = 0;
1649 if (lm_resp) sparams->utils->free(lm_resp);
1650 if (nt_resp) sparams->utils->free(nt_resp);
1651 if (domain) sparams->utils->free(domain);
1652 if (authid) sparams->utils->free(authid);
1657 static int ntlm_server_mech_step(void *conn_context,
1658 sasl_server_params_t *sparams,
1659 const char *clientin,
1660 unsigned clientinlen,
1661 const char **serverout,
1662 unsigned *serveroutlen,
1663 sasl_out_params_t *oparams)
1665 server_context_t *text = (server_context_t *) conn_context;
1670 sparams->utils->log(NULL, SASL_LOG_DEBUG,
1671 "NTLM server step %d\n", text->state);
1673 switch (text->state) {
1676 return ntlm_server_mech_step1(text, sparams, clientin, clientinlen,
1677 serverout, serveroutlen, oparams);
1680 return ntlm_server_mech_step2(text, sparams, clientin, clientinlen,
1681 serverout, serveroutlen, oparams);
1684 sparams->utils->log(NULL, SASL_LOG_ERR,
1685 "Invalid NTLM server step %d\n", text->state);
1689 return SASL_FAIL; /* should never get here */
1692 static void ntlm_server_mech_dispose(void *conn_context,
1693 const sasl_utils_t *utils)
1695 server_context_t *text = (server_context_t *) conn_context;
1699 if (text->out_buf) utils->free(text->out_buf);
1700 if (text->sock != -1) closesocket(text->sock);
1705 static sasl_server_plug_t ntlm_server_plugins[] =
1708 "NTLM", /* mech_name */
1710 SASL_SEC_NOPLAINTEXT
1711 | SASL_SEC_NOANONYMOUS, /* security_flags */
1712 SASL_FEAT_WANT_CLIENT_FIRST, /* features */
1713 NULL, /* glob_context */
1714 &ntlm_server_mech_new, /* mech_new */
1715 &ntlm_server_mech_step, /* mech_step */
1716 &ntlm_server_mech_dispose, /* mech_dispose */
1717 NULL, /* mech_free */
1719 NULL, /* user_query */
1721 NULL, /* mech_avail */
1726 int ntlm_server_plug_init(sasl_utils_t *utils,
1729 sasl_server_plug_t **pluglist,
1732 if (maxversion < SASL_SERVER_PLUG_VERSION) {
1733 SETERROR(utils, "NTLM version mismatch");
1734 return SASL_BADVERS;
1737 *out_version = SASL_SERVER_PLUG_VERSION;
1738 *pluglist = ntlm_server_plugins;
1744 /***************************** Client Section *****************************/
1746 typedef struct client_context {
1749 /* per-step mem management */
1751 unsigned out_buf_len;
1756 * Create a client request (type 1) consisting of:
1758 * signature (8 bytes)
1759 * message type (uint32)
1762 * workstation (buffer)
1765 static int create_request(const sasl_utils_t *utils,
1766 char **buf, unsigned *buflen,
1767 const char *domain, const char *wkstn,
1770 uint32 flags = ( NTLM_USE_UNICODE | NTLM_USE_ASCII |
1771 NTLM_ASK_TARGET | NTLM_AUTH_NTLM );
1772 uint32 offset = NTLM_TYPE1_DATA_OFFSET;
1775 *outlen = offset + xstrlen(domain) + xstrlen(wkstn);
1776 if (_plug_buf_alloc(utils, buf, buflen, *outlen) != SASL_OK) {
1777 SETERROR(utils, "cannot allocate NTLM request");
1782 memset(base, 0, *outlen);
1783 memcpy(base + NTLM_SIG_OFFSET, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
1784 htoil(base + NTLM_TYPE_OFFSET, NTLM_TYPE_REQUEST);
1785 htoil(base + NTLM_TYPE1_FLAGS_OFFSET, flags);
1786 load_buffer(base + NTLM_TYPE1_DOMAIN_OFFSET,
1787 domain, (uint16) xstrlen(domain), 0, base, &offset);
1788 load_buffer(base + NTLM_TYPE1_WORKSTN_OFFSET,
1789 wkstn, (uint16) xstrlen(wkstn), 0, base, &offset);
1795 * Create a client response (type 3) consisting of:
1797 * signature (8 bytes)
1798 * message type (uint32)
1799 * LM/LMv2 response (buffer)
1800 * NTLM/NTLMv2 response (buffer)
1802 * user name (buffer)
1803 * workstation (buffer)
1804 * session key (buffer)
1808 static int create_response(const sasl_utils_t *utils,
1809 char **buf, unsigned *buflen,
1810 const u_char *lm_resp, const u_char *nt_resp,
1811 const char *domain, const char *user,
1812 const char *wkstn, const u_char *key,
1813 uint32 flags, unsigned *outlen)
1815 uint32 offset = NTLM_TYPE3_DATA_OFFSET;
1818 if (!lm_resp && !nt_resp) {
1819 SETERROR(utils, "need at least one NT/LM response");
1823 *outlen = offset + (flags & NTLM_USE_UNICODE ? 2 : 1) *
1824 (xstrlen(domain) + xstrlen(user) + xstrlen(wkstn));
1825 if (lm_resp) *outlen += NTLM_RESP_LENGTH;
1826 if (nt_resp) *outlen += NTLM_RESP_LENGTH;
1827 if (key) *outlen += NTLM_SESSKEY_LENGTH;
1829 if (_plug_buf_alloc(utils, buf, buflen, *outlen) != SASL_OK) {
1830 SETERROR(utils, "cannot allocate NTLM response");
1835 memset(base, 0, *outlen);
1836 memcpy(base + NTLM_SIG_OFFSET, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
1837 htoil(base + NTLM_TYPE_OFFSET, NTLM_TYPE_RESPONSE);
1838 load_buffer(base + NTLM_TYPE3_LMRESP_OFFSET,
1839 lm_resp, lm_resp ? NTLM_RESP_LENGTH : 0, 0, base, &offset);
1840 load_buffer(base + NTLM_TYPE3_NTRESP_OFFSET,
1841 nt_resp, nt_resp ? NTLM_RESP_LENGTH : 0, 0, base, &offset);
1842 load_buffer(base + NTLM_TYPE3_DOMAIN_OFFSET,
1843 ucase(domain, 0), (uint16) xstrlen(domain), flags & NTLM_USE_UNICODE,
1845 load_buffer(base + NTLM_TYPE3_USER_OFFSET,
1846 user, (uint16) xstrlen(user), flags & NTLM_USE_UNICODE, base, &offset);
1847 load_buffer(base + NTLM_TYPE3_WORKSTN_OFFSET,
1848 ucase(wkstn, 0), (uint16) xstrlen(wkstn), flags & NTLM_USE_UNICODE,
1850 load_buffer(base + NTLM_TYPE3_SESSIONKEY_OFFSET,
1851 key, key ? NTLM_SESSKEY_LENGTH : 0, 0, base, &offset);
1852 htoil(base + NTLM_TYPE3_FLAGS_OFFSET, flags);
1857 static int ntlm_client_mech_new(void *glob_context __attribute__((unused)),
1858 sasl_client_params_t *params,
1859 void **conn_context)
1861 client_context_t *text;
1863 /* holds state are in */
1864 text = params->utils->malloc(sizeof(client_context_t));
1866 MEMERROR( params->utils );
1870 memset(text, 0, sizeof(client_context_t));
1874 *conn_context = text;
1879 static int ntlm_client_mech_step1(client_context_t *text,
1880 sasl_client_params_t *params,
1881 const char *serverin __attribute__((unused)),
1882 unsigned serverinlen __attribute__((unused)),
1883 sasl_interact_t **prompt_need __attribute__((unused)),
1884 const char **clientout,
1885 unsigned *clientoutlen,
1886 sasl_out_params_t *oparams __attribute__((unused)))
1890 /* check if sec layer strong enough */
1891 if (params->props.min_ssf > params->external_ssf) {
1892 SETERROR(params->utils, "SSF requested of NTLM plugin");
1893 return SASL_TOOWEAK;
1896 /* we don't care about domain or wkstn */
1897 result = create_request(params->utils, &text->out_buf, &text->out_buf_len,
1898 NULL, NULL, clientoutlen);
1899 if (result != SASL_OK) return result;
1901 *clientout = text->out_buf;
1905 return SASL_CONTINUE;
1908 static int ntlm_client_mech_step2(client_context_t *text,
1909 sasl_client_params_t *params,
1910 const char *serverin,
1911 unsigned serverinlen,
1912 sasl_interact_t **prompt_need,
1913 const char **clientout,
1914 unsigned *clientoutlen,
1915 sasl_out_params_t *oparams)
1917 const char *authid = NULL;
1918 sasl_secret_t *password = NULL;
1919 unsigned int free_password; /* set if we need to free password */
1920 char *domain = NULL;
1921 int auth_result = SASL_OK;
1922 int pass_result = SASL_OK;
1924 unsigned char hash[NTLM_HASH_LENGTH];
1925 unsigned char resp[NTLM_RESP_LENGTH], *lm_resp = NULL, *nt_resp = NULL;
1929 if (!serverin || serverinlen < NTLM_TYPE2_MINSIZE ||
1930 memcmp(serverin, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) ||
1931 itohl(serverin + NTLM_TYPE_OFFSET) != NTLM_TYPE_CHALLENGE) {
1932 SETERROR(params->utils, "server didn't issue valid NTLM challenge");
1933 return SASL_BADPROT;
1936 /* try to get the authid */
1937 if (oparams->authid == NULL) {
1938 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1940 if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
1944 /* try to get the password */
1945 if (password == NULL) {
1946 pass_result = _plug_get_password(params->utils, &password,
1947 &free_password, prompt_need);
1949 if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
1953 /* free prompts we got */
1954 if (prompt_need && *prompt_need) {
1955 params->utils->free(*prompt_need);
1956 *prompt_need = NULL;
1959 /* if there are prompts not filled in */
1960 if ((auth_result == SASL_INTERACT) || (pass_result == SASL_INTERACT)) {
1961 /* make the prompt list */
1963 _plug_make_prompts(params->utils, prompt_need,
1965 auth_result == SASL_INTERACT ?
1966 "Please enter your authentication name" : NULL,
1968 pass_result == SASL_INTERACT ?
1969 "Please enter your password" : NULL, NULL,
1972 if (result != SASL_OK) goto cleanup;
1974 return SASL_INTERACT;
1977 result = params->canon_user(params->utils->conn, authid, 0,
1978 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
1979 if (result != SASL_OK) goto cleanup;
1981 flags = itohl(serverin + NTLM_TYPE2_FLAGS_OFFSET);
1982 params->utils->log(NULL, SASL_LOG_DEBUG,
1983 "server flags: %x", flags);
1985 flags &= NTLM_FLAGS_MASK; /* mask off the bits we don't support */
1987 result = unload_buffer(params->utils, serverin + NTLM_TYPE2_TARGET_OFFSET,
1988 (u_char **) &domain, NULL,
1989 flags & NTLM_USE_UNICODE,
1990 (u_char *) serverin, serverinlen);
1991 if (result != SASL_OK) goto cleanup;
1992 params->utils->log(NULL, SASL_LOG_DEBUG,
1993 "server domain: %s", domain);
1995 /* should we send a NTLMv2 response? */
1996 params->utils->getopt(params->utils->getopt_context,
1997 "NTLM", "ntlm_v2", &sendv2, NULL);
1999 (*sendv2 == '1' || *sendv2 == 'y' ||
2000 (*sendv2 == 'o' && *sendv2 == 'n') || *sendv2 == 't')) {
2002 /* put the cnonce in place after the LMv2 HMAC */
2003 char *cnonce = resp + MD5_DIGEST_LENGTH;
2005 params->utils->log(NULL, SASL_LOG_DEBUG,
2006 "calculating LMv2 response");
2008 params->utils->rand(params->utils->rpool, cnonce, NTLM_NONCE_LENGTH);
2010 V2(resp, password, oparams->authid, domain,
2011 serverin + NTLM_TYPE2_CHALLENGE_OFFSET, cnonce, NTLM_NONCE_LENGTH,
2012 params->utils, &text->out_buf, &text->out_buf_len, &result);
2016 else if (flags & NTLM_AUTH_NTLM) {
2017 params->utils->log(NULL, SASL_LOG_DEBUG,
2018 "calculating NT response");
2019 P24(resp, P21(hash, password, P16_nt, params->utils,
2020 &text->out_buf, &text->out_buf_len, &result),
2021 (unsigned char *) serverin + NTLM_TYPE2_CHALLENGE_OFFSET);
2025 params->utils->log(NULL, SASL_LOG_DEBUG,
2026 "calculating LM response");
2027 P24(resp, P21(hash, password, P16_lm, params->utils,
2028 &text->out_buf, &text->out_buf_len, &result),
2029 (unsigned char *) serverin + NTLM_TYPE2_CHALLENGE_OFFSET);
2032 if (result != SASL_OK) goto cleanup;
2034 /* we don't care about workstn or session key */
2035 result = create_response(params->utils, &text->out_buf, &text->out_buf_len,
2036 lm_resp, nt_resp, domain, oparams->authid,
2037 NULL, NULL, flags, clientoutlen);
2038 if (result != SASL_OK) goto cleanup;
2040 *clientout = text->out_buf;
2043 oparams->doneflag = 1;
2044 oparams->mech_ssf = 0;
2045 oparams->maxoutbuf = 0;
2046 oparams->encode_context = NULL;
2047 oparams->encode = NULL;
2048 oparams->decode_context = NULL;
2049 oparams->decode = NULL;
2050 oparams->param_version = 0;
2055 if (domain) params->utils->free(domain);
2056 if (free_password) _plug_free_secret(params->utils, &password);
2061 static int ntlm_client_mech_step(void *conn_context,
2062 sasl_client_params_t *params,
2063 const char *serverin,
2064 unsigned serverinlen,
2065 sasl_interact_t **prompt_need,
2066 const char **clientout,
2067 unsigned *clientoutlen,
2068 sasl_out_params_t *oparams)
2070 client_context_t *text = (client_context_t *) conn_context;
2075 params->utils->log(NULL, SASL_LOG_DEBUG,
2076 "NTLM client step %d\n", text->state);
2078 switch (text->state) {
2081 return ntlm_client_mech_step1(text, params, serverin, serverinlen,
2082 prompt_need, clientout, clientoutlen,
2086 return ntlm_client_mech_step2(text, params, serverin, serverinlen,
2087 prompt_need, clientout, clientoutlen,
2091 params->utils->log(NULL, SASL_LOG_ERR,
2092 "Invalid NTLM client step %d\n", text->state);
2096 return SASL_FAIL; /* should never get here */
2099 static void ntlm_client_mech_dispose(void *conn_context,
2100 const sasl_utils_t *utils)
2102 client_context_t *text = (client_context_t *) conn_context;
2106 if (text->out_buf) utils->free(text->out_buf);
2111 static sasl_client_plug_t ntlm_client_plugins[] =
2114 "NTLM", /* mech_name */
2116 SASL_SEC_NOPLAINTEXT
2117 | SASL_SEC_NOANONYMOUS, /* security_flags */
2118 SASL_FEAT_WANT_CLIENT_FIRST, /* features */
2119 NULL, /* required_prompts */
2120 NULL, /* glob_context */
2121 &ntlm_client_mech_new, /* mech_new */
2122 &ntlm_client_mech_step, /* mech_step */
2123 &ntlm_client_mech_dispose, /* mech_dispose */
2124 NULL, /* mech_free */
2131 int ntlm_client_plug_init(sasl_utils_t *utils,
2134 sasl_client_plug_t **pluglist,
2137 if (maxversion < SASL_CLIENT_PLUG_VERSION) {
2138 SETERROR(utils, "NTLM version mismatch");
2139 return SASL_BADVERS;
2142 *out_version = SASL_CLIENT_PLUG_VERSION;
2143 *pluglist = ntlm_client_plugins;