Print this page
NEX-18708 Domain logons may get STATUS_ACCESS_DENIED
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
1575 untangle libmlrpc from SMB server
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
NEX-15558 SMB logon fails during 1st second after service start
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15558 SMB logon fails during 1st second after service start
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9106 SMB should support "Resource SID Compression"
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-4272 Allow authenticating computer accounts
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3080 SMB1 signing problem with Kerberos auth.
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-2225 Unable to join NexentaStor to 2008 AD
NEX-1810 extended security Kerberos (inbound)
re #7126 rb4153 smbd panic with missing negotiate challenge

*** 19,29 **** * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* * NETR SamLogon and SamLogoff RPC client functions. */ --- 19,29 ---- * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * NETR SamLogon and SamLogoff RPC client functions. */
*** 35,56 **** #include <alloca.h> #include <unistd.h> #include <netdb.h> #include <thread.h> #include <smbsrv/libsmb.h> - #include <smbsrv/libmlrpc.h> #include <smbsrv/libmlsvc.h> #include <smbsrv/ndl/netlogon.ndl> #include <smbsrv/netrauth.h> #include <smbsrv/smbinfo.h> #include <smbsrv/smb_token.h> #include <mlsvc.h> ! #define NETLOGON_ATTEMPTS 2 ! ! static uint32_t netlogon_logon(smb_logon_t *, smb_token_t *); static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *, smb_logon_t *, smb_token_t *); static void netr_invalidate_chain(void); static void netr_interactive_samlogon(netr_info_t *, smb_logon_t *, struct netr_logon_info1 *); --- 35,54 ---- #include <alloca.h> #include <unistd.h> #include <netdb.h> #include <thread.h> + #include <libmlrpc/libmlrpc.h> #include <smbsrv/libsmb.h> #include <smbsrv/libmlsvc.h> #include <smbsrv/ndl/netlogon.ndl> #include <smbsrv/netrauth.h> #include <smbsrv/smbinfo.h> #include <smbsrv/smb_token.h> #include <mlsvc.h> ! static uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *); static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *, smb_logon_t *, smb_token_t *); static void netr_invalidate_chain(void); static void netr_interactive_samlogon(netr_info_t *, smb_logon_t *, struct netr_logon_info1 *);
*** 59,70 **** static void netr_setup_identity(ndr_heap_t *, smb_logon_t *, netr_logon_id_t *); static boolean_t netr_isadmin(struct netr_validation_info3 *); static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *, smb_ids_t *); ! static uint32_t netr_setup_token_info3(struct netr_validation_info3 *, ! smb_token_t *); static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *, smb_token_t *); /* * Shared with netr_auth.c --- 57,68 ---- static void netr_setup_identity(ndr_heap_t *, smb_logon_t *, netr_logon_id_t *); static boolean_t netr_isadmin(struct netr_validation_info3 *); static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *, smb_ids_t *); ! static uint32_t netr_setup_krb5res_groups(struct krb5_validation_info *, ! smb_ids_t *); static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *, smb_token_t *); /* * Shared with netr_auth.c
*** 82,91 **** --- 80,90 ---- uint32_t smb_decode_krb5_pac(smb_token_t *token, char *data, uint_t len) { struct krb5_validation_info info; ndr_buf_t *nbuf; + smb_sid_t *domsid; uint32_t status = NT_STATUS_NO_MEMORY; int rc; bzero(&info, sizeof (info));
*** 99,158 **** if (rc != NDR_DRC_OK) { status = RPC_NT_PROTOCOL_ERROR; goto out; } ! status = netr_setup_token_info3(&info.info3, token); ! ! /* Deal with the "resource groups"? */ ! ! ! out: ! if (nbuf != NULL) ! ndr_buf_fini(nbuf); ! ! return (status); ! } ! ! /* ! * Code factored out of netr_setup_token() */ ! static uint32_t ! netr_setup_token_info3(struct netr_validation_info3 *info3, ! smb_token_t *token) ! { ! smb_sid_t *domsid; - domsid = (smb_sid_t *)info3->LogonDomainId; - token->tkn_user.i_sid = smb_sid_splice(domsid, ! info3->UserId); if (token->tkn_user.i_sid == NULL) ! goto errout; token->tkn_primary_grp.i_sid = smb_sid_splice(domsid, ! info3->PrimaryGroupId); if (token->tkn_primary_grp.i_sid == NULL) ! goto errout; ! if (info3->EffectiveName.str) { token->tkn_account_name = ! strdup((char *)info3->EffectiveName.str); if (token->tkn_account_name == NULL) ! goto errout; } ! if (info3->LogonDomainName.str) { token->tkn_domain_name = ! strdup((char *)info3->LogonDomainName.str); if (token->tkn_domain_name == NULL) ! goto errout; } ! return (netr_setup_token_wingrps(info3, token)); ! errout: ! return (NT_STATUS_INSUFF_SERVER_RESOURCES); } /* * Abort impending domain logon requests. */ --- 98,154 ---- if (rc != NDR_DRC_OK) { status = RPC_NT_PROTOCOL_ERROR; goto out; } ! /* ! * Copy the decoded info into the token, ! * similar to netr_setup_token() */ ! domsid = (smb_sid_t *)info.info3.LogonDomainId; token->tkn_user.i_sid = smb_sid_splice(domsid, ! info.info3.UserId); if (token->tkn_user.i_sid == NULL) ! goto out; token->tkn_primary_grp.i_sid = smb_sid_splice(domsid, ! info.info3.PrimaryGroupId); if (token->tkn_primary_grp.i_sid == NULL) ! goto out; ! if (info.info3.EffectiveName.str) { token->tkn_account_name = ! strdup((char *)info.info3.EffectiveName.str); if (token->tkn_account_name == NULL) ! goto out; } ! if (info.info3.LogonDomainName.str) { token->tkn_domain_name = ! strdup((char *)info.info3.LogonDomainName.str); if (token->tkn_domain_name == NULL) ! goto out; } ! status = netr_setup_domain_groups(&info.info3, &token->tkn_win_grps); ! if (status != NT_STATUS_SUCCESS) ! goto out; ! ! if (info.rg_rid_cnt != 0) { ! status = netr_setup_krb5res_groups(&info, &token->tkn_win_grps); ! if (status != NT_STATUS_SUCCESS) ! goto out; ! } ! ! status = netr_setup_token_wingrps(&info.info3, token); ! ! out: ! if (nbuf != NULL) ! ndr_buf_fini(nbuf); ! ! return (status); } /* * Abort impending domain logon requests. */
*** 174,285 **** * this function must return without updating the status. * * If the user is successfully authenticated, we build an * access token and the status will be NT_STATUS_SUCCESS. * Otherwise, the token contents are invalid. */ void smb_logon_domain(smb_logon_t *user_info, smb_token_t *token) { uint32_t status; ! int i; if (user_info->lg_secmode != SMB_SECMODE_DOMAIN) return; if (user_info->lg_domain_type == SMB_DOMAIN_LOCAL) return; ! for (i = 0; i < NETLOGON_ATTEMPTS; ++i) { (void) mutex_lock(&netlogon_mutex); while (netlogon_busy && !netlogon_abort) (void) cond_wait(&netlogon_cv, &netlogon_mutex); if (netlogon_abort) { (void) mutex_unlock(&netlogon_mutex); ! user_info->lg_status = NT_STATUS_REQUEST_ABORTED; ! return; } netlogon_busy = B_TRUE; (void) mutex_unlock(&netlogon_mutex); ! status = netlogon_logon(user_info, token); (void) mutex_lock(&netlogon_mutex); netlogon_busy = B_FALSE; if (netlogon_abort) status = NT_STATUS_REQUEST_ABORTED; (void) cond_signal(&netlogon_cv); (void) mutex_unlock(&netlogon_mutex); ! if (status != NT_STATUS_CANT_ACCESS_DOMAIN_INFO) break; } if (status != NT_STATUS_SUCCESS) syslog(LOG_INFO, "logon[%s\\%s]: %s", user_info->lg_e_domain, user_info->lg_e_username, xlate_nt_status(status)); - user_info->lg_status = status; } static uint32_t ! netlogon_logon(smb_logon_t *user_info, smb_token_t *token) { - char resource_domain[SMB_PI_MAX_DOMAIN]; char server[MAXHOSTNAMELEN]; mlsvc_handle_t netr_handle; - smb_domainex_t di; uint32_t status; ! int retries = 0; ! (void) smb_getdomainname(resource_domain, SMB_PI_MAX_DOMAIN); ! ! /* Avoid interfering with DC discovery. */ ! if (smb_ddiscover_wait() != 0 || ! !smb_domain_getinfo(&di)) { ! netr_invalidate_chain(); ! return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); } ! do { ! if (netr_open(di.d_dci.dc_name, di.d_primary.di_nbname, ! &netr_handle) != 0) ! return (NT_STATUS_OPEN_FAILED); ! ! if (di.d_dci.dc_name[0] != '\0' && (*netr_global_info.server != '\0')) { (void) snprintf(server, sizeof (server), ! "\\\\%s", di.d_dci.dc_name); if (strncasecmp(netr_global_info.server, server, strlen(server)) != 0) netr_invalidate_chain(); } if ((netr_global_info.flags & NETR_FLG_VALID) == 0 || !smb_match_netlogon_seqnum()) { ! status = netlogon_auth(di.d_dci.dc_name, &netr_handle, NETR_FLG_NULL); if (status != 0) { (void) netr_close(&netr_handle); ! return (NT_STATUS_LOGON_FAILURE); } netr_global_info.flags |= NETR_FLG_VALID; } status = netr_server_samlogon(&netr_handle, ! &netr_global_info, di.d_dci.dc_name, user_info, token); (void) netr_close(&netr_handle); - } while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3); ! if (retries >= 3) ! status = NT_STATUS_LOGON_FAILURE; return (status); } static uint32_t netr_setup_token(struct netr_validation_info3 *info3, smb_logon_t *user_info, --- 170,384 ---- * this function must return without updating the status. * * If the user is successfully authenticated, we build an * access token and the status will be NT_STATUS_SUCCESS. * Otherwise, the token contents are invalid. + * + * This will retry a few times for errors indicating that the + * current DC might have gone off-line or become too busy etc. + * With such errors, smb_ddiscover_bad_dc is called and then + * the smb_domain_getinfo call here waits for new DC info. */ + int smb_netr_logon_retries = 3; void smb_logon_domain(smb_logon_t *user_info, smb_token_t *token) { + smb_domainex_t di; uint32_t status; ! int retries = smb_netr_logon_retries; if (user_info->lg_secmode != SMB_SECMODE_DOMAIN) return; if (user_info->lg_domain_type == SMB_DOMAIN_LOCAL) return; ! while (--retries > 0) { ! ! if (!smb_domain_getinfo(&di)) { ! syslog(LOG_ERR, "logon DC getinfo failed"); ! status = NT_STATUS_NO_LOGON_SERVERS; ! goto out; ! } ! (void) mutex_lock(&netlogon_mutex); while (netlogon_busy && !netlogon_abort) (void) cond_wait(&netlogon_cv, &netlogon_mutex); if (netlogon_abort) { (void) mutex_unlock(&netlogon_mutex); ! status = NT_STATUS_REQUEST_ABORTED; ! goto out; } netlogon_busy = B_TRUE; (void) mutex_unlock(&netlogon_mutex); ! status = netlogon_logon(user_info, token, &di); (void) mutex_lock(&netlogon_mutex); netlogon_busy = B_FALSE; if (netlogon_abort) status = NT_STATUS_REQUEST_ABORTED; (void) cond_signal(&netlogon_cv); (void) mutex_unlock(&netlogon_mutex); ! switch (status) { ! case NT_STATUS_BAD_NETWORK_PATH: ! case NT_STATUS_BAD_NETWORK_NAME: ! case RPC_NT_SERVER_TOO_BUSY: ! /* ! * May retry with a new DC, or if we're ! * out of retries, will return... ! */ ! status = NT_STATUS_NO_LOGON_SERVERS; break; + default: + goto out; } + } + out: if (status != NT_STATUS_SUCCESS) syslog(LOG_INFO, "logon[%s\\%s]: %s", user_info->lg_e_domain, user_info->lg_e_username, xlate_nt_status(status)); user_info->lg_status = status; } + /* + * Run a netr_server_samlogon call, dealing with the possible need to + * re-establish the NetLogon credential chain. If that fails, return + * NT_STATUS_DOMAIN_TRUST_INCONSISTENT indicating the machine account + * needs it's password reset (or whatever). Other errors are from the + * netr_server_samlogon() call including the many possibilities listed + * above that function. + */ static uint32_t ! netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di) { char server[MAXHOSTNAMELEN]; mlsvc_handle_t netr_handle; uint32_t status; ! boolean_t did_reauth = B_FALSE; ! /* ! * This netr_open call does the work to connect to the DC, ! * get the IPC share, open the named pipe, RPC bind, etc. ! */ ! status = netr_open(di->d_dci.dc_name, di->d_primary.di_nbname, ! &netr_handle); ! if (status != 0) { ! syslog(LOG_ERR, "netlogon remote open failed (%s)", ! xlate_nt_status(status)); ! return (status); } ! if (di->d_dci.dc_name[0] != '\0' && (*netr_global_info.server != '\0')) { (void) snprintf(server, sizeof (server), ! "\\\\%s", di->d_dci.dc_name); if (strncasecmp(netr_global_info.server, server, strlen(server)) != 0) netr_invalidate_chain(); } + reauth: if ((netr_global_info.flags & NETR_FLG_VALID) == 0 || !smb_match_netlogon_seqnum()) { ! /* ! * This does netr_server_req_challenge() and ! * netr_server_authenticate2(), updating the ! * current netlogon sequence number. ! */ ! status = netlogon_auth(di->d_dci.dc_name, &netr_handle, NETR_FLG_NULL); if (status != 0) { + syslog(LOG_ERR, "netlogon remote auth failed (%s)", + xlate_nt_status(status)); (void) netr_close(&netr_handle); ! return (NT_STATUS_DOMAIN_TRUST_INCONSISTENT); } netr_global_info.flags |= NETR_FLG_VALID; } status = netr_server_samlogon(&netr_handle, ! &netr_global_info, di->d_dci.dc_name, user_info, token); + if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { + if (!did_reauth) { + /* Call netlogon_auth() again, just once. */ + did_reauth = B_TRUE; + goto reauth; + } + status = NT_STATUS_DOMAIN_TRUST_INCONSISTENT; + } + (void) netr_close(&netr_handle); ! return (status); ! } + /* + * Helper for mlsvc_netlogon + * + * Call netlogon_auth with appropriate locks etc. + * Serialize like smb_logon_domain does for + * netlogon_logon / netlogon_auth + */ + uint32_t + smb_netlogon_check(char *server, char *domain) + { + mlsvc_handle_t netr_handle; + uint32_t status; + + (void) mutex_lock(&netlogon_mutex); + while (netlogon_busy) + (void) cond_wait(&netlogon_cv, &netlogon_mutex); + + netlogon_busy = B_TRUE; + (void) mutex_unlock(&netlogon_mutex); + + /* + * This section like netlogon_logon(), but only does + * one pass and no netr_server_samlogon call. + */ + + status = netr_open(server, domain, + &netr_handle); + if (status != 0) { + syslog(LOG_ERR, "netlogon remote open failed (%s)", + xlate_nt_status(status)); + goto unlock_out; + } + + if ((netr_global_info.flags & NETR_FLG_VALID) == 0 || + !smb_match_netlogon_seqnum()) { + /* + * This does netr_server_req_challenge() and + * netr_server_authenticate2(), updating the + * current netlogon sequence number. + */ + status = netlogon_auth(server, &netr_handle, + NETR_FLG_NULL); + if (status != 0) { + syslog(LOG_ERR, "netlogon remote auth failed (%s)", + xlate_nt_status(status)); + } else { + netr_global_info.flags |= NETR_FLG_VALID; + } + } + + (void) netr_close(&netr_handle); + + unlock_out: + (void) mutex_lock(&netlogon_mutex); + netlogon_busy = B_FALSE; + (void) cond_signal(&netlogon_cv); + (void) mutex_unlock(&netlogon_mutex); + return (status); } static uint32_t netr_setup_token(struct netr_validation_info3 *info3, smb_logon_t *user_info,
*** 320,329 **** --- 419,432 ---- token->tkn_domain_name = strdup(domain); if (token->tkn_account_name == NULL || token->tkn_domain_name == NULL) return (NT_STATUS_NO_MEMORY); + status = netr_setup_domain_groups(info3, &token->tkn_win_grps); + if (status != NT_STATUS_SUCCESS) + return (status); + status = netr_setup_token_wingrps(info3, token); if (status != NT_STATUS_SUCCESS) return (status); /*
*** 709,756 **** ndr_heap_mkvcs(heap, user_info->lg_workstation, (ndr_vcstr_t *)&identity->workstation); } /* ! * Sets up domain, local and well-known group membership for the given ! * token. Two assumptions have been made here: ! * ! * a) token already contains a valid user SID so that group ! * memberships can be established ! * ! * b) token belongs to a domain user */ static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *info3, smb_token_t *token) { - smb_ids_t tkn_grps; uint32_t status; ! tkn_grps.i_cnt = 0; ! tkn_grps.i_ids = NULL; ! ! status = netr_setup_domain_groups(info3, &tkn_grps); ! if (status != NT_STATUS_SUCCESS) { ! smb_ids_free(&tkn_grps); return (status); - } - status = smb_sam_usr_groups(token->tkn_user.i_sid, &tkn_grps); - if (status != NT_STATUS_SUCCESS) { - smb_ids_free(&tkn_grps); - return (status); - } - if (netr_isadmin(info3)) token->tkn_flags |= SMB_ATF_ADMIN; ! status = smb_wka_token_groups(token->tkn_flags, &tkn_grps); ! if (status == NT_STATUS_SUCCESS) ! token->tkn_win_grps = tkn_grps; ! else ! smb_ids_free(&tkn_grps); return (status); } /* --- 812,839 ---- ndr_heap_mkvcs(heap, user_info->lg_workstation, (ndr_vcstr_t *)&identity->workstation); } /* ! * Add local and well-known group membership to the given ! * token. Called after domain groups have been added. */ static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *info3, smb_token_t *token) { uint32_t status; ! status = smb_sam_usr_groups(token->tkn_user.i_sid, ! &token->tkn_win_grps); ! if (status != NT_STATUS_SUCCESS) return (status); if (netr_isadmin(info3)) token->tkn_flags |= SMB_ATF_ADMIN; ! status = smb_wka_token_groups(token->tkn_flags, &token->tkn_win_grps); return (status); } /*
*** 808,817 **** --- 891,931 ---- } return (NT_STATUS_SUCCESS); } + /* + * Converts additional "resource" groups (from krb5_validation_info) + * into the internal representation (gids), appending to the list + * already put in place by netr_setup_domain_groups(). + */ + static uint32_t netr_setup_krb5res_groups(struct krb5_validation_info *info, + smb_ids_t *gids) + { + smb_sid_t *domain_sid; + smb_id_t *ids; + int i, total_cnt; + + total_cnt = gids->i_cnt + info->rg_rid_cnt; + + gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t)); + if (gids->i_ids == NULL) + return (NT_STATUS_NO_MEMORY); + + domain_sid = (smb_sid_t *)info->rg_dom_sid; + + ids = gids->i_ids + gids->i_cnt; + for (i = 0; i < info->rg_rid_cnt; i++, gids->i_cnt++, ids++) { + ids->i_sid = smb_sid_splice(domain_sid, info->rg_rids[i].rid); + if (ids->i_sid == NULL) + return (NT_STATUS_NO_MEMORY); + ids->i_attrs = info->rg_rids[i].attributes; + } + + return (0); + } + /* * Determines if the given user is the domain Administrator or a * member of Domain Admins */ static boolean_t