xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/netr_ssp.c (revision 3cac7b0d73edf3f2674ad0f64d1fff3d2e59ae8c)
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  */
15 
16 #include <sys/md5.h>
17 #include <strings.h>
18 #include <stdio.h>
19 #include <smbsrv/netrauth.h>
20 #include <smbsrv/string.h>
21 #include <smbsrv/libsmb.h>
22 #include <libmlsvc.h>
23 #include <resolv.h>
24 
25 /*
26  * NETLOGON SSP for "Secure RPC" works as follows:
27  * 1. The client binds to the DC without RPC-level authentication.
28  * 2. The client and server negotiate a Session Key using a client
29  * and server challenge, plus a shared secret (the machine password).
30  * This happens via NetrServerReqChallenge and NetrServerAuthenticate.
31  * The key is bound to a particular Computer/Server Name pair.
32  * 3. The client then establishes a new bind (or alters its existing one),
33  * this time requesting the NETLOGON provider for RPC-level authentication.
34  * The server uses the Computer and Domain names provided in the
35  * authentication token in the bind request in order to find
36  * the previously-negotiated Session Key (and rejects the bind if none
37  * exists).
38  * 4. The client and server then use this Session Key to provide
39  * integrity and/or confidentiality to future NETLOGON RPC messages.
40  *
41  * The functions in this file implement the NETLOGON SSP, as defined in
42  * [MS-NRPC] 3.3 "Netlogon as a Security Support Provider".
43  *
44  * Session Key negotiation is implemented in netr_auth.c.
45  * It is the same key that is used for generating NETLOGON credentials.
46  */
47 
48 enum nl_token_type {
49 	NL_AUTH_REQUEST = 0x00000000,
50 	NL_AUTH_RESPONSE = 0x00000001
51 };
52 
53 /*
54  * DOMAIN = domain name
55  * COMPUTER = client computer name
56  * HOST = client host name
57  *
58  * NB = NetBios format
59  * DNS = FQDN
60  *
61  * OEM = OEM_STRING
62  * COMPRESSED = Compressed UTF-8 string
63  *
64  * Each of these is NULL-terminated, and delinated by such.
65  * They are always found in this order, when specified.
66  *
67  * We currently use everything but NL_HOST_DNS_COMPRESSED_FLAG.
68  */
69 #define	NL_DOMAIN_NB_OEM_FLAG		0x00000001
70 #define	NL_COMPUTER_NB_OEM_FLAG		0x00000002
71 #define	NL_DOMAIN_DNS_COMPRESSED_FLAG	0x00000004
72 #define	NL_HOST_DNS_COMPRESSED_FLAG	0x00000008
73 #define	NL_COMPUTER_NB_COMPRESSED_FLAG	0x00000010
74 
75 #define	NL_DOMAIN_FLAGS			\
76 	(NL_DOMAIN_NB_OEM_FLAG|NL_DOMAIN_DNS_COMPRESSED_FLAG)
77 #define	NL_COMPUTER_FLAGS		\
78 	(NL_COMPUTER_NB_OEM_FLAG|		\
79 	NL_HOST_DNS_COMPRESSED_FLAG|		\
80 	NL_COMPUTER_NB_COMPRESSED_FLAG)
81 
82 #define	MD_DIGEST_LEN 16
83 
84 /* These structures are OPAQUE at the RPC level - not marshalled. */
85 typedef struct nl_auth_message {
86 	uint32_t nam_type;
87 	uint32_t nam_flags;
88 	uchar_t nam_str[1];
89 } nl_auth_message_t;
90 
91 /*
92  * The size of this structure is used for space accounting.
93  * The confounder is not present on the wire unless confidentiality
94  * has been negotiated. If we ever support confidentiality,
95  * we'll need to adjust space accounting based on whether
96  * the confounder is needed.
97  */
98 typedef struct nl_auth_sig {
99 	uint16_t nas_sig_alg;
100 	uint16_t nas_seal_alg;
101 	uint16_t nas_pad;
102 	uint16_t nas_flags;
103 	uchar_t nas_seqnum[8];
104 	uchar_t nas_sig[8];
105 	/* uchar_t nas_confounder[8]; */ /* only for encryption */
106 } nl_auth_sig_t;
107 
108 void
109 netr_show_msg(nl_auth_message_t *nam, ndr_stream_t *nds)
110 {
111 	ndo_printf(nds, NULL, "nl_auth_message: type=0x%x flags=0x%x");
112 }
113 
114 void
115 netr_show_sig(nl_auth_sig_t *nas, ndr_stream_t *nds)
116 {
117 	ndo_printf(nds, NULL, "nl_auth_sig: SignatureAlg=0x%x SealAlg=0x%x "
118 	    "pad=0x%x flags=0x%x SequenceNumber=%llu Signature=0x%x",
119 	    nas->nas_sig_alg, nas->nas_seal_alg, nas->nas_pad,
120 	    nas->nas_flags, *(uint64_t *)nas->nas_seqnum,
121 	    *(uint64_t *)nas->nas_sig);
122 }
123 
124 /*
125  * NETLOGON SSP gss_init_sec_context equivalent
126  * [MS-RPCE] 3.3.4.1.1 "Generating an Initial NL_AUTH_MESSAGE"
127  *
128  * We need to encode at least one Computer name and at least one
129  * Domain name. The server uses this to find the Session Key
130  * negotiated earlier between this client and server.
131  *
132  * We attempt to provide NL_DOMAIN_NB_OEM_FLAG, NL_COMPUTER_NB_OEM_FLAG,
133  * NL_DOMAIN_DNS_COMPRESSED_FLAG, and NL_COMPUTER_NB_COMPRESSED_FLAG.
134  *
135  * See the above comments for how these are encoded.
136  */
137 int
138 netr_ssp_init(void *arg, ndr_xa_t *mxa)
139 {
140 	netr_info_t *auth = arg;
141 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
142 	nl_auth_message_t *nam;
143 	size_t domain_len, comp_len, len;
144 	int slen;
145 	uchar_t *dnptrs[3], **dnlastptr;
146 
147 	domain_len = smb_sbequiv_strlen(auth->nb_domain);
148 	comp_len = smb_sbequiv_strlen(auth->hostname);
149 
150 	/*
151 	 * Need to allocate length for two OEM_STRINGs + NULL bytes, plus space
152 	 * sufficient for two NULL-terminated compressed UTF-8 strings.
153 	 * For the UTF-8 strings, use 2*len as a heuristic.
154 	 */
155 	len = domain_len + 1 + comp_len + 1 +
156 	    strlen(auth->hostname) * 2 + strlen(auth->fqdn_domain) * 2;
157 
158 	hdr->auth_length = 0;
159 
160 	nam = NDR_MALLOC(mxa, len);
161 	if (nam == NULL)
162 		return (NDR_DRC_FAULT_SEC_OUT_OF_MEMORY);
163 
164 	nam->nam_type = NL_AUTH_REQUEST;
165 	nam->nam_flags = 0;
166 
167 	if (domain_len != -1) {
168 		slen = smb_mbstooem(nam->nam_str, auth->nb_domain, domain_len);
169 		if (slen >= 0) {
170 			hdr->auth_length += slen + 1;
171 			nam->nam_str[hdr->auth_length - 1] = '\0';
172 			nam->nam_flags |= NL_DOMAIN_NB_OEM_FLAG;
173 		}
174 	}
175 
176 	if (comp_len != -1) {
177 		slen = smb_mbstooem(nam->nam_str + hdr->auth_length,
178 		    auth->hostname, comp_len);
179 		if (slen >= 0) {
180 			hdr->auth_length += slen + 1;
181 			nam->nam_str[hdr->auth_length - 1] = '\0';
182 			nam->nam_flags |= NL_COMPUTER_NB_OEM_FLAG;
183 		}
184 	}
185 
186 	dnptrs[0] = NULL;
187 	dnlastptr = &dnptrs[sizeof (dnptrs) / sizeof (dnptrs[0])];
188 
189 	slen = dn_comp(auth->fqdn_domain, nam->nam_str + hdr->auth_length,
190 	    len - hdr->auth_length, dnptrs, dnlastptr);
191 
192 	if (slen >= 0) {
193 		hdr->auth_length += slen;
194 		nam->nam_str[hdr->auth_length] = '\0';
195 		nam->nam_flags |= NL_DOMAIN_DNS_COMPRESSED_FLAG;
196 	}
197 
198 	slen = dn_comp(auth->hostname, nam->nam_str + hdr->auth_length,
199 	    len - hdr->auth_length, dnptrs, dnlastptr);
200 	if (slen >= 0) {
201 		hdr->auth_length += slen;
202 		nam->nam_str[hdr->auth_length] = '\0';
203 		nam->nam_flags |= NL_COMPUTER_NB_COMPRESSED_FLAG;
204 	}
205 
206 	/* We must provide at least one Domain Name and Computer Name */
207 	if ((nam->nam_flags & NL_DOMAIN_FLAGS) == 0 ||
208 	    (nam->nam_flags & NL_COMPUTER_FLAGS) == 0)
209 		return (NDR_DRC_FAULT_SEC_ENCODE_FAILED);
210 
211 	mxa->send_auth.auth_value = (void *)nam;
212 	hdr->auth_length += sizeof (nam->nam_flags) + sizeof (nam->nam_type);
213 
214 	return (0);
215 }
216 
217 /*
218  * NETLOGON SSP response-side gss_init_sec_context equivalent
219  * [MS-RPCE] 3.3.4.1.4 "Receiving a Return NL_AUTH_MESSAGE"
220  */
221 int
222 netr_ssp_recv(void *arg, ndr_xa_t *mxa)
223 {
224 	netr_info_t *auth = arg;
225 	ndr_common_header_t *ahdr = &mxa->recv_hdr.common_hdr;
226 	ndr_sec_t *ack_secp = &mxa->recv_auth;
227 	nl_auth_message_t *nam;
228 	int rc;
229 
230 	nam = (nl_auth_message_t *)ack_secp->auth_value;
231 
232 	/* We only need to verify the length ("at least 12") and the type */
233 	if (ahdr->auth_length < 12) {
234 		rc = NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID;
235 		goto errout;
236 	}
237 	if (nam->nam_type != NL_AUTH_RESPONSE) {
238 		rc = NDR_DRC_FAULT_SEC_META_INVALID;
239 		goto errout;
240 	}
241 	auth->clh_seqnum = 0;
242 
243 	return (NDR_DRC_OK);
244 
245 errout:
246 	netr_show_msg(nam, &mxa->recv_nds);
247 	return (rc);
248 }
249 
250 /* returns byte N of seqnum */
251 #define	CLS_BYTE(n, seqnum) ((seqnum >> (8 * (n))) & 0xff)
252 
253 /*
254  * NETLOGON SSP gss_MICEx equivalent
255  * [MS-RPCE] 3.3.4.2.1 "Generating a Client Netlogon Signature Token"
256  *
257  * Set up the metadata, encrypt and increment the SequenceNumber,
258  * and sign the PDU body.
259  */
260 int
261 netr_ssp_sign(void *arg, ndr_xa_t *mxa)
262 {
263 	uint32_t zeroes = 0;
264 	netr_info_t *auth = arg;
265 	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
266 	ndr_stream_t *nds = &mxa->send_nds;
267 	nl_auth_sig_t *nas;
268 	MD5_CTX md5h;
269 	BYTE local_sig[MD_DIGEST_LEN];
270 	BYTE enc_key[MD_DIGEST_LEN];
271 
272 	hdr->auth_length = sizeof (nl_auth_sig_t);
273 
274 	nas = NDR_MALLOC(mxa, hdr->auth_length);
275 	if (nas == NULL)
276 		return (NDR_DRC_FAULT_SEC_OUT_OF_MEMORY);
277 
278 	/*
279 	 * SignatureAlgorithm is first byte 0x77, second byte 00 for HMAC-MD5
280 	 * or 0x13, 0x00 for AES-HMAC-SHA256.
281 	 *
282 	 * SealAlgorithm is first byte 0x7A, second byte 00 for RC4
283 	 * or 0x1A, 0x00 for AES-CFB8, or 0xffff for No Sealing.
284 	 *
285 	 * Pad is always 0xffff, and flags is always 0x0000.
286 	 *
287 	 * SequenceNumber is a computed, encrypted, 64-bit number.
288 	 *
289 	 * Each of these is always encoded in little-endian order.
290 	 */
291 	nas->nas_sig_alg = 0x0077;
292 	nas->nas_seal_alg = 0xffff;
293 	nas->nas_pad = 0xffff;
294 	nas->nas_flags = 0;
295 
296 	/*
297 	 * Calculate the SequenceNumber.
298 	 * Note that byte 4 gets modified, as per the spec -
299 	 * It's the only byte that is not just set to some other byte.
300 	 */
301 	nas->nas_seqnum[0] = CLS_BYTE(3, auth->clh_seqnum);
302 	nas->nas_seqnum[1] = CLS_BYTE(2, auth->clh_seqnum);
303 	nas->nas_seqnum[2] = CLS_BYTE(1, auth->clh_seqnum);
304 	nas->nas_seqnum[3] = CLS_BYTE(0, auth->clh_seqnum);
305 	nas->nas_seqnum[4] = CLS_BYTE(7, auth->clh_seqnum) | 0x80;
306 	nas->nas_seqnum[5] = CLS_BYTE(6, auth->clh_seqnum);
307 	nas->nas_seqnum[6] = CLS_BYTE(5, auth->clh_seqnum);
308 	nas->nas_seqnum[7] = CLS_BYTE(4, auth->clh_seqnum);
309 
310 	auth->clh_seqnum++;
311 
312 	/*
313 	 * The HMAC-MD5 signature is computed as follows:
314 	 * First 8 bytes of
315 	 * HMAC_MD5(
316 	 *	MD5(0x00000000 | sig_alg | seal_alg | pad | flags | PDU body),
317 	 *	session_key)
318 	 */
319 	MD5Init(&md5h);
320 	MD5Update(&md5h, (uchar_t *)&zeroes, 4);
321 	MD5Update(&md5h, (uchar_t *)nas, 8);
322 	MD5Update(&md5h,
323 	    (uchar_t *)nds->pdu_base_addr + nds->pdu_body_offset,
324 	    nds->pdu_body_size);
325 
326 	MD5Final(local_sig, &md5h);
327 	if (smb_auth_hmac_md5(local_sig, sizeof (local_sig),
328 	    auth->session_key.key, auth->session_key.len,
329 	    local_sig) != 0)
330 		return (NDR_DRC_FAULT_SEC_SSP_FAILED);
331 
332 	bcopy(local_sig, nas->nas_sig, 8);
333 
334 	/*
335 	 * Encrypt the SequenceNumber.
336 	 * For RC4 Encryption, the EncryptionKey is computed as follows:
337 	 * HMAC_MD5(signature, HMAC_MD5(0x00000000, session_key))
338 	 */
339 	if (smb_auth_hmac_md5((uchar_t *)&zeroes, 4,
340 	    auth->session_key.key, auth->session_key.len,
341 	    enc_key) != 0)
342 		return (NDR_DRC_FAULT_SEC_SSP_FAILED);
343 	if (smb_auth_hmac_md5((uchar_t *)nas->nas_sig, sizeof (nas->nas_sig),
344 	    enc_key, sizeof (enc_key),
345 	    enc_key) != 0)
346 		return (NDR_DRC_FAULT_SEC_SSP_FAILED);
347 
348 	if (smb_auth_RC4(nas->nas_seqnum, sizeof (nas->nas_seqnum),
349 	    enc_key, sizeof (enc_key),
350 	    nas->nas_seqnum, sizeof (nas->nas_seqnum)) != 0)
351 		return (NDR_DRC_FAULT_SEC_SSP_FAILED);
352 
353 	mxa->send_auth.auth_value = (void *)nas;
354 
355 	return (NDR_DRC_OK);
356 }
357 
358 /*
359  * NETLOGON SSP gss_VerifyMICEx equivalent
360  * [MS-RPCE] 3.3.4.2.4 "Receiving a Server Netlogon Signature Token"
361  *
362  * Verify the metadata, decrypt, verify, and increment the SequenceNumber,
363  * and validate the PDU body against the provided signature.
364  */
365 int
366 netr_ssp_verify(void *arg, ndr_xa_t *mxa, boolean_t verify_resp)
367 {
368 	uint32_t zeroes = 0;
369 	netr_info_t *auth = arg;
370 	ndr_sec_t *secp = &mxa->recv_auth;
371 	ndr_stream_t *nds = &mxa->recv_nds;
372 	nl_auth_sig_t *nas;
373 	MD5_CTX md5h;
374 	BYTE local_sig[MD_DIGEST_LEN];
375 	BYTE dec_key[MD_DIGEST_LEN];
376 	BYTE local_seqnum[8];
377 	int rc;
378 	boolean_t seqnum_bumped = B_FALSE;
379 
380 	nas = (nl_auth_sig_t *)secp->auth_value;
381 
382 	/*
383 	 * Verify SignatureAlgorithm, SealAlgorithm, and Pad are as expected.
384 	 * These follow the same values as in the Client Signature.
385 	 */
386 	if (nas->nas_sig_alg != 0x0077 ||
387 	    nas->nas_seal_alg != 0xffff ||
388 	    nas->nas_pad != 0xffff) {
389 		rc = NDR_DRC_FAULT_SEC_META_INVALID;
390 		goto errout;
391 	}
392 
393 	/* Decrypt the SequenceNumber. This is done the same as the Client. */
394 	if (smb_auth_hmac_md5((uchar_t *)&zeroes, 4,
395 	    auth->session_key.key, auth->session_key.len,
396 	    dec_key) != 0) {
397 		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
398 		goto errout;
399 	}
400 	if (smb_auth_hmac_md5((uchar_t *)nas->nas_sig, sizeof (nas->nas_sig),
401 	    dec_key, sizeof (dec_key),
402 	    dec_key) != 0) {
403 		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
404 		goto errout;
405 	}
406 
407 	if (smb_auth_RC4(nas->nas_seqnum, sizeof (nas->nas_seqnum),
408 	    dec_key, sizeof (dec_key),
409 	    nas->nas_seqnum, sizeof (nas->nas_seqnum)) != 0) {
410 		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
411 		goto errout;
412 	}
413 
414 	/*
415 	 * Calculate a local version of the SequenceNumber.
416 	 * Note that byte 4 does NOT get modified, unlike the client.
417 	 */
418 	local_seqnum[0] = CLS_BYTE(3, auth->clh_seqnum);
419 	local_seqnum[1] = CLS_BYTE(2, auth->clh_seqnum);
420 	local_seqnum[2] = CLS_BYTE(1, auth->clh_seqnum);
421 	local_seqnum[3] = CLS_BYTE(0, auth->clh_seqnum);
422 	local_seqnum[4] = CLS_BYTE(7, auth->clh_seqnum);
423 	local_seqnum[5] = CLS_BYTE(6, auth->clh_seqnum);
424 	local_seqnum[6] = CLS_BYTE(5, auth->clh_seqnum);
425 	local_seqnum[7] = CLS_BYTE(4, auth->clh_seqnum);
426 
427 	/* If the SequenceNumbers don't match, this is out of order - drop it */
428 	if (bcmp(local_seqnum, nas->nas_seqnum, sizeof (local_seqnum)) != 0) {
429 		ndo_printf(nds, NULL, "CalculatedSeqnum: %llu "
430 		    "DecryptedSeqnum: %llu",
431 		    *(uint64_t *)local_seqnum, *(uint64_t *)nas->nas_seqnum);
432 		rc = NDR_DRC_FAULT_SEC_SEQNUM_INVALID;
433 		goto errout;
434 	}
435 
436 	auth->clh_seqnum++;
437 	seqnum_bumped = B_TRUE;
438 
439 	/*
440 	 * Calculate the signature.
441 	 * This is done the same as the Client.
442 	 */
443 	MD5Init(&md5h);
444 	MD5Update(&md5h, (uchar_t *)&zeroes, 4);
445 	MD5Update(&md5h, (uchar_t *)nas, 8);
446 	MD5Update(&md5h,
447 	    (uchar_t *)nds->pdu_base_addr + nds->pdu_body_offset,
448 	    nds->pdu_body_size);
449 	MD5Final(local_sig, &md5h);
450 	if (smb_auth_hmac_md5(local_sig, sizeof (local_sig),
451 	    auth->session_key.key, auth->session_key.len,
452 	    local_sig) != 0) {
453 		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
454 		goto errout;
455 	}
456 
457 	/* If the first 8 bytes don't match, drop it */
458 	if (bcmp(local_sig, nas->nas_sig, 8) != 0) {
459 		ndo_printf(nds, NULL, "CalculatedSig: %llu "
460 		    "PacketSig: %llu",
461 		    *(uint64_t *)local_sig, *(uint64_t *)nas->nas_sig);
462 		rc = NDR_DRC_FAULT_SEC_SIG_INVALID;
463 		goto errout;
464 	}
465 
466 	return (NDR_DRC_OK);
467 
468 errout:
469 	netr_show_sig(nas, &mxa->recv_nds);
470 
471 	if (!verify_resp) {
472 		if (!seqnum_bumped)
473 			auth->clh_seqnum++;
474 		return (NDR_DRC_OK);
475 	}
476 
477 	return (rc);
478 }
479 
480 extern struct netr_info netr_global_info;
481 
482 ndr_auth_ctx_t netr_ssp_ctx = {
483 	.auth_ops = {
484 		.nao_init = netr_ssp_init,
485 		.nao_recv = netr_ssp_recv,
486 		.nao_sign = netr_ssp_sign,
487 		.nao_verify = netr_ssp_verify
488 	},
489 	.auth_ctx = &netr_global_info,
490 	.auth_context_id = 0,
491 	.auth_type = NDR_C_AUTHN_GSS_NETLOGON,
492 	.auth_level = NDR_C_AUTHN_LEVEL_PKT_INTEGRITY,
493 	.auth_verify_resp = B_TRUE
494 };
495