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