2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
17 * SPNEGO back-end for NTLMSSP. See [MS-NLMP]
20 #include <sys/types.h>
21 #include <sys/byteorder.h>
24 #include "smbd_authsvc.h"
25 #include "netsmb/ntlmssp.h"
28 /* A shorter alias for a crazy long name from [MS-NLMP] */
29 #define NTLMSSP_NEGOTIATE_NTLM2 \
30 NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
32 /* Need this in a header somewhere */
34 /* little-endian values on little-endian */
35 #define htolel(x) ((uint32_t)(x))
36 #define letohl(x) ((uint32_t)(x))
37 #else /* (BYTE_ORDER == LITTLE_ENDIAN) */
38 /* little-endian values on big-endian (swap) */
39 #define letohl(x) BSWAP_32(x)
40 #define htolel(x) BSWAP_32(x)
41 #endif /* (BYTE_ORDER == LITTLE_ENDIAN) */
43 typedef struct ntlmssp_backend
{
47 char srv_challenge
[8];
51 char h_id
[8]; /* "NTLMSSP" */
65 /* workstation domain, name (place holders) */
73 struct sec_buf h_lm_resp
;
74 struct sec_buf h_nt_resp
;
75 struct sec_buf h_domain
;
76 struct sec_buf h_user
;
77 struct sec_buf h_wksta
;
78 struct sec_buf h_essn_key
; /* encrypted session key */
80 /* Version struct (optional) */
81 /* MIC hash (optional) */
84 /* Allow turning these off for debugging, etc. */
85 int smbd_signing_enabled
= 1;
87 int smbd_constant_challenge
= 0;
88 static uint8_t constant_chal
[8] = {
89 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 };
91 static int smbd_ntlmssp_negotiate(authsvc_context_t
*);
92 static int smbd_ntlmssp_authenticate(authsvc_context_t
*);
93 static int encode_avpair_str(smb_msgbuf_t
*, uint16_t, char *);
94 static int decode_secbuf_bin(smb_msgbuf_t
*, struct sec_buf
*, void **);
95 static int decode_secbuf_str(smb_msgbuf_t
*, struct sec_buf
*, char **);
98 * Initialize this context for NTLMSSP, if possible.
101 smbd_ntlmssp_init(authsvc_context_t
*ctx
)
103 ntlmssp_backend_t
*be
;
105 be
= malloc(sizeof (*be
));
107 return (NT_STATUS_NO_MEMORY
);
108 bzero(be
, sizeof (*be
));
109 be
->expect_type
= NTLMSSP_MSGTYPE_NEGOTIATE
;
110 ctx
->ctx_backend
= be
;
116 smbd_ntlmssp_fini(authsvc_context_t
*ctx
)
118 free(ctx
->ctx_backend
);
122 * Handle an auth message
125 smbd_ntlmssp_work(authsvc_context_t
*ctx
)
127 struct genhdr
*ihdr
= ctx
->ctx_ibodybuf
;
128 ntlmssp_backend_t
*be
= ctx
->ctx_backend
;
132 if (ctx
->ctx_ibodylen
< sizeof (*ihdr
))
133 return (NT_STATUS_INVALID_PARAMETER
);
135 if (bcmp(ihdr
->h_id
, "NTLMSSP", 8))
136 return (NT_STATUS_INVALID_PARAMETER
);
137 mtype
= letohl(ihdr
->h_type
);
138 if (mtype
!= be
->expect_type
)
139 return (NT_STATUS_INVALID_PARAMETER
);
142 case NTLMSSP_MSGTYPE_NEGOTIATE
:
143 ctx
->ctx_orawtype
= LSA_MTYPE_ES_CONT
;
144 rc
= smbd_ntlmssp_negotiate(ctx
);
146 case NTLMSSP_MSGTYPE_AUTHENTICATE
:
147 ctx
->ctx_orawtype
= LSA_MTYPE_ES_DONE
;
148 rc
= smbd_ntlmssp_authenticate(ctx
);
152 case NTLMSSP_MSGTYPE_CHALLENGE
:
153 /* Sent by servers, not received. */
154 rc
= NT_STATUS_INVALID_PARAMETER
;
161 #if (MAXHOSTNAMELEN < NETBIOS_NAME_SZ)
162 #error "MAXHOSTNAMELEN < NETBIOS_NAME_SZ"
166 * Handle an NTLMSSP_MSGTYPE_NEGOTIATE message, and reply
167 * with an NTLMSSP_MSGTYPE_CHALLENGE message.
168 * See: [MS-NLMP] 2.2.1.1, 3.2.5.1.1
171 smbd_ntlmssp_negotiate(authsvc_context_t
*ctx
)
173 char tmp_name
[MAXHOSTNAMELEN
];
174 ntlmssp_backend_t
*be
= ctx
->ctx_backend
;
175 struct nego_hdr
*ihdr
= ctx
->ctx_ibodybuf
;
181 size_t var_start
, var_end
;
184 if (ctx
->ctx_ibodylen
< sizeof (*ihdr
))
185 return (NT_STATUS_INVALID_PARAMETER
);
186 be
->clnt_flags
= letohl(ihdr
->h_flags
);
189 * Looks like we can ignore ws_dom, ws_name.
190 * Otherwise would parse those here.
193 secmode
= smb_config_get_secmode();
194 if (smbd_constant_challenge
) {
195 (void) memcpy(be
->srv_challenge
, constant_chal
,
196 sizeof (be
->srv_challenge
));
198 randomize(be
->srv_challenge
, sizeof (be
->srv_challenge
));
205 NTLMSSP_REQUEST_TARGET
|
206 NTLMSSP_NEGOTIATE_NTLM
|
207 NTLMSSP_NEGOTIATE_TARGET_INFO
;
208 be
->srv_flags
|= be
->clnt_flags
& (
209 NTLMSSP_NEGOTIATE_NTLM2
|
210 NTLMSSP_NEGOTIATE_128
|
211 NTLMSSP_NEGOTIATE_KEY_EXCH
|
212 NTLMSSP_NEGOTIATE_56
);
214 if (smbd_signing_enabled
) {
215 be
->srv_flags
|= be
->clnt_flags
& (
216 NTLMSSP_NEGOTIATE_SIGN
|
217 NTLMSSP_NEGOTIATE_SEAL
|
218 NTLMSSP_NEGOTIATE_ALWAYS_SIGN
);
221 if (be
->clnt_flags
& NTLMSSP_NEGOTIATE_UNICODE
)
222 be
->srv_flags
|= NTLMSSP_NEGOTIATE_UNICODE
;
223 else if (be
->clnt_flags
& NTLMSSP_NEGOTIATE_OEM
)
224 be
->srv_flags
|= NTLMSSP_NEGOTIATE_OEM
;
226 /* LM Key is mutually exclusive with NTLM2 */
227 if ((be
->srv_flags
& NTLMSSP_NEGOTIATE_NTLM2
) == 0 &&
228 (be
->clnt_flags
& NTLMSSP_NEGOTIATE_LM_KEY
) != 0)
229 be
->srv_flags
|= NTLMSSP_NEGOTIATE_LM_KEY
;
231 /* Get our "target name" */
232 if (secmode
== SMB_SECMODE_DOMAIN
) {
233 be
->srv_flags
|= NTLMSSP_TARGET_TYPE_DOMAIN
;
234 rc
= smb_getdomainname(tmp_name
, NETBIOS_NAME_SZ
);
236 be
->srv_flags
|= NTLMSSP_TARGET_TYPE_SERVER
;
237 rc
= smb_getnetbiosname(tmp_name
, NETBIOS_NAME_SZ
);
243 * Build the NTLMSSP_MSGTYPE_CHALLENGE message.
245 mbflags
= SMB_MSGBUF_NOTERM
;
246 if (be
->srv_flags
& NTLMSSP_NEGOTIATE_UNICODE
)
247 mbflags
|= SMB_MSGBUF_UNICODE
;
248 smb_msgbuf_init(&mb
, ctx
->ctx_obodybuf
, ctx
->ctx_obodylen
, mbflags
);
253 rc
= smb_msgbuf_encode(
254 &mb
, "8clwwll8cllwwl", /* offset, name (fmt) */
255 "NTLMSSP", /* 0: signature (8c) */
256 NTLMSSP_MSGTYPE_CHALLENGE
, /* 8: type (l) */
257 0, 0, 0, /* filled later: 12: target name (wwl) */
258 be
->srv_flags
, /* 20: flags (l) */
259 be
->srv_challenge
, /* 24: (8c) */
260 0, 0, /* 32: reserved (ll) */
261 0, 0, 0); /* filled later: 40: target info (wwl) */
262 #define TARGET_NAME_OFFSET 12
263 #define TARGET_INFO_OFFSET 40
268 * Variable length parts.
272 var_start
= smb_msgbuf_used(&mb
);
273 rc
= smb_msgbuf_encode(&mb
, "u", tmp_name
);
274 var_end
= smb_msgbuf_used(&mb
);
275 var_size
= (uint16_t)(var_end
- var_start
);
279 /* overwrite target name offset+lengths */
281 mb
.scan
= mb
.base
+ TARGET_NAME_OFFSET
;
282 (void) smb_msgbuf_encode(&mb
, "wwl", var_size
, var_size
, var_start
);
286 * Target info (AvPairList)
288 * These AV pairs are like our name/value pairs, but have
289 * numeric identifiers instead of names. There are many
290 * of these, but we put only the four expected by Windows:
291 * NetBIOS computer name
292 * NetBIOS domain name
295 * Note that "domain" above (even "DNS domain") refers to
296 * the AD domain of which we're a member, which may be
297 * _different_ from the configured DNS domain.
299 * Also note that in "workgroup" mode (not a domain member)
300 * all "domain" fields should be set to the same values as
301 * the "computer" fields ("bare" host name, not FQDN).
303 var_start
= smb_msgbuf_used(&mb
);
305 /* NetBIOS Computer Name */
306 if (smb_getnetbiosname(tmp_name
, NETBIOS_NAME_SZ
))
308 if (encode_avpair_str(&mb
, MsvAvNbComputerName
, tmp_name
) < 0)
311 if (secmode
!= SMB_SECMODE_DOMAIN
) {
313 * Workgroup mode. Set all to hostname.
314 * tmp_name = netbios hostname from above.
316 if (encode_avpair_str(&mb
, MsvAvNbDomainName
, tmp_name
) < 0)
319 * Want the bare computer name here (not FQDN).
321 if (smb_gethostname(tmp_name
, MAXHOSTNAMELEN
, SMB_CASE_LOWER
))
323 if (encode_avpair_str(&mb
, MsvAvDnsComputerName
, tmp_name
) < 0)
325 if (encode_avpair_str(&mb
, MsvAvDnsDomainName
, tmp_name
) < 0)
329 * Domain mode. Use real host and domain values.
332 /* NetBIOS Domain Name */
333 if (smb_getdomainname(tmp_name
, NETBIOS_NAME_SZ
))
335 if (encode_avpair_str(&mb
, MsvAvNbDomainName
, tmp_name
) < 0)
338 /* DNS Computer Name */
339 if (smb_getfqhostname(tmp_name
, MAXHOSTNAMELEN
))
341 if (encode_avpair_str(&mb
, MsvAvDnsComputerName
, tmp_name
) < 0)
344 /* DNS Domain Name */
345 if (smb_getfqdomainname(tmp_name
, MAXHOSTNAMELEN
))
347 if (encode_avpair_str(&mb
, MsvAvDnsDomainName
, tmp_name
) < 0)
352 if (smb_msgbuf_encode(&mb
, "ww", MsvAvEOL
, 0) < 0)
354 var_end
= smb_msgbuf_used(&mb
);
355 var_size
= (uint16_t)(var_end
- var_start
);
357 /* overwrite target offset+lengths */
359 mb
.scan
= mb
.base
+ TARGET_INFO_OFFSET
;
360 (void) smb_msgbuf_encode(&mb
, "wwl", var_size
, var_size
, var_start
);
363 ctx
->ctx_obodylen
= smb_msgbuf_used(&mb
);
364 smb_msgbuf_term(&mb
);
366 be
->expect_type
= NTLMSSP_MSGTYPE_AUTHENTICATE
;
371 smb_msgbuf_term(&mb
);
372 return (NT_STATUS_INTERNAL_ERROR
);
376 encode_avpair_str(smb_msgbuf_t
*mb
, uint16_t AvId
, char *name
)
381 len
= smb_wcequiv_strlen(name
);
382 rc
= smb_msgbuf_encode(mb
, "wwU", AvId
, len
, name
);
387 * Handle an NTLMSSP_MSGTYPE_AUTHENTICATE message.
388 * See: [MS-NLMP] 2.2.1.3, 3.2.5.1.2
391 smbd_ntlmssp_authenticate(authsvc_context_t
*ctx
)
395 smb_logon_t user_info
;
396 smb_token_t
*token
= NULL
;
397 ntlmssp_backend_t
*be
= ctx
->ctx_backend
;
403 void *essn_key
; /* encrypted session key (optional) */
405 uint_t status
= NT_STATUS_INTERNAL_ERROR
;
406 char combined_challenge
[SMBAUTH_CHAL_SZ
];
407 unsigned char kxkey
[SMBAUTH_HASH_SZ
];
408 boolean_t ntlm_v1x
= B_FALSE
;
410 bzero(&user_info
, sizeof (user_info
));
413 * Parse the NTLMSSP_MSGTYPE_AUTHENTICATE message.
415 if (ctx
->ctx_ibodylen
< sizeof (hdr
))
416 return (NT_STATUS_INVALID_PARAMETER
);
417 mbflags
= SMB_MSGBUF_NOTERM
;
418 if (be
->srv_flags
& NTLMSSP_NEGOTIATE_UNICODE
)
419 mbflags
|= SMB_MSGBUF_UNICODE
;
420 smb_msgbuf_init(&mb
, ctx
->ctx_ibodybuf
, ctx
->ctx_ibodylen
, mbflags
);
421 bzero(&hdr
, sizeof (hdr
));
423 if (smb_msgbuf_decode(&mb
, "12.") < 0)
425 if (decode_secbuf_bin(&mb
, &hdr
.h_lm_resp
, &lm_resp
) < 0)
427 if (decode_secbuf_bin(&mb
, &hdr
.h_nt_resp
, &nt_resp
) < 0)
429 if (decode_secbuf_str(&mb
, &hdr
.h_domain
, &domain
) < 0)
431 if (decode_secbuf_str(&mb
, &hdr
.h_user
, &user
) < 0)
433 if (decode_secbuf_str(&mb
, &hdr
.h_wksta
, &wksta
) < 0)
435 if (decode_secbuf_bin(&mb
, &hdr
.h_essn_key
, &essn_key
) < 0)
437 if (smb_msgbuf_decode(&mb
, "l", &be
->clnt_flags
) < 0)
440 if (be
->clnt_flags
& NTLMSSP_NEGOTIATE_KEY_EXCH
) {
441 if (hdr
.h_essn_key
.sb_length
< 16 || essn_key
== NULL
)
445 user_info
.lg_level
= NETR_NETWORK_LOGON
;
446 user_info
.lg_flags
= 0;
448 user_info
.lg_ntlm_flags
= be
->clnt_flags
;
449 user_info
.lg_username
= (user
) ? user
: "";
450 user_info
.lg_domain
= (domain
) ? domain
: "";
451 user_info
.lg_workstation
= (wksta
) ? wksta
: "";
453 user_info
.lg_clnt_ipaddr
=
454 ctx
->ctx_clinfo
.lci_clnt_ipaddr
;
455 user_info
.lg_local_port
= 445;
457 user_info
.lg_challenge_key
.len
= SMBAUTH_CHAL_SZ
;
458 user_info
.lg_challenge_key
.val
= (uint8_t *)be
->srv_challenge
;
460 user_info
.lg_nt_password
.len
= hdr
.h_nt_resp
.sb_length
;
461 user_info
.lg_nt_password
.val
= nt_resp
;
463 user_info
.lg_lm_password
.len
= hdr
.h_lm_resp
.sb_length
;
464 user_info
.lg_lm_password
.val
= lm_resp
;
466 user_info
.lg_native_os
= ctx
->ctx_clinfo
.lci_native_os
;
467 user_info
.lg_native_lm
= ctx
->ctx_clinfo
.lci_native_lm
;
470 * If we're doing extended session security, the challenge
471 * this OWF was computed with is different. [MS-NLMP 3.3.1]
472 * It's: MD5(concat(ServerChallenge,ClientChallenge))
473 * where the ClientChallenge is in the LM resp. field.
475 if (user_info
.lg_nt_password
.len
== SMBAUTH_LM_RESP_SZ
&&
476 user_info
.lg_lm_password
.len
>= SMBAUTH_CHAL_SZ
&&
477 (be
->clnt_flags
& NTLMSSP_NEGOTIATE_NTLM2
) != 0) {
478 smb_auth_ntlm2_mkchallenge(combined_challenge
,
479 be
->srv_challenge
, lm_resp
);
480 user_info
.lg_challenge_key
.val
=
481 (uint8_t *)combined_challenge
;
482 user_info
.lg_lm_password
.len
= 0;
487 * This (indirectly) calls smb_auth_validate() to
488 * check that the client gave us a valid hash.
490 token
= smbd_user_auth_logon(&user_info
);
492 status
= NT_STATUS_ACCESS_DENIED
;
496 if (token
->tkn_ssnkey
.val
!= NULL
&&
497 token
->tkn_ssnkey
.len
== SMBAUTH_HASH_SZ
) {
500 * At this point, token->tkn_session_key is the
501 * "Session Base Key" [MS-NLMP] 3.2.5.1.2
502 * Compute the final session key. First need the
503 * "Key Exchange Key" [MS-NLMP] 3.4.5.1
506 smb_auth_ntlm2_kxkey(kxkey
,
507 be
->srv_challenge
, lm_resp
,
508 token
->tkn_ssnkey
.val
);
510 /* KXKEY is the Session Base Key. */
511 (void) memcpy(kxkey
, token
->tkn_ssnkey
.val
,
516 * If the client give us an encrypted session key,
517 * decrypt it (RC4) using the "key exchange key".
519 if (be
->clnt_flags
& NTLMSSP_NEGOTIATE_KEY_EXCH
) {
520 /* RC4 args: result, key, data */
521 (void) smb_auth_RC4(token
->tkn_ssnkey
.val
,
522 SMBAUTH_HASH_SZ
, kxkey
, SMBAUTH_HASH_SZ
,
523 essn_key
, hdr
.h_essn_key
.sb_length
);
525 /* Final key is the KXKEY */
526 (void) memcpy(token
->tkn_ssnkey
.val
, kxkey
,
531 ctx
->ctx_token
= token
;
532 ctx
->ctx_obodylen
= 0;
534 smb_msgbuf_term(&mb
);
538 smb_msgbuf_term(&mb
);
543 decode_secbuf_bin(smb_msgbuf_t
*mb
, struct sec_buf
*sb
, void **binp
)
548 rc
= smb_msgbuf_decode(
556 if (sb
->sb_offset
> mb
->max
)
557 return (SMB_MSGBUF_UNDERFLOW
);
558 if (sb
->sb_length
> (mb
->max
- sb
->sb_offset
))
559 return (SMB_MSGBUF_UNDERFLOW
);
560 if (sb
->sb_length
== 0)
563 *binp
= mb
->base
+ sb
->sb_offset
;
568 decode_secbuf_str(smb_msgbuf_t
*mb
, struct sec_buf
*sb
, char **cpp
)
574 rc
= smb_msgbuf_decode(
582 if (sb
->sb_offset
> mb
->max
)
583 return (SMB_MSGBUF_UNDERFLOW
);
584 if (sb
->sb_length
> (mb
->max
- sb
->sb_offset
))
585 return (SMB_MSGBUF_UNDERFLOW
);
586 if (sb
->sb_length
== 0)
589 save_scan
= mb
->scan
;
590 mb
->scan
= mb
->base
+ sb
->sb_offset
;
591 rc
= smb_msgbuf_decode(mb
, "#u", (int)sb
->sb_length
, cpp
);
592 mb
->scan
= save_scan
;