Merge commit '42a3762d0138d6dd0c0f96964be98d0b21ab6bef'
[unleashed.git] / usr / src / lib / smbsrv / libmlsvc / common / netr_logon.c
blob8b70814951e47841efb40972264f0af01c366fcc
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
28 * NETR SamLogon and SamLogoff RPC client functions.
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>
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>
49 static uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *);
50 static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *,
51 smb_logon_t *, smb_token_t *);
52 static void netr_invalidate_chain(void);
53 static void netr_interactive_samlogon(netr_info_t *, smb_logon_t *,
54 struct netr_logon_info1 *);
55 static void netr_network_samlogon(ndr_heap_t *, netr_info_t *,
56 smb_logon_t *, struct netr_logon_info2 *);
57 static void netr_setup_identity(ndr_heap_t *, smb_logon_t *,
58 netr_logon_id_t *);
59 static boolean_t netr_isadmin(struct netr_validation_info3 *);
60 static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *,
61 smb_ids_t *);
62 static uint32_t netr_setup_token_info3(struct netr_validation_info3 *,
63 smb_token_t *);
64 static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *,
65 smb_token_t *);
68 * Shared with netr_auth.c
70 extern netr_info_t netr_global_info;
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;
78 * Helper for Kerberos authentication
80 uint32_t
81 smb_decode_krb5_pac(smb_token_t *token, char *data, uint_t len)
83 struct krb5_validation_info info;
84 ndr_buf_t *nbuf;
85 uint32_t status = NT_STATUS_NO_MEMORY;
86 int rc;
88 bzero(&info, sizeof (info));
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;
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;
102 status = netr_setup_token_info3(&info.info3, token);
104 /* Deal with the "resource groups"? */
107 out:
108 if (nbuf != NULL)
109 ndr_buf_fini(nbuf);
111 return (status);
115 * Code factored out of netr_setup_token()
117 static uint32_t
118 netr_setup_token_info3(struct netr_validation_info3 *info3,
119 smb_token_t *token)
121 smb_sid_t *domsid;
123 domsid = (smb_sid_t *)info3->LogonDomainId;
125 token->tkn_user.i_sid = smb_sid_splice(domsid,
126 info3->UserId);
127 if (token->tkn_user.i_sid == NULL)
128 goto errout;
130 token->tkn_primary_grp.i_sid = smb_sid_splice(domsid,
131 info3->PrimaryGroupId);
132 if (token->tkn_primary_grp.i_sid == NULL)
133 goto errout;
135 if (info3->EffectiveName.str) {
136 token->tkn_account_name =
137 strdup((char *)info3->EffectiveName.str);
138 if (token->tkn_account_name == NULL)
139 goto errout;
142 if (info3->LogonDomainName.str) {
143 token->tkn_domain_name =
144 strdup((char *)info3->LogonDomainName.str);
145 if (token->tkn_domain_name == NULL)
146 goto errout;
149 return (netr_setup_token_wingrps(info3, token));
150 errout:
151 return (NT_STATUS_INSUFF_SERVER_RESOURCES);
155 * Abort impending domain logon requests.
157 void
158 smb_logon_abort(void)
160 (void) mutex_lock(&netlogon_mutex);
161 if (netlogon_busy && !netlogon_abort)
162 syslog(LOG_DEBUG, "logon abort");
163 netlogon_abort = B_TRUE;
164 (void) cond_broadcast(&netlogon_cv);
165 (void) mutex_unlock(&netlogon_mutex);
169 * This is the entry point for authenticating domain users.
171 * If we are not going to attempt to authenticate the user,
172 * this function must return without updating the status.
174 * If the user is successfully authenticated, we build an
175 * access token and the status will be NT_STATUS_SUCCESS.
176 * Otherwise, the token contents are invalid.
178 * This will retry a few times for errors indicating that the
179 * current DC might have gone off-line or become too busy etc.
180 * With such errors, smb_ddiscover_bad_dc is called and then
181 * the smb_domain_getinfo call here waits for new DC info.
183 int smb_netr_logon_retries = 3;
184 void
185 smb_logon_domain(smb_logon_t *user_info, smb_token_t *token)
187 smb_domainex_t di;
188 uint32_t status;
189 int retries = smb_netr_logon_retries;
191 if (user_info->lg_secmode != SMB_SECMODE_DOMAIN)
192 return;
194 if (user_info->lg_domain_type == SMB_DOMAIN_LOCAL)
195 return;
197 while (--retries > 0) {
199 if (!smb_domain_getinfo(&di)) {
200 syslog(LOG_ERR, "logon DC getinfo failed");
201 status = NT_STATUS_NO_LOGON_SERVERS;
202 goto out;
205 (void) mutex_lock(&netlogon_mutex);
206 while (netlogon_busy && !netlogon_abort)
207 (void) cond_wait(&netlogon_cv, &netlogon_mutex);
209 if (netlogon_abort) {
210 (void) mutex_unlock(&netlogon_mutex);
211 status = NT_STATUS_REQUEST_ABORTED;
212 goto out;
215 netlogon_busy = B_TRUE;
216 (void) mutex_unlock(&netlogon_mutex);
218 status = netlogon_logon(user_info, token, &di);
220 (void) mutex_lock(&netlogon_mutex);
221 netlogon_busy = B_FALSE;
222 if (netlogon_abort)
223 status = NT_STATUS_REQUEST_ABORTED;
224 (void) cond_signal(&netlogon_cv);
225 (void) mutex_unlock(&netlogon_mutex);
227 switch (status) {
228 case NT_STATUS_BAD_NETWORK_PATH:
229 case NT_STATUS_BAD_NETWORK_NAME:
230 case RPC_NT_SERVER_TOO_BUSY:
232 * May retry with a new DC, or if we're
233 * out of retries, will return...
235 status = NT_STATUS_NO_LOGON_SERVERS;
236 break;
237 default:
238 goto out;
242 out:
243 if (status != NT_STATUS_SUCCESS)
244 syslog(LOG_INFO, "logon[%s\\%s]: %s", user_info->lg_e_domain,
245 user_info->lg_e_username, xlate_nt_status(status));
246 user_info->lg_status = status;
250 * Run a netr_server_samlogon call, dealing with the possible need to
251 * re-establish the NetLogon credential chain. If that fails, return
252 * NT_STATUS_DOMAIN_TRUST_INCONSISTENT indicating the machine account
253 * needs it's password reset (or whatever). Other errors are from the
254 * netr_server_samlogon() call including the many possibilities listed
255 * above that function.
257 static uint32_t
258 netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di)
260 char server[MAXHOSTNAMELEN];
261 mlsvc_handle_t netr_handle;
262 uint32_t status;
263 boolean_t did_reauth = B_FALSE;
266 * This netr_open call does the work to connect to the DC,
267 * get the IPC share, open the named pipe, RPC bind, etc.
269 status = netr_open(di->d_dci.dc_name, di->d_primary.di_nbname,
270 &netr_handle);
271 if (status != 0) {
272 syslog(LOG_ERR, "netlogon remote open failed (%s)",
273 xlate_nt_status(status));
274 return (status);
277 if (di->d_dci.dc_name[0] != '\0' &&
278 (*netr_global_info.server != '\0')) {
279 (void) snprintf(server, sizeof (server),
280 "\\\\%s", di->d_dci.dc_name);
281 if (strncasecmp(netr_global_info.server,
282 server, strlen(server)) != 0)
283 netr_invalidate_chain();
286 reauth:
287 if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
288 !smb_match_netlogon_seqnum()) {
290 * This does netr_server_req_challenge() and
291 * netr_server_authenticate2(), updating the
292 * current netlogon sequence number.
294 status = netlogon_auth(di->d_dci.dc_name, &netr_handle,
295 NETR_FLG_NULL);
297 if (status != 0) {
298 syslog(LOG_ERR, "netlogon remote auth failed (%s)",
299 xlate_nt_status(status));
300 (void) netr_close(&netr_handle);
301 return (NT_STATUS_DOMAIN_TRUST_INCONSISTENT);
304 netr_global_info.flags |= NETR_FLG_VALID;
307 status = netr_server_samlogon(&netr_handle,
308 &netr_global_info, di->d_dci.dc_name, user_info, token);
310 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
311 if (!did_reauth) {
312 /* Call netlogon_auth() again, just once. */
313 did_reauth = B_TRUE;
314 goto reauth;
316 status = NT_STATUS_DOMAIN_TRUST_INCONSISTENT;
319 (void) netr_close(&netr_handle);
321 return (status);
325 * Helper for mlsvc_netlogon
327 * Call netlogon_auth with appropriate locks etc.
328 * Serialize like smb_logon_domain does for
329 * netlogon_logon / netlogon_auth
331 uint32_t
332 smb_netlogon_check(char *server, char *domain)
334 mlsvc_handle_t netr_handle;
335 uint32_t status;
337 (void) mutex_lock(&netlogon_mutex);
338 while (netlogon_busy)
339 (void) cond_wait(&netlogon_cv, &netlogon_mutex);
341 netlogon_busy = B_TRUE;
342 (void) mutex_unlock(&netlogon_mutex);
345 * This section like netlogon_logon(), but only does
346 * one pass and no netr_server_samlogon call.
349 status = netr_open(server, domain,
350 &netr_handle);
351 if (status != 0) {
352 syslog(LOG_ERR, "netlogon remote open failed (%s)",
353 xlate_nt_status(status));
354 goto unlock_out;
357 if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
358 !smb_match_netlogon_seqnum()) {
360 * This does netr_server_req_challenge() and
361 * netr_server_authenticate2(), updating the
362 * current netlogon sequence number.
364 status = netlogon_auth(server, &netr_handle,
365 NETR_FLG_NULL);
366 if (status != 0) {
367 syslog(LOG_ERR, "netlogon remote auth failed (%s)",
368 xlate_nt_status(status));
369 } else {
370 netr_global_info.flags |= NETR_FLG_VALID;
374 (void) netr_close(&netr_handle);
376 unlock_out:
377 (void) mutex_lock(&netlogon_mutex);
378 netlogon_busy = B_FALSE;
379 (void) cond_signal(&netlogon_cv);
380 (void) mutex_unlock(&netlogon_mutex);
382 return (status);
385 static uint32_t
386 netr_setup_token(struct netr_validation_info3 *info3, smb_logon_t *user_info,
387 netr_info_t *netr_info, smb_token_t *token)
389 char *username, *domain;
390 unsigned char rc4key[SMBAUTH_SESSION_KEY_SZ];
391 smb_sid_t *domsid;
392 uint32_t status;
393 char nbdomain[NETBIOS_NAME_SZ];
395 domsid = (smb_sid_t *)info3->LogonDomainId;
397 token->tkn_user.i_sid = smb_sid_splice(domsid, info3->UserId);
398 if (token->tkn_user.i_sid == NULL)
399 return (NT_STATUS_NO_MEMORY);
401 token->tkn_primary_grp.i_sid = smb_sid_splice(domsid,
402 info3->PrimaryGroupId);
403 if (token->tkn_primary_grp.i_sid == NULL)
404 return (NT_STATUS_NO_MEMORY);
406 username = (info3->EffectiveName.str)
407 ? (char *)info3->EffectiveName.str : user_info->lg_e_username;
409 if (info3->LogonDomainName.str) {
410 domain = (char *)info3->LogonDomainName.str;
411 } else if (*user_info->lg_e_domain != '\0') {
412 domain = user_info->lg_e_domain;
413 } else {
414 (void) smb_getdomainname(nbdomain, sizeof (nbdomain));
415 domain = nbdomain;
418 if (username)
419 token->tkn_account_name = strdup(username);
420 if (domain)
421 token->tkn_domain_name = strdup(domain);
423 if (token->tkn_account_name == NULL || token->tkn_domain_name == NULL)
424 return (NT_STATUS_NO_MEMORY);
426 status = netr_setup_token_wingrps(info3, token);
427 if (status != NT_STATUS_SUCCESS)
428 return (status);
431 * The UserSessionKey in NetrSamLogon RPC is obfuscated using the
432 * session key obtained in the NETLOGON credential chain.
433 * An 8 byte session key is zero extended to 16 bytes. This 16 byte
434 * key is the key to the RC4 algorithm. The RC4 byte stream is
435 * exclusively ored with the 16 byte UserSessionKey to recover
436 * the the clear form.
438 if ((token->tkn_ssnkey.val = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL)
439 return (NT_STATUS_NO_MEMORY);
440 token->tkn_ssnkey.len = SMBAUTH_SESSION_KEY_SZ;
441 bzero(rc4key, SMBAUTH_SESSION_KEY_SZ);
442 bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len);
443 bcopy(info3->UserSessionKey.data, token->tkn_ssnkey.val,
444 SMBAUTH_SESSION_KEY_SZ);
445 rand_hash((unsigned char *)token->tkn_ssnkey.val,
446 SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ);
448 return (NT_STATUS_SUCCESS);
452 * netr_server_samlogon
454 * NetrServerSamLogon RPC: interactive or network. It is assumed that
455 * we have already authenticated with the PDC. If everything works,
456 * we build a user info structure and return it, where the caller will
457 * probably build an access token.
459 * Returns an NT status. There are numerous possibilities here.
460 * For example:
461 * NT_STATUS_INVALID_INFO_CLASS
462 * NT_STATUS_INVALID_PARAMETER
463 * NT_STATUS_ACCESS_DENIED
464 * NT_STATUS_PASSWORD_MUST_CHANGE
465 * NT_STATUS_NO_SUCH_USER
466 * NT_STATUS_WRONG_PASSWORD
467 * NT_STATUS_LOGON_FAILURE
468 * NT_STATUS_ACCOUNT_RESTRICTION
469 * NT_STATUS_INVALID_LOGON_HOURS
470 * NT_STATUS_INVALID_WORKSTATION
471 * NT_STATUS_INTERNAL_ERROR
472 * NT_STATUS_PASSWORD_EXPIRED
473 * NT_STATUS_ACCOUNT_DISABLED
475 uint32_t
476 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
477 char *server, smb_logon_t *user_info, smb_token_t *token)
479 struct netr_SamLogon arg;
480 struct netr_authenticator auth;
481 struct netr_authenticator ret_auth;
482 struct netr_logon_info1 info1;
483 struct netr_logon_info2 info2;
484 struct netr_validation_info3 *info3;
485 ndr_heap_t *heap;
486 int opnum;
487 int rc, len;
488 uint32_t status;
490 bzero(&arg, sizeof (struct netr_SamLogon));
491 opnum = NETR_OPNUM_SamLogon;
494 * Should we get the server and hostname from netr_info?
497 len = strlen(server) + 4;
498 arg.servername = ndr_rpc_malloc(netr_handle, len);
499 arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ);
500 if (arg.servername == NULL || arg.hostname == NULL) {
501 ndr_rpc_release(netr_handle);
502 return (NT_STATUS_INTERNAL_ERROR);
505 (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
506 if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) {
507 ndr_rpc_release(netr_handle);
508 return (NT_STATUS_INTERNAL_ERROR);
511 rc = netr_setup_authenticator(netr_info, &auth, &ret_auth);
512 if (rc != SMBAUTH_SUCCESS) {
513 ndr_rpc_release(netr_handle);
514 return (NT_STATUS_INTERNAL_ERROR);
517 arg.auth = &auth;
518 arg.ret_auth = &ret_auth;
519 arg.validation_level = NETR_VALIDATION_LEVEL3;
520 arg.logon_info.logon_level = user_info->lg_level;
521 arg.logon_info.switch_value = user_info->lg_level;
523 heap = ndr_rpc_get_heap(netr_handle);
525 switch (user_info->lg_level) {
526 case NETR_INTERACTIVE_LOGON:
527 netr_setup_identity(heap, user_info, &info1.identity);
528 netr_interactive_samlogon(netr_info, user_info, &info1);
529 arg.logon_info.ru.info1 = &info1;
530 break;
532 case NETR_NETWORK_LOGON:
533 if (user_info->lg_challenge_key.len < 8 ||
534 user_info->lg_challenge_key.val == NULL) {
535 ndr_rpc_release(netr_handle);
536 return (NT_STATUS_INVALID_PARAMETER);
538 netr_setup_identity(heap, user_info, &info2.identity);
539 netr_network_samlogon(heap, netr_info, user_info, &info2);
540 arg.logon_info.ru.info2 = &info2;
541 break;
543 default:
544 ndr_rpc_release(netr_handle);
545 return (NT_STATUS_INVALID_PARAMETER);
548 rc = ndr_rpc_call(netr_handle, opnum, &arg);
549 if (rc != 0) {
550 bzero(netr_info, sizeof (netr_info_t));
551 status = NT_STATUS_INVALID_PARAMETER;
552 } else if (arg.status != 0) {
553 status = NT_SC_VALUE(arg.status);
556 * We need to validate the chain even though we have
557 * a non-zero status. If the status is ACCESS_DENIED
558 * this will trigger a new credential chain. However,
559 * a valid credential is returned with some status
560 * codes; for example, WRONG_PASSWORD.
562 (void) netr_validate_chain(netr_info, arg.ret_auth);
563 } else {
564 status = netr_validate_chain(netr_info, arg.ret_auth);
565 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
566 ndr_rpc_release(netr_handle);
567 return (status);
570 info3 = arg.ru.info3;
571 status = netr_setup_token(info3, user_info, netr_info, token);
574 ndr_rpc_release(netr_handle);
575 return (status);
579 * netr_interactive_samlogon
581 * Set things up for an interactive SamLogon. Copy the NT and LM
582 * passwords to the logon structure and hash them with the session
583 * key.
585 static void
586 netr_interactive_samlogon(netr_info_t *netr_info, smb_logon_t *user_info,
587 struct netr_logon_info1 *info1)
589 BYTE key[NETR_OWF_PASSWORD_SZ];
591 (void) memcpy(&info1->lm_owf_password,
592 user_info->lg_lm_password.val, sizeof (netr_owf_password_t));
594 (void) memcpy(&info1->nt_owf_password,
595 user_info->lg_nt_password.val, sizeof (netr_owf_password_t));
597 (void) memset(key, 0, NETR_OWF_PASSWORD_SZ);
598 (void) memcpy(key, netr_info->session_key.key,
599 netr_info->session_key.len);
601 rand_hash((unsigned char *)&info1->lm_owf_password,
602 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
604 rand_hash((unsigned char *)&info1->nt_owf_password,
605 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
609 * netr_network_samlogon
611 * Set things up for a network SamLogon. We provide a copy of the random
612 * challenge, that we sent to the client, to the domain controller. This
613 * is the key that the client will have used to encrypt the NT and LM
614 * passwords. Note that Windows 9x clients may not provide both passwords.
616 /*ARGSUSED*/
617 static void
618 netr_network_samlogon(ndr_heap_t *heap, netr_info_t *netr_info,
619 smb_logon_t *user_info, struct netr_logon_info2 *info2)
621 uint32_t len;
623 if (user_info->lg_challenge_key.len >= 8 &&
624 user_info->lg_challenge_key.val != 0) {
625 bcopy(user_info->lg_challenge_key.val,
626 info2->lm_challenge.data, 8);
627 } else {
628 bzero(info2->lm_challenge.data, 8);
631 if ((len = user_info->lg_nt_password.len) != 0) {
632 ndr_heap_mkvcb(heap, user_info->lg_nt_password.val, len,
633 (ndr_vcbuf_t *)&info2->nt_response);
634 } else {
635 bzero(&info2->nt_response, sizeof (netr_vcbuf_t));
638 if ((len = user_info->lg_lm_password.len) != 0) {
639 ndr_heap_mkvcb(heap, user_info->lg_lm_password.val, len,
640 (ndr_vcbuf_t *)&info2->lm_response);
641 } else {
642 bzero(&info2->lm_response, sizeof (netr_vcbuf_t));
647 * netr_setup_authenticator
649 * Set up the request and return authenticators. A new credential is
650 * generated from the session key, the current client credential and
651 * the current time, i.e.
653 * NewCredential = Cred(SessionKey, OldCredential, time);
655 * The timestamp, which is used as a random seed, is stored in both
656 * the request and return authenticators.
658 * If any difficulties occur using the cryptographic framework, the
659 * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is
660 * returned.
663 netr_setup_authenticator(netr_info_t *netr_info,
664 struct netr_authenticator *auth, struct netr_authenticator *ret_auth)
666 bzero(auth, sizeof (struct netr_authenticator));
668 netr_info->timestamp = time(0);
669 auth->timestamp = netr_info->timestamp;
671 if (netr_gen_credentials(netr_info->session_key.key,
672 &netr_info->client_credential,
673 netr_info->timestamp,
674 (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS)
675 return (SMBAUTH_FAILURE);
677 if (ret_auth) {
678 bzero(ret_auth, sizeof (struct netr_authenticator));
679 ret_auth->timestamp = netr_info->timestamp;
682 return (SMBAUTH_SUCCESS);
686 * Validate the returned credentials and update the credential chain.
687 * The server returns an updated client credential rather than a new
688 * server credential. The server uses (timestamp + 1) when generating
689 * the credential.
691 * Generate the new seed for the credential chain. The new seed is
692 * formed by adding (timestamp + 1) to the current client credential.
693 * The only quirk is the uint32_t style addition.
695 * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a
696 * NULL pointer. The Authenticator field of the SamLogon response packet
697 * sent by the Samba 3 PDC always return NULL pointer if the received
698 * SamLogon request is not immediately followed by the ServerReqChallenge
699 * and ServerAuthenticate2 requests.
701 * Returns NT_STATUS_SUCCESS if the server returned a valid credential.
702 * Otherwise we retirm NT_STATUS_UNSUCCESSFUL.
704 uint32_t
705 netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth)
707 netr_cred_t cred;
708 uint32_t result = NT_STATUS_SUCCESS;
709 uint32_t *dwp;
711 ++netr_info->timestamp;
713 if (netr_gen_credentials(netr_info->session_key.key,
714 &netr_info->client_credential,
715 netr_info->timestamp, &cred) != SMBAUTH_SUCCESS)
716 return (NT_STATUS_INTERNAL_ERROR);
718 if (&auth->credential == 0) {
720 * If the validation fails, destroy the credential chain.
721 * This should trigger a new authentication chain.
723 bzero(netr_info, sizeof (netr_info_t));
724 return (NT_STATUS_INSUFFICIENT_LOGON_INFO);
727 result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t));
728 if (result != 0) {
730 * If the validation fails, destroy the credential chain.
731 * This should trigger a new authentication chain.
733 bzero(netr_info, sizeof (netr_info_t));
734 result = NT_STATUS_UNSUCCESSFUL;
735 } else {
737 * Otherwise generate the next step in the chain.
739 /*LINTED E_BAD_PTR_CAST_ALIGN*/
740 dwp = (uint32_t *)&netr_info->client_credential;
741 dwp[0] += netr_info->timestamp;
743 netr_info->flags |= NETR_FLG_VALID;
746 return (result);
750 * netr_invalidate_chain
752 * Mark the credential chain as invalid so that it will be recreated
753 * on the next attempt.
755 static void
756 netr_invalidate_chain(void)
758 netr_global_info.flags &= ~NETR_FLG_VALID;
762 * netr_setup_identity
764 * Set up the client identity information. All of this information is
765 * specifically related to the client user and workstation attempting
766 * to access this system. It may not be in our primary domain.
768 * I don't know what logon_id is, it seems to be a unique identifier.
769 * Increment it before each use.
771 static void
772 netr_setup_identity(ndr_heap_t *heap, smb_logon_t *user_info,
773 netr_logon_id_t *identity)
775 static mutex_t logon_id_mutex;
776 static uint32_t logon_id;
778 (void) mutex_lock(&logon_id_mutex);
780 if (logon_id == 0)
781 logon_id = 0xDCD0;
783 ++logon_id;
784 user_info->lg_logon_id = logon_id;
786 (void) mutex_unlock(&logon_id_mutex);
789 * [MS-APDS] 3.1.5.2 "NTLM Network Logon" says to set
790 * ParameterControl to the 'E' + 'K' bits. Those are:
791 * (1 << 5) | (1 << 11), a.k.a
793 identity->parameter_control =
794 MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
795 MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
796 identity->logon_id.LowPart = logon_id;
797 identity->logon_id.HighPart = 0;
799 ndr_heap_mkvcs(heap, user_info->lg_domain,
800 (ndr_vcstr_t *)&identity->domain_name);
802 ndr_heap_mkvcs(heap, user_info->lg_username,
803 (ndr_vcstr_t *)&identity->username);
806 * Some systems prefix the client workstation name with \\.
807 * It doesn't seem to make any difference whether it's there
808 * or not.
810 ndr_heap_mkvcs(heap, user_info->lg_workstation,
811 (ndr_vcstr_t *)&identity->workstation);
815 * Sets up domain, local and well-known group membership for the given
816 * token. Two assumptions have been made here:
818 * a) token already contains a valid user SID so that group
819 * memberships can be established
821 * b) token belongs to a domain user
823 static uint32_t
824 netr_setup_token_wingrps(struct netr_validation_info3 *info3,
825 smb_token_t *token)
827 smb_ids_t tkn_grps;
828 uint32_t status;
830 tkn_grps.i_cnt = 0;
831 tkn_grps.i_ids = NULL;
833 status = netr_setup_domain_groups(info3, &tkn_grps);
834 if (status != NT_STATUS_SUCCESS) {
835 smb_ids_free(&tkn_grps);
836 return (status);
839 status = smb_sam_usr_groups(token->tkn_user.i_sid, &tkn_grps);
840 if (status != NT_STATUS_SUCCESS) {
841 smb_ids_free(&tkn_grps);
842 return (status);
845 if (netr_isadmin(info3))
846 token->tkn_flags |= SMB_ATF_ADMIN;
848 status = smb_wka_token_groups(token->tkn_flags, &tkn_grps);
849 if (status == NT_STATUS_SUCCESS)
850 token->tkn_win_grps = tkn_grps;
851 else
852 smb_ids_free(&tkn_grps);
854 return (status);
858 * Converts groups information in the returned structure by domain controller
859 * (info3) to an internal representation (gids)
861 static uint32_t
862 netr_setup_domain_groups(struct netr_validation_info3 *info3, smb_ids_t *gids)
864 smb_sid_t *domain_sid;
865 smb_id_t *ids;
866 int i, total_cnt;
868 if ((i = info3->GroupCount) == 0)
869 i++;
870 i += info3->SidCount;
872 total_cnt = gids->i_cnt + i;
874 gids->i_ids = reallocarray(gids->i_ids, total_cnt, sizeof (smb_id_t));
875 if (gids->i_ids == NULL)
876 return (NT_STATUS_NO_MEMORY);
878 domain_sid = (smb_sid_t *)info3->LogonDomainId;
880 ids = gids->i_ids + gids->i_cnt;
881 for (i = 0; i < info3->GroupCount; i++, gids->i_cnt++, ids++) {
882 ids->i_sid = smb_sid_splice(domain_sid, info3->GroupIds[i].rid);
883 if (ids->i_sid == NULL)
884 return (NT_STATUS_NO_MEMORY);
886 ids->i_attrs = info3->GroupIds[i].attributes;
889 if (info3->GroupCount == 0) {
891 * if there's no global group should add the primary group.
893 ids->i_sid = smb_sid_splice(domain_sid, info3->PrimaryGroupId);
894 if (ids->i_sid == NULL)
895 return (NT_STATUS_NO_MEMORY);
897 ids->i_attrs = 0x7;
898 gids->i_cnt++;
899 ids++;
902 /* Add the extra SIDs */
903 for (i = 0; i < info3->SidCount; i++, gids->i_cnt++, ids++) {
904 ids->i_sid = smb_sid_dup((smb_sid_t *)info3->ExtraSids[i].sid);
905 if (ids->i_sid == NULL)
906 return (NT_STATUS_NO_MEMORY);
908 ids->i_attrs = info3->ExtraSids[i].attributes;
911 return (NT_STATUS_SUCCESS);
915 * Determines if the given user is the domain Administrator or a
916 * member of Domain Admins
918 static boolean_t
919 netr_isadmin(struct netr_validation_info3 *info3)
921 smb_domain_t di;
922 int i;
924 if (!smb_domain_lookup_sid((smb_sid_t *)info3->LogonDomainId, &di))
925 return (B_FALSE);
927 if (di.di_type != SMB_DOMAIN_PRIMARY)
928 return (B_FALSE);
930 if ((info3->UserId == DOMAIN_USER_RID_ADMIN) ||
931 (info3->PrimaryGroupId == DOMAIN_GROUP_RID_ADMINS))
932 return (B_TRUE);
934 for (i = 0; i < info3->GroupCount; i++)
935 if (info3->GroupIds[i].rid == DOMAIN_GROUP_RID_ADMINS)
936 return (B_TRUE);
938 return (B_FALSE);