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 2018 Nexenta Systems, 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 static 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(void); 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 boolean_t netr_isadmin(struct netr_validation_info3 *); 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 /* 248 * Run a netr_server_samlogon call, dealing with the possible need to 249 * re-establish the NetLogon credential chain. If that fails, return 250 * NT_STATUS_DOMAIN_TRUST_INCONSISTENT indicating the machine account 251 * needs it's password reset (or whatever). Other errors are from the 252 * netr_server_samlogon() call including the many possibilities listed 253 * above that function. 254 */ 255 static uint32_t 256 netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di) 257 { 258 char server[MAXHOSTNAMELEN]; 259 mlsvc_handle_t netr_handle; 260 uint32_t status; 261 boolean_t did_reauth = B_FALSE; 262 263 /* 264 * This netr_open call does the work to connect to the DC, 265 * get the IPC share, open the named pipe, RPC bind, etc. 266 */ 267 status = netr_open(di->d_dci.dc_name, di->d_primary.di_nbname, 268 &netr_handle); 269 if (status != 0) { 270 syslog(LOG_ERR, "netlogon remote open failed (%s)", 271 xlate_nt_status(status)); 272 return (status); 273 } 274 275 if (di->d_dci.dc_name[0] != '\0' && 276 (*netr_global_info.server != '\0')) { 277 (void) snprintf(server, sizeof (server), 278 "\\\\%s", di->d_dci.dc_name); 279 if (strncasecmp(netr_global_info.server, 280 server, strlen(server)) != 0) 281 netr_invalidate_chain(); 282 } 283 284 reauth: 285 if ((netr_global_info.flags & NETR_FLG_VALID) == 0 || 286 !smb_match_netlogon_seqnum()) { 287 /* 288 * This does netr_server_req_challenge() and 289 * netr_server_authenticate2(), updating the 290 * current netlogon sequence number. 291 */ 292 status = netlogon_auth(di->d_dci.dc_name, &netr_handle, 293 NETR_FLG_NULL); 294 295 if (status != 0) { 296 syslog(LOG_ERR, "netlogon remote auth failed (%s)", 297 xlate_nt_status(status)); 298 (void) netr_close(&netr_handle); 299 return (NT_STATUS_DOMAIN_TRUST_INCONSISTENT); 300 } 301 302 netr_global_info.flags |= NETR_FLG_VALID; 303 } 304 305 status = netr_server_samlogon(&netr_handle, 306 &netr_global_info, di->d_dci.dc_name, user_info, token); 307 308 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { 309 if (!did_reauth) { 310 /* Call netlogon_auth() again, just once. */ 311 did_reauth = B_TRUE; 312 goto reauth; 313 } 314 status = NT_STATUS_DOMAIN_TRUST_INCONSISTENT; 315 } 316 317 (void) netr_close(&netr_handle); 318 319 return (status); 320 } 321 322 /* 323 * Helper for mlsvc_netlogon 324 * 325 * Call netlogon_auth with appropriate locks etc. 326 * Serialize like smb_logon_domain does for 327 * netlogon_logon / netlogon_auth 328 */ 329 uint32_t 330 smb_netlogon_check(char *server, char *domain) 331 { 332 mlsvc_handle_t netr_handle; 333 uint32_t status; 334 335 (void) mutex_lock(&netlogon_mutex); 336 while (netlogon_busy) 337 (void) cond_wait(&netlogon_cv, &netlogon_mutex); 338 339 netlogon_busy = B_TRUE; 340 (void) mutex_unlock(&netlogon_mutex); 341 342 /* 343 * This section like netlogon_logon(), but only does 344 * one pass and no netr_server_samlogon call. 345 */ 346 347 status = netr_open(server, domain, 348 &netr_handle); 349 if (status != 0) { 350 syslog(LOG_ERR, "netlogon remote open failed (%s)", 351 xlate_nt_status(status)); 352 goto unlock_out; 353 } 354 355 if ((netr_global_info.flags & NETR_FLG_VALID) == 0 || 356 !smb_match_netlogon_seqnum()) { 357 /* 358 * This does netr_server_req_challenge() and 359 * netr_server_authenticate2(), updating the 360 * current netlogon sequence number. 361 */ 362 status = netlogon_auth(server, &netr_handle, 363 NETR_FLG_NULL); 364 if (status != 0) { 365 syslog(LOG_ERR, "netlogon remote auth failed (%s)", 366 xlate_nt_status(status)); 367 } else { 368 netr_global_info.flags |= NETR_FLG_VALID; 369 } 370 } 371 372 (void) netr_close(&netr_handle); 373 374 unlock_out: 375 (void) mutex_lock(&netlogon_mutex); 376 netlogon_busy = B_FALSE; 377 (void) cond_signal(&netlogon_cv); 378 (void) mutex_unlock(&netlogon_mutex); 379 380 return (status); 381 } 382 383 static uint32_t 384 netr_setup_token(struct netr_validation_info3 *info3, smb_logon_t *user_info, 385 netr_info_t *netr_info, smb_token_t *token) 386 { 387 char *username, *domain; 388 unsigned char rc4key[SMBAUTH_SESSION_KEY_SZ]; 389 smb_sid_t *domsid; 390 uint32_t status; 391 char nbdomain[NETBIOS_NAME_SZ]; 392 393 domsid = (smb_sid_t *)info3->LogonDomainId; 394 395 token->tkn_user.i_sid = smb_sid_splice(domsid, info3->UserId); 396 if (token->tkn_user.i_sid == NULL) 397 return (NT_STATUS_NO_MEMORY); 398 399 token->tkn_primary_grp.i_sid = smb_sid_splice(domsid, 400 info3->PrimaryGroupId); 401 if (token->tkn_primary_grp.i_sid == NULL) 402 return (NT_STATUS_NO_MEMORY); 403 404 username = (info3->EffectiveName.str) 405 ? (char *)info3->EffectiveName.str : user_info->lg_e_username; 406 407 if (info3->LogonDomainName.str) { 408 domain = (char *)info3->LogonDomainName.str; 409 } else if (*user_info->lg_e_domain != '\0') { 410 domain = user_info->lg_e_domain; 411 } else { 412 (void) smb_getdomainname(nbdomain, sizeof (nbdomain)); 413 domain = nbdomain; 414 } 415 416 if (username) 417 token->tkn_account_name = strdup(username); 418 if (domain) 419 token->tkn_domain_name = strdup(domain); 420 421 if (token->tkn_account_name == NULL || token->tkn_domain_name == NULL) 422 return (NT_STATUS_NO_MEMORY); 423 424 status = netr_setup_domain_groups(info3, &token->tkn_win_grps); 425 if (status != NT_STATUS_SUCCESS) 426 return (status); 427 428 status = netr_setup_token_wingrps(info3, token); 429 if (status != NT_STATUS_SUCCESS) 430 return (status); 431 432 /* 433 * The UserSessionKey in NetrSamLogon RPC is obfuscated using the 434 * session key obtained in the NETLOGON credential chain. 435 * An 8 byte session key is zero extended to 16 bytes. This 16 byte 436 * key is the key to the RC4 algorithm. The RC4 byte stream is 437 * exclusively ored with the 16 byte UserSessionKey to recover 438 * the the clear form. 439 */ 440 if ((token->tkn_ssnkey.val = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL) 441 return (NT_STATUS_NO_MEMORY); 442 token->tkn_ssnkey.len = SMBAUTH_SESSION_KEY_SZ; 443 bzero(rc4key, SMBAUTH_SESSION_KEY_SZ); 444 bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len); 445 bcopy(info3->UserSessionKey.data, token->tkn_ssnkey.val, 446 SMBAUTH_SESSION_KEY_SZ); 447 rand_hash((unsigned char *)token->tkn_ssnkey.val, 448 SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ); 449 450 return (NT_STATUS_SUCCESS); 451 } 452 453 /* 454 * netr_server_samlogon 455 * 456 * NetrServerSamLogon RPC: interactive or network. It is assumed that 457 * we have already authenticated with the PDC. If everything works, 458 * we build a user info structure and return it, where the caller will 459 * probably build an access token. 460 * 461 * Returns an NT status. There are numerous possibilities here. 462 * For example: 463 * NT_STATUS_INVALID_INFO_CLASS 464 * NT_STATUS_INVALID_PARAMETER 465 * NT_STATUS_ACCESS_DENIED 466 * NT_STATUS_PASSWORD_MUST_CHANGE 467 * NT_STATUS_NO_SUCH_USER 468 * NT_STATUS_WRONG_PASSWORD 469 * NT_STATUS_LOGON_FAILURE 470 * NT_STATUS_ACCOUNT_RESTRICTION 471 * NT_STATUS_INVALID_LOGON_HOURS 472 * NT_STATUS_INVALID_WORKSTATION 473 * NT_STATUS_INTERNAL_ERROR 474 * NT_STATUS_PASSWORD_EXPIRED 475 * NT_STATUS_ACCOUNT_DISABLED 476 */ 477 uint32_t 478 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info, 479 char *server, smb_logon_t *user_info, smb_token_t *token) 480 { 481 struct netr_SamLogon arg; 482 struct netr_authenticator auth; 483 struct netr_authenticator ret_auth; 484 struct netr_logon_info1 info1; 485 struct netr_logon_info2 info2; 486 struct netr_validation_info3 *info3; 487 ndr_heap_t *heap; 488 int opnum; 489 int rc, len; 490 uint32_t status; 491 492 bzero(&arg, sizeof (struct netr_SamLogon)); 493 opnum = NETR_OPNUM_SamLogon; 494 495 /* 496 * Should we get the server and hostname from netr_info? 497 */ 498 499 len = strlen(server) + 4; 500 arg.servername = ndr_rpc_malloc(netr_handle, len); 501 arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ); 502 if (arg.servername == NULL || arg.hostname == NULL) { 503 ndr_rpc_release(netr_handle); 504 return (NT_STATUS_INTERNAL_ERROR); 505 } 506 507 (void) snprintf((char *)arg.servername, len, "\\\\%s", server); 508 if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) { 509 ndr_rpc_release(netr_handle); 510 return (NT_STATUS_INTERNAL_ERROR); 511 } 512 513 rc = netr_setup_authenticator(netr_info, &auth, &ret_auth); 514 if (rc != SMBAUTH_SUCCESS) { 515 ndr_rpc_release(netr_handle); 516 return (NT_STATUS_INTERNAL_ERROR); 517 } 518 519 arg.auth = &auth; 520 arg.ret_auth = &ret_auth; 521 arg.validation_level = NETR_VALIDATION_LEVEL3; 522 arg.logon_info.logon_level = user_info->lg_level; 523 arg.logon_info.switch_value = user_info->lg_level; 524 525 heap = ndr_rpc_get_heap(netr_handle); 526 527 switch (user_info->lg_level) { 528 case NETR_INTERACTIVE_LOGON: 529 netr_setup_identity(heap, user_info, &info1.identity); 530 netr_interactive_samlogon(netr_info, user_info, &info1); 531 arg.logon_info.ru.info1 = &info1; 532 break; 533 534 case NETR_NETWORK_LOGON: 535 if (user_info->lg_challenge_key.len < 8 || 536 user_info->lg_challenge_key.val == NULL) { 537 ndr_rpc_release(netr_handle); 538 return (NT_STATUS_INVALID_PARAMETER); 539 } 540 netr_setup_identity(heap, user_info, &info2.identity); 541 netr_network_samlogon(heap, netr_info, user_info, &info2); 542 arg.logon_info.ru.info2 = &info2; 543 break; 544 545 default: 546 ndr_rpc_release(netr_handle); 547 return (NT_STATUS_INVALID_PARAMETER); 548 } 549 550 rc = ndr_rpc_call(netr_handle, opnum, &arg); 551 if (rc != 0) { 552 bzero(netr_info, sizeof (netr_info_t)); 553 status = NT_STATUS_INVALID_PARAMETER; 554 } else if (arg.status != 0) { 555 status = NT_SC_VALUE(arg.status); 556 557 /* 558 * We need to validate the chain even though we have 559 * a non-zero status. If the status is ACCESS_DENIED 560 * this will trigger a new credential chain. However, 561 * a valid credential is returned with some status 562 * codes; for example, WRONG_PASSWORD. 563 */ 564 (void) netr_validate_chain(netr_info, arg.ret_auth); 565 } else { 566 status = netr_validate_chain(netr_info, arg.ret_auth); 567 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { 568 ndr_rpc_release(netr_handle); 569 return (status); 570 } 571 572 info3 = arg.ru.info3; 573 status = netr_setup_token(info3, user_info, netr_info, token); 574 } 575 576 ndr_rpc_release(netr_handle); 577 return (status); 578 } 579 580 /* 581 * netr_interactive_samlogon 582 * 583 * Set things up for an interactive SamLogon. Copy the NT and LM 584 * passwords to the logon structure and hash them with the session 585 * key. 586 */ 587 static void 588 netr_interactive_samlogon(netr_info_t *netr_info, smb_logon_t *user_info, 589 struct netr_logon_info1 *info1) 590 { 591 BYTE key[NETR_OWF_PASSWORD_SZ]; 592 593 (void) memcpy(&info1->lm_owf_password, 594 user_info->lg_lm_password.val, sizeof (netr_owf_password_t)); 595 596 (void) memcpy(&info1->nt_owf_password, 597 user_info->lg_nt_password.val, sizeof (netr_owf_password_t)); 598 599 (void) memset(key, 0, NETR_OWF_PASSWORD_SZ); 600 (void) memcpy(key, netr_info->session_key.key, 601 netr_info->session_key.len); 602 603 rand_hash((unsigned char *)&info1->lm_owf_password, 604 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ); 605 606 rand_hash((unsigned char *)&info1->nt_owf_password, 607 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ); 608 } 609 610 /* 611 * netr_network_samlogon 612 * 613 * Set things up for a network SamLogon. We provide a copy of the random 614 * challenge, that we sent to the client, to the domain controller. This 615 * is the key that the client will have used to encrypt the NT and LM 616 * passwords. Note that Windows 9x clients may not provide both passwords. 617 */ 618 /*ARGSUSED*/ 619 static void 620 netr_network_samlogon(ndr_heap_t *heap, netr_info_t *netr_info, 621 smb_logon_t *user_info, struct netr_logon_info2 *info2) 622 { 623 uint32_t len; 624 625 if (user_info->lg_challenge_key.len >= 8 && 626 user_info->lg_challenge_key.val != 0) { 627 bcopy(user_info->lg_challenge_key.val, 628 info2->lm_challenge.data, 8); 629 } else { 630 bzero(info2->lm_challenge.data, 8); 631 } 632 633 if ((len = user_info->lg_nt_password.len) != 0) { 634 ndr_heap_mkvcb(heap, user_info->lg_nt_password.val, len, 635 (ndr_vcbuf_t *)&info2->nt_response); 636 } else { 637 bzero(&info2->nt_response, sizeof (netr_vcbuf_t)); 638 } 639 640 if ((len = user_info->lg_lm_password.len) != 0) { 641 ndr_heap_mkvcb(heap, user_info->lg_lm_password.val, len, 642 (ndr_vcbuf_t *)&info2->lm_response); 643 } else { 644 bzero(&info2->lm_response, sizeof (netr_vcbuf_t)); 645 } 646 } 647 648 /* 649 * netr_setup_authenticator 650 * 651 * Set up the request and return authenticators. A new credential is 652 * generated from the session key, the current client credential and 653 * the current time, i.e. 654 * 655 * NewCredential = Cred(SessionKey, OldCredential, time); 656 * 657 * The timestamp, which is used as a random seed, is stored in both 658 * the request and return authenticators. 659 * 660 * If any difficulties occur using the cryptographic framework, the 661 * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is 662 * returned. 663 */ 664 int 665 netr_setup_authenticator(netr_info_t *netr_info, 666 struct netr_authenticator *auth, struct netr_authenticator *ret_auth) 667 { 668 bzero(auth, sizeof (struct netr_authenticator)); 669 670 netr_info->timestamp = time(0); 671 auth->timestamp = netr_info->timestamp; 672 673 if (netr_gen_credentials(netr_info->session_key.key, 674 &netr_info->client_credential, 675 netr_info->timestamp, 676 (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS) 677 return (SMBAUTH_FAILURE); 678 679 if (ret_auth) { 680 bzero(ret_auth, sizeof (struct netr_authenticator)); 681 ret_auth->timestamp = netr_info->timestamp; 682 } 683 684 return (SMBAUTH_SUCCESS); 685 } 686 687 /* 688 * Validate the returned credentials and update the credential chain. 689 * The server returns an updated client credential rather than a new 690 * server credential. The server uses (timestamp + 1) when generating 691 * the credential. 692 * 693 * Generate the new seed for the credential chain. The new seed is 694 * formed by adding (timestamp + 1) to the current client credential. 695 * The only quirk is the uint32_t style addition. 696 * 697 * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a 698 * NULL pointer. The Authenticator field of the SamLogon response packet 699 * sent by the Samba 3 PDC always return NULL pointer if the received 700 * SamLogon request is not immediately followed by the ServerReqChallenge 701 * and ServerAuthenticate2 requests. 702 * 703 * Returns NT_STATUS_SUCCESS if the server returned a valid credential. 704 * Otherwise we retirm NT_STATUS_UNSUCCESSFUL. 705 */ 706 uint32_t 707 netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth) 708 { 709 netr_cred_t cred; 710 uint32_t result = NT_STATUS_SUCCESS; 711 uint32_t *dwp; 712 713 ++netr_info->timestamp; 714 715 if (netr_gen_credentials(netr_info->session_key.key, 716 &netr_info->client_credential, 717 netr_info->timestamp, &cred) != SMBAUTH_SUCCESS) 718 return (NT_STATUS_INTERNAL_ERROR); 719 720 if (&auth->credential == 0) { 721 /* 722 * If the validation fails, destroy the credential chain. 723 * This should trigger a new authentication chain. 724 */ 725 bzero(netr_info, sizeof (netr_info_t)); 726 return (NT_STATUS_INSUFFICIENT_LOGON_INFO); 727 } 728 729 result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t)); 730 if (result != 0) { 731 /* 732 * If the validation fails, destroy the credential chain. 733 * This should trigger a new authentication chain. 734 */ 735 bzero(netr_info, sizeof (netr_info_t)); 736 result = NT_STATUS_UNSUCCESSFUL; 737 } else { 738 /* 739 * Otherwise generate the next step in the chain. 740 */ 741 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 742 dwp = (uint32_t *)&netr_info->client_credential; 743 dwp[0] += netr_info->timestamp; 744 745 netr_info->flags |= NETR_FLG_VALID; 746 } 747 748 return (result); 749 } 750 751 /* 752 * netr_invalidate_chain 753 * 754 * Mark the credential chain as invalid so that it will be recreated 755 * on the next attempt. 756 */ 757 static void 758 netr_invalidate_chain(void) 759 { 760 netr_global_info.flags &= ~NETR_FLG_VALID; 761 } 762 763 /* 764 * netr_setup_identity 765 * 766 * Set up the client identity information. All of this information is 767 * specifically related to the client user and workstation attempting 768 * to access this system. It may not be in our primary domain. 769 * 770 * I don't know what logon_id is, it seems to be a unique identifier. 771 * Increment it before each use. 772 */ 773 static void 774 netr_setup_identity(ndr_heap_t *heap, smb_logon_t *user_info, 775 netr_logon_id_t *identity) 776 { 777 static mutex_t logon_id_mutex; 778 static uint32_t logon_id; 779 780 (void) mutex_lock(&logon_id_mutex); 781 782 if (logon_id == 0) 783 logon_id = 0xDCD0; 784 785 ++logon_id; 786 user_info->lg_logon_id = logon_id; 787 788 (void) mutex_unlock(&logon_id_mutex); 789 790 /* 791 * [MS-APDS] 3.1.5.2 "NTLM Network Logon" says to set 792 * ParameterControl to the 'E' + 'K' bits. Those are: 793 * (1 << 5) | (1 << 11), a.k.a 794 */ 795 identity->parameter_control = 796 MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | 797 MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT; 798 identity->logon_id.LowPart = logon_id; 799 identity->logon_id.HighPart = 0; 800 801 ndr_heap_mkvcs(heap, user_info->lg_domain, 802 (ndr_vcstr_t *)&identity->domain_name); 803 804 ndr_heap_mkvcs(heap, user_info->lg_username, 805 (ndr_vcstr_t *)&identity->username); 806 807 /* 808 * Some systems prefix the client workstation name with \\. 809 * It doesn't seem to make any difference whether it's there 810 * or not. 811 */ 812 ndr_heap_mkvcs(heap, user_info->lg_workstation, 813 (ndr_vcstr_t *)&identity->workstation); 814 } 815 816 /* 817 * Add local and well-known group membership to the given 818 * token. Called after domain groups have been added. 819 */ 820 static uint32_t 821 netr_setup_token_wingrps(struct netr_validation_info3 *info3, 822 smb_token_t *token) 823 { 824 uint32_t status; 825 826 status = smb_sam_usr_groups(token->tkn_user.i_sid, 827 &token->tkn_win_grps); 828 if (status != NT_STATUS_SUCCESS) 829 return (status); 830 831 if (netr_isadmin(info3)) 832 token->tkn_flags |= SMB_ATF_ADMIN; 833 834 status = smb_wka_token_groups(token->tkn_flags, &token->tkn_win_grps); 835 836 return (status); 837 } 838 839 /* 840 * Converts groups information in the returned structure by domain controller 841 * (info3) to an internal representation (gids) 842 */ 843 static uint32_t 844 netr_setup_domain_groups(struct netr_validation_info3 *info3, smb_ids_t *gids) 845 { 846 smb_sid_t *domain_sid; 847 smb_id_t *ids; 848 int i, total_cnt; 849 850 if ((i = info3->GroupCount) == 0) 851 i++; 852 i += info3->SidCount; 853 854 total_cnt = gids->i_cnt + i; 855 856 gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t)); 857 if (gids->i_ids == NULL) 858 return (NT_STATUS_NO_MEMORY); 859 860 domain_sid = (smb_sid_t *)info3->LogonDomainId; 861 862 ids = gids->i_ids + gids->i_cnt; 863 for (i = 0; i < info3->GroupCount; i++, gids->i_cnt++, ids++) { 864 ids->i_sid = smb_sid_splice(domain_sid, info3->GroupIds[i].rid); 865 if (ids->i_sid == NULL) 866 return (NT_STATUS_NO_MEMORY); 867 868 ids->i_attrs = info3->GroupIds[i].attributes; 869 } 870 871 if (info3->GroupCount == 0) { 872 /* 873 * if there's no global group should add the primary group. 874 */ 875 ids->i_sid = smb_sid_splice(domain_sid, info3->PrimaryGroupId); 876 if (ids->i_sid == NULL) 877 return (NT_STATUS_NO_MEMORY); 878 879 ids->i_attrs = 0x7; 880 gids->i_cnt++; 881 ids++; 882 } 883 884 /* Add the extra SIDs */ 885 for (i = 0; i < info3->SidCount; i++, gids->i_cnt++, ids++) { 886 ids->i_sid = smb_sid_dup((smb_sid_t *)info3->ExtraSids[i].sid); 887 if (ids->i_sid == NULL) 888 return (NT_STATUS_NO_MEMORY); 889 890 ids->i_attrs = info3->ExtraSids[i].attributes; 891 } 892 893 return (NT_STATUS_SUCCESS); 894 } 895 896 /* 897 * Converts additional "resource" groups (from krb5_validation_info) 898 * into the internal representation (gids), appending to the list 899 * already put in place by netr_setup_domain_groups(). 900 */ 901 static uint32_t netr_setup_krb5res_groups(struct krb5_validation_info *info, 902 smb_ids_t *gids) 903 { 904 smb_sid_t *domain_sid; 905 smb_id_t *ids; 906 int i, total_cnt; 907 908 total_cnt = gids->i_cnt + info->rg_rid_cnt; 909 910 gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t)); 911 if (gids->i_ids == NULL) 912 return (NT_STATUS_NO_MEMORY); 913 914 domain_sid = (smb_sid_t *)info->rg_dom_sid; 915 916 ids = gids->i_ids + gids->i_cnt; 917 for (i = 0; i < info->rg_rid_cnt; i++, gids->i_cnt++, ids++) { 918 ids->i_sid = smb_sid_splice(domain_sid, info->rg_rids[i].rid); 919 if (ids->i_sid == NULL) 920 return (NT_STATUS_NO_MEMORY); 921 ids->i_attrs = info->rg_rids[i].attributes; 922 } 923 924 return (0); 925 } 926 927 /* 928 * Determines if the given user is the domain Administrator or a 929 * member of Domain Admins 930 */ 931 static boolean_t 932 netr_isadmin(struct netr_validation_info3 *info3) 933 { 934 smb_domain_t di; 935 int i; 936 937 if (!smb_domain_lookup_sid((smb_sid_t *)info3->LogonDomainId, &di)) 938 return (B_FALSE); 939 940 if (di.di_type != SMB_DOMAIN_PRIMARY) 941 return (B_FALSE); 942 943 if ((info3->UserId == DOMAIN_USER_RID_ADMIN) || 944 (info3->PrimaryGroupId == DOMAIN_GROUP_RID_ADMINS)) 945 return (B_TRUE); 946 947 for (i = 0; i < info3->GroupCount; i++) 948 if (info3->GroupIds[i].rid == DOMAIN_GROUP_RID_ADMINS) 949 return (B_TRUE); 950 951 return (B_FALSE); 952 } 953