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