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