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