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
netr_show_msg(nl_auth_message_t * nam,ndr_stream_t * nds)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
netr_show_sig(nl_auth_sig_t * nas,ndr_stream_t * nds)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
netr_ssp_init(void * arg,ndr_xa_t * mxa)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
netr_ssp_recv(void * arg,ndr_xa_t * mxa)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
netr_ssp_sign(void * arg,ndr_xa_t * mxa)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
netr_ssp_verify(void * arg,ndr_xa_t * mxa,boolean_t verify_resp)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