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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * NETR SamLogon and SamLogoff RPC client functions. 28 */ 29 30 #include <stdio.h> 31 #include <strings.h> 32 #include <stdlib.h> 33 #include <time.h> 34 #include <alloca.h> 35 #include <unistd.h> 36 #include <netdb.h> 37 #include <thread.h> 38 39 #include <smbsrv/libsmb.h> 40 #include <smbsrv/libsmbrdr.h> 41 #include <smbsrv/libmlrpc.h> 42 #include <smbsrv/libmlsvc.h> 43 #include <smbsrv/ndl/netlogon.ndl> 44 #include <smbsrv/netrauth.h> 45 #include <smbsrv/ntstatus.h> 46 #include <smbsrv/smbinfo.h> 47 #include <smbsrv/smb_token.h> 48 #include <mlsvc.h> 49 50 static uint32_t netlogon_logon_private(netr_client_t *, smb_token_t *); 51 static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *, 52 netr_client_t *, smb_token_t *); 53 static void netr_invalidate_chain(void); 54 static void netr_interactive_samlogon(netr_info_t *, netr_client_t *, 55 struct netr_logon_info1 *); 56 static void netr_network_samlogon(ndr_heap_t *, netr_info_t *, 57 netr_client_t *, struct netr_logon_info2 *); 58 static void netr_setup_identity(ndr_heap_t *, netr_client_t *, 59 netr_logon_id_t *); 60 static boolean_t netr_isadmin(struct netr_validation_info3 *); 61 static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *, 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_logon_mutex; 72 73 /* 74 * netlogon_logon 75 * 76 * This is the entry point for authenticating a remote logon. The 77 * parameters here all refer to the remote user and workstation, i.e. 78 * the domain is the user's account domain, not our primary domain. 79 * In order to make it easy to track which domain is being used at 80 * each stage, and to reduce the number of things being pushed on the 81 * stack, the client information is bundled up in the clnt structure. 82 * 83 * If the user is successfully authenticated, an access token will be 84 * built and NT_STATUS_SUCCESS will be returned. Otherwise a non-zero 85 * NT status will be returned, in which case the token contents will 86 * be invalid. 87 */ 88 uint32_t 89 netlogon_logon(netr_client_t *clnt, smb_token_t *token) 90 { 91 uint32_t status; 92 93 (void) mutex_lock(&netlogon_logon_mutex); 94 95 status = netlogon_logon_private(clnt, token); 96 97 (void) mutex_unlock(&netlogon_logon_mutex); 98 return (status); 99 } 100 101 static uint32_t 102 netlogon_logon_private(netr_client_t *clnt, smb_token_t *token) 103 { 104 char resource_domain[SMB_PI_MAX_DOMAIN]; 105 char server[NETBIOS_NAME_SZ * 2]; 106 mlsvc_handle_t netr_handle; 107 smb_domain_t di; 108 uint32_t status; 109 int retries = 0, server_changed = 0; 110 111 (void) smb_getdomainname(resource_domain, SMB_PI_MAX_DOMAIN); 112 113 if (!smb_domain_getinfo(&di)) 114 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 115 116 if ((mlsvc_echo(di.d_dc)) < 0) { 117 /* 118 * We had a session to the DC but it's not responding. 119 * So drop the credential chain. 120 */ 121 netr_invalidate_chain(); 122 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 123 } 124 125 do { 126 if (netr_open(di.d_dc, di.d_info.di_nbname, &netr_handle) != 0) 127 return (NT_STATUS_OPEN_FAILED); 128 129 if (di.d_dc && (*netr_global_info.server != '\0')) { 130 (void) snprintf(server, sizeof (server), 131 "\\\\%s", di.d_dc); 132 server_changed = strncasecmp(netr_global_info.server, 133 server, strlen(server)); 134 } 135 136 if (server_changed || 137 (netr_global_info.flags & NETR_FLG_VALID) == 0 || 138 !smb_match_netlogon_seqnum()) { 139 status = netlogon_auth(di.d_dc, &netr_handle, 140 NETR_FLG_NULL); 141 142 if (status != 0) { 143 (void) netr_close(&netr_handle); 144 return (NT_STATUS_LOGON_FAILURE); 145 } 146 147 netr_global_info.flags |= NETR_FLG_VALID; 148 } 149 150 status = netr_server_samlogon(&netr_handle, 151 &netr_global_info, di.d_dc, clnt, token); 152 153 (void) netr_close(&netr_handle); 154 } while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3); 155 156 if (retries >= 3) 157 status = NT_STATUS_LOGON_FAILURE; 158 159 return (status); 160 } 161 162 static uint32_t 163 netr_setup_token(struct netr_validation_info3 *info3, netr_client_t *clnt, 164 netr_info_t *netr_info, smb_token_t *token) 165 { 166 char *username, *domain; 167 unsigned char rc4key[SMBAUTH_SESSION_KEY_SZ]; 168 smb_sid_t *domsid; 169 uint32_t status; 170 char nbdomain[NETBIOS_NAME_SZ]; 171 172 domsid = (smb_sid_t *)info3->LogonDomainId; 173 174 token->tkn_user.i_sid = smb_sid_splice(domsid, info3->UserId); 175 if (token->tkn_user.i_sid == NULL) 176 return (NT_STATUS_NO_MEMORY); 177 178 token->tkn_primary_grp.i_sid = smb_sid_splice(domsid, 179 info3->PrimaryGroupId); 180 if (token->tkn_primary_grp.i_sid == NULL) 181 return (NT_STATUS_NO_MEMORY); 182 183 username = (info3->EffectiveName.str) 184 ? (char *)info3->EffectiveName.str : clnt->e_username; 185 186 if (info3->LogonDomainName.str) { 187 domain = (char *)info3->LogonDomainName.str; 188 } else if (*clnt->e_domain != '\0') { 189 domain = clnt->e_domain; 190 } else { 191 (void) smb_getdomainname(nbdomain, sizeof (nbdomain)); 192 domain = nbdomain; 193 } 194 195 if (username) 196 token->tkn_account_name = strdup(username); 197 if (domain) 198 token->tkn_domain_name = strdup(domain); 199 200 if (token->tkn_account_name == NULL || token->tkn_domain_name == NULL) 201 return (NT_STATUS_NO_MEMORY); 202 203 status = netr_setup_token_wingrps(info3, token); 204 if (status != NT_STATUS_SUCCESS) 205 return (status); 206 207 /* 208 * The UserSessionKey in NetrSamLogon RPC is obfuscated using the 209 * session key obtained in the NETLOGON credential chain. 210 * An 8 byte session key is zero extended to 16 bytes. This 16 byte 211 * key is the key to the RC4 algorithm. The RC4 byte stream is 212 * exclusively ored with the 16 byte UserSessionKey to recover 213 * the the clear form. 214 */ 215 if ((token->tkn_session_key = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL) 216 return (NT_STATUS_NO_MEMORY); 217 bzero(rc4key, SMBAUTH_SESSION_KEY_SZ); 218 bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len); 219 bcopy(info3->UserSessionKey.data, token->tkn_session_key, 220 SMBAUTH_SESSION_KEY_SZ); 221 rand_hash((unsigned char *)token->tkn_session_key, 222 SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ); 223 224 return (NT_STATUS_SUCCESS); 225 } 226 227 /* 228 * netr_server_samlogon 229 * 230 * NetrServerSamLogon RPC: interactive or network. It is assumed that 231 * we have already authenticated with the PDC. If everything works, 232 * we build a user info structure and return it, where the caller will 233 * probably build an access token. 234 * 235 * Returns an NT status. There are numerous possibilities here. 236 * For example: 237 * NT_STATUS_INVALID_INFO_CLASS 238 * NT_STATUS_INVALID_PARAMETER 239 * NT_STATUS_ACCESS_DENIED 240 * NT_STATUS_PASSWORD_MUST_CHANGE 241 * NT_STATUS_NO_SUCH_USER 242 * NT_STATUS_WRONG_PASSWORD 243 * NT_STATUS_LOGON_FAILURE 244 * NT_STATUS_ACCOUNT_RESTRICTION 245 * NT_STATUS_INVALID_LOGON_HOURS 246 * NT_STATUS_INVALID_WORKSTATION 247 * NT_STATUS_INTERNAL_ERROR 248 * NT_STATUS_PASSWORD_EXPIRED 249 * NT_STATUS_ACCOUNT_DISABLED 250 */ 251 uint32_t 252 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info, 253 char *server, netr_client_t *clnt, smb_token_t *token) 254 { 255 struct netr_SamLogon arg; 256 struct netr_authenticator auth; 257 struct netr_authenticator ret_auth; 258 struct netr_logon_info1 info1; 259 struct netr_logon_info2 info2; 260 struct netr_validation_info3 *info3; 261 ndr_heap_t *heap; 262 int opnum; 263 int rc, len; 264 uint32_t status; 265 266 bzero(&arg, sizeof (struct netr_SamLogon)); 267 opnum = NETR_OPNUM_SamLogon; 268 269 /* 270 * Should we get the server and hostname from netr_info? 271 */ 272 273 len = strlen(server) + 4; 274 arg.servername = ndr_rpc_malloc(netr_handle, len); 275 arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ); 276 if (arg.servername == NULL || arg.hostname == NULL) { 277 ndr_rpc_release(netr_handle); 278 return (NT_STATUS_INTERNAL_ERROR); 279 } 280 281 (void) snprintf((char *)arg.servername, len, "\\\\%s", server); 282 if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) { 283 ndr_rpc_release(netr_handle); 284 return (NT_STATUS_INTERNAL_ERROR); 285 } 286 287 rc = netr_setup_authenticator(netr_info, &auth, &ret_auth); 288 if (rc != SMBAUTH_SUCCESS) { 289 ndr_rpc_release(netr_handle); 290 return (NT_STATUS_INTERNAL_ERROR); 291 } 292 293 arg.auth = &auth; 294 arg.ret_auth = &ret_auth; 295 arg.validation_level = NETR_VALIDATION_LEVEL3; 296 arg.logon_info.logon_level = clnt->logon_level; 297 arg.logon_info.switch_value = clnt->logon_level; 298 299 heap = ndr_rpc_get_heap(netr_handle); 300 301 switch (clnt->logon_level) { 302 case NETR_INTERACTIVE_LOGON: 303 netr_setup_identity(heap, clnt, &info1.identity); 304 netr_interactive_samlogon(netr_info, clnt, &info1); 305 arg.logon_info.ru.info1 = &info1; 306 break; 307 308 case NETR_NETWORK_LOGON: 309 netr_setup_identity(heap, clnt, &info2.identity); 310 netr_network_samlogon(heap, netr_info, clnt, &info2); 311 arg.logon_info.ru.info2 = &info2; 312 break; 313 314 default: 315 ndr_rpc_release(netr_handle); 316 return (NT_STATUS_INVALID_PARAMETER); 317 } 318 319 rc = ndr_rpc_call(netr_handle, opnum, &arg); 320 if (rc != 0) { 321 bzero(netr_info, sizeof (netr_info_t)); 322 status = NT_STATUS_INVALID_PARAMETER; 323 } else if (arg.status != 0) { 324 status = NT_SC_VALUE(arg.status); 325 326 /* 327 * We need to validate the chain even though we have 328 * a non-zero status. If the status is ACCESS_DENIED 329 * this will trigger a new credential chain. However, 330 * a valid credential is returned with some status 331 * codes; for example, WRONG_PASSWORD. 332 */ 333 (void) netr_validate_chain(netr_info, arg.ret_auth); 334 } else { 335 status = netr_validate_chain(netr_info, arg.ret_auth); 336 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { 337 ndr_rpc_release(netr_handle); 338 return (status); 339 } 340 341 info3 = arg.ru.info3; 342 status = netr_setup_token(info3, clnt, netr_info, token); 343 } 344 345 ndr_rpc_release(netr_handle); 346 return (status); 347 } 348 349 /* 350 * netr_interactive_samlogon 351 * 352 * Set things up for an interactive SamLogon. Copy the NT and LM 353 * passwords to the logon structure and hash them with the session 354 * key. 355 */ 356 static void 357 netr_interactive_samlogon(netr_info_t *netr_info, netr_client_t *clnt, 358 struct netr_logon_info1 *info1) 359 { 360 BYTE key[NETR_OWF_PASSWORD_SZ]; 361 362 (void) memcpy(&info1->lm_owf_password, 363 clnt->lm_password.lm_password_val, sizeof (netr_owf_password_t)); 364 365 (void) memcpy(&info1->nt_owf_password, 366 clnt->nt_password.nt_password_val, sizeof (netr_owf_password_t)); 367 368 (void) memset(key, 0, NETR_OWF_PASSWORD_SZ); 369 (void) memcpy(key, netr_info->session_key.key, 370 netr_info->session_key.len); 371 372 rand_hash((unsigned char *)&info1->lm_owf_password, 373 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ); 374 375 rand_hash((unsigned char *)&info1->nt_owf_password, 376 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ); 377 } 378 379 /* 380 * netr_network_samlogon 381 * 382 * Set things up for a network SamLogon. We provide a copy of the random 383 * challenge, that we sent to the client, to the domain controller. This 384 * is the key that the client will have used to encrypt the NT and LM 385 * passwords. Note that Windows 9x clients may not provide both passwords. 386 */ 387 /*ARGSUSED*/ 388 static void 389 netr_network_samlogon(ndr_heap_t *heap, netr_info_t *netr_info, 390 netr_client_t *clnt, struct netr_logon_info2 *info2) 391 { 392 uint32_t len; 393 394 bcopy(clnt->challenge_key.challenge_key_val, info2->lm_challenge.data, 395 8); 396 397 if ((len = clnt->nt_password.nt_password_len) != 0) { 398 ndr_heap_mkvcb(heap, clnt->nt_password.nt_password_val, len, 399 (ndr_vcbuf_t *)&info2->nt_response); 400 } else { 401 bzero(&info2->nt_response, sizeof (netr_vcbuf_t)); 402 } 403 404 if ((len = clnt->lm_password.lm_password_len) != 0) { 405 ndr_heap_mkvcb(heap, clnt->lm_password.lm_password_val, len, 406 (ndr_vcbuf_t *)&info2->lm_response); 407 } else { 408 bzero(&info2->lm_response, sizeof (netr_vcbuf_t)); 409 } 410 } 411 412 /* 413 * netr_setup_authenticator 414 * 415 * Set up the request and return authenticators. A new credential is 416 * generated from the session key, the current client credential and 417 * the current time, i.e. 418 * 419 * NewCredential = Cred(SessionKey, OldCredential, time); 420 * 421 * The timestamp, which is used as a random seed, is stored in both 422 * the request and return authenticators. 423 * 424 * If any difficulties occur using the cryptographic framework, the 425 * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is 426 * returned. 427 */ 428 int 429 netr_setup_authenticator(netr_info_t *netr_info, 430 struct netr_authenticator *auth, struct netr_authenticator *ret_auth) 431 { 432 bzero(auth, sizeof (struct netr_authenticator)); 433 434 netr_info->timestamp = time(0); 435 auth->timestamp = netr_info->timestamp; 436 437 if (netr_gen_credentials(netr_info->session_key.key, 438 &netr_info->client_credential, 439 netr_info->timestamp, 440 (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS) 441 return (SMBAUTH_FAILURE); 442 443 if (ret_auth) { 444 bzero(ret_auth, sizeof (struct netr_authenticator)); 445 ret_auth->timestamp = netr_info->timestamp; 446 } 447 448 return (SMBAUTH_SUCCESS); 449 } 450 451 /* 452 * Validate the returned credentials and update the credential chain. 453 * The server returns an updated client credential rather than a new 454 * server credential. The server uses (timestamp + 1) when generating 455 * the credential. 456 * 457 * Generate the new seed for the credential chain. The new seed is 458 * formed by adding (timestamp + 1) to the current client credential. 459 * The only quirk is the uint32_t style addition. 460 * 461 * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a 462 * NULL pointer. The Authenticator field of the SamLogon response packet 463 * sent by the Samba 3 PDC always return NULL pointer if the received 464 * SamLogon request is not immediately followed by the ServerReqChallenge 465 * and ServerAuthenticate2 requests. 466 * 467 * Returns NT_STATUS_SUCCESS if the server returned a valid credential. 468 * Otherwise we retirm NT_STATUS_UNSUCCESSFUL. 469 */ 470 uint32_t 471 netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth) 472 { 473 netr_cred_t cred; 474 uint32_t result = NT_STATUS_SUCCESS; 475 uint32_t *dwp; 476 477 ++netr_info->timestamp; 478 479 if (netr_gen_credentials(netr_info->session_key.key, 480 &netr_info->client_credential, 481 netr_info->timestamp, &cred) != SMBAUTH_SUCCESS) 482 return (NT_STATUS_INTERNAL_ERROR); 483 484 if (&auth->credential == 0) { 485 /* 486 * If the validation fails, destroy the credential chain. 487 * This should trigger a new authentication chain. 488 */ 489 bzero(netr_info, sizeof (netr_info_t)); 490 return (NT_STATUS_INSUFFICIENT_LOGON_INFO); 491 } 492 493 result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t)); 494 if (result != 0) { 495 /* 496 * If the validation fails, destroy the credential chain. 497 * This should trigger a new authentication chain. 498 */ 499 bzero(netr_info, sizeof (netr_info_t)); 500 result = NT_STATUS_UNSUCCESSFUL; 501 } else { 502 /* 503 * Otherwise generate the next step in the chain. 504 */ 505 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 506 dwp = (uint32_t *)&netr_info->client_credential; 507 dwp[0] += netr_info->timestamp; 508 509 netr_info->flags |= NETR_FLG_VALID; 510 } 511 512 return (result); 513 } 514 515 /* 516 * netr_invalidate_chain 517 * 518 * Mark the credential chain as invalid so that it will be recreated 519 * on the next attempt. 520 */ 521 static void 522 netr_invalidate_chain(void) 523 { 524 netr_global_info.flags &= ~NETR_FLG_VALID; 525 } 526 527 /* 528 * netr_setup_identity 529 * 530 * Set up the client identity information. All of this information is 531 * specifically related to the client user and workstation attempting 532 * to access this system. It may not be in our primary domain. 533 * 534 * I don't know what logon_id is, it seems to be a unique identifier. 535 * Increment it before each use. 536 */ 537 static void 538 netr_setup_identity(ndr_heap_t *heap, netr_client_t *clnt, 539 netr_logon_id_t *identity) 540 { 541 static mutex_t logon_id_mutex; 542 static uint32_t logon_id; 543 544 (void) mutex_lock(&logon_id_mutex); 545 546 if (logon_id == 0) 547 logon_id = 0xDCD0; 548 549 ++logon_id; 550 clnt->logon_id = logon_id; 551 552 (void) mutex_unlock(&logon_id_mutex); 553 554 identity->parameter_control = 0; 555 identity->logon_id.LowPart = logon_id; 556 identity->logon_id.HighPart = 0; 557 558 ndr_heap_mkvcs(heap, clnt->domain, 559 (ndr_vcstr_t *)&identity->domain_name); 560 561 ndr_heap_mkvcs(heap, clnt->username, 562 (ndr_vcstr_t *)&identity->username); 563 564 /* 565 * Some systems prefix the client workstation name with \\. 566 * It doesn't seem to make any difference whether it's there 567 * or not. 568 */ 569 ndr_heap_mkvcs(heap, clnt->workstation, 570 (ndr_vcstr_t *)&identity->workstation); 571 } 572 573 /* 574 * Sets up domain, local and well-known group membership for the given 575 * token. Two assumptions have been made here: 576 * 577 * a) token already contains a valid user SID so that group 578 * memberships can be established 579 * 580 * b) token belongs to a domain user 581 */ 582 static uint32_t 583 netr_setup_token_wingrps(struct netr_validation_info3 *info3, 584 smb_token_t *token) 585 { 586 smb_ids_t tkn_grps; 587 uint32_t status; 588 589 tkn_grps.i_cnt = 0; 590 tkn_grps.i_ids = NULL; 591 592 status = netr_setup_domain_groups(info3, &tkn_grps); 593 if (status != NT_STATUS_SUCCESS) { 594 smb_ids_free(&tkn_grps); 595 return (status); 596 } 597 598 status = smb_sam_usr_groups(token->tkn_user.i_sid, &tkn_grps); 599 if (status != NT_STATUS_SUCCESS) { 600 smb_ids_free(&tkn_grps); 601 return (status); 602 } 603 604 if (netr_isadmin(info3)) 605 token->tkn_flags |= SMB_ATF_ADMIN; 606 607 status = smb_wka_token_groups(token->tkn_flags, &tkn_grps); 608 if (status == NT_STATUS_SUCCESS) 609 token->tkn_win_grps = tkn_grps; 610 else 611 smb_ids_free(&tkn_grps); 612 613 return (status); 614 } 615 616 /* 617 * Converts groups information in the returned structure by domain controller 618 * (info3) to an internal representation (gids) 619 */ 620 static uint32_t 621 netr_setup_domain_groups(struct netr_validation_info3 *info3, smb_ids_t *gids) 622 { 623 smb_sid_t *domain_sid; 624 smb_id_t *ids; 625 int i, total_cnt; 626 627 if ((i = info3->GroupCount) == 0) 628 i++; 629 i += info3->SidCount; 630 631 total_cnt = gids->i_cnt + i; 632 633 gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t)); 634 if (gids->i_ids == NULL) 635 return (NT_STATUS_NO_MEMORY); 636 637 domain_sid = (smb_sid_t *)info3->LogonDomainId; 638 639 ids = gids->i_ids + gids->i_cnt; 640 for (i = 0; i < info3->GroupCount; i++, gids->i_cnt++, ids++) { 641 ids->i_sid = smb_sid_splice(domain_sid, info3->GroupIds[i].rid); 642 if (ids->i_sid == NULL) 643 return (NT_STATUS_NO_MEMORY); 644 645 ids->i_attrs = info3->GroupIds[i].attributes; 646 } 647 648 if (info3->GroupCount == 0) { 649 /* 650 * if there's no global group should add the primary group. 651 */ 652 ids->i_sid = smb_sid_splice(domain_sid, info3->PrimaryGroupId); 653 if (ids->i_sid == NULL) 654 return (NT_STATUS_NO_MEMORY); 655 656 ids->i_attrs = 0x7; 657 gids->i_cnt++; 658 ids++; 659 } 660 661 /* Add the extra SIDs */ 662 for (i = 0; i < info3->SidCount; i++, gids->i_cnt++, ids++) { 663 ids->i_sid = smb_sid_dup((smb_sid_t *)info3->ExtraSids[i].sid); 664 if (ids->i_sid == NULL) 665 return (NT_STATUS_NO_MEMORY); 666 667 ids->i_attrs = info3->ExtraSids[i].attributes; 668 } 669 670 return (NT_STATUS_SUCCESS); 671 } 672 673 /* 674 * Determines if the given user is the domain Administrator or a 675 * member of Domain Admins 676 */ 677 static boolean_t 678 netr_isadmin(struct netr_validation_info3 *info3) 679 { 680 nt_domain_t di; 681 int i; 682 683 if (!nt_domain_lookup_sid((smb_sid_t *)info3->LogonDomainId, &di)) 684 return (B_FALSE); 685 686 if (di.di_type != NT_DOMAIN_PRIMARY) 687 return (B_FALSE); 688 689 if ((info3->UserId == DOMAIN_USER_RID_ADMIN) || 690 (info3->PrimaryGroupId == DOMAIN_GROUP_RID_ADMINS)) 691 return (B_TRUE); 692 693 for (i = 0; i < info3->GroupCount; i++) 694 if (info3->GroupIds[i].rid == DOMAIN_GROUP_RID_ADMINS) 695 return (B_TRUE); 696 697 return (B_FALSE); 698 } 699