xref: /titanic_54/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c (revision 12b65585e720714b31036daaa2b30eb76014048e)
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 2015 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 <smbsrv/libsmb.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/smbinfo.h>
46 #include <smbsrv/smb_token.h>
47 #include <mlsvc.h>
48 
49 #define	NETLOGON_ATTEMPTS	2
50 
51 static uint32_t netlogon_logon(smb_logon_t *, smb_token_t *);
52 static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *,
53     smb_logon_t *, smb_token_t *);
54 static void netr_invalidate_chain(void);
55 static void netr_interactive_samlogon(netr_info_t *, smb_logon_t *,
56     struct netr_logon_info1 *);
57 static void netr_network_samlogon(ndr_heap_t *, netr_info_t *,
58     smb_logon_t *, struct netr_logon_info2 *);
59 static void netr_setup_identity(ndr_heap_t *, smb_logon_t *,
60     netr_logon_id_t *);
61 static boolean_t netr_isadmin(struct netr_validation_info3 *);
62 static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *,
63     smb_ids_t *);
64 static uint32_t netr_setup_token_info3(struct netr_validation_info3 *,
65     smb_token_t *);
66 static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *,
67     smb_token_t *);
68 
69 /*
70  * Shared with netr_auth.c
71  */
72 extern netr_info_t netr_global_info;
73 
74 static mutex_t netlogon_mutex;
75 static cond_t netlogon_cv;
76 static boolean_t netlogon_busy = B_FALSE;
77 static boolean_t netlogon_abort = B_FALSE;
78 
79 /*
80  * Helper for Kerberos authentication
81  */
82 uint32_t
83 smb_decode_krb5_pac(smb_token_t *token, char *data, uint_t len)
84 {
85 	struct krb5_validation_info info;
86 	ndr_buf_t *nbuf;
87 	uint32_t status = NT_STATUS_NO_MEMORY;
88 	int rc;
89 
90 	bzero(&info, sizeof (info));
91 
92 	/* Need to keep this until we're done with &info */
93 	nbuf = ndr_buf_init(&TYPEINFO(netr_interface));
94 	if (nbuf == NULL)
95 		goto out;
96 
97 	rc = ndr_buf_decode(nbuf, NDR_PTYPE_PAC,
98 	    NETR_OPNUM_decode_krb5_pac, data, len, &info);
99 	if (rc != NDR_DRC_OK) {
100 		status = RPC_NT_PROTOCOL_ERROR;
101 		goto out;
102 	}
103 
104 	status = netr_setup_token_info3(&info.info3, token);
105 
106 	/* Deal with the "resource groups"? */
107 
108 
109 out:
110 	if (nbuf != NULL)
111 		ndr_buf_fini(nbuf);
112 
113 	return (status);
114 }
115 
116 /*
117  * Code factored out of netr_setup_token()
118  */
119 static uint32_t
120 netr_setup_token_info3(struct netr_validation_info3 *info3,
121     smb_token_t *token)
122 {
123 	smb_sid_t *domsid;
124 
125 	domsid = (smb_sid_t *)info3->LogonDomainId;
126 
127 	token->tkn_user.i_sid = smb_sid_splice(domsid,
128 	    info3->UserId);
129 	if (token->tkn_user.i_sid == NULL)
130 		goto errout;
131 
132 	token->tkn_primary_grp.i_sid = smb_sid_splice(domsid,
133 	    info3->PrimaryGroupId);
134 	if (token->tkn_primary_grp.i_sid == NULL)
135 		goto errout;
136 
137 	if (info3->EffectiveName.str) {
138 		token->tkn_account_name =
139 		    strdup((char *)info3->EffectiveName.str);
140 		if (token->tkn_account_name == NULL)
141 			goto errout;
142 	}
143 
144 	if (info3->LogonDomainName.str) {
145 		token->tkn_domain_name =
146 		    strdup((char *)info3->LogonDomainName.str);
147 		if (token->tkn_domain_name == NULL)
148 			goto errout;
149 	}
150 
151 	return (netr_setup_token_wingrps(info3, token));
152 errout:
153 	return (NT_STATUS_INSUFF_SERVER_RESOURCES);
154 }
155 
156 /*
157  * Abort impending domain logon requests.
158  */
159 void
160 smb_logon_abort(void)
161 {
162 	(void) mutex_lock(&netlogon_mutex);
163 	if (netlogon_busy && !netlogon_abort)
164 		syslog(LOG_DEBUG, "logon abort");
165 	netlogon_abort = B_TRUE;
166 	(void) cond_broadcast(&netlogon_cv);
167 	(void) mutex_unlock(&netlogon_mutex);
168 }
169 
170 /*
171  * This is the entry point for authenticating domain users.
172  *
173  * If we are not going to attempt to authenticate the user,
174  * this function must return without updating the status.
175  *
176  * If the user is successfully authenticated, we build an
177  * access token and the status will be NT_STATUS_SUCCESS.
178  * Otherwise, the token contents are invalid.
179  */
180 void
181 smb_logon_domain(smb_logon_t *user_info, smb_token_t *token)
182 {
183 	uint32_t	status;
184 	int		i;
185 
186 	if (user_info->lg_secmode != SMB_SECMODE_DOMAIN)
187 		return;
188 
189 	if (user_info->lg_domain_type == SMB_DOMAIN_LOCAL)
190 		return;
191 
192 	for (i = 0; i < NETLOGON_ATTEMPTS; ++i) {
193 		(void) mutex_lock(&netlogon_mutex);
194 		while (netlogon_busy && !netlogon_abort)
195 			(void) cond_wait(&netlogon_cv, &netlogon_mutex);
196 
197 		if (netlogon_abort) {
198 			(void) mutex_unlock(&netlogon_mutex);
199 			user_info->lg_status = NT_STATUS_REQUEST_ABORTED;
200 			return;
201 		}
202 
203 		netlogon_busy = B_TRUE;
204 		(void) mutex_unlock(&netlogon_mutex);
205 
206 		status = netlogon_logon(user_info, token);
207 
208 		(void) mutex_lock(&netlogon_mutex);
209 		netlogon_busy = B_FALSE;
210 		if (netlogon_abort)
211 			status = NT_STATUS_REQUEST_ABORTED;
212 		(void) cond_signal(&netlogon_cv);
213 		(void) mutex_unlock(&netlogon_mutex);
214 
215 		if (status != NT_STATUS_CANT_ACCESS_DOMAIN_INFO)
216 			break;
217 	}
218 
219 	if (status != NT_STATUS_SUCCESS)
220 		syslog(LOG_INFO, "logon[%s\\%s]: %s", user_info->lg_e_domain,
221 		    user_info->lg_e_username, xlate_nt_status(status));
222 
223 	user_info->lg_status = status;
224 }
225 
226 static uint32_t
227 netlogon_logon(smb_logon_t *user_info, smb_token_t *token)
228 {
229 	char resource_domain[SMB_PI_MAX_DOMAIN];
230 	char server[NETBIOS_NAME_SZ * 2];
231 	mlsvc_handle_t netr_handle;
232 	smb_domainex_t di;
233 	uint32_t status;
234 	int retries = 0;
235 
236 	(void) smb_getdomainname(resource_domain, SMB_PI_MAX_DOMAIN);
237 
238 	/* Avoid interfering with DC discovery. */
239 	if (smb_ddiscover_wait() != 0 ||
240 	    !smb_domain_getinfo(&di)) {
241 		netr_invalidate_chain();
242 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
243 	}
244 
245 	do {
246 		if (netr_open(di.d_dc, di.d_primary.di_nbname, &netr_handle)
247 		    != 0)
248 			return (NT_STATUS_OPEN_FAILED);
249 
250 		if (di.d_dc && (*netr_global_info.server != '\0')) {
251 			(void) snprintf(server, sizeof (server),
252 			    "\\\\%s", di.d_dc);
253 			if (strncasecmp(netr_global_info.server,
254 			    server, strlen(server)) != 0)
255 				netr_invalidate_chain();
256 		}
257 
258 		if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
259 		    !smb_match_netlogon_seqnum()) {
260 			status = netlogon_auth(di.d_dc, &netr_handle,
261 			    NETR_FLG_NULL);
262 
263 			if (status != 0) {
264 				(void) netr_close(&netr_handle);
265 				return (NT_STATUS_LOGON_FAILURE);
266 			}
267 
268 			netr_global_info.flags |= NETR_FLG_VALID;
269 		}
270 
271 		status = netr_server_samlogon(&netr_handle,
272 		    &netr_global_info, di.d_dc, user_info, token);
273 
274 		(void) netr_close(&netr_handle);
275 	} while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3);
276 
277 	if (retries >= 3)
278 		status = NT_STATUS_LOGON_FAILURE;
279 
280 	return (status);
281 }
282 
283 static uint32_t
284 netr_setup_token(struct netr_validation_info3 *info3, smb_logon_t *user_info,
285     netr_info_t *netr_info, smb_token_t *token)
286 {
287 	char *username, *domain;
288 	unsigned char rc4key[SMBAUTH_SESSION_KEY_SZ];
289 	smb_sid_t *domsid;
290 	uint32_t status;
291 	char nbdomain[NETBIOS_NAME_SZ];
292 
293 	domsid = (smb_sid_t *)info3->LogonDomainId;
294 
295 	token->tkn_user.i_sid = smb_sid_splice(domsid, info3->UserId);
296 	if (token->tkn_user.i_sid == NULL)
297 		return (NT_STATUS_NO_MEMORY);
298 
299 	token->tkn_primary_grp.i_sid = smb_sid_splice(domsid,
300 	    info3->PrimaryGroupId);
301 	if (token->tkn_primary_grp.i_sid == NULL)
302 		return (NT_STATUS_NO_MEMORY);
303 
304 	username = (info3->EffectiveName.str)
305 	    ? (char *)info3->EffectiveName.str : user_info->lg_e_username;
306 
307 	if (info3->LogonDomainName.str) {
308 		domain = (char *)info3->LogonDomainName.str;
309 	} else if (*user_info->lg_e_domain != '\0') {
310 		domain = user_info->lg_e_domain;
311 	} else {
312 		(void) smb_getdomainname(nbdomain, sizeof (nbdomain));
313 		domain = nbdomain;
314 	}
315 
316 	if (username)
317 		token->tkn_account_name = strdup(username);
318 	if (domain)
319 		token->tkn_domain_name = strdup(domain);
320 
321 	if (token->tkn_account_name == NULL || token->tkn_domain_name == NULL)
322 		return (NT_STATUS_NO_MEMORY);
323 
324 	status = netr_setup_token_wingrps(info3, token);
325 	if (status != NT_STATUS_SUCCESS)
326 		return (status);
327 
328 	/*
329 	 * The UserSessionKey in NetrSamLogon RPC is obfuscated using the
330 	 * session key obtained in the NETLOGON credential chain.
331 	 * An 8 byte session key is zero extended to 16 bytes. This 16 byte
332 	 * key is the key to the RC4 algorithm. The RC4 byte stream is
333 	 * exclusively ored with the 16 byte UserSessionKey to recover
334 	 * the the clear form.
335 	 */
336 	if ((token->tkn_ssnkey.val = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL)
337 		return (NT_STATUS_NO_MEMORY);
338 	token->tkn_ssnkey.len = SMBAUTH_SESSION_KEY_SZ;
339 	bzero(rc4key, SMBAUTH_SESSION_KEY_SZ);
340 	bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len);
341 	bcopy(info3->UserSessionKey.data, token->tkn_ssnkey.val,
342 	    SMBAUTH_SESSION_KEY_SZ);
343 	rand_hash((unsigned char *)token->tkn_ssnkey.val,
344 	    SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ);
345 
346 	return (NT_STATUS_SUCCESS);
347 }
348 
349 /*
350  * netr_server_samlogon
351  *
352  * NetrServerSamLogon RPC: interactive or network. It is assumed that
353  * we have already authenticated with the PDC. If everything works,
354  * we build a user info structure and return it, where the caller will
355  * probably build an access token.
356  *
357  * Returns an NT status. There are numerous possibilities here.
358  * For example:
359  *	NT_STATUS_INVALID_INFO_CLASS
360  *	NT_STATUS_INVALID_PARAMETER
361  *	NT_STATUS_ACCESS_DENIED
362  *	NT_STATUS_PASSWORD_MUST_CHANGE
363  *	NT_STATUS_NO_SUCH_USER
364  *	NT_STATUS_WRONG_PASSWORD
365  *	NT_STATUS_LOGON_FAILURE
366  *	NT_STATUS_ACCOUNT_RESTRICTION
367  *	NT_STATUS_INVALID_LOGON_HOURS
368  *	NT_STATUS_INVALID_WORKSTATION
369  *	NT_STATUS_INTERNAL_ERROR
370  *	NT_STATUS_PASSWORD_EXPIRED
371  *	NT_STATUS_ACCOUNT_DISABLED
372  */
373 uint32_t
374 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
375     char *server, smb_logon_t *user_info, smb_token_t *token)
376 {
377 	struct netr_SamLogon arg;
378 	struct netr_authenticator auth;
379 	struct netr_authenticator ret_auth;
380 	struct netr_logon_info1 info1;
381 	struct netr_logon_info2 info2;
382 	struct netr_validation_info3 *info3;
383 	ndr_heap_t *heap;
384 	int opnum;
385 	int rc, len;
386 	uint32_t status;
387 
388 	bzero(&arg, sizeof (struct netr_SamLogon));
389 	opnum = NETR_OPNUM_SamLogon;
390 
391 	/*
392 	 * Should we get the server and hostname from netr_info?
393 	 */
394 
395 	len = strlen(server) + 4;
396 	arg.servername = ndr_rpc_malloc(netr_handle, len);
397 	arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ);
398 	if (arg.servername == NULL || arg.hostname == NULL) {
399 		ndr_rpc_release(netr_handle);
400 		return (NT_STATUS_INTERNAL_ERROR);
401 	}
402 
403 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
404 	if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) {
405 		ndr_rpc_release(netr_handle);
406 		return (NT_STATUS_INTERNAL_ERROR);
407 	}
408 
409 	rc = netr_setup_authenticator(netr_info, &auth, &ret_auth);
410 	if (rc != SMBAUTH_SUCCESS) {
411 		ndr_rpc_release(netr_handle);
412 		return (NT_STATUS_INTERNAL_ERROR);
413 	}
414 
415 	arg.auth = &auth;
416 	arg.ret_auth = &ret_auth;
417 	arg.validation_level = NETR_VALIDATION_LEVEL3;
418 	arg.logon_info.logon_level = user_info->lg_level;
419 	arg.logon_info.switch_value = user_info->lg_level;
420 
421 	heap = ndr_rpc_get_heap(netr_handle);
422 
423 	switch (user_info->lg_level) {
424 	case NETR_INTERACTIVE_LOGON:
425 		netr_setup_identity(heap, user_info, &info1.identity);
426 		netr_interactive_samlogon(netr_info, user_info, &info1);
427 		arg.logon_info.ru.info1 = &info1;
428 		break;
429 
430 	case NETR_NETWORK_LOGON:
431 		if (user_info->lg_challenge_key.len < 8 ||
432 		    user_info->lg_challenge_key.val == NULL) {
433 			ndr_rpc_release(netr_handle);
434 			return (NT_STATUS_INVALID_PARAMETER);
435 		}
436 		netr_setup_identity(heap, user_info, &info2.identity);
437 		netr_network_samlogon(heap, netr_info, user_info, &info2);
438 		arg.logon_info.ru.info2 = &info2;
439 		break;
440 
441 	default:
442 		ndr_rpc_release(netr_handle);
443 		return (NT_STATUS_INVALID_PARAMETER);
444 	}
445 
446 	rc = ndr_rpc_call(netr_handle, opnum, &arg);
447 	if (rc != 0) {
448 		bzero(netr_info, sizeof (netr_info_t));
449 		status = NT_STATUS_INVALID_PARAMETER;
450 	} else if (arg.status != 0) {
451 		status = NT_SC_VALUE(arg.status);
452 
453 		/*
454 		 * We need to validate the chain even though we have
455 		 * a non-zero status. If the status is ACCESS_DENIED
456 		 * this will trigger a new credential chain. However,
457 		 * a valid credential is returned with some status
458 		 * codes; for example, WRONG_PASSWORD.
459 		 */
460 		(void) netr_validate_chain(netr_info, arg.ret_auth);
461 	} else {
462 		status = netr_validate_chain(netr_info, arg.ret_auth);
463 		if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
464 			ndr_rpc_release(netr_handle);
465 			return (status);
466 		}
467 
468 		info3 = arg.ru.info3;
469 		status = netr_setup_token(info3, user_info, netr_info, token);
470 	}
471 
472 	ndr_rpc_release(netr_handle);
473 	return (status);
474 }
475 
476 /*
477  * netr_interactive_samlogon
478  *
479  * Set things up for an interactive SamLogon. Copy the NT and LM
480  * passwords to the logon structure and hash them with the session
481  * key.
482  */
483 static void
484 netr_interactive_samlogon(netr_info_t *netr_info, smb_logon_t *user_info,
485     struct netr_logon_info1 *info1)
486 {
487 	BYTE key[NETR_OWF_PASSWORD_SZ];
488 
489 	(void) memcpy(&info1->lm_owf_password,
490 	    user_info->lg_lm_password.val, sizeof (netr_owf_password_t));
491 
492 	(void) memcpy(&info1->nt_owf_password,
493 	    user_info->lg_nt_password.val, sizeof (netr_owf_password_t));
494 
495 	(void) memset(key, 0, NETR_OWF_PASSWORD_SZ);
496 	(void) memcpy(key, netr_info->session_key.key,
497 	    netr_info->session_key.len);
498 
499 	rand_hash((unsigned char *)&info1->lm_owf_password,
500 	    NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
501 
502 	rand_hash((unsigned char *)&info1->nt_owf_password,
503 	    NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
504 }
505 
506 /*
507  * netr_network_samlogon
508  *
509  * Set things up for a network SamLogon.  We provide a copy of the random
510  * challenge, that we sent to the client, to the domain controller.  This
511  * is the key that the client will have used to encrypt the NT and LM
512  * passwords.  Note that Windows 9x clients may not provide both passwords.
513  */
514 /*ARGSUSED*/
515 static void
516 netr_network_samlogon(ndr_heap_t *heap, netr_info_t *netr_info,
517     smb_logon_t *user_info, struct netr_logon_info2 *info2)
518 {
519 	uint32_t len;
520 
521 	if (user_info->lg_challenge_key.len >= 8 &&
522 	    user_info->lg_challenge_key.val != 0) {
523 		bcopy(user_info->lg_challenge_key.val,
524 		    info2->lm_challenge.data, 8);
525 	} else {
526 		bzero(info2->lm_challenge.data, 8);
527 	}
528 
529 	if ((len = user_info->lg_nt_password.len) != 0) {
530 		ndr_heap_mkvcb(heap, user_info->lg_nt_password.val, len,
531 		    (ndr_vcbuf_t *)&info2->nt_response);
532 	} else {
533 		bzero(&info2->nt_response, sizeof (netr_vcbuf_t));
534 	}
535 
536 	if ((len = user_info->lg_lm_password.len) != 0) {
537 		ndr_heap_mkvcb(heap, user_info->lg_lm_password.val, len,
538 		    (ndr_vcbuf_t *)&info2->lm_response);
539 	} else {
540 		bzero(&info2->lm_response, sizeof (netr_vcbuf_t));
541 	}
542 }
543 
544 /*
545  * netr_setup_authenticator
546  *
547  * Set up the request and return authenticators. A new credential is
548  * generated from the session key, the current client credential and
549  * the current time, i.e.
550  *
551  *		NewCredential = Cred(SessionKey, OldCredential, time);
552  *
553  * The timestamp, which is used as a random seed, is stored in both
554  * the request and return authenticators.
555  *
556  * If any difficulties occur using the cryptographic framework, the
557  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
558  * returned.
559  */
560 int
561 netr_setup_authenticator(netr_info_t *netr_info,
562     struct netr_authenticator *auth, struct netr_authenticator *ret_auth)
563 {
564 	bzero(auth, sizeof (struct netr_authenticator));
565 
566 	netr_info->timestamp = time(0);
567 	auth->timestamp = netr_info->timestamp;
568 
569 	if (netr_gen_credentials(netr_info->session_key.key,
570 	    &netr_info->client_credential,
571 	    netr_info->timestamp,
572 	    (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS)
573 		return (SMBAUTH_FAILURE);
574 
575 	if (ret_auth) {
576 		bzero(ret_auth, sizeof (struct netr_authenticator));
577 		ret_auth->timestamp = netr_info->timestamp;
578 	}
579 
580 	return (SMBAUTH_SUCCESS);
581 }
582 
583 /*
584  * Validate the returned credentials and update the credential chain.
585  * The server returns an updated client credential rather than a new
586  * server credential.  The server uses (timestamp + 1) when generating
587  * the credential.
588  *
589  * Generate the new seed for the credential chain. The new seed is
590  * formed by adding (timestamp + 1) to the current client credential.
591  * The only quirk is the uint32_t style addition.
592  *
593  * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a
594  * NULL pointer. The Authenticator field of the SamLogon response packet
595  * sent by the Samba 3 PDC always return NULL pointer if the received
596  * SamLogon request is not immediately followed by the ServerReqChallenge
597  * and ServerAuthenticate2 requests.
598  *
599  * Returns NT_STATUS_SUCCESS if the server returned a valid credential.
600  * Otherwise we retirm NT_STATUS_UNSUCCESSFUL.
601  */
602 uint32_t
603 netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth)
604 {
605 	netr_cred_t cred;
606 	uint32_t result = NT_STATUS_SUCCESS;
607 	uint32_t *dwp;
608 
609 	++netr_info->timestamp;
610 
611 	if (netr_gen_credentials(netr_info->session_key.key,
612 	    &netr_info->client_credential,
613 	    netr_info->timestamp, &cred) != SMBAUTH_SUCCESS)
614 		return (NT_STATUS_INTERNAL_ERROR);
615 
616 	if (&auth->credential == 0) {
617 		/*
618 		 * If the validation fails, destroy the credential chain.
619 		 * This should trigger a new authentication chain.
620 		 */
621 		bzero(netr_info, sizeof (netr_info_t));
622 		return (NT_STATUS_INSUFFICIENT_LOGON_INFO);
623 	}
624 
625 	result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t));
626 	if (result != 0) {
627 		/*
628 		 * If the validation fails, destroy the credential chain.
629 		 * This should trigger a new authentication chain.
630 		 */
631 		bzero(netr_info, sizeof (netr_info_t));
632 		result = NT_STATUS_UNSUCCESSFUL;
633 	} else {
634 		/*
635 		 * Otherwise generate the next step in the chain.
636 		 */
637 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
638 		dwp = (uint32_t *)&netr_info->client_credential;
639 		dwp[0] += netr_info->timestamp;
640 
641 		netr_info->flags |= NETR_FLG_VALID;
642 	}
643 
644 	return (result);
645 }
646 
647 /*
648  * netr_invalidate_chain
649  *
650  * Mark the credential chain as invalid so that it will be recreated
651  * on the next attempt.
652  */
653 static void
654 netr_invalidate_chain(void)
655 {
656 	netr_global_info.flags &= ~NETR_FLG_VALID;
657 }
658 
659 /*
660  * netr_setup_identity
661  *
662  * Set up the client identity information. All of this information is
663  * specifically related to the client user and workstation attempting
664  * to access this system. It may not be in our primary domain.
665  *
666  * I don't know what logon_id is, it seems to be a unique identifier.
667  * Increment it before each use.
668  */
669 static void
670 netr_setup_identity(ndr_heap_t *heap, smb_logon_t *user_info,
671     netr_logon_id_t *identity)
672 {
673 	static mutex_t logon_id_mutex;
674 	static uint32_t logon_id;
675 
676 	(void) mutex_lock(&logon_id_mutex);
677 
678 	if (logon_id == 0)
679 		logon_id = 0xDCD0;
680 
681 	++logon_id;
682 	user_info->lg_logon_id = logon_id;
683 
684 	(void) mutex_unlock(&logon_id_mutex);
685 
686 	/*
687 	 * [MS-APDS] 3.1.5.2 "NTLM Network Logon" says to set
688 	 * ParameterControl to the 'E' + 'K' bits.  Those are:
689 	 * (1 << 5) | (1 << 11), a.k.a
690 	 */
691 	identity->parameter_control =
692 	    MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
693 	    MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
694 	identity->logon_id.LowPart = logon_id;
695 	identity->logon_id.HighPart = 0;
696 
697 	ndr_heap_mkvcs(heap, user_info->lg_domain,
698 	    (ndr_vcstr_t *)&identity->domain_name);
699 
700 	ndr_heap_mkvcs(heap, user_info->lg_username,
701 	    (ndr_vcstr_t *)&identity->username);
702 
703 	/*
704 	 * Some systems prefix the client workstation name with \\.
705 	 * It doesn't seem to make any difference whether it's there
706 	 * or not.
707 	 */
708 	ndr_heap_mkvcs(heap, user_info->lg_workstation,
709 	    (ndr_vcstr_t *)&identity->workstation);
710 }
711 
712 /*
713  * Sets up domain, local and well-known group membership for the given
714  * token. Two assumptions have been made here:
715  *
716  *   a) token already contains a valid user SID so that group
717  *      memberships can be established
718  *
719  *   b) token belongs to a domain user
720  */
721 static uint32_t
722 netr_setup_token_wingrps(struct netr_validation_info3 *info3,
723     smb_token_t *token)
724 {
725 	smb_ids_t tkn_grps;
726 	uint32_t status;
727 
728 	tkn_grps.i_cnt = 0;
729 	tkn_grps.i_ids = NULL;
730 
731 	status = netr_setup_domain_groups(info3, &tkn_grps);
732 	if (status != NT_STATUS_SUCCESS) {
733 		smb_ids_free(&tkn_grps);
734 		return (status);
735 	}
736 
737 	status = smb_sam_usr_groups(token->tkn_user.i_sid, &tkn_grps);
738 	if (status != NT_STATUS_SUCCESS) {
739 		smb_ids_free(&tkn_grps);
740 		return (status);
741 	}
742 
743 	if (netr_isadmin(info3))
744 		token->tkn_flags |= SMB_ATF_ADMIN;
745 
746 	status = smb_wka_token_groups(token->tkn_flags, &tkn_grps);
747 	if (status == NT_STATUS_SUCCESS)
748 		token->tkn_win_grps = tkn_grps;
749 	else
750 		smb_ids_free(&tkn_grps);
751 
752 	return (status);
753 }
754 
755 /*
756  * Converts groups information in the returned structure by domain controller
757  * (info3) to an internal representation (gids)
758  */
759 static uint32_t
760 netr_setup_domain_groups(struct netr_validation_info3 *info3, smb_ids_t *gids)
761 {
762 	smb_sid_t *domain_sid;
763 	smb_id_t *ids;
764 	int i, total_cnt;
765 
766 	if ((i = info3->GroupCount) == 0)
767 		i++;
768 	i += info3->SidCount;
769 
770 	total_cnt = gids->i_cnt + i;
771 
772 	gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t));
773 	if (gids->i_ids == NULL)
774 		return (NT_STATUS_NO_MEMORY);
775 
776 	domain_sid = (smb_sid_t *)info3->LogonDomainId;
777 
778 	ids = gids->i_ids + gids->i_cnt;
779 	for (i = 0; i < info3->GroupCount; i++, gids->i_cnt++, ids++) {
780 		ids->i_sid = smb_sid_splice(domain_sid, info3->GroupIds[i].rid);
781 		if (ids->i_sid == NULL)
782 			return (NT_STATUS_NO_MEMORY);
783 
784 		ids->i_attrs = info3->GroupIds[i].attributes;
785 	}
786 
787 	if (info3->GroupCount == 0) {
788 		/*
789 		 * if there's no global group should add the primary group.
790 		 */
791 		ids->i_sid = smb_sid_splice(domain_sid, info3->PrimaryGroupId);
792 		if (ids->i_sid == NULL)
793 			return (NT_STATUS_NO_MEMORY);
794 
795 		ids->i_attrs = 0x7;
796 		gids->i_cnt++;
797 		ids++;
798 	}
799 
800 	/* Add the extra SIDs */
801 	for (i = 0; i < info3->SidCount; i++, gids->i_cnt++, ids++) {
802 		ids->i_sid = smb_sid_dup((smb_sid_t *)info3->ExtraSids[i].sid);
803 		if (ids->i_sid == NULL)
804 			return (NT_STATUS_NO_MEMORY);
805 
806 		ids->i_attrs = info3->ExtraSids[i].attributes;
807 	}
808 
809 	return (NT_STATUS_SUCCESS);
810 }
811 
812 /*
813  * Determines if the given user is the domain Administrator or a
814  * member of Domain Admins
815  */
816 static boolean_t
817 netr_isadmin(struct netr_validation_info3 *info3)
818 {
819 	smb_domain_t di;
820 	int i;
821 
822 	if (!smb_domain_lookup_sid((smb_sid_t *)info3->LogonDomainId, &di))
823 		return (B_FALSE);
824 
825 	if (di.di_type != SMB_DOMAIN_PRIMARY)
826 		return (B_FALSE);
827 
828 	if ((info3->UserId == DOMAIN_USER_RID_ADMIN) ||
829 	    (info3->PrimaryGroupId == DOMAIN_GROUP_RID_ADMINS))
830 		return (B_TRUE);
831 
832 	for (i = 0; i < info3->GroupCount; i++)
833 		if (info3->GroupIds[i].rid == DOMAIN_GROUP_RID_ADMINS)
834 			return (B_TRUE);
835 
836 	return (B_FALSE);
837 }
838