xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/netr_ssp.c (revision fc910014e8a32a65612105835a10995f2c13d942)
1 /*
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
5  * 1.0 of the CDDL.
6  *
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.
10  */
11 
12 /*
13  * Copyright 2020 Tintri by DDN, Inc. All Rights Reserved.
14  * Copyright 2023 RackTop Systems, Inc.
15  */
16 
17 #include <sys/md5.h>
18 #include <sys/debug.h>
19 #include <strings.h>
20 #include <stdio.h>
21 #include <smbsrv/netrauth.h>
22 #include <smbsrv/string.h>
23 #include <smbsrv/libsmb.h>
24 #include <libmlsvc.h>
25 #include <resolv.h>
26 
27 /*
28  * NETLOGON SSP for "Secure RPC" works as follows:
29  * 1. The client binds to the DC without RPC-level authentication.
30  * 2. The client and server negotiate a Session Key using a client
31  * and server challenge, plus a shared secret (the machine password).
32  * This happens via NetrServerReqChallenge and NetrServerAuthenticate.
33  * The key is bound to a particular Computer/Server Name pair.
34  * 3. The client then establishes a new bind (or alters its existing one),
35  * this time requesting the NETLOGON provider for RPC-level authentication.
36  * The server uses the Computer and Domain names provided in the
37  * authentication token in the bind request in order to find
38  * the previously-negotiated Session Key (and rejects the bind if none
39  * exists).
40  * 4. The client and server then use this Session Key to provide
41  * integrity and/or confidentiality to future NETLOGON RPC messages.
42  *
43  * The functions in this file implement the NETLOGON SSP, as defined in
44  * [MS-NRPC] 3.3 "Netlogon as a Security Support Provider".
45  *
46  * Session Key negotiation is implemented in netr_auth.c.
47  * It is the same key that is used for generating NETLOGON credentials.
48  */
49 
50 enum nl_token_type {
51 	NL_AUTH_REQUEST = 0x00000000,
52 	NL_AUTH_RESPONSE = 0x00000001
53 };
54 
55 /*
56  * DOMAIN = domain name
57  * COMPUTER = client computer name
58  * HOST = client host name
59  *
60  * NB = NetBios format
61  * DNS = FQDN
62  *
63  * OEM = OEM_STRING
64  * COMPRESSED = Compressed UTF-8 string
65  *
66  * Each of these is NULL-terminated, and delinated by such.
67  * They are always found in this order, when specified.
68  *
69  * We currently use everything but NL_HOST_DNS_COMPRESSED_FLAG.
70  */
71 #define	NL_DOMAIN_NB_OEM_FLAG		0x00000001
72 #define	NL_COMPUTER_NB_OEM_FLAG		0x00000002
73 #define	NL_DOMAIN_DNS_COMPRESSED_FLAG	0x00000004
74 #define	NL_HOST_DNS_COMPRESSED_FLAG	0x00000008
75 #define	NL_COMPUTER_NB_COMPRESSED_FLAG	0x00000010
76 
77 #define	NL_DOMAIN_FLAGS			\
78 	(NL_DOMAIN_NB_OEM_FLAG|NL_DOMAIN_DNS_COMPRESSED_FLAG)
79 #define	NL_COMPUTER_FLAGS		\
80 	(NL_COMPUTER_NB_OEM_FLAG|		\
81 	NL_HOST_DNS_COMPRESSED_FLAG|		\
82 	NL_COMPUTER_NB_COMPRESSED_FLAG)
83 
84 #define	MD_DIGEST_LEN 16
85 
86 /* These structures are OPAQUE at the RPC level - not marshalled. */
87 typedef struct nl_auth_message {
88 	uint32_t nam_type;
89 	uint32_t nam_flags;
90 	uchar_t nam_str[1];
91 } nl_auth_message_t;
92 
93 /*
94  * The size of this structure is used for space accounting.
95  * The confounder is not present on the wire unless confidentiality
96  * has been negotiated. When generating a token, we'll adjust space accounting
97  * based on whether the confounder is present.
98  */
99 #define	NL_AUTH_CONFOUNDER_SIZE	8
100 typedef struct nl_auth_sig {
101 	uint16_t nas_sig_alg;
102 	uint16_t nas_seal_alg;
103 	uint16_t nas_pad;
104 	uint16_t nas_flags;
105 	uchar_t nas_seqnum[8];
106 	uchar_t nas_sig[8];
107 	uchar_t nas_confounder[NL_AUTH_CONFOUNDER_SIZE]; /* Sealing only */
108 } nl_auth_sig_t;
109 
110 #define	NL_AUTH_SIG_SIGN_SIZE	24
111 #define	NL_AUTH_SIG_SEAL_SIZE	32
112 
113 void
114 netr_show_msg(nl_auth_message_t *nam, ndr_stream_t *nds)
115 {
116 	ndo_printf(nds, NULL, "nl_auth_message: type=0x%x flags=0x%x");
117 }
118 
119 void
120 netr_show_sig(nl_auth_sig_t *nas, ndr_stream_t *nds)
121 {
122 	ndo_printf(nds, NULL, "nl_auth_sig: SignatureAlg=0x%x SealAlg=0x%x "
123 	    "pad=0x%x flags=0x%x SequenceNumber=%llu Signature=0x%x "
124 	    "Confounder=0x%x",
125 	    nas->nas_sig_alg, nas->nas_seal_alg, nas->nas_pad,
126 	    nas->nas_flags, *(uint64_t *)nas->nas_seqnum,
127 	    *(uint64_t *)nas->nas_sig, *(uint64_t *)nas->nas_confounder);
128 }
129 
130 /*
131  * NETLOGON SSP gss_init_sec_context equivalent
132  * [MS-NRPC] 3.3.4.1.1 "Generating an Initial NL_AUTH_MESSAGE"
133  *
134  * We need to encode at least one Computer name and at least one
135  * Domain name. The server uses this to find the Session Key
136  * negotiated earlier between this client and server.
137  *
138  * We attempt to provide NL_DOMAIN_NB_OEM_FLAG, NL_COMPUTER_NB_OEM_FLAG,
139  * NL_DOMAIN_DNS_COMPRESSED_FLAG, and NL_COMPUTER_NB_COMPRESSED_FLAG.
140  *
141  * See the above comments for how these are encoded.
142  */
143 int
144 netr_ssp_init(void *arg, ndr_xa_t *mxa)
145 {
146 	netr_info_t *auth = arg;
147 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
148 	nl_auth_message_t *nam;
149 	size_t domain_len, comp_len, len;
150 	int slen;
151 	uchar_t *dnptrs[3], **dnlastptr;
152 
153 	domain_len = smb_sbequiv_strlen(auth->nb_domain);
154 	comp_len = smb_sbequiv_strlen(auth->hostname);
155 
156 	/*
157 	 * Need to allocate length for two OEM_STRINGs + NULL bytes, plus space
158 	 * sufficient for two NULL-terminated compressed UTF-8 strings.
159 	 * For the UTF-8 strings, use 2*len as a heuristic.
160 	 */
161 	len = domain_len + 1 + comp_len + 1 +
162 	    strlen(auth->hostname) * 2 + strlen(auth->fqdn_domain) * 2;
163 
164 	hdr->auth_length = 0;
165 
166 	nam = NDR_MALLOC(mxa, len);
167 	if (nam == NULL)
168 		return (NDR_DRC_FAULT_SEC_OUT_OF_MEMORY);
169 
170 	nam->nam_type = NL_AUTH_REQUEST;
171 	nam->nam_flags = 0;
172 
173 	if (domain_len != -1) {
174 		slen = smb_mbstooem(nam->nam_str, auth->nb_domain, domain_len);
175 		if (slen >= 0) {
176 			hdr->auth_length += slen + 1;
177 			nam->nam_str[hdr->auth_length - 1] = '\0';
178 			nam->nam_flags |= NL_DOMAIN_NB_OEM_FLAG;
179 		}
180 	}
181 
182 	if (comp_len != -1) {
183 		slen = smb_mbstooem(nam->nam_str + hdr->auth_length,
184 		    auth->hostname, comp_len);
185 		if (slen >= 0) {
186 			hdr->auth_length += slen + 1;
187 			nam->nam_str[hdr->auth_length - 1] = '\0';
188 			nam->nam_flags |= NL_COMPUTER_NB_OEM_FLAG;
189 		}
190 	}
191 
192 	dnptrs[0] = NULL;
193 	dnlastptr = &dnptrs[sizeof (dnptrs) / sizeof (dnptrs[0])];
194 
195 	slen = dn_comp(auth->fqdn_domain, nam->nam_str + hdr->auth_length,
196 	    len - hdr->auth_length, dnptrs, dnlastptr);
197 
198 	if (slen >= 0) {
199 		hdr->auth_length += slen;
200 		nam->nam_str[hdr->auth_length] = '\0';
201 		nam->nam_flags |= NL_DOMAIN_DNS_COMPRESSED_FLAG;
202 	}
203 
204 	slen = dn_comp(auth->hostname, nam->nam_str + hdr->auth_length,
205 	    len - hdr->auth_length, dnptrs, dnlastptr);
206 	if (slen >= 0) {
207 		hdr->auth_length += slen;
208 		nam->nam_str[hdr->auth_length] = '\0';
209 		nam->nam_flags |= NL_COMPUTER_NB_COMPRESSED_FLAG;
210 	}
211 
212 	/* We must provide at least one Domain Name and Computer Name */
213 	if ((nam->nam_flags & NL_DOMAIN_FLAGS) == 0 ||
214 	    (nam->nam_flags & NL_COMPUTER_FLAGS) == 0)
215 		return (NDR_DRC_FAULT_SEC_ENCODE_FAILED);
216 
217 	mxa->send_auth.auth_value = (void *)nam;
218 	hdr->auth_length += sizeof (nam->nam_flags) + sizeof (nam->nam_type);
219 
220 	return (0);
221 }
222 
223 /*
224  * NETLOGON SSP response-side gss_init_sec_context equivalent
225  * [MS-NRPC] 3.3.4.1.4 "Receiving a Return NL_AUTH_MESSAGE"
226  */
227 int
228 netr_ssp_recv(void *arg, ndr_xa_t *mxa)
229 {
230 	netr_info_t *auth = arg;
231 	ndr_common_header_t *ahdr = &mxa->recv_hdr.common_hdr;
232 	ndr_sec_t *ack_secp = &mxa->recv_auth;
233 	nl_auth_message_t *nam;
234 	int rc;
235 
236 	nam = (nl_auth_message_t *)ack_secp->auth_value;
237 
238 	/* We only need to verify the length ("at least 12") and the type */
239 	if (ahdr->auth_length < 12) {
240 		rc = NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID;
241 		goto errout;
242 	}
243 	if (nam->nam_type != NL_AUTH_RESPONSE) {
244 		rc = NDR_DRC_FAULT_SEC_META_INVALID;
245 		goto errout;
246 	}
247 	auth->clh_seqnum = 0;
248 
249 	return (NDR_DRC_OK);
250 
251 errout:
252 	netr_show_msg(nam, &mxa->recv_nds);
253 	return (rc);
254 }
255 
256 /* returns byte N of seqnum */
257 #define	CLS_BYTE(n, seqnum) ((seqnum >> (8 * (n))) & 0xff)
258 
259 /*
260  * Perform key derivation using HMAC-MD5, as specified in MS-NRPC.
261  * This generates an intermediate key using HMAC-MD5 over zeroes using
262  * the input key, then generates the final key using HMAC-MD5 over some
263  * caller-specified data using the intermediate key.
264  * See callers for spec references (there are multiple!).
265  */
266 int
267 netr_ssp_derive_key(uchar_t *input_key, uint_t key_len,
268     uchar_t *data, uint_t data_len, uchar_t *out_key)
269 {
270 	int rc;
271 	uint32_t zeroes = 0;
272 
273 	rc = smb_auth_hmac_md5((uchar_t *)&zeroes, 4,
274 	    input_key, key_len,
275 	    out_key);
276 
277 	if (rc == 0)
278 		rc = smb_auth_hmac_md5(data, data_len,
279 		    out_key, MD_DIGEST_LEN,
280 		    out_key);
281 
282 	return (rc);
283 }
284 /*
285  * [MS-NRPC] 3.3.4.2.1 "Generating a Client Netlogon Signature Token"
286  *
287  * Set up the metadata, encrypt and increment the SequenceNumber,
288  * and sign the PDU body. If a confounder is provided, encrypt it and
289  * the PDU body.
290  * Encryption happens here because the RC4 key is derived from data
291  * generated locally for signing/verification.
292  */
293 int
294 netr_ssp_make_token(netr_info_t *auth, ndr_stream_t *nds, nl_auth_sig_t *nas,
295     uchar_t *confounder)
296 {
297 	uint32_t zeroes = 0;
298 	MD5_CTX md5h;
299 	BYTE local_sig[MD_DIGEST_LEN];
300 	BYTE enc_key[MD_DIGEST_LEN];
301 	uchar_t *pdu_body = nds->pdu_base_addr + nds->pdu_body_offset;
302 
303 	/*
304 	 * SignatureAlgorithm is first byte 0x77, second byte 00 for HMAC-MD5
305 	 * or 0x13, 0x00 for AES-HMAC-SHA256.
306 	 *
307 	 * SealAlgorithm is first byte 0x7A, second byte 00 for RC4
308 	 * or 0x1A, 0x00 for AES-CFB8, or 0xffff for No Sealing.
309 	 *
310 	 * Pad is always 0xffff, and flags is always 0x0000.
311 	 *
312 	 * SequenceNumber is a computed, encrypted, 64-bit number.
313 	 *
314 	 * Each of these is always encoded in little-endian order.
315 	 */
316 	nas->nas_sig_alg = 0x0077;
317 	if (confounder != NULL)
318 		nas->nas_seal_alg = 0x007A;
319 	else
320 		nas->nas_seal_alg = 0xffff;
321 	nas->nas_pad = 0xffff;
322 	nas->nas_flags = 0;
323 
324 	/*
325 	 * Calculate the SequenceNumber.
326 	 * Note that byte 4 gets modified, as per the spec -
327 	 * It's the only byte that is not just set to some other byte.
328 	 */
329 	nas->nas_seqnum[0] = CLS_BYTE(3, auth->clh_seqnum);
330 	nas->nas_seqnum[1] = CLS_BYTE(2, auth->clh_seqnum);
331 	nas->nas_seqnum[2] = CLS_BYTE(1, auth->clh_seqnum);
332 	nas->nas_seqnum[3] = CLS_BYTE(0, auth->clh_seqnum);
333 	nas->nas_seqnum[4] = CLS_BYTE(7, auth->clh_seqnum) | 0x80;
334 	nas->nas_seqnum[5] = CLS_BYTE(6, auth->clh_seqnum);
335 	nas->nas_seqnum[6] = CLS_BYTE(5, auth->clh_seqnum);
336 	nas->nas_seqnum[7] = CLS_BYTE(4, auth->clh_seqnum);
337 
338 	auth->clh_seqnum++;
339 
340 	/*
341 	 * The HMAC-MD5 signature is computed as follows:
342 	 * First 8 bytes of
343 	 * HMAC_MD5(
344 	 *	MD5(0x00000000 | sig_alg | seal_alg | pad | flags |
345 	 *		<confounder if present> | PDU body),
346 	 *	session_key)
347 	 */
348 	MD5Init(&md5h);
349 	MD5Update(&md5h, (uchar_t *)&zeroes, 4);
350 	MD5Update(&md5h, (uchar_t *)nas, 8);
351 	if (confounder != NULL)
352 		MD5Update(&md5h, confounder, NL_AUTH_CONFOUNDER_SIZE);
353 	MD5Update(&md5h, pdu_body, nds->pdu_body_size);
354 
355 	MD5Final(local_sig, &md5h);
356 	if (smb_auth_hmac_md5(local_sig, sizeof (local_sig),
357 	    auth->session_key.key, auth->session_key.len,
358 	    local_sig) != 0)
359 		return (NDR_DRC_FAULT_SEC_SSP_FAILED);
360 
361 	bcopy(local_sig, nas->nas_sig, 8);
362 
363 	/*
364 	 * [MS-NRPC] 3.3.4.2.1 "Generating a Client Netlogon Signature Token"
365 	 * Encrypt the Confounder and PDU Body.
366 	 * For RC4 Encryption, the EncryptionKey is computed as follows:
367 	 * XorKey = (XOR each byte of session_key with 0xf0)
368 	 * HMAC_MD5(local_seqnum, HMAC_MD5(0x00000000, xor_key))
369 	 *
370 	 * Step 8 says "the Confounder field and the data MUST be encrypted,
371 	 * in that order, using the same encryption algorithm". It also says
372 	 * "The server MUST initialize RC4 only once, before encrypting the
373 	 * Confounder field".
374 	 * However, that's a lie! They're encrypted independently for RC4.
375 	 */
376 	if (confounder != NULL) {
377 		int rc;
378 
379 		rc = netr_ssp_derive_key(
380 		    auth->rpc_seal_key.key, auth->rpc_seal_key.len,
381 		    (uchar_t *)nas->nas_seqnum, sizeof (nas->nas_seqnum),
382 		    enc_key);
383 
384 		if (rc != 0)
385 			goto errout;
386 
387 		if (smb_auth_RC4(nas->nas_confounder,
388 		    sizeof (nas->nas_confounder),
389 		    enc_key, sizeof (enc_key),
390 		    confounder, NL_AUTH_CONFOUNDER_SIZE) != 0)
391 			goto errout;
392 
393 		if (smb_auth_RC4(pdu_body, nds->pdu_body_size,
394 		    enc_key, sizeof (enc_key),
395 		    pdu_body, nds->pdu_body_size) != 0)
396 			goto errout;
397 	}
398 
399 	/*
400 	 * Encrypt the SequenceNumber.
401 	 * For RC4 Encryption, the EncryptionKey is computed as follows:
402 	 * HMAC_MD5(signature, HMAC_MD5(0x00000000, session_key))
403 	 */
404 	if (netr_ssp_derive_key(auth->session_key.key, auth->session_key.len,
405 	    (uchar_t *)nas->nas_sig, sizeof (nas->nas_sig), enc_key) != 0)
406 		goto errout;
407 
408 	if (smb_auth_RC4(nas->nas_seqnum, sizeof (nas->nas_seqnum),
409 	    enc_key, sizeof (enc_key),
410 	    nas->nas_seqnum, sizeof (nas->nas_seqnum)) != 0)
411 		goto errout;
412 
413 	explicit_bzero(enc_key, sizeof (enc_key));
414 	return (NDR_DRC_OK);
415 
416 errout:
417 	explicit_bzero(enc_key, sizeof (enc_key));
418 	return (NDR_DRC_FAULT_SEC_SSP_FAILED);
419 }
420 
421 /*
422  * [MS-NRPC] 3.3.4.2.4 "Receiving a Server Netlogon Signature Token"
423  *
424  * Verify the metadata; decrypt, verify, and increment the SequenceNumber;
425  * and validate the PDU body against the provided signature.
426  * If the confounder is present, decrypt it and the PDU body. This is done here
427  * because the key relies on local_seqnum ("CopySeqNumber"), which can only be
428  * retrieved here.
429  */
430 int
431 netr_ssp_check_token(netr_info_t *auth, ndr_stream_t *nds, nl_auth_sig_t *nas,
432     boolean_t verify_resp, boolean_t has_confounder)
433 {
434 	uint32_t zeroes = 0;
435 	MD5_CTX md5h;
436 	BYTE local_sig[MD_DIGEST_LEN];
437 	BYTE dec_key[MD_DIGEST_LEN];
438 	BYTE local_seqnum[8];
439 	uchar_t confounder[NL_AUTH_CONFOUNDER_SIZE];
440 	uchar_t *pdu_body = nds->pdu_base_addr + nds->pdu_body_offset;
441 	int rc;
442 	boolean_t seqnum_bumped = B_FALSE;
443 
444 	/*
445 	 * Verify SignatureAlgorithm, SealAlgorithm, and Pad are as expected.
446 	 * These follow the same values as in the Client Signature.
447 	 */
448 	if (nas->nas_sig_alg != 0x0077 ||
449 	    nas->nas_seal_alg != (has_confounder ? 0x007A : 0xffff) ||
450 	    nas->nas_pad != 0xffff) {
451 		rc = NDR_DRC_FAULT_SEC_META_INVALID;
452 		goto errout;
453 	}
454 
455 	/* Decrypt the SequenceNumber. This is done the same as the Client. */
456 	if (netr_ssp_derive_key(auth->session_key.key, auth->session_key.len,
457 	    (uchar_t *)nas->nas_sig, sizeof (nas->nas_sig), dec_key) != 0) {
458 		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
459 		goto errout;
460 	}
461 	if (smb_auth_RC4(nas->nas_seqnum, sizeof (nas->nas_seqnum),
462 	    dec_key, sizeof (dec_key),
463 	    nas->nas_seqnum, sizeof (nas->nas_seqnum)) != 0) {
464 		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
465 		goto errout;
466 	}
467 
468 	/*
469 	 * Calculate a local version of the SequenceNumber.
470 	 * Note that byte 4 does NOT get modified, unlike the client.
471 	 */
472 	local_seqnum[0] = CLS_BYTE(3, auth->clh_seqnum);
473 	local_seqnum[1] = CLS_BYTE(2, auth->clh_seqnum);
474 	local_seqnum[2] = CLS_BYTE(1, auth->clh_seqnum);
475 	local_seqnum[3] = CLS_BYTE(0, auth->clh_seqnum);
476 	local_seqnum[4] = CLS_BYTE(7, auth->clh_seqnum);
477 	local_seqnum[5] = CLS_BYTE(6, auth->clh_seqnum);
478 	local_seqnum[6] = CLS_BYTE(5, auth->clh_seqnum);
479 	local_seqnum[7] = CLS_BYTE(4, auth->clh_seqnum);
480 
481 	/* If the SequenceNumbers don't match, this is out of order - drop it */
482 	if (bcmp(local_seqnum, nas->nas_seqnum, sizeof (local_seqnum)) != 0) {
483 		ndo_printf(nds, NULL, "CalculatedSeqnum: %llu "
484 		    "DecryptedSeqnum: %llu",
485 		    *(uint64_t *)local_seqnum, *(uint64_t *)nas->nas_seqnum);
486 		rc = NDR_DRC_FAULT_SEC_SEQNUM_INVALID;
487 		goto errout;
488 	}
489 
490 	auth->clh_seqnum++;
491 	seqnum_bumped = B_TRUE;
492 
493 	/*
494 	 * Decrypt the Confounder and PDU Body.
495 	 * For RC4 Encryption, the EncryptionKey is computed as follows:
496 	 * XorKey = (XOR each byte of session_key with 0xf0)
497 	 * HMAC_MD5(local_seqnum, HMAC_MD5(0x00000000, xor_key))
498 	 */
499 	if (has_confounder) {
500 		if (netr_ssp_derive_key(
501 		    auth->rpc_seal_key.key, auth->rpc_seal_key.len,
502 		    (uchar_t *)local_seqnum, sizeof (local_seqnum),
503 		    dec_key) != 0) {
504 			rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
505 			goto errout;
506 		}
507 
508 		if (smb_auth_RC4(confounder, NL_AUTH_CONFOUNDER_SIZE,
509 		    dec_key, sizeof (dec_key),
510 		    nas->nas_confounder, sizeof (nas->nas_confounder)) != 0) {
511 			rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
512 			goto errout;
513 		}
514 		if (smb_auth_RC4(pdu_body, nds->pdu_body_size,
515 		    dec_key, sizeof (dec_key),
516 		    pdu_body, nds->pdu_body_size) != 0) {
517 			rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
518 			goto errout;
519 		}
520 	}
521 
522 	/*
523 	 * Calculate the signature.
524 	 * This is done the same as the Client.
525 	 */
526 	MD5Init(&md5h);
527 	MD5Update(&md5h, (uchar_t *)&zeroes, 4);
528 	MD5Update(&md5h, (uchar_t *)nas, 8);
529 	if (has_confounder)
530 		MD5Update(&md5h, confounder, NL_AUTH_CONFOUNDER_SIZE);
531 	MD5Update(&md5h, pdu_body, nds->pdu_body_size);
532 	MD5Final(local_sig, &md5h);
533 	if (smb_auth_hmac_md5(local_sig, sizeof (local_sig),
534 	    auth->session_key.key, auth->session_key.len,
535 	    local_sig) != 0) {
536 		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
537 		goto errout;
538 	}
539 
540 	/* If the first 8 bytes don't match, drop it */
541 	if (bcmp(local_sig, nas->nas_sig, 8) != 0) {
542 		ndo_printf(nds, NULL, "CalculatedSig: %llu "
543 		    "PacketSig: %llu",
544 		    *(uint64_t *)local_sig, *(uint64_t *)nas->nas_sig);
545 		rc = NDR_DRC_FAULT_SEC_SIG_INVALID;
546 		goto errout;
547 	}
548 
549 	explicit_bzero(dec_key, sizeof (dec_key));
550 	return (NDR_DRC_OK);
551 
552 errout:
553 	netr_show_sig(nas, nds);
554 
555 	explicit_bzero(dec_key, sizeof (dec_key));
556 	if (!verify_resp) {
557 		if (!seqnum_bumped)
558 			auth->clh_seqnum++;
559 		return (NDR_DRC_OK);
560 	}
561 
562 	return (rc);
563 }
564 
565 /*
566  * NETLOGON SSP gss_MICEx equivalent.
567  * Note that auth_length does NOT include the length of the confounder.
568  */
569 int
570 netr_ssp_sign(void *arg, ndr_xa_t *mxa)
571 {
572 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
573 	nl_auth_sig_t *nas;
574 
575 	hdr->auth_length = NL_AUTH_SIG_SIGN_SIZE;
576 	CTASSERT(NL_AUTH_SIG_SIGN_SIZE ==
577 	    (offsetof(nl_auth_sig_t, nas_sig) + sizeof (nas->nas_sig)));
578 	nas = NDR_MALLOC(mxa, sizeof (*nas));
579 	if (nas == NULL)
580 		return (NDR_DRC_FAULT_SEC_OUT_OF_MEMORY);
581 
582 	mxa->send_auth.auth_value = (void *)nas;
583 
584 	return (netr_ssp_make_token(arg, &mxa->send_nds, nas, NULL));
585 }
586 
587 /*
588  * NETLOGON SSP gss_VerifyMICEx equivalent.
589  */
590 int
591 netr_ssp_verify(void *arg, ndr_xa_t *mxa, boolean_t verify_resp)
592 {
593 	ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr;
594 	ndr_sec_t *secp = &mxa->recv_auth;
595 	nl_auth_sig_t *nas = (nl_auth_sig_t *)secp->auth_value;
596 
597 	if (hdr->auth_length < NL_AUTH_SIG_SIGN_SIZE)
598 		return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID);
599 
600 	return (netr_ssp_check_token(arg, &mxa->recv_nds, nas, verify_resp,
601 	    B_FALSE));
602 }
603 
604 /*
605  * NETLOGON SSP gss_WrapEx equivalent.
606  * Generate a cryptographically random confounder before signing/sealing.
607  * It is included in auth_length here.
608  */
609 int
610 netr_ssp_encrypt(void *arg, ndr_xa_t *mxa)
611 {
612 	uchar_t confounder[NL_AUTH_CONFOUNDER_SIZE];
613 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
614 	nl_auth_sig_t *nas;
615 
616 	hdr->auth_length = NL_AUTH_SIG_SEAL_SIZE;
617 	CTASSERT(NL_AUTH_SIG_SEAL_SIZE == sizeof (*nas));
618 	nas = NDR_MALLOC(mxa, sizeof (*nas));
619 	if (nas == NULL)
620 		return (NDR_DRC_FAULT_SEC_OUT_OF_MEMORY);
621 
622 	mxa->send_auth.auth_value = (void *)nas;
623 
624 	arc4random_buf(confounder, NL_AUTH_CONFOUNDER_SIZE);
625 	return (netr_ssp_make_token(arg, &mxa->send_nds, nas, confounder));
626 }
627 
628 /*
629  * NETLOGON SSP gss_UnwrapEx equivalent.
630  * Note that the Confounder is only used for calculating the signature.
631  */
632 int
633 netr_ssp_decrypt(void *arg, ndr_xa_t *mxa, boolean_t verify_resp)
634 {
635 	ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr;
636 	ndr_sec_t *secp = &mxa->recv_auth;
637 	nl_auth_sig_t *nas = (nl_auth_sig_t *)secp->auth_value;
638 
639 	if (hdr->auth_length < NL_AUTH_SIG_SEAL_SIZE)
640 		return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID);
641 
642 	return (netr_ssp_check_token(arg, &mxa->recv_nds, nas, verify_resp,
643 	    B_TRUE));
644 }
645 
646 extern struct netr_info netr_global_info;
647 
648 ndr_auth_ctx_t netr_ssp_ctx = {
649 	.auth_ops = {
650 		.nao_init = netr_ssp_init,
651 		.nao_recv = netr_ssp_recv,
652 		.nao_sign = netr_ssp_sign,
653 		.nao_verify = netr_ssp_verify,
654 		.nao_encrypt = netr_ssp_encrypt,
655 		.nao_decrypt = netr_ssp_decrypt
656 	},
657 	.auth_ctx = &netr_global_info,
658 	.auth_context_id = 0,
659 	.auth_type = NDR_C_AUTHN_GSS_NETLOGON,
660 	.auth_level = NDR_C_AUTHN_LEVEL_PKT_PRIVACY,
661 	.auth_verify_resp = B_TRUE
662 };
663