Home | History | Annotate | Download | only in common
      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/libmlrpc.h>
     41 #include <smbsrv/libmlsvc.h>
     42 #include <smbsrv/ndl/netlogon.ndl>
     43 #include <smbsrv/netrauth.h>
     44 #include <smbsrv/ntstatus.h>
     45 #include <smbsrv/smbinfo.h>
     46 #include <smbsrv/smb_token.h>
     47 #include <mlsvc.h>
     48 
     49 static uint32_t netlogon_logon_private(netr_client_t *, smb_token_t *);
     50 static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *,
     51     netr_client_t *, smb_token_t *);
     52 static void netr_invalidate_chain(void);
     53 static void netr_interactive_samlogon(netr_info_t *, netr_client_t *,
     54     struct netr_logon_info1 *);
     55 static void netr_network_samlogon(ndr_heap_t *, netr_info_t *,
     56     netr_client_t *, struct netr_logon_info2 *);
     57 static void netr_setup_identity(ndr_heap_t *, netr_client_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_token_wingrps(struct netr_validation_info3 *,
     63     smb_token_t *);
     64 
     65 /*
     66  * Shared with netr_auth.c
     67  */
     68 extern netr_info_t netr_global_info;
     69 
     70 static mutex_t netlogon_logon_mutex;
     71 
     72 /*
     73  * netlogon_logon
     74  *
     75  * This is the entry point for authenticating a remote logon. The
     76  * parameters here all refer to the remote user and workstation, i.e.
     77  * the domain is the user's account domain, not our primary domain.
     78  * In order to make it easy to track which domain is being used at
     79  * each stage, and to reduce the number of things being pushed on the
     80  * stack, the client information is bundled up in the clnt structure.
     81  *
     82  * If the user is successfully authenticated, an access token will be
     83  * built and NT_STATUS_SUCCESS will be returned. Otherwise a non-zero
     84  * NT status will be returned, in which case the token contents will
     85  * be invalid.
     86  */
     87 uint32_t
     88 netlogon_logon(netr_client_t *clnt, smb_token_t *token)
     89 {
     90 	uint32_t status;
     91 
     92 	(void) mutex_lock(&netlogon_logon_mutex);
     93 
     94 	status = netlogon_logon_private(clnt, token);
     95 
     96 	(void) mutex_unlock(&netlogon_logon_mutex);
     97 	return (status);
     98 }
     99 
    100 static uint32_t
    101 netlogon_logon_private(netr_client_t *clnt, smb_token_t *token)
    102 {
    103 	char resource_domain[SMB_PI_MAX_DOMAIN];
    104 	char server[NETBIOS_NAME_SZ * 2];
    105 	mlsvc_handle_t netr_handle;
    106 	smb_domainex_t di;
    107 	uint32_t status;
    108 	int retries = 0, server_changed = 0;
    109 
    110 	(void) smb_getdomainname(resource_domain, SMB_PI_MAX_DOMAIN);
    111 
    112 	if (!smb_domain_getinfo(&di))
    113 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
    114 
    115 	if (mlsvc_ping(di.d_dc) < 0) {
    116 		/*
    117 		 * We had a session to the DC but it's not responding.
    118 		 * So drop the credential chain.
    119 		 */
    120 		netr_invalidate_chain();
    121 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
    122 	}
    123 
    124 	do {
    125 		if (netr_open(di.d_dc, di.d_primary.di_nbname, &netr_handle)
    126 		    != 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 	smb_domain_t di;
    681 	int i;
    682 
    683 	if (!smb_domain_lookup_sid((smb_sid_t *)info3->LogonDomainId, &di))
    684 		return (B_FALSE);
    685 
    686 	if (di.di_type != SMB_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