1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2021 Tintri by DDN, Inc. All rights reserved. 25 * Copyright 2023 RackTop Systems, Inc. 26 */ 27 28 /* 29 * NETR SamLogon and SamLogoff RPC client functions. 30 */ 31 32 #include <stdio.h> 33 #include <strings.h> 34 #include <stdlib.h> 35 #include <time.h> 36 #include <alloca.h> 37 #include <unistd.h> 38 #include <netdb.h> 39 #include <thread.h> 40 41 #include <libmlrpc/libmlrpc.h> 42 #include <smbsrv/libsmb.h> 43 #include <smbsrv/libmlsvc.h> 44 #include <smbsrv/ndl/netlogon.ndl> 45 #include <smbsrv/netrauth.h> 46 #include <smbsrv/smbinfo.h> 47 #include <smbsrv/smb_token.h> 48 #include <mlsvc.h> 49 50 uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *); 51 static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *, 52 smb_logon_t *, smb_token_t *); 53 static void netr_invalidate_chain(netr_info_t *); 54 static void netr_interactive_samlogon(netr_info_t *, smb_logon_t *, 55 struct netr_logon_info1 *); 56 static void netr_network_samlogon(ndr_heap_t *, netr_info_t *, 57 smb_logon_t *, struct netr_logon_info2 *); 58 static void netr_setup_identity(ndr_heap_t *, smb_logon_t *, 59 netr_logon_id_t *); 60 static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *, 61 smb_ids_t *); 62 static uint32_t netr_setup_krb5res_groups(struct krb5_validation_info *, 63 smb_ids_t *); 64 static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *, 65 smb_token_t *); 66 67 /* 68 * Shared with netr_auth.c 69 */ 70 extern netr_info_t netr_global_info; 71 72 static mutex_t netlogon_mutex; 73 static cond_t netlogon_cv; 74 static boolean_t netlogon_busy = B_FALSE; 75 static boolean_t netlogon_abort = B_FALSE; 76 77 /* 78 * Helper for Kerberos authentication 79 */ 80 uint32_t 81 smb_decode_krb5_pac(smb_token_t *token, char *data, uint_t len) 82 { 83 struct krb5_validation_info info; 84 ndr_buf_t *nbuf; 85 smb_sid_t *domsid; 86 uint32_t status = NT_STATUS_NO_MEMORY; 87 int rc; 88 89 bzero(&info, sizeof (info)); 90 91 /* Need to keep this until we're done with &info */ 92 nbuf = ndr_buf_init(&TYPEINFO(netr_interface)); 93 if (nbuf == NULL) 94 goto out; 95 96 rc = ndr_buf_decode(nbuf, NDR_PTYPE_PAC, 97 NETR_OPNUM_decode_krb5_pac, data, len, &info); 98 if (rc != NDR_DRC_OK) { 99 status = RPC_NT_PROTOCOL_ERROR; 100 goto out; 101 } 102 103 /* 104 * Copy the decoded info into the token, 105 * similar to netr_setup_token() 106 */ 107 domsid = (smb_sid_t *)info.info3.LogonDomainId; 108 109 token->tkn_user.i_sid = smb_sid_splice(domsid, 110 info.info3.UserId); 111 if (token->tkn_user.i_sid == NULL) 112 goto out; 113 114 token->tkn_primary_grp.i_sid = smb_sid_splice(domsid, 115 info.info3.PrimaryGroupId); 116 if (token->tkn_primary_grp.i_sid == NULL) 117 goto out; 118 119 if (info.info3.EffectiveName.str) { 120 token->tkn_account_name = 121 strdup((char *)info.info3.EffectiveName.str); 122 if (token->tkn_account_name == NULL) 123 goto out; 124 } 125 126 if (info.info3.LogonDomainName.str) { 127 token->tkn_domain_name = 128 strdup((char *)info.info3.LogonDomainName.str); 129 if (token->tkn_domain_name == NULL) 130 goto out; 131 } 132 133 status = netr_setup_domain_groups(&info.info3, &token->tkn_win_grps); 134 if (status != NT_STATUS_SUCCESS) 135 goto out; 136 137 if (info.rg_rid_cnt != 0) { 138 status = netr_setup_krb5res_groups(&info, &token->tkn_win_grps); 139 if (status != NT_STATUS_SUCCESS) 140 goto out; 141 } 142 143 status = netr_setup_token_wingrps(&info.info3, token); 144 145 out: 146 if (nbuf != NULL) 147 ndr_buf_fini(nbuf); 148 149 return (status); 150 } 151 152 /* 153 * Abort impending domain logon requests. 154 */ 155 void 156 smb_logon_abort(void) 157 { 158 (void) mutex_lock(&netlogon_mutex); 159 if (netlogon_busy && !netlogon_abort) 160 syslog(LOG_DEBUG, "logon abort"); 161 netlogon_abort = B_TRUE; 162 (void) cond_broadcast(&netlogon_cv); 163 (void) mutex_unlock(&netlogon_mutex); 164 } 165 166 /* 167 * This is the entry point for authenticating domain users. 168 * 169 * If we are not going to attempt to authenticate the user, 170 * this function must return without updating the status. 171 * 172 * If the user is successfully authenticated, we build an 173 * access token and the status will be NT_STATUS_SUCCESS. 174 * Otherwise, the token contents are invalid. 175 * 176 * This will retry a few times for errors indicating that the 177 * current DC might have gone off-line or become too busy etc. 178 * With such errors, smb_ddiscover_bad_dc is called and then 179 * the smb_domain_getinfo call here waits for new DC info. 180 */ 181 int smb_netr_logon_retries = 3; 182 void 183 smb_logon_domain(smb_logon_t *user_info, smb_token_t *token) 184 { 185 smb_domainex_t di; 186 uint32_t status; 187 int retries = smb_netr_logon_retries; 188 189 if (user_info->lg_secmode != SMB_SECMODE_DOMAIN) 190 return; 191 192 if (user_info->lg_domain_type == SMB_DOMAIN_LOCAL) 193 return; 194 195 while (--retries > 0) { 196 197 if (!smb_domain_getinfo(&di)) { 198 syslog(LOG_ERR, "logon DC getinfo failed"); 199 status = NT_STATUS_NO_LOGON_SERVERS; 200 goto out; 201 } 202 203 (void) mutex_lock(&netlogon_mutex); 204 while (netlogon_busy && !netlogon_abort) 205 (void) cond_wait(&netlogon_cv, &netlogon_mutex); 206 207 if (netlogon_abort) { 208 (void) mutex_unlock(&netlogon_mutex); 209 status = NT_STATUS_REQUEST_ABORTED; 210 goto out; 211 } 212 213 netlogon_busy = B_TRUE; 214 (void) mutex_unlock(&netlogon_mutex); 215 216 status = netlogon_logon(user_info, token, &di); 217 218 (void) mutex_lock(&netlogon_mutex); 219 netlogon_busy = B_FALSE; 220 if (netlogon_abort) 221 status = NT_STATUS_REQUEST_ABORTED; 222 (void) cond_signal(&netlogon_cv); 223 (void) mutex_unlock(&netlogon_mutex); 224 225 switch (status) { 226 case NT_STATUS_BAD_NETWORK_PATH: 227 case NT_STATUS_BAD_NETWORK_NAME: 228 case RPC_NT_SERVER_TOO_BUSY: 229 /* 230 * May retry with a new DC, or if we're 231 * out of retries, will return... 232 */ 233 status = NT_STATUS_NO_LOGON_SERVERS; 234 break; 235 default: 236 goto out; 237 } 238 } 239 240 out: 241 if (status != NT_STATUS_SUCCESS) 242 syslog(LOG_INFO, "logon[%s\\%s]: %s", user_info->lg_e_domain, 243 user_info->lg_e_username, xlate_nt_status(status)); 244 user_info->lg_status = status; 245 } 246 247 static uint32_t 248 netr_get_handle(char *server, char *domain, mlsvc_handle_t *netr_handle) 249 { 250 uint32_t status; 251 boolean_t did_renego = B_FALSE; 252 253 reauth: 254 if ((netr_global_info.flags & NETR_FLG_VALID) == 0 || 255 !smb_match_netlogon_seqnum()) { 256 /* 257 * This does netr_server_req_challenge() and 258 * netr_server_authenticate2(), updating the 259 * current netlogon sequence number. 260 */ 261 status = netlogon_auth(server, domain, NETR_FLG_NULL); 262 263 if (status != 0) { 264 syslog(LOG_ERR, "%s: auth failed (%s)", 265 __func__, xlate_nt_status(status)); 266 return (status); 267 } 268 269 netr_global_info.flags |= NETR_FLG_VALID; 270 } 271 272 /* 273 * This netr_open_secure call does the work to connect to the DC, 274 * get the IPC share, open the named pipe, RPC bind, etc. 275 */ 276 status = netr_open_secure(server, domain, netr_handle); 277 if (status != 0) { 278 /* 279 * This may have failed because the DC restarted. 280 * Re-negotiate once. 281 */ 282 if (!did_renego) { 283 did_renego = B_TRUE; 284 netr_invalidate_chain(&netr_global_info); 285 syslog(LOG_ERR, "%s: open failed (%s); " 286 "renegotiating...", 287 __func__, xlate_nt_status(status)); 288 goto reauth; 289 } 290 syslog(LOG_ERR, "%s: open failed (%s)", 291 __func__, xlate_nt_status(status)); 292 } 293 294 return (status); 295 } 296 297 /* 298 * Run a netr_server_samlogon call, dealing with the possible need to 299 * re-establish the NetLogon credential chain. If that fails, return 300 * NT_STATUS_DOMAIN_TRUST_INCONSISTENT indicating the machine account 301 * needs it's password reset (or whatever). Other errors are from the 302 * netr_server_samlogon() call including the many possibilities listed 303 * above that function. 304 */ 305 uint32_t 306 netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di) 307 { 308 char server[MAXHOSTNAMELEN]; 309 mlsvc_handle_t netr_handle; 310 uint32_t status; 311 boolean_t did_reauth = B_FALSE; 312 313 if (di->d_dci.dc_name[0] != '\0' && 314 (*netr_global_info.server != '\0')) { 315 (void) snprintf(server, sizeof (server), 316 "\\\\%s", di->d_dci.dc_name); 317 if (strncasecmp(netr_global_info.server, 318 server, strlen(server)) != 0) 319 netr_invalidate_chain(&netr_global_info); 320 } 321 322 reauth: 323 status = netr_get_handle(di->d_dci.dc_name, 324 di->d_primary.di_nbname, &netr_handle); 325 326 if (status != 0) { 327 syslog(LOG_ERR, "%s: failed to get handle (%s)", 328 __func__, xlate_nt_status(status)); 329 return (NT_STATUS_DOMAIN_TRUST_INCONSISTENT); 330 } 331 332 status = netr_server_samlogon(&netr_handle, 333 &netr_global_info, di->d_dci.dc_name, user_info, token); 334 335 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { 336 if (!did_reauth) { 337 /* Call netlogon_auth() again, just once. */ 338 (void) netr_close(&netr_handle); 339 did_reauth = B_TRUE; 340 goto reauth; 341 } 342 status = NT_STATUS_DOMAIN_TRUST_INCONSISTENT; 343 } 344 345 (void) netr_close(&netr_handle); 346 347 return (status); 348 } 349 350 /* 351 * Helper for mlsvc_netlogon 352 * 353 * Call netlogon_auth with appropriate locks etc. 354 * Serialize like smb_logon_domain does for 355 * netlogon_logon / netlogon_auth 356 */ 357 uint32_t 358 smb_netlogon_check(char *server, char *domain) 359 { 360 mlsvc_handle_t netr_handle; 361 uint32_t status; 362 363 (void) mutex_lock(&netlogon_mutex); 364 while (netlogon_busy) 365 (void) cond_wait(&netlogon_cv, &netlogon_mutex); 366 367 netlogon_busy = B_TRUE; 368 (void) mutex_unlock(&netlogon_mutex); 369 370 /* 371 * Like netlogon_logon(), but no netr_server_samlogon call. 372 * We're just making sure we can connect to the NETLOGON server. 373 */ 374 status = netr_get_handle(server, domain, &netr_handle); 375 if (status == 0) 376 (void) netr_close(&netr_handle); 377 else 378 syslog(LOG_ERR, "%s: failed to get handle (%s)", 379 __func__, xlate_nt_status(status)); 380 381 (void) mutex_lock(&netlogon_mutex); 382 netlogon_busy = B_FALSE; 383 (void) cond_signal(&netlogon_cv); 384 (void) mutex_unlock(&netlogon_mutex); 385 386 return (status); 387 } 388 389 static uint32_t 390 netr_setup_token(struct netr_validation_info3 *info3, smb_logon_t *user_info, 391 netr_info_t *netr_info, smb_token_t *token) 392 { 393 char *username, *domain; 394 unsigned char rc4key[SMBAUTH_SESSION_KEY_SZ]; 395 smb_sid_t *domsid; 396 uint32_t status; 397 char nbdomain[NETBIOS_NAME_SZ]; 398 399 domsid = (smb_sid_t *)info3->LogonDomainId; 400 401 token->tkn_user.i_sid = smb_sid_splice(domsid, info3->UserId); 402 if (token->tkn_user.i_sid == NULL) 403 return (NT_STATUS_NO_MEMORY); 404 405 token->tkn_primary_grp.i_sid = smb_sid_splice(domsid, 406 info3->PrimaryGroupId); 407 if (token->tkn_primary_grp.i_sid == NULL) 408 return (NT_STATUS_NO_MEMORY); 409 410 username = (info3->EffectiveName.str) 411 ? (char *)info3->EffectiveName.str : user_info->lg_e_username; 412 413 if (info3->LogonDomainName.str) { 414 domain = (char *)info3->LogonDomainName.str; 415 } else if (*user_info->lg_e_domain != '\0') { 416 domain = user_info->lg_e_domain; 417 } else { 418 (void) smb_getdomainname(nbdomain, sizeof (nbdomain)); 419 domain = nbdomain; 420 } 421 422 if (username) 423 token->tkn_account_name = strdup(username); 424 if (domain) 425 token->tkn_domain_name = strdup(domain); 426 427 if (token->tkn_account_name == NULL || token->tkn_domain_name == NULL) 428 return (NT_STATUS_NO_MEMORY); 429 430 status = netr_setup_domain_groups(info3, &token->tkn_win_grps); 431 if (status != NT_STATUS_SUCCESS) 432 return (status); 433 434 status = netr_setup_token_wingrps(info3, token); 435 if (status != NT_STATUS_SUCCESS) 436 return (status); 437 438 /* 439 * The UserSessionKey in NetrSamLogon RPC is obfuscated using the 440 * session key obtained in the NETLOGON credential chain. 441 * An 8 byte session key is zero extended to 16 bytes. This 16 byte 442 * key is the key to the RC4 algorithm. The RC4 byte stream is 443 * exclusively ored with the 16 byte UserSessionKey to recover 444 * the the clear form. 445 */ 446 if ((token->tkn_ssnkey.val = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL) 447 return (NT_STATUS_NO_MEMORY); 448 token->tkn_ssnkey.len = SMBAUTH_SESSION_KEY_SZ; 449 bzero(rc4key, SMBAUTH_SESSION_KEY_SZ); 450 bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len); 451 bcopy(info3->UserSessionKey.data, token->tkn_ssnkey.val, 452 SMBAUTH_SESSION_KEY_SZ); 453 rand_hash((unsigned char *)token->tkn_ssnkey.val, 454 SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ); 455 456 return (NT_STATUS_SUCCESS); 457 } 458 459 /* 460 * netr_server_samlogon 461 * 462 * NetrServerSamLogon RPC: interactive or network. It is assumed that 463 * we have already authenticated with the PDC. If everything works, 464 * we build a user info structure and return it, where the caller will 465 * probably build an access token. 466 * 467 * Returns an NT status. There are numerous possibilities here. 468 * For example: 469 * NT_STATUS_INVALID_INFO_CLASS 470 * NT_STATUS_INVALID_PARAMETER 471 * NT_STATUS_ACCESS_DENIED 472 * NT_STATUS_PASSWORD_MUST_CHANGE 473 * NT_STATUS_NO_SUCH_USER 474 * NT_STATUS_WRONG_PASSWORD 475 * NT_STATUS_LOGON_FAILURE 476 * NT_STATUS_ACCOUNT_RESTRICTION 477 * NT_STATUS_INVALID_LOGON_HOURS 478 * NT_STATUS_INVALID_WORKSTATION 479 * NT_STATUS_INTERNAL_ERROR 480 * NT_STATUS_PASSWORD_EXPIRED 481 * NT_STATUS_ACCOUNT_DISABLED 482 */ 483 uint32_t 484 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info, 485 char *server, smb_logon_t *user_info, smb_token_t *token) 486 { 487 struct netr_SamLogon logon_op; 488 struct netr_SamLogonEx logon_ex_op; 489 struct netr_authenticator auth; 490 struct netr_authenticator ret_auth; 491 struct netr_logon_info1 info1; 492 struct netr_logon_info2 info2; 493 struct netr_validation_info3 *info3; 494 union netr_validation_u *valid_info; 495 union netr_logon_info_u *logon_info; 496 LPTSTR servername, hostname; 497 ndr_heap_t *heap; 498 int opnum; 499 int rc, len; 500 uint32_t status, *rpc_status; 501 void *rpc_arg; 502 503 /* 504 * Should we get the server and hostname from netr_info? 505 */ 506 507 len = strlen(server) + 4; 508 servername = ndr_rpc_malloc(netr_handle, len); 509 hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ); 510 if (servername == NULL || hostname == NULL) { 511 ndr_rpc_release(netr_handle); 512 return (NT_STATUS_INTERNAL_ERROR); 513 } 514 515 (void) snprintf((char *)servername, len, "\\\\%s", server); 516 if (smb_getnetbiosname((char *)hostname, NETBIOS_NAME_SZ) != 0) { 517 ndr_rpc_release(netr_handle); 518 return (NT_STATUS_INTERNAL_ERROR); 519 } 520 521 rc = netr_setup_authenticator(netr_info, &auth, &ret_auth); 522 if (rc != SMBAUTH_SUCCESS) { 523 ndr_rpc_release(netr_handle); 524 return (NT_STATUS_INTERNAL_ERROR); 525 } 526 527 /* 528 * If we use Secure RPC, we can use SamLogonEx instead of SamLogon. 529 * SamLogonEx doesn't use NetLogon authenticators, instead relying 530 * on Secure RPC to provide security. 531 * This allows us to avoid being bitten by mitigations in 532 * the authenticator verification logic on DCs. 533 */ 534 if (netr_info->use_logon_ex && 535 (netr_info->nego_flags & NETR_NEGO_SECURE_RPC_FLAG) != 0) { 536 bzero(&logon_ex_op, sizeof (struct netr_SamLogonEx)); 537 logon_ex_op.servername = servername; 538 logon_ex_op.hostname = hostname; 539 logon_ex_op.logon_info.logon_level = user_info->lg_level; 540 logon_ex_op.logon_info.switch_value = user_info->lg_level; 541 logon_ex_op.validation_level = NETR_VALIDATION_LEVEL3; 542 logon_ex_op.extra_flags = 0; 543 logon_info = &logon_ex_op.logon_info.ru; 544 valid_info = &logon_ex_op.ru; 545 rpc_status = &logon_ex_op.status; 546 rpc_arg = &logon_ex_op; 547 opnum = NETR_OPNUM_SamLogonEx; 548 } else { 549 bzero(&logon_op, sizeof (struct netr_SamLogon)); 550 logon_op.servername = servername; 551 logon_op.hostname = hostname; 552 logon_op.auth = &auth; 553 logon_op.ret_auth = &ret_auth; 554 logon_op.logon_info.logon_level = user_info->lg_level; 555 logon_op.logon_info.switch_value = user_info->lg_level; 556 logon_op.validation_level = NETR_VALIDATION_LEVEL3; 557 logon_info = &logon_op.logon_info.ru; 558 valid_info = &logon_op.ru; 559 rpc_status = &logon_op.status; 560 rpc_arg = &logon_op; 561 opnum = NETR_OPNUM_SamLogon; 562 } 563 heap = ndr_rpc_get_heap(netr_handle); 564 565 switch (user_info->lg_level) { 566 case NETR_INTERACTIVE_LOGON: 567 netr_setup_identity(heap, user_info, &info1.identity); 568 netr_interactive_samlogon(netr_info, user_info, &info1); 569 logon_info->info1 = &info1; 570 break; 571 572 case NETR_NETWORK_LOGON: 573 if (user_info->lg_challenge_key.len < 8 || 574 user_info->lg_challenge_key.val == NULL) { 575 ndr_rpc_release(netr_handle); 576 return (NT_STATUS_INVALID_PARAMETER); 577 } 578 netr_setup_identity(heap, user_info, &info2.identity); 579 netr_network_samlogon(heap, netr_info, user_info, &info2); 580 logon_info->info2 = &info2; 581 break; 582 583 default: 584 ndr_rpc_release(netr_handle); 585 return (NT_STATUS_INVALID_PARAMETER); 586 } 587 588 rc = ndr_rpc_call(netr_handle, opnum, rpc_arg); 589 if (rc != 0) { 590 netr_invalidate_chain(netr_info); 591 status = NT_STATUS_INVALID_PARAMETER; 592 } else if (*rpc_status != 0) { 593 status = NT_SC_VALUE(*rpc_status); 594 595 /* 596 * We need to validate the chain even though we have 597 * a non-zero status. If the status is ACCESS_DENIED 598 * this will trigger a new credential chain. However, 599 * a valid credential is returned with some status 600 * codes; for example, WRONG_PASSWORD. 601 * 602 * SamLogonEx doesn't use authenticators - nothing to validate. 603 */ 604 if (rpc_arg == &logon_op) 605 (void) netr_validate_chain(netr_info, 606 logon_op.ret_auth); 607 } else { 608 if (rpc_arg == &logon_op) { 609 status = netr_validate_chain(netr_info, 610 logon_op.ret_auth); 611 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { 612 ndr_rpc_release(netr_handle); 613 return (status); 614 } 615 } 616 617 info3 = valid_info->info3; 618 status = netr_setup_token(info3, user_info, netr_info, token); 619 } 620 621 ndr_rpc_release(netr_handle); 622 return (status); 623 } 624 625 /* 626 * netr_interactive_samlogon 627 * 628 * Set things up for an interactive SamLogon. Copy the NT and LM 629 * passwords to the logon structure and hash them with the session 630 * key. 631 */ 632 static void 633 netr_interactive_samlogon(netr_info_t *netr_info, smb_logon_t *user_info, 634 struct netr_logon_info1 *info1) 635 { 636 BYTE key[NETR_OWF_PASSWORD_SZ]; 637 638 (void) memcpy(&info1->lm_owf_password, 639 user_info->lg_lm_password.val, sizeof (netr_owf_password_t)); 640 641 (void) memcpy(&info1->nt_owf_password, 642 user_info->lg_nt_password.val, sizeof (netr_owf_password_t)); 643 644 (void) memset(key, 0, NETR_OWF_PASSWORD_SZ); 645 (void) memcpy(key, netr_info->session_key.key, 646 netr_info->session_key.len); 647 648 rand_hash((unsigned char *)&info1->lm_owf_password, 649 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ); 650 651 rand_hash((unsigned char *)&info1->nt_owf_password, 652 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ); 653 } 654 655 /* 656 * netr_network_samlogon 657 * 658 * Set things up for a network SamLogon. We provide a copy of the random 659 * challenge, that we sent to the client, to the domain controller. This 660 * is the key that the client will have used to encrypt the NT and LM 661 * passwords. Note that Windows 9x clients may not provide both passwords. 662 */ 663 /*ARGSUSED*/ 664 static void 665 netr_network_samlogon(ndr_heap_t *heap, netr_info_t *netr_info, 666 smb_logon_t *user_info, struct netr_logon_info2 *info2) 667 { 668 uint32_t len; 669 670 if (user_info->lg_challenge_key.len >= 8 && 671 user_info->lg_challenge_key.val != 0) { 672 bcopy(user_info->lg_challenge_key.val, 673 info2->lm_challenge.data, 8); 674 } else { 675 bzero(info2->lm_challenge.data, 8); 676 } 677 678 if ((len = user_info->lg_nt_password.len) != 0) { 679 ndr_heap_mkvcb(heap, user_info->lg_nt_password.val, len, 680 (ndr_vcbuf_t *)&info2->nt_response); 681 } else { 682 bzero(&info2->nt_response, sizeof (netr_vcbuf_t)); 683 } 684 685 if ((len = user_info->lg_lm_password.len) != 0) { 686 ndr_heap_mkvcb(heap, user_info->lg_lm_password.val, len, 687 (ndr_vcbuf_t *)&info2->lm_response); 688 } else { 689 bzero(&info2->lm_response, sizeof (netr_vcbuf_t)); 690 } 691 } 692 693 /* 694 * netr_setup_authenticator 695 * 696 * Set up the request and return authenticators. A new credential is 697 * generated from the session key, the current client credential and 698 * the current time, i.e. 699 * 700 * NewCredential = Cred(SessionKey, OldCredential, time); 701 * 702 * The timestamp, which is used as a random seed, is stored in both 703 * the request and return authenticators. 704 * 705 * If any difficulties occur using the cryptographic framework, the 706 * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is 707 * returned. 708 */ 709 int 710 netr_setup_authenticator(netr_info_t *netr_info, 711 struct netr_authenticator *auth, struct netr_authenticator *ret_auth) 712 { 713 int rc; 714 bzero(auth, sizeof (struct netr_authenticator)); 715 716 /* 717 * Windows DCs will reject Authenticators if none of the first 718 * 5 bytes of the ClientStoredCredential are unique. 719 * Keep retrying until we've generated one that satisfies this. 720 */ 721 netr_info->timestamp = time(0) - 1; 722 do { 723 auth->timestamp = ++netr_info->timestamp; 724 rc = netr_gen_credentials(netr_info->session_key.key, 725 &netr_info->client_credential, 726 netr_info->timestamp, 727 (netr_cred_t *)&auth->credential, B_TRUE); 728 if (rc != SMBAUTH_SUCCESS && rc != SMBAUTH_RETRY) 729 return (SMBAUTH_FAILURE); 730 } while (rc == SMBAUTH_RETRY); 731 732 if (ret_auth) { 733 bzero(ret_auth, sizeof (struct netr_authenticator)); 734 ret_auth->timestamp = netr_info->timestamp; 735 } 736 737 return (SMBAUTH_SUCCESS); 738 } 739 740 /* 741 * Validate the returned credentials and update the credential chain. 742 * The server returns an updated client credential rather than a new 743 * server credential. The server uses (timestamp + 1) when generating 744 * the credential. 745 * 746 * Generate the new seed for the credential chain. The new seed is 747 * formed by adding (timestamp + 1) to the current client credential. 748 * The only quirk is the uint32_t style addition. 749 * 750 * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a 751 * NULL pointer. The Authenticator field of the SamLogon response packet 752 * sent by the Samba 3 PDC always return NULL pointer if the received 753 * SamLogon request is not immediately followed by the ServerReqChallenge 754 * and ServerAuthenticate2 requests. 755 * 756 * Returns NT_STATUS_SUCCESS if the server returned a valid credential. 757 * Otherwise we retirm NT_STATUS_UNSUCCESSFUL. 758 */ 759 uint32_t 760 netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth) 761 { 762 netr_cred_t cred; 763 uint32_t result = NT_STATUS_SUCCESS; 764 uint32_t *dwp; 765 766 ++netr_info->timestamp; 767 768 if (netr_gen_credentials(netr_info->session_key.key, 769 &netr_info->client_credential, 770 netr_info->timestamp, &cred, B_FALSE) != SMBAUTH_SUCCESS) 771 return (NT_STATUS_INTERNAL_ERROR); 772 773 if (auth == NULL) { 774 /* 775 * If the validation fails, destroy the credential chain. 776 * This should trigger a new authentication chain. 777 */ 778 netr_invalidate_chain(netr_info); 779 return (NT_STATUS_INSUFFICIENT_LOGON_INFO); 780 } 781 782 result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t)); 783 if (result != 0) { 784 /* 785 * If the validation fails, destroy the credential chain. 786 * This should trigger a new authentication chain. 787 */ 788 netr_invalidate_chain(netr_info); 789 result = NT_STATUS_UNSUCCESSFUL; 790 } else { 791 /* 792 * Otherwise generate the next step in the chain. 793 */ 794 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 795 dwp = (uint32_t *)&netr_info->client_credential; 796 dwp[0] += netr_info->timestamp; 797 798 netr_info->flags |= NETR_FLG_VALID; 799 } 800 801 return (result); 802 } 803 804 /* 805 * netr_invalidate_chain 806 * 807 * Mark the credential chain as invalid so that it will be recreated 808 * on the next attempt. 809 */ 810 static void 811 netr_invalidate_chain(netr_info_t *netr_info) 812 { 813 if ((netr_info->flags & NETR_FLG_VALID) == 0) 814 return; 815 816 netr_info->flags &= ~NETR_FLG_VALID; 817 explicit_bzero(&netr_info->session_key, 818 sizeof (netr_info->session_key)); 819 explicit_bzero(&netr_info->rpc_seal_key, 820 sizeof (netr_info->rpc_seal_key)); 821 explicit_bzero(&netr_info->client_credential, 822 sizeof (netr_info->client_credential)); 823 explicit_bzero(&netr_info->server_credential, 824 sizeof (netr_info->server_credential)); 825 } 826 827 /* 828 * netr_setup_identity 829 * 830 * Set up the client identity information. All of this information is 831 * specifically related to the client user and workstation attempting 832 * to access this system. It may not be in our primary domain. 833 * 834 * I don't know what logon_id is, it seems to be a unique identifier. 835 * Increment it before each use. 836 */ 837 static void 838 netr_setup_identity(ndr_heap_t *heap, smb_logon_t *user_info, 839 netr_logon_id_t *identity) 840 { 841 static mutex_t logon_id_mutex; 842 static uint32_t logon_id; 843 844 (void) mutex_lock(&logon_id_mutex); 845 846 if (logon_id == 0) 847 logon_id = 0xDCD0; 848 849 ++logon_id; 850 user_info->lg_logon_id = logon_id; 851 852 (void) mutex_unlock(&logon_id_mutex); 853 854 /* 855 * [MS-APDS] 3.1.5.2 "NTLM Network Logon" says to set 856 * ParameterControl to the 'E' + 'K' bits. Those are: 857 * (1 << 5) | (1 << 11), a.k.a 858 */ 859 identity->parameter_control = 860 MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | 861 MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT; 862 identity->logon_id.LowPart = logon_id; 863 identity->logon_id.HighPart = 0; 864 865 ndr_heap_mkvcs(heap, user_info->lg_domain, 866 (ndr_vcstr_t *)&identity->domain_name); 867 868 ndr_heap_mkvcs(heap, user_info->lg_username, 869 (ndr_vcstr_t *)&identity->username); 870 871 /* 872 * Some systems prefix the client workstation name with \\. 873 * It doesn't seem to make any difference whether it's there 874 * or not. 875 */ 876 ndr_heap_mkvcs(heap, user_info->lg_workstation, 877 (ndr_vcstr_t *)&identity->workstation); 878 } 879 880 /* 881 * Add local and well-known group membership to the given 882 * token. Called after domain groups have been added. 883 */ 884 static uint32_t 885 netr_setup_token_wingrps(struct netr_validation_info3 *info3 __unused, 886 smb_token_t *token) 887 { 888 uint32_t status; 889 890 status = smb_sam_usr_groups(token->tkn_user.i_sid, 891 &token->tkn_win_grps); 892 if (status != NT_STATUS_SUCCESS) 893 return (status); 894 895 status = smb_wka_token_groups(token->tkn_flags, &token->tkn_win_grps); 896 897 return (status); 898 } 899 900 /* 901 * Converts groups information in the returned structure by domain controller 902 * (info3) to an internal representation (gids) 903 */ 904 static uint32_t 905 netr_setup_domain_groups(struct netr_validation_info3 *info3, smb_ids_t *gids) 906 { 907 smb_sid_t *domain_sid; 908 smb_id_t *ids; 909 int i, total_cnt; 910 911 if ((i = info3->GroupCount) == 0) 912 i++; 913 i += info3->SidCount; 914 915 total_cnt = gids->i_cnt + i; 916 917 gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t)); 918 if (gids->i_ids == NULL) 919 return (NT_STATUS_NO_MEMORY); 920 921 domain_sid = (smb_sid_t *)info3->LogonDomainId; 922 923 ids = gids->i_ids + gids->i_cnt; 924 for (i = 0; i < info3->GroupCount; i++, gids->i_cnt++, ids++) { 925 ids->i_sid = smb_sid_splice(domain_sid, info3->GroupIds[i].rid); 926 if (ids->i_sid == NULL) 927 return (NT_STATUS_NO_MEMORY); 928 929 ids->i_attrs = info3->GroupIds[i].attributes; 930 } 931 932 if (info3->GroupCount == 0) { 933 /* 934 * if there's no global group should add the primary group. 935 */ 936 ids->i_sid = smb_sid_splice(domain_sid, info3->PrimaryGroupId); 937 if (ids->i_sid == NULL) 938 return (NT_STATUS_NO_MEMORY); 939 940 ids->i_attrs = 0x7; 941 gids->i_cnt++; 942 ids++; 943 } 944 945 /* Add the extra SIDs */ 946 for (i = 0; i < info3->SidCount; i++, gids->i_cnt++, ids++) { 947 ids->i_sid = smb_sid_dup((smb_sid_t *)info3->ExtraSids[i].sid); 948 if (ids->i_sid == NULL) 949 return (NT_STATUS_NO_MEMORY); 950 951 ids->i_attrs = info3->ExtraSids[i].attributes; 952 } 953 954 return (NT_STATUS_SUCCESS); 955 } 956 957 /* 958 * Converts additional "resource" groups (from krb5_validation_info) 959 * into the internal representation (gids), appending to the list 960 * already put in place by netr_setup_domain_groups(). 961 */ 962 static uint32_t netr_setup_krb5res_groups(struct krb5_validation_info *info, 963 smb_ids_t *gids) 964 { 965 smb_sid_t *domain_sid; 966 smb_id_t *ids; 967 int i, total_cnt; 968 969 total_cnt = gids->i_cnt + info->rg_rid_cnt; 970 971 gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t)); 972 if (gids->i_ids == NULL) 973 return (NT_STATUS_NO_MEMORY); 974 975 domain_sid = (smb_sid_t *)info->rg_dom_sid; 976 977 ids = gids->i_ids + gids->i_cnt; 978 for (i = 0; i < info->rg_rid_cnt; i++, gids->i_cnt++, ids++) { 979 ids->i_sid = smb_sid_splice(domain_sid, info->rg_rids[i].rid); 980 if (ids->i_sid == NULL) 981 return (NT_STATUS_NO_MEMORY); 982 ids->i_attrs = info->rg_rids[i].attributes; 983 } 984 985 return (0); 986 } 987