summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGordon Ross <gwr@nexenta.com>2017-11-25 15:46:51 -0500
committerGordon Ross <gwr@nexenta.com>2018-10-20 08:42:08 -0400
commit975041dd3b571af240661f84d186e0cd0e36217b (patch)
treeaee382fe7152d4dc8a9d9f8860f8544cacf5352b
parentab618543cc6fc4bc273c077ef5d247961cdb29d4 (diff)
downloadillumos-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.c10
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_logon.c23
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_ntlmssp.c12
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h3
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h4
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c63
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c6
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c17
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c197
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/smb_logon.c27
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c11
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/libsmb.h7
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/mapfile-vers1
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_domain.c93
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;