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