diff options
author | Gordon Ross <gwr@nexenta.com> | 2017-11-25 15:46:51 -0500 |
---|---|---|
committer | Gordon Ross <gwr@nexenta.com> | 2018-10-20 08:42:08 -0400 |
commit | 975041dd3b571af240661f84d186e0cd0e36217b (patch) | |
tree | aee382fe7152d4dc8a9d9f8860f8544cacf5352b | |
parent | ab618543cc6fc4bc273c077ef5d247961cdb29d4 (diff) | |
download | illumos-joyent-975041dd3b571af240661f84d186e0cd0e36217b.tar.gz |
9873 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>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r-- | usr/src/cmd/smbsrv/smbd/smbd_authsvc.c | 10 | ||||
-rw-r--r-- | usr/src/cmd/smbsrv/smbd/smbd_logon.c | 23 | ||||
-rw-r--r-- | usr/src/cmd/smbsrv/smbd/smbd_ntlmssp.c | 12 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h | 3 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h | 4 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c | 63 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c | 6 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c | 17 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c | 197 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/smb_logon.c | 27 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c | 11 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libsmb/common/libsmb.h | 7 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libsmb/common/mapfile-vers | 1 | ||||
-rw-r--r-- | usr/src/lib/smbsrv/libsmb/common/smb_domain.c | 93 |
14 files changed, 342 insertions, 132 deletions
diff --git a/usr/src/cmd/smbsrv/smbd/smbd_authsvc.c b/usr/src/cmd/smbsrv/smbd/smbd_authsvc.c index 0b6af80bd8..b507148b72 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd_authsvc.c +++ b/usr/src/cmd/smbsrv/smbd/smbd_authsvc.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* @@ -549,8 +549,12 @@ smbd_authsvc_oldreq(authsvc_context_t *ctx) token = smbd_user_auth_logon(&user_info); xdr_free(smb_logon_xdr, (char *)&user_info); - if (token == NULL) - return (NT_STATUS_ACCESS_DENIED); + if (token == NULL) { + rc = user_info.lg_status; + if (rc == 0) /* should not happen */ + rc = NT_STATUS_INTERNAL_ERROR; + return (rc); + } ctx->ctx_token = token; diff --git a/usr/src/cmd/smbsrv/smbd/smbd_logon.c b/usr/src/cmd/smbsrv/smbd/smbd_logon.c index ab6d4c2f7e..69b32b2ec9 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd_logon.c +++ b/usr/src/cmd/smbsrv/smbd/smbd_logon.c @@ -77,6 +77,8 @@ static smb_audit_t *smbd_audit_unlink(uint32_t); /* * Invoked at user logon due to SmbSessionSetupX. Authenticate the * user, start an audit session and audit the event. + * + * On error, returns NULL, and status in user_info->lg_status */ smb_token_t * smbd_user_auth_logon(smb_logon_t *user_info) @@ -101,9 +103,16 @@ smbd_user_auth_logon(smb_logon_t *user_info) if (user_info->lg_username == NULL || user_info->lg_domain == NULL || user_info->lg_workstation == NULL) { + user_info->lg_status = NT_STATUS_INVALID_PARAMETER; return (NULL); } + /* + * Avoid modifying the caller-provided struct because it + * may or may not point to allocated strings etc. + * Copy to tmp_user, auth, then copy the (out) lg_status + * member back to the caller-provided struct. + */ tmp_user = *user_info; if (tmp_user.lg_username[0] == '\0') { tmp_user.lg_flags |= SMB_ATF_ANON; @@ -126,7 +135,12 @@ smbd_user_auth_logon(smb_logon_t *user_info) tmp_user.lg_e_domain = tmp_user.lg_domain; } - if ((token = smb_logon(&tmp_user)) == NULL) { + token = smb_logon(&tmp_user); + user_info->lg_status = tmp_user.lg_status; + + if (token == NULL) { + if (user_info->lg_status == 0) /* should not happen */ + user_info->lg_status = NT_STATUS_INTERNAL_ERROR; uid = ADT_NO_ATTRIB; gid = ADT_NO_ATTRIB; sid = NT_NULL_SIDSTR; @@ -147,12 +161,14 @@ smbd_user_auth_logon(smb_logon_t *user_info) if (adt_start_session(&ah, NULL, 0)) { syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m"); + user_info->lg_status = NT_STATUS_AUDIT_FAILED; goto errout; } if ((event = adt_alloc_event(ah, ADT_smbd_session)) == NULL) { syslog(LOG_AUTH | LOG_ALERT, "adt_alloc_event(ADT_smbd_session): %m"); + user_info->lg_status = NT_STATUS_AUDIT_FAILED; goto errout; } @@ -172,6 +188,7 @@ smbd_user_auth_logon(smb_logon_t *user_info) if (adt_set_user(ah, uid, gid, uid, gid, NULL, ADT_NEW)) { syslog(LOG_AUTH | LOG_ALERT, "adt_set_user: %m"); adt_free_event(event); + user_info->lg_status = NT_STATUS_AUDIT_FAILED; goto errout; } @@ -187,6 +204,8 @@ smbd_user_auth_logon(smb_logon_t *user_info) if (token) { if ((entry = malloc(sizeof (smb_audit_t))) == NULL) { syslog(LOG_ERR, "smbd_user_auth_logon: %m"); + user_info->lg_status = + NT_STATUS_INSUFFICIENT_RESOURCES; goto errout; } @@ -199,6 +218,8 @@ smbd_user_auth_logon(smb_logon_t *user_info) smb_autohome_add(token); smbd_audit_link(entry); token->tkn_audit_sid = entry->sa_audit_sid; + + user_info->lg_status = NT_STATUS_SUCCESS; } free(buf); diff --git a/usr/src/cmd/smbsrv/smbd/smbd_ntlmssp.c b/usr/src/cmd/smbsrv/smbd/smbd_ntlmssp.c index 8027e3272b..80deababfa 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd_ntlmssp.c +++ b/usr/src/cmd/smbsrv/smbd/smbd_ntlmssp.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* @@ -36,8 +36,8 @@ #define letohl(x) ((uint32_t)(x)) #else /* (BYTE_ORDER == LITTLE_ENDIAN) */ /* little-endian values on big-endian (swap) */ -#define letohl(x) BSWAP_32(x) -#define htolel(x) BSWAP_32(x) +#define letohl(x) BSWAP_32(x) +#define htolel(x) BSWAP_32(x) #endif /* (BYTE_ORDER == LITTLE_ENDIAN) */ typedef struct ntlmssp_backend { @@ -256,7 +256,7 @@ smbd_ntlmssp_negotiate(authsvc_context_t *ctx) NTLMSSP_MSGTYPE_CHALLENGE, /* 8: type (l) */ 0, 0, 0, /* filled later: 12: target name (wwl) */ be->srv_flags, /* 20: flags (l) */ - be->srv_challenge, /* 24: (8c) */ + be->srv_challenge, /* 24: (8c) */ 0, 0, /* 32: reserved (ll) */ 0, 0, 0); /* filled later: 40: target info (wwl) */ #define TARGET_NAME_OFFSET 12 @@ -489,7 +489,9 @@ smbd_ntlmssp_authenticate(authsvc_context_t *ctx) */ token = smbd_user_auth_logon(&user_info); if (token == NULL) { - status = NT_STATUS_ACCESS_DENIED; + status = user_info.lg_status; + if (status == 0) /* should not happen */ + status = NT_STATUS_INTERNAL_ERROR; goto errout; } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h index 838353b8e9..0506704d71 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h +++ b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #ifndef _LIBMLSVC_H @@ -66,7 +66,6 @@ extern boolean_t smb_locate_dc(char *, smb_domainex_t *); uint32_t smb_ddiscover_dns(char *, smb_domainex_t *); extern void smb_ddiscover_bad_dc(char *); extern void smb_ddiscover_refresh(void); -extern int smb_ddiscover_wait(void); extern int dssetup_check_service(void); extern void dssetup_clear_domain_info(void); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h index 2afc62a02d..0aa45b0292 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMBSRV_MLSVC_H @@ -63,6 +63,8 @@ int netr_setup_authenticator(struct netr_info *, struct netr_authenticator *, struct netr_authenticator *); DWORD netr_validate_chain(struct netr_info *, struct netr_authenticator *); +uint32_t smb_netlogon_check(char *, char *); + int srvsvc_gettime(unsigned long *); void srvsvc_timecheck(char *, char *); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c index 74da88b2d5..51bef159c3 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #include <syslog.h> @@ -52,6 +52,9 @@ #define SMB_DCLOCATOR_TIMEOUT 45 /* seconds */ #define SMB_IS_FQDN(domain) (strchr(domain, '.') != NULL) +/* How long to pause after a failure to find any domain controllers. */ +int smb_ddiscover_failure_pause = 5; /* sec. */ + typedef struct smb_dclocator { smb_dcinfo_t sdl_dci; /* .dc_name .dc_addr */ char sdl_domain[SMB_PI_MAX_DOMAIN]; @@ -90,6 +93,14 @@ smb_dclocator_init(void) pthread_attr_t tattr; int rc; + /* + * We need the smb_ddiscover_service to run on startup, + * so it will enter smb_ddiscover_main() and put the + * SMB "domain cache" into "updating" state so clients + * trying to logon will wait while we're finding a DC. + */ + smb_dclocator.sdl_locate = B_TRUE; + (void) pthread_attr_init(&tattr); (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&smb_dclocator_thr, &tattr, @@ -241,6 +252,7 @@ smb_ddiscover_bad_dc(char *bad_dc) */ syslog(LOG_INFO, "smb_ddiscover, bad DC: %s", bad_dc); smb_dclocator.sdl_bad_dc = B_TRUE; + smb_domain_bad_dc(); /* In-line smb_ddiscover_kick */ if (!smb_dclocator.sdl_locate) { @@ -252,29 +264,6 @@ out: (void) mutex_unlock(&smb_dclocator.sdl_mtx); } -/* - * If domain discovery is running, wait for it to finish. - */ -int -smb_ddiscover_wait(void) -{ - timestruc_t to; - int rc = 0; - - (void) mutex_lock(&smb_dclocator.sdl_mtx); - - if (smb_dclocator.sdl_locate) { - to.tv_sec = SMB_DCLOCATOR_TIMEOUT; - to.tv_nsec = 0; - rc = cond_reltimedwait(&smb_dclocator.sdl_cv, - &smb_dclocator.sdl_mtx, &to); - } - - (void) mutex_unlock(&smb_dclocator.sdl_mtx); - - return (rc); -} - /* * ========================================================== @@ -354,10 +343,19 @@ smb_ddiscover_service(void *arg) status = smb_ddiscover_main(sdl->sdl_domain, &dxi); if (status == 0) smb_domain_save(); + (void) mutex_lock(&sdl->sdl_mtx); + sdl->sdl_status = status; - if (status == 0) + if (status == 0) { sdl->sdl_dci = dxi.d_dci; + } else { + syslog(LOG_DEBUG, "smb_ddiscover_service " + "retry after STATUS_%s", + xlate_nt_status(status)); + (void) sleep(smb_ddiscover_failure_pause); + goto find_again; + } /* * Run again if either of cfg_chg or bad_dc @@ -405,11 +403,6 @@ smb_ddiscover_main(char *domain, smb_domainex_t *dxi) return (NT_STATUS_INTERNAL_ERROR); } - if (smb_domain_start_update() != SMB_DOMAIN_SUCCESS) { - syslog(LOG_DEBUG, "smb_ddiscover_main can't get lock"); - return (NT_STATUS_INTERNAL_ERROR); - } - status = smb_ads_lookup_msdcs(domain, &dxi->d_dci); if (status != 0) { syslog(LOG_DEBUG, "smb_ddiscover_main can't find DC (%s)", @@ -425,11 +418,15 @@ smb_ddiscover_main(char *domain, smb_domainex_t *dxi) goto out; } - smb_domain_update(dxi); + if (smb_domain_start_update() != SMB_DOMAIN_SUCCESS) { + syslog(LOG_DEBUG, "smb_ddiscover_main can't get lock"); + status = NT_STATUS_INTERNAL_ERROR; + } else { + smb_domain_update(dxi); + smb_domain_end_update(); + } out: - smb_domain_end_update(); - /* Don't need the trusted domain list anymore. */ smb_domainex_free(dxi); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c index cdb6478f5b..d31fdd5ed0 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #include <sys/errno.h> @@ -104,10 +104,6 @@ mlsvc_timecheck(void *arg) if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) continue; - /* Avoid interfering with DC discovery. */ - if (smb_ddiscover_wait() != 0) - continue; - if (!smb_domain_getinfo(&di)) continue; diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c index b46cf99f87..956fbbad15 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c @@ -57,22 +57,16 @@ static DWORD mlsvc_join_noauth(smb_domainex_t *dxi, char *machine_name, char *machine_pw); - +/* + * This is called by smbd_dc_update just after we've learned about a + * new domain controller. Make sure we can authenticate with this DC. + */ DWORD mlsvc_netlogon(char *server, char *domain) { - mlsvc_handle_t netr_handle; DWORD status; - status = netr_open(server, domain, &netr_handle); - if (status != 0) { - syslog(LOG_NOTICE, "Failed to connect to %s " - "for domain %s (%s)", server, domain, - xlate_nt_status(status)); - return (status); - } - - status = netlogon_auth(server, &netr_handle, NETR_FLG_INIT); + status = smb_netlogon_check(server, domain); if (status != NT_STATUS_SUCCESS) { syslog(LOG_NOTICE, "Failed to establish NETLOGON " "credential chain with DC: %s (%s)", server, @@ -81,7 +75,6 @@ mlsvc_netlogon(char *server, char *domain) "domain controller does not match the local storage."); syslog(LOG_NOTICE, "To correct this, use 'smbadm join'"); } - (void) netr_close(&netr_handle); return (status); } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c index 6fdd3a9ca4..0a60a1a8fc 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -46,9 +46,7 @@ #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 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); @@ -176,12 +174,19 @@ smb_logon_abort(void) * 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 i; + int retries = smb_netr_logon_retries; if (user_info->lg_secmode != SMB_SECMODE_DOMAIN) return; @@ -189,21 +194,28 @@ smb_logon_domain(smb_logon_t *user_info, smb_token_t *token) if (user_info->lg_domain_type == SMB_DOMAIN_LOCAL) return; - for (i = 0; i < NETLOGON_ATTEMPTS; ++i) { + 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); - user_info->lg_status = NT_STATUS_REQUEST_ABORTED; - return; + status = NT_STATUS_REQUEST_ABORTED; + goto out; } netlogon_busy = B_TRUE; (void) mutex_unlock(&netlogon_mutex); - status = netlogon_logon(user_info, token); + status = netlogon_logon(user_info, token, &di); (void) mutex_lock(&netlogon_mutex); netlogon_busy = B_FALSE; @@ -212,71 +224,160 @@ smb_logon_domain(smb_logon_t *user_info, smb_token_t *token) (void) cond_signal(&netlogon_cv); (void) mutex_unlock(&netlogon_mutex); - if (status != NT_STATUS_CANT_ACCESS_DOMAIN_INFO) + 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) +netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di) { - 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; + boolean_t did_reauth = B_FALSE; - (void) smb_getdomainname(resource_domain, SMB_PI_MAX_DOMAIN); + /* + * 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); + } - /* Avoid interfering with DC discovery. */ - if (smb_ddiscover_wait() != 0 || - !smb_domain_getinfo(&di)) { - netr_invalidate_chain(); - return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + 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(); } - 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(); +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); } - 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); + netr_global_info.flags |= NETR_FLG_VALID; + } - if (status != 0) { - (void) netr_close(&netr_handle); - return (NT_STATUS_LOGON_FAILURE); - } + status = netr_server_samlogon(&netr_handle, + &netr_global_info, di->d_dci.dc_name, user_info, token); - netr_global_info.flags |= NETR_FLG_VALID; + 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); +} - status = netr_server_samlogon(&netr_handle, - &netr_global_info, di.d_dci.dc_name, user_info, token); +/* + * 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); - } while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3); + (void) netr_close(&netr_handle); - if (retries >= 3) - status = NT_STATUS_LOGON_FAILURE; +unlock_out: + (void) mutex_lock(&netlogon_mutex); + netlogon_busy = B_FALSE; + (void) cond_signal(&netlogon_cv); + (void) mutex_unlock(&netlogon_mutex); return (status); } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smb_logon.c b/usr/src/lib/smbsrv/libmlsvc/common/smb_logon.c index 237968469e..4ed50f2f1c 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/smb_logon.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_logon.c @@ -20,8 +20,8 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #include <unistd.h> @@ -417,6 +417,7 @@ smb_logon_fini(void) * attempt to authenticate the user. * * On success, a pointer to a new access token is returned. + * On failure, NULL return and status in user_info->lg_status */ smb_token_t * smb_logon(smb_logon_t *user_info) @@ -433,7 +434,6 @@ smb_logon(smb_logon_t *user_info) int i; user_info->lg_secmode = smb_config_get_secmode(); - user_info->lg_status = NT_STATUS_NO_SUCH_USER; if (smb_domain_lookup_name(user_info->lg_e_domain, &domain)) user_info->lg_domain_type = domain.di_type; @@ -446,6 +446,12 @@ smb_logon(smb_logon_t *user_info) return (NULL); } + /* + * If any logonop function takes significant action + * (logon or authoratative failure) it will change + * this status field to something else. + */ + user_info->lg_status = NT_STATUS_NO_SUCH_USER; for (i = 0; i < n_op; ++i) { (*ops[i])(user_info, token); @@ -455,10 +461,25 @@ smb_logon(smb_logon_t *user_info) if (user_info->lg_status == NT_STATUS_SUCCESS) { if (smb_token_setup_common(token)) - return (token); + return (token); /* success */ + /* + * (else) smb_token_setup_common failed, which usually + * means smb_token_sids2ids() failed to map some SIDs to + * Unix IDs. This indicates an idmap config problem. + */ + user_info->lg_status = NT_STATUS_INTERNAL_ERROR; } smb_token_destroy(token); + + /* + * Any unknown user or bad password should result in + * NT_STATUS_LOGON_FAILURE (so we don't give hints). + */ + if (user_info->lg_status == NT_STATUS_NO_SUCH_USER || + user_info->lg_status == NT_STATUS_WRONG_PASSWORD) + user_info->lg_status = NT_STATUS_LOGON_FAILURE; + return (NULL); } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c b/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c index 724c509d8d..7a467f3628 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* @@ -158,7 +158,14 @@ smbrdr_ctx_new(struct smb_ctx **ctx_p, char *server, goto errout; } if ((err = smb_ctx_get_ssn(ctx)) != 0) { - err = NT_STATUS_NETWORK_ACCESS_DENIED; + switch (err) { + case EAUTH: + err = NT_STATUS_NETWORK_ACCESS_DENIED; + break; + default: + err = NT_STATUS_BAD_NETWORK_PATH; + break; + } goto errout; } if ((err = smb_ctx_get_tree(ctx)) != 0) { diff --git a/usr/src/lib/smbsrv/libsmb/common/libsmb.h b/usr/src/lib/smbsrv/libsmb/common/libsmb.h index 82d6bc1cc3..db6f6c6650 100644 --- a/usr/src/lib/smbsrv/libsmb/common/libsmb.h +++ b/usr/src/lib/smbsrv/libsmb/common/libsmb.h @@ -347,10 +347,10 @@ void libsmb_redirect_syslog(__FILE_TAG *fp, int priority); * 0x0004 The name is a W2K Domain name (a DNS name). */ #define SMBAUTH_NAME_TYPE_LIST_END 0x0000 -#define SMBAUTH_NAME_TYPE_SERVER_NETBIOS 0x0001 -#define SMBAUTH_NAME_TYPE_DOMAIN_NETBIOS 0x0002 +#define SMBAUTH_NAME_TYPE_SERVER_NETBIOS 0x0001 +#define SMBAUTH_NAME_TYPE_DOMAIN_NETBIOS 0x0002 #define SMBAUTH_NAME_TYPE_SERVER_DNS 0x0003 -#define SMBAUTH_NAME_TYPE_DOMAIN_DNS 0x0004 +#define SMBAUTH_NAME_TYPE_DOMAIN_DNS 0x0004 /* * smb_auth_name_entry_t @@ -662,6 +662,7 @@ void smb_domain_set_dns_info(char *, char *, char *, char *, char *, void smb_domain_set_trust_info(char *, char *, char *, uint32_t, uint32_t, uint32_t, smb_domain_t *); void smb_domain_current_dc(smb_dcinfo_t *); +void smb_domain_bad_dc(void); typedef struct smb_gsid { smb_sid_t *gs_sid; diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers index 2af8c7579b..aaa9d368b3 100644 --- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers +++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers @@ -129,6 +129,7 @@ SYMBOL_VERSION SUNWprivate { smb_ctxbuf_printf; smb_dlclose; smb_dlopen; + smb_domain_bad_dc; smb_domain_current_dc; smb_domain_end_update; smb_domain_fini; diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_domain.c b/usr/src/lib/smbsrv/libsmb/common/smb_domain.c index 69769af98b..d920fccb01 100644 --- a/usr/src/lib/smbsrv/libsmb/common/smb_domain.c +++ b/usr/src/lib/smbsrv/libsmb/common/smb_domain.c @@ -22,7 +22,7 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* @@ -37,6 +37,7 @@ #include <stdio.h> #include <strings.h> #include <string.h> +#include <syslog.h> #include <unistd.h> #include <stdlib.h> #include <synch.h> @@ -70,10 +71,13 @@ typedef struct smb_domain_cache { list_t dc_cache; rwlock_t dc_cache_lck; - mutex_t dc_mtx; - cond_t dc_cv; uint32_t dc_state; uint32_t dc_nops; + mutex_t dc_mtx; + cond_t dc_cv; + /* domain controller information */ + cond_t dc_dci_cv; + boolean_t dc_dci_valid; smb_dcinfo_t dc_dci; } smb_domain_cache_t; @@ -90,7 +94,7 @@ static uint32_t smb_dcache_lock(int); static void smb_dcache_unlock(void); static void smb_dcache_remove(smb_domain_t *); static uint32_t smb_dcache_add(smb_domain_t *); -static boolean_t smb_dcache_getdc(smb_dcinfo_t *); +static boolean_t smb_dcache_getdc(smb_dcinfo_t *, boolean_t); static void smb_dcache_setdc(const smb_dcinfo_t *); static boolean_t smb_dcache_wait(void); static uint32_t smb_dcache_updating(void); @@ -113,6 +117,7 @@ smb_domain_init(uint32_t secmode) if ((rc = smb_domain_add_local()) != 0) return (rc); + bzero(&di, sizeof (di)); smb_domain_set_basic_info(NT_BUILTIN_DOMAIN_SIDSTR, "BUILTIN", "", &di); (void) smb_domain_add(SMB_DOMAIN_BUILTIN, &di); @@ -289,6 +294,8 @@ smb_domain_lookup_type(smb_domain_type_t type, smb_domain_t *di) /* * Returns primary domain information plus the name of * the selected domain controller. + * + * Returns TRUE on success. */ boolean_t smb_domain_getinfo(smb_domainex_t *dxi) @@ -297,10 +304,27 @@ smb_domain_getinfo(smb_domainex_t *dxi) /* Note: this waits for the dcache lock. */ rv = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &dxi->d_primary); - if (rv) - rv = smb_dcache_getdc(&dxi->d_dci); + if (!rv) { + syslog(LOG_ERR, "smb_domain_getinfo: no primary domain"); + return (B_FALSE); + } - return (rv); + /* + * The 2nd arg TRUE means this will wait for DC info. + * + * Note that we do NOT hold the dcache rwlock here + * (not even as reader) because we already have what we + * need from the dcache (our primary domain) and we don't + * want to interfere with the DC locator which will take + * the dcache lock as writer to update the domain info. + */ + rv = smb_dcache_getdc(&dxi->d_dci, B_TRUE); + if (!rv) { + syslog(LOG_ERR, "smb_domain_getinfo: no DC info"); + return (B_FALSE); + } + + return (B_TRUE); } /* @@ -310,7 +334,7 @@ smb_domain_getinfo(smb_domainex_t *dxi) void smb_domain_current_dc(smb_dcinfo_t *dci) { - (void) smb_dcache_getdc(dci); + (void) smb_dcache_getdc(dci, B_FALSE); } /* @@ -334,6 +358,18 @@ smb_domain_end_update(void) } /* + * Mark the current domain controller (DC) info invalid + * until the DC locator calls smb_domain_update(). + */ +void +smb_domain_bad_dc(void) +{ + (void) mutex_lock(&smb_dcache.dc_mtx); + smb_dcache.dc_dci_valid = B_FALSE; + (void) mutex_unlock(&smb_dcache.dc_mtx); +} + +/* * Updates the cache with given information for the primary * domain, possible trusted domains and the selected domain * controller. @@ -484,6 +520,7 @@ smb_domain_set_dns_info(char *sid, char *nb_domain, char *fq_domain, if (di == NULL || forest == NULL || guid == NULL) return; + /* Caller zeros out *di before this. */ smb_domain_set_basic_info(sid, nb_domain, fq_domain, di); (void) strlcpy(di->di_u.di_dns.ddi_forest, forest, MAXHOSTNAMELEN); (void) strlcpy(di->di_u.di_dns.ddi_guid, guid, @@ -500,6 +537,7 @@ smb_domain_set_trust_info(char *sid, char *nb_domain, char *fq_domain, if (di == NULL) return; + /* Caller zeros out *di before this. */ di->di_type = SMB_DOMAIN_TRUSTED; ti = &di->di_u.di_trust; smb_domain_set_basic_info(sid, nb_domain, fq_domain, di); @@ -540,6 +578,7 @@ smb_domain_add_local(void) return (SMB_DOMAIN_NOMACHINE_SID); } + bzero(&di, sizeof (di)); *fq_name = '\0'; (void) smb_getfqhostname(fq_name, MAXHOSTNAMELEN); smb_domain_set_basic_info(lsidstr, hostname, fq_name, &di); @@ -572,6 +611,7 @@ smb_domain_add_primary(uint32_t secmode) if ((rc != SMBD_SMF_OK) || (*nb_name == '\0')) return (SMB_DOMAIN_NODOMAIN_NAME); + bzero(&di, sizeof (di)); (void) smb_getfqdomainname(fq_name, MAXHOSTNAMELEN); smb_domain_set_basic_info(sidstr, nb_name, fq_name, &di); (void) smb_domain_add(SMB_DOMAIN_PRIMARY, &di); @@ -596,6 +636,7 @@ smb_dcache_create(void) smb_dcache.dc_nops = 0; bzero(&smb_dcache.dc_dci, sizeof (smb_dcache.dc_dci)); + smb_dcache.dc_dci_valid = B_FALSE; smb_dcache.dc_state = SMB_DCACHE_STATE_READY; (void) mutex_unlock(&smb_dcache.dc_mtx); } @@ -652,6 +693,7 @@ smb_dcache_lock(int mode) switch (smb_dcache.dc_state) { case SMB_DCACHE_STATE_NONE: case SMB_DCACHE_STATE_DESTROYING: + default: (void) mutex_unlock(&smb_dcache.dc_mtx); return (SMB_DOMAIN_INTERNAL_ERR); @@ -668,7 +710,7 @@ smb_dcache_lock(int mode) } /* FALLTHROUGH */ - default: + case SMB_DCACHE_STATE_READY: smb_dcache.dc_nops++; break; } @@ -733,19 +775,40 @@ smb_dcache_setdc(const smb_dcinfo_t *dci) { (void) mutex_lock(&smb_dcache.dc_mtx); smb_dcache.dc_dci = *dci; /* struct assignment! */ + smb_dcache.dc_dci_valid = B_TRUE; + (void) cond_broadcast(&smb_dcache.dc_dci_cv); (void) mutex_unlock(&smb_dcache.dc_mtx); } /* - * Return B_TRUE if we have DC information. + * Get information about our domain controller. If the wait arg + * is true, wait for the DC locator to finish before copying. + * Returns TRUE on success (have DC info). */ static boolean_t -smb_dcache_getdc(smb_dcinfo_t *dci) +smb_dcache_getdc(smb_dcinfo_t *dci, boolean_t wait) { + timestruc_t to; + boolean_t rv; + int err; + + to.tv_sec = time(NULL) + SMB_DCACHE_UPDATE_WAIT; + to.tv_nsec = 0; + (void) mutex_lock(&smb_dcache.dc_mtx); + + while (wait && !smb_dcache.dc_dci_valid) { + err = cond_timedwait(&smb_dcache.dc_dci_cv, + &smb_dcache.dc_mtx, &to); + if (err == ETIME) + break; + } *dci = smb_dcache.dc_dci; /* struct assignment! */ + rv = smb_dcache.dc_dci_valid; + (void) mutex_unlock(&smb_dcache.dc_mtx); - return (dci->dc_name[0] != '\0'); + + return (rv); } /* @@ -759,10 +822,12 @@ smb_dcache_wait(void) timestruc_t to; int err; - to.tv_sec = SMB_DCACHE_UPDATE_WAIT; + assert(MUTEX_HELD(&smb_dcache.dc_mtx)); + + to.tv_sec = time(NULL) + SMB_DCACHE_UPDATE_WAIT; to.tv_nsec = 0; while (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING) { - err = cond_reltimedwait(&smb_dcache.dc_cv, + err = cond_timedwait(&smb_dcache.dc_cv, &smb_dcache.dc_mtx, &to); if (err == ETIME) break; |