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