1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
25  */
26 
27 /*
28  * NETR SamLogon and SamLogoff RPC client functions.
29  */
30 
31 #include <stdio.h>
32 #include <strings.h>
33 #include <stdlib.h>
34 #include <time.h>
35 #include <alloca.h>
36 #include <unistd.h>
37 #include <netdb.h>
38 #include <thread.h>
39 
40 #include <libmlrpc/libmlrpc.h>
41 #include <smbsrv/libsmb.h>
42 #include <smbsrv/libmlsvc.h>
43 #include <smbsrv/ndl/netlogon.ndl>
44 #include <smbsrv/netrauth.h>
45 #include <smbsrv/smbinfo.h>
46 #include <smbsrv/smb_token.h>
47 #include <mlsvc.h>
48 
49 static uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *);
50 static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *,
51     smb_logon_t *, smb_token_t *);
52 static void netr_invalidate_chain(void);
53 static void netr_interactive_samlogon(netr_info_t *, smb_logon_t *,
54     struct netr_logon_info1 *);
55 static void netr_network_samlogon(ndr_heap_t *, netr_info_t *,
56     smb_logon_t *, struct netr_logon_info2 *);
57 static void netr_setup_identity(ndr_heap_t *, smb_logon_t *,
58     netr_logon_id_t *);
59 static boolean_t netr_isadmin(struct netr_validation_info3 *);
60 static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *,
61     smb_ids_t *);
62 static uint32_t netr_setup_krb5res_groups(struct krb5_validation_info *,
63     smb_ids_t *);
64 static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *,
65     smb_token_t *);
66 
67 /*
68  * Shared with netr_auth.c
69  */
70 extern netr_info_t netr_global_info;
71 
72 static mutex_t netlogon_mutex;
73 static cond_t netlogon_cv;
74 static boolean_t netlogon_busy = B_FALSE;
75 static boolean_t netlogon_abort = B_FALSE;
76 
77 /*
78  * Helper for Kerberos authentication
79  */
80 uint32_t
81 smb_decode_krb5_pac(smb_token_t *token, char *data, uint_t len)
82 {
83 	struct krb5_validation_info info;
84 	ndr_buf_t *nbuf;
85 	smb_sid_t *domsid;
86 	uint32_t status = NT_STATUS_NO_MEMORY;
87 	int rc;
88 
89 	bzero(&info, sizeof (info));
90 
91 	/* Need to keep this until we're done with &info */
92 	nbuf = ndr_buf_init(&TYPEINFO(netr_interface));
93 	if (nbuf == NULL)
94 		goto out;
95 
96 	rc = ndr_buf_decode(nbuf, NDR_PTYPE_PAC,
97 	    NETR_OPNUM_decode_krb5_pac, data, len, &info);
98 	if (rc != NDR_DRC_OK) {
99 		status = RPC_NT_PROTOCOL_ERROR;
100 		goto out;
101 	}
102 
103 	/*
104 	 * Copy the decoded info into the token,
105 	 * similar to netr_setup_token()
106 	 */
107 	domsid = (smb_sid_t *)info.info3.LogonDomainId;
108 
109 	token->tkn_user.i_sid = smb_sid_splice(domsid,
110 	    info.info3.UserId);
111 	if (token->tkn_user.i_sid == NULL)
112 		goto out;
113 
114 	token->tkn_primary_grp.i_sid = smb_sid_splice(domsid,
115 	    info.info3.PrimaryGroupId);
116 	if (token->tkn_primary_grp.i_sid == NULL)
117 		goto out;
118 
119 	if (info.info3.EffectiveName.str) {
120 		token->tkn_account_name =
121 		    strdup((char *)info.info3.EffectiveName.str);
122 		if (token->tkn_account_name == NULL)
123 			goto out;
124 	}
125 
126 	if (info.info3.LogonDomainName.str) {
127 		token->tkn_domain_name =
128 		    strdup((char *)info.info3.LogonDomainName.str);
129 		if (token->tkn_domain_name == NULL)
130 			goto out;
131 	}
132 
133 	status = netr_setup_domain_groups(&info.info3, &token->tkn_win_grps);
134 	if (status != NT_STATUS_SUCCESS)
135 		goto out;
136 
137 	if (info.rg_rid_cnt != 0) {
138 		status = netr_setup_krb5res_groups(&info, &token->tkn_win_grps);
139 		if (status != NT_STATUS_SUCCESS)
140 			goto out;
141 	}
142 
143 	status = netr_setup_token_wingrps(&info.info3, token);
144 
145 out:
146 	if (nbuf != NULL)
147 		ndr_buf_fini(nbuf);
148 
149 	return (status);
150 }
151 
152 /*
153  * Abort impending domain logon requests.
154  */
155 void
156 smb_logon_abort(void)
157 {
158 	(void) mutex_lock(&netlogon_mutex);
159 	if (netlogon_busy && !netlogon_abort)
160 		syslog(LOG_DEBUG, "logon abort");
161 	netlogon_abort = B_TRUE;
162 	(void) cond_broadcast(&netlogon_cv);
163 	(void) mutex_unlock(&netlogon_mutex);
164 }
165 
166 /*
167  * This is the entry point for authenticating domain users.
168  *
169  * If we are not going to attempt to authenticate the user,
170  * this function must return without updating the status.
171  *
172  * If the user is successfully authenticated, we build an
173  * access token and the status will be NT_STATUS_SUCCESS.
174  * Otherwise, the token contents are invalid.
175  *
176  * This will retry a few times for errors indicating that the
177  * current DC might have gone off-line or become too busy etc.
178  * With such errors, smb_ddiscover_bad_dc is called and then
179  * the smb_domain_getinfo call here waits for new DC info.
180  */
181 int smb_netr_logon_retries = 3;
182 void
183 smb_logon_domain(smb_logon_t *user_info, smb_token_t *token)
184 {
185 	smb_domainex_t	di;
186 	uint32_t	status;
187 	int		retries = smb_netr_logon_retries;
188 
189 	if (user_info->lg_secmode != SMB_SECMODE_DOMAIN)
190 		return;
191 
192 	if (user_info->lg_domain_type == SMB_DOMAIN_LOCAL)
193 		return;
194 
195 	while (--retries > 0) {
196 
197 		if (!smb_domain_getinfo(&di)) {
198 			syslog(LOG_ERR, "logon DC getinfo failed");
199 			status = NT_STATUS_NO_LOGON_SERVERS;
200 			goto out;
201 		}
202 
203 		(void) mutex_lock(&netlogon_mutex);
204 		while (netlogon_busy && !netlogon_abort)
205 			(void) cond_wait(&netlogon_cv, &netlogon_mutex);
206 
207 		if (netlogon_abort) {
208 			(void) mutex_unlock(&netlogon_mutex);
209 			status = NT_STATUS_REQUEST_ABORTED;
210 			goto out;
211 		}
212 
213 		netlogon_busy = B_TRUE;
214 		(void) mutex_unlock(&netlogon_mutex);
215 
216 		status = netlogon_logon(user_info, token, &di);
217 
218 		(void) mutex_lock(&netlogon_mutex);
219 		netlogon_busy = B_FALSE;
220 		if (netlogon_abort)
221 			status = NT_STATUS_REQUEST_ABORTED;
222 		(void) cond_signal(&netlogon_cv);
223 		(void) mutex_unlock(&netlogon_mutex);
224 
225 		switch (status) {
226 		case NT_STATUS_BAD_NETWORK_PATH:
227 		case NT_STATUS_BAD_NETWORK_NAME:
228 		case RPC_NT_SERVER_TOO_BUSY:
229 			/*
230 			 * May retry with a new DC, or if we're
231 			 * out of retries, will return...
232 			 */
233 			status = NT_STATUS_NO_LOGON_SERVERS;
234 			break;
235 		default:
236 			goto out;
237 		}
238 	}
239 
240 out:
241 	if (status != NT_STATUS_SUCCESS)
242 		syslog(LOG_INFO, "logon[%s\\%s]: %s", user_info->lg_e_domain,
243 		    user_info->lg_e_username, xlate_nt_status(status));
244 	user_info->lg_status = status;
245 }
246 
247 /*
248  * Run a netr_server_samlogon call, dealing with the possible need to
249  * re-establish the NetLogon credential chain.  If that fails, return
250  * NT_STATUS_DOMAIN_TRUST_INCONSISTENT indicating the machine account
251  * needs it's password reset (or whatever).  Other errors are from the
252  * netr_server_samlogon() call including the many possibilities listed
253  * above that function.
254  */
255 static uint32_t
256 netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di)
257 {
258 	char server[MAXHOSTNAMELEN];
259 	mlsvc_handle_t netr_handle;
260 	uint32_t status;
261 	boolean_t did_reauth = B_FALSE;
262 
263 	/*
264 	 * This netr_open call does the work to connect to the DC,
265 	 * get the IPC share, open the named pipe, RPC bind, etc.
266 	 */
267 	status = netr_open(di->d_dci.dc_name, di->d_primary.di_nbname,
268 	    &netr_handle);
269 	if (status != 0) {
270 		syslog(LOG_ERR, "netlogon remote open failed (%s)",
271 		    xlate_nt_status(status));
272 		return (status);
273 	}
274 
275 	if (di->d_dci.dc_name[0] != '\0' &&
276 	    (*netr_global_info.server != '\0')) {
277 		(void) snprintf(server, sizeof (server),
278 		    "\\\\%s", di->d_dci.dc_name);
279 		if (strncasecmp(netr_global_info.server,
280 		    server, strlen(server)) != 0)
281 			netr_invalidate_chain();
282 	}
283 
284 reauth:
285 	if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
286 	    !smb_match_netlogon_seqnum()) {
287 		/*
288 		 * This does netr_server_req_challenge() and
289 		 * netr_server_authenticate2(), updating the
290 		 * current netlogon sequence number.
291 		 */
292 		status = netlogon_auth(di->d_dci.dc_name, &netr_handle,
293 		    NETR_FLG_NULL);
294 
295 		if (status != 0) {
296 			syslog(LOG_ERR, "netlogon remote auth failed (%s)",
297 			    xlate_nt_status(status));
298 			(void) netr_close(&netr_handle);
299 			return (NT_STATUS_DOMAIN_TRUST_INCONSISTENT);
300 		}
301 
302 		netr_global_info.flags |= NETR_FLG_VALID;
303 	}
304 
305 	status = netr_server_samlogon(&netr_handle,
306 	    &netr_global_info, di->d_dci.dc_name, user_info, token);
307 
308 	if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
309 		if (!did_reauth) {
310 			/* Call netlogon_auth() again, just once. */
311 			did_reauth = B_TRUE;
312 			goto reauth;
313 		}
314 		status = NT_STATUS_DOMAIN_TRUST_INCONSISTENT;
315 	}
316 
317 	(void) netr_close(&netr_handle);
318 
319 	return (status);
320 }
321 
322 /*
323  * Helper for mlsvc_netlogon
324  *
325  * Call netlogon_auth with appropriate locks etc.
326  * Serialize like smb_logon_domain does for
327  * netlogon_logon / netlogon_auth
328  */
329 uint32_t
330 smb_netlogon_check(char *server, char *domain)
331 {
332 	mlsvc_handle_t netr_handle;
333 	uint32_t	status;
334 
335 	(void) mutex_lock(&netlogon_mutex);
336 	while (netlogon_busy)
337 		(void) cond_wait(&netlogon_cv, &netlogon_mutex);
338 
339 	netlogon_busy = B_TRUE;
340 	(void) mutex_unlock(&netlogon_mutex);
341 
342 	/*
343 	 * This section like netlogon_logon(), but only does
344 	 * one pass and no netr_server_samlogon call.
345 	 */
346 
347 	status = netr_open(server, domain,
348 	    &netr_handle);
349 	if (status != 0) {
350 		syslog(LOG_ERR, "netlogon remote open failed (%s)",
351 		    xlate_nt_status(status));
352 		goto unlock_out;
353 	}
354 
355 	if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
356 	    !smb_match_netlogon_seqnum()) {
357 		/*
358 		 * This does netr_server_req_challenge() and
359 		 * netr_server_authenticate2(), updating the
360 		 * current netlogon sequence number.
361 		 */
362 		status = netlogon_auth(server, &netr_handle,
363 		    NETR_FLG_NULL);
364 		if (status != 0) {
365 			syslog(LOG_ERR, "netlogon remote auth failed (%s)",
366 			    xlate_nt_status(status));
367 		} else {
368 			netr_global_info.flags |= NETR_FLG_VALID;
369 		}
370 	}
371 
372 	(void) netr_close(&netr_handle);
373 
374 unlock_out:
375 	(void) mutex_lock(&netlogon_mutex);
376 	netlogon_busy = B_FALSE;
377 	(void) cond_signal(&netlogon_cv);
378 	(void) mutex_unlock(&netlogon_mutex);
379 
380 	return (status);
381 }
382 
383 static uint32_t
384 netr_setup_token(struct netr_validation_info3 *info3, smb_logon_t *user_info,
385     netr_info_t *netr_info, smb_token_t *token)
386 {
387 	char *username, *domain;
388 	unsigned char rc4key[SMBAUTH_SESSION_KEY_SZ];
389 	smb_sid_t *domsid;
390 	uint32_t status;
391 	char nbdomain[NETBIOS_NAME_SZ];
392 
393 	domsid = (smb_sid_t *)info3->LogonDomainId;
394 
395 	token->tkn_user.i_sid = smb_sid_splice(domsid, info3->UserId);
396 	if (token->tkn_user.i_sid == NULL)
397 		return (NT_STATUS_NO_MEMORY);
398 
399 	token->tkn_primary_grp.i_sid = smb_sid_splice(domsid,
400 	    info3->PrimaryGroupId);
401 	if (token->tkn_primary_grp.i_sid == NULL)
402 		return (NT_STATUS_NO_MEMORY);
403 
404 	username = (info3->EffectiveName.str)
405 	    ? (char *)info3->EffectiveName.str : user_info->lg_e_username;
406 
407 	if (info3->LogonDomainName.str) {
408 		domain = (char *)info3->LogonDomainName.str;
409 	} else if (*user_info->lg_e_domain != '\0') {
410 		domain = user_info->lg_e_domain;
411 	} else {
412 		(void) smb_getdomainname(nbdomain, sizeof (nbdomain));
413 		domain = nbdomain;
414 	}
415 
416 	if (username)
417 		token->tkn_account_name = strdup(username);
418 	if (domain)
419 		token->tkn_domain_name = strdup(domain);
420 
421 	if (token->tkn_account_name == NULL || token->tkn_domain_name == NULL)
422 		return (NT_STATUS_NO_MEMORY);
423 
424 	status = netr_setup_domain_groups(info3, &token->tkn_win_grps);
425 	if (status != NT_STATUS_SUCCESS)
426 		return (status);
427 
428 	status = netr_setup_token_wingrps(info3, token);
429 	if (status != NT_STATUS_SUCCESS)
430 		return (status);
431 
432 	/*
433 	 * The UserSessionKey in NetrSamLogon RPC is obfuscated using the
434 	 * session key obtained in the NETLOGON credential chain.
435 	 * An 8 byte session key is zero extended to 16 bytes. This 16 byte
436 	 * key is the key to the RC4 algorithm. The RC4 byte stream is
437 	 * exclusively ored with the 16 byte UserSessionKey to recover
438 	 * the the clear form.
439 	 */
440 	if ((token->tkn_ssnkey.val = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL)
441 		return (NT_STATUS_NO_MEMORY);
442 	token->tkn_ssnkey.len = SMBAUTH_SESSION_KEY_SZ;
443 	bzero(rc4key, SMBAUTH_SESSION_KEY_SZ);
444 	bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len);
445 	bcopy(info3->UserSessionKey.data, token->tkn_ssnkey.val,
446 	    SMBAUTH_SESSION_KEY_SZ);
447 	rand_hash((unsigned char *)token->tkn_ssnkey.val,
448 	    SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ);
449 
450 	return (NT_STATUS_SUCCESS);
451 }
452 
453 /*
454  * netr_server_samlogon
455  *
456  * NetrServerSamLogon RPC: interactive or network. It is assumed that
457  * we have already authenticated with the PDC. If everything works,
458  * we build a user info structure and return it, where the caller will
459  * probably build an access token.
460  *
461  * Returns an NT status. There are numerous possibilities here.
462  * For example:
463  *	NT_STATUS_INVALID_INFO_CLASS
464  *	NT_STATUS_INVALID_PARAMETER
465  *	NT_STATUS_ACCESS_DENIED
466  *	NT_STATUS_PASSWORD_MUST_CHANGE
467  *	NT_STATUS_NO_SUCH_USER
468  *	NT_STATUS_WRONG_PASSWORD
469  *	NT_STATUS_LOGON_FAILURE
470  *	NT_STATUS_ACCOUNT_RESTRICTION
471  *	NT_STATUS_INVALID_LOGON_HOURS
472  *	NT_STATUS_INVALID_WORKSTATION
473  *	NT_STATUS_INTERNAL_ERROR
474  *	NT_STATUS_PASSWORD_EXPIRED
475  *	NT_STATUS_ACCOUNT_DISABLED
476  */
477 uint32_t
478 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
479     char *server, smb_logon_t *user_info, smb_token_t *token)
480 {
481 	struct netr_SamLogon arg;
482 	struct netr_authenticator auth;
483 	struct netr_authenticator ret_auth;
484 	struct netr_logon_info1 info1;
485 	struct netr_logon_info2 info2;
486 	struct netr_validation_info3 *info3;
487 	ndr_heap_t *heap;
488 	int opnum;
489 	int rc, len;
490 	uint32_t status;
491 
492 	bzero(&arg, sizeof (struct netr_SamLogon));
493 	opnum = NETR_OPNUM_SamLogon;
494 
495 	/*
496 	 * Should we get the server and hostname from netr_info?
497 	 */
498 
499 	len = strlen(server) + 4;
500 	arg.servername = ndr_rpc_malloc(netr_handle, len);
501 	arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ);
502 	if (arg.servername == NULL || arg.hostname == NULL) {
503 		ndr_rpc_release(netr_handle);
504 		return (NT_STATUS_INTERNAL_ERROR);
505 	}
506 
507 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
508 	if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) {
509 		ndr_rpc_release(netr_handle);
510 		return (NT_STATUS_INTERNAL_ERROR);
511 	}
512 
513 	rc = netr_setup_authenticator(netr_info, &auth, &ret_auth);
514 	if (rc != SMBAUTH_SUCCESS) {
515 		ndr_rpc_release(netr_handle);
516 		return (NT_STATUS_INTERNAL_ERROR);
517 	}
518 
519 	arg.auth = &auth;
520 	arg.ret_auth = &ret_auth;
521 	arg.validation_level = NETR_VALIDATION_LEVEL3;
522 	arg.logon_info.logon_level = user_info->lg_level;
523 	arg.logon_info.switch_value = user_info->lg_level;
524 
525 	heap = ndr_rpc_get_heap(netr_handle);
526 
527 	switch (user_info->lg_level) {
528 	case NETR_INTERACTIVE_LOGON:
529 		netr_setup_identity(heap, user_info, &info1.identity);
530 		netr_interactive_samlogon(netr_info, user_info, &info1);
531 		arg.logon_info.ru.info1 = &info1;
532 		break;
533 
534 	case NETR_NETWORK_LOGON:
535 		if (user_info->lg_challenge_key.len < 8 ||
536 		    user_info->lg_challenge_key.val == NULL) {
537 			ndr_rpc_release(netr_handle);
538 			return (NT_STATUS_INVALID_PARAMETER);
539 		}
540 		netr_setup_identity(heap, user_info, &info2.identity);
541 		netr_network_samlogon(heap, netr_info, user_info, &info2);
542 		arg.logon_info.ru.info2 = &info2;
543 		break;
544 
545 	default:
546 		ndr_rpc_release(netr_handle);
547 		return (NT_STATUS_INVALID_PARAMETER);
548 	}
549 
550 	rc = ndr_rpc_call(netr_handle, opnum, &arg);
551 	if (rc != 0) {
552 		bzero(netr_info, sizeof (netr_info_t));
553 		status = NT_STATUS_INVALID_PARAMETER;
554 	} else if (arg.status != 0) {
555 		status = NT_SC_VALUE(arg.status);
556 
557 		/*
558 		 * We need to validate the chain even though we have
559 		 * a non-zero status. If the status is ACCESS_DENIED
560 		 * this will trigger a new credential chain. However,
561 		 * a valid credential is returned with some status
562 		 * codes; for example, WRONG_PASSWORD.
563 		 */
564 		(void) netr_validate_chain(netr_info, arg.ret_auth);
565 	} else {
566 		status = netr_validate_chain(netr_info, arg.ret_auth);
567 		if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
568 			ndr_rpc_release(netr_handle);
569 			return (status);
570 		}
571 
572 		info3 = arg.ru.info3;
573 		status = netr_setup_token(info3, user_info, netr_info, token);
574 	}
575 
576 	ndr_rpc_release(netr_handle);
577 	return (status);
578 }
579 
580 /*
581  * netr_interactive_samlogon
582  *
583  * Set things up for an interactive SamLogon. Copy the NT and LM
584  * passwords to the logon structure and hash them with the session
585  * key.
586  */
587 static void
588 netr_interactive_samlogon(netr_info_t *netr_info, smb_logon_t *user_info,
589     struct netr_logon_info1 *info1)
590 {
591 	BYTE key[NETR_OWF_PASSWORD_SZ];
592 
593 	(void) memcpy(&info1->lm_owf_password,
594 	    user_info->lg_lm_password.val, sizeof (netr_owf_password_t));
595 
596 	(void) memcpy(&info1->nt_owf_password,
597 	    user_info->lg_nt_password.val, sizeof (netr_owf_password_t));
598 
599 	(void) memset(key, 0, NETR_OWF_PASSWORD_SZ);
600 	(void) memcpy(key, netr_info->session_key.key,
601 	    netr_info->session_key.len);
602 
603 	rand_hash((unsigned char *)&info1->lm_owf_password,
604 	    NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
605 
606 	rand_hash((unsigned char *)&info1->nt_owf_password,
607 	    NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
608 }
609 
610 /*
611  * netr_network_samlogon
612  *
613  * Set things up for a network SamLogon.  We provide a copy of the random
614  * challenge, that we sent to the client, to the domain controller.  This
615  * is the key that the client will have used to encrypt the NT and LM
616  * passwords.  Note that Windows 9x clients may not provide both passwords.
617  */
618 /*ARGSUSED*/
619 static void
620 netr_network_samlogon(ndr_heap_t *heap, netr_info_t *netr_info,
621     smb_logon_t *user_info, struct netr_logon_info2 *info2)
622 {
623 	uint32_t len;
624 
625 	if (user_info->lg_challenge_key.len >= 8 &&
626 	    user_info->lg_challenge_key.val != 0) {
627 		bcopy(user_info->lg_challenge_key.val,
628 		    info2->lm_challenge.data, 8);
629 	} else {
630 		bzero(info2->lm_challenge.data, 8);
631 	}
632 
633 	if ((len = user_info->lg_nt_password.len) != 0) {
634 		ndr_heap_mkvcb(heap, user_info->lg_nt_password.val, len,
635 		    (ndr_vcbuf_t *)&info2->nt_response);
636 	} else {
637 		bzero(&info2->nt_response, sizeof (netr_vcbuf_t));
638 	}
639 
640 	if ((len = user_info->lg_lm_password.len) != 0) {
641 		ndr_heap_mkvcb(heap, user_info->lg_lm_password.val, len,
642 		    (ndr_vcbuf_t *)&info2->lm_response);
643 	} else {
644 		bzero(&info2->lm_response, sizeof (netr_vcbuf_t));
645 	}
646 }
647 
648 /*
649  * netr_setup_authenticator
650  *
651  * Set up the request and return authenticators. A new credential is
652  * generated from the session key, the current client credential and
653  * the current time, i.e.
654  *
655  *		NewCredential = Cred(SessionKey, OldCredential, time);
656  *
657  * The timestamp, which is used as a random seed, is stored in both
658  * the request and return authenticators.
659  *
660  * If any difficulties occur using the cryptographic framework, the
661  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
662  * returned.
663  */
664 int
665 netr_setup_authenticator(netr_info_t *netr_info,
666     struct netr_authenticator *auth, struct netr_authenticator *ret_auth)
667 {
668 	bzero(auth, sizeof (struct netr_authenticator));
669 
670 	netr_info->timestamp = time(0);
671 	auth->timestamp = netr_info->timestamp;
672 
673 	if (netr_gen_credentials(netr_info->session_key.key,
674 	    &netr_info->client_credential,
675 	    netr_info->timestamp,
676 	    (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS)
677 		return (SMBAUTH_FAILURE);
678 
679 	if (ret_auth) {
680 		bzero(ret_auth, sizeof (struct netr_authenticator));
681 		ret_auth->timestamp = netr_info->timestamp;
682 	}
683 
684 	return (SMBAUTH_SUCCESS);
685 }
686 
687 /*
688  * Validate the returned credentials and update the credential chain.
689  * The server returns an updated client credential rather than a new
690  * server credential.  The server uses (timestamp + 1) when generating
691  * the credential.
692  *
693  * Generate the new seed for the credential chain. The new seed is
694  * formed by adding (timestamp + 1) to the current client credential.
695  * The only quirk is the uint32_t style addition.
696  *
697  * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a
698  * NULL pointer. The Authenticator field of the SamLogon response packet
699  * sent by the Samba 3 PDC always return NULL pointer if the received
700  * SamLogon request is not immediately followed by the ServerReqChallenge
701  * and ServerAuthenticate2 requests.
702  *
703  * Returns NT_STATUS_SUCCESS if the server returned a valid credential.
704  * Otherwise we retirm NT_STATUS_UNSUCCESSFUL.
705  */
706 uint32_t
707 netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth)
708 {
709 	netr_cred_t cred;
710 	uint32_t result = NT_STATUS_SUCCESS;
711 	uint32_t *dwp;
712 
713 	++netr_info->timestamp;
714 
715 	if (netr_gen_credentials(netr_info->session_key.key,
716 	    &netr_info->client_credential,
717 	    netr_info->timestamp, &cred) != SMBAUTH_SUCCESS)
718 		return (NT_STATUS_INTERNAL_ERROR);
719 
720 	if (&auth->credential == 0) {
721 		/*
722 		 * If the validation fails, destroy the credential chain.
723 		 * This should trigger a new authentication chain.
724 		 */
725 		bzero(netr_info, sizeof (netr_info_t));
726 		return (NT_STATUS_INSUFFICIENT_LOGON_INFO);
727 	}
728 
729 	result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t));
730 	if (result != 0) {
731 		/*
732 		 * If the validation fails, destroy the credential chain.
733 		 * This should trigger a new authentication chain.
734 		 */
735 		bzero(netr_info, sizeof (netr_info_t));
736 		result = NT_STATUS_UNSUCCESSFUL;
737 	} else {
738 		/*
739 		 * Otherwise generate the next step in the chain.
740 		 */
741 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
742 		dwp = (uint32_t *)&netr_info->client_credential;
743 		dwp[0] += netr_info->timestamp;
744 
745 		netr_info->flags |= NETR_FLG_VALID;
746 	}
747 
748 	return (result);
749 }
750 
751 /*
752  * netr_invalidate_chain
753  *
754  * Mark the credential chain as invalid so that it will be recreated
755  * on the next attempt.
756  */
757 static void
758 netr_invalidate_chain(void)
759 {
760 	netr_global_info.flags &= ~NETR_FLG_VALID;
761 }
762 
763 /*
764  * netr_setup_identity
765  *
766  * Set up the client identity information. All of this information is
767  * specifically related to the client user and workstation attempting
768  * to access this system. It may not be in our primary domain.
769  *
770  * I don't know what logon_id is, it seems to be a unique identifier.
771  * Increment it before each use.
772  */
773 static void
774 netr_setup_identity(ndr_heap_t *heap, smb_logon_t *user_info,
775     netr_logon_id_t *identity)
776 {
777 	static mutex_t logon_id_mutex;
778 	static uint32_t logon_id;
779 
780 	(void) mutex_lock(&logon_id_mutex);
781 
782 	if (logon_id == 0)
783 		logon_id = 0xDCD0;
784 
785 	++logon_id;
786 	user_info->lg_logon_id = logon_id;
787 
788 	(void) mutex_unlock(&logon_id_mutex);
789 
790 	/*
791 	 * [MS-APDS] 3.1.5.2 "NTLM Network Logon" says to set
792 	 * ParameterControl to the 'E' + 'K' bits.  Those are:
793 	 * (1 << 5) | (1 << 11), a.k.a
794 	 */
795 	identity->parameter_control =
796 	    MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
797 	    MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
798 	identity->logon_id.LowPart = logon_id;
799 	identity->logon_id.HighPart = 0;
800 
801 	ndr_heap_mkvcs(heap, user_info->lg_domain,
802 	    (ndr_vcstr_t *)&identity->domain_name);
803 
804 	ndr_heap_mkvcs(heap, user_info->lg_username,
805 	    (ndr_vcstr_t *)&identity->username);
806 
807 	/*
808 	 * Some systems prefix the client workstation name with \\.
809 	 * It doesn't seem to make any difference whether it's there
810 	 * or not.
811 	 */
812 	ndr_heap_mkvcs(heap, user_info->lg_workstation,
813 	    (ndr_vcstr_t *)&identity->workstation);
814 }
815 
816 /*
817  * Add local and well-known group membership to the given
818  * token.  Called after domain groups have been added.
819  */
820 static uint32_t
821 netr_setup_token_wingrps(struct netr_validation_info3 *info3,
822     smb_token_t *token)
823 {
824 	uint32_t status;
825 
826 	status = smb_sam_usr_groups(token->tkn_user.i_sid,
827 	    &token->tkn_win_grps);
828 	if (status != NT_STATUS_SUCCESS)
829 		return (status);
830 
831 	if (netr_isadmin(info3))
832 		token->tkn_flags |= SMB_ATF_ADMIN;
833 
834 	status = smb_wka_token_groups(token->tkn_flags, &token->tkn_win_grps);
835 
836 	return (status);
837 }
838 
839 /*
840  * Converts groups information in the returned structure by domain controller
841  * (info3) to an internal representation (gids)
842  */
843 static uint32_t
844 netr_setup_domain_groups(struct netr_validation_info3 *info3, smb_ids_t *gids)
845 {
846 	smb_sid_t *domain_sid;
847 	smb_id_t *ids;
848 	int i, total_cnt;
849 
850 	if ((i = info3->GroupCount) == 0)
851 		i++;
852 	i += info3->SidCount;
853 
854 	total_cnt = gids->i_cnt + i;
855 
856 	gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t));
857 	if (gids->i_ids == NULL)
858 		return (NT_STATUS_NO_MEMORY);
859 
860 	domain_sid = (smb_sid_t *)info3->LogonDomainId;
861 
862 	ids = gids->i_ids + gids->i_cnt;
863 	for (i = 0; i < info3->GroupCount; i++, gids->i_cnt++, ids++) {
864 		ids->i_sid = smb_sid_splice(domain_sid, info3->GroupIds[i].rid);
865 		if (ids->i_sid == NULL)
866 			return (NT_STATUS_NO_MEMORY);
867 
868 		ids->i_attrs = info3->GroupIds[i].attributes;
869 	}
870 
871 	if (info3->GroupCount == 0) {
872 		/*
873 		 * if there's no global group should add the primary group.
874 		 */
875 		ids->i_sid = smb_sid_splice(domain_sid, info3->PrimaryGroupId);
876 		if (ids->i_sid == NULL)
877 			return (NT_STATUS_NO_MEMORY);
878 
879 		ids->i_attrs = 0x7;
880 		gids->i_cnt++;
881 		ids++;
882 	}
883 
884 	/* Add the extra SIDs */
885 	for (i = 0; i < info3->SidCount; i++, gids->i_cnt++, ids++) {
886 		ids->i_sid = smb_sid_dup((smb_sid_t *)info3->ExtraSids[i].sid);
887 		if (ids->i_sid == NULL)
888 			return (NT_STATUS_NO_MEMORY);
889 
890 		ids->i_attrs = info3->ExtraSids[i].attributes;
891 	}
892 
893 	return (NT_STATUS_SUCCESS);
894 }
895 
896 /*
897  * Converts additional "resource" groups (from krb5_validation_info)
898  * into the internal representation (gids), appending to the list
899  * already put in place by netr_setup_domain_groups().
900  */
901 static uint32_t netr_setup_krb5res_groups(struct krb5_validation_info *info,
902     smb_ids_t *gids)
903 {
904 	smb_sid_t *domain_sid;
905 	smb_id_t *ids;
906 	int i, total_cnt;
907 
908 	total_cnt = gids->i_cnt + info->rg_rid_cnt;
909 
910 	gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t));
911 	if (gids->i_ids == NULL)
912 		return (NT_STATUS_NO_MEMORY);
913 
914 	domain_sid = (smb_sid_t *)info->rg_dom_sid;
915 
916 	ids = gids->i_ids + gids->i_cnt;
917 	for (i = 0; i < info->rg_rid_cnt; i++, gids->i_cnt++, ids++) {
918 		ids->i_sid = smb_sid_splice(domain_sid, info->rg_rids[i].rid);
919 		if (ids->i_sid == NULL)
920 			return (NT_STATUS_NO_MEMORY);
921 		ids->i_attrs = info->rg_rids[i].attributes;
922 	}
923 
924 	return (0);
925 }
926 
927 /*
928  * Determines if the given user is the domain Administrator or a
929  * member of Domain Admins
930  */
931 static boolean_t
932 netr_isadmin(struct netr_validation_info3 *info3)
933 {
934 	smb_domain_t di;
935 	int i;
936 
937 	if (!smb_domain_lookup_sid((smb_sid_t *)info3->LogonDomainId, &di))
938 		return (B_FALSE);
939 
940 	if (di.di_type != SMB_DOMAIN_PRIMARY)
941 		return (B_FALSE);
942 
943 	if ((info3->UserId == DOMAIN_USER_RID_ADMIN) ||
944 	    (info3->PrimaryGroupId == DOMAIN_GROUP_RID_ADMINS))
945 		return (B_TRUE);
946 
947 	for (i = 0; i < info3->GroupCount; i++)
948 		if (info3->GroupIds[i].rid == DOMAIN_GROUP_RID_ADMINS)
949 			return (B_TRUE);
950 
951 	return (B_FALSE);
952 }
953