Print this page
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-2225 Unable to join NexentaStor to 2008 AD

@@ -20,11 +20,11 @@
  */
 /*
  * 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.
  */
 
 /*
  * This file defines the domain environment values and the domain
  * database interface. The database is a single linked list of

@@ -35,10 +35,11 @@
 #include <sys/stat.h>
 #include <sys/list.h>
 #include <stdio.h>
 #include <strings.h>
 #include <string.h>
+#include <syslog.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <synch.h>
 #include <pwd.h>
 #include <grp.h>

@@ -68,14 +69,17 @@
 #define SMB_DCACHE_WRLOCK       1
 
 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;
 
 static smb_domain_cache_t smb_dcache;
 

@@ -88,11 +92,11 @@
 static void smb_dcache_destroy(void);
 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);
 static void smb_dcache_ready(void);
 

@@ -111,10 +115,11 @@
         smb_dcache_create();
 
         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);
 
         return (smb_domain_add_primary(secmode));
 }

@@ -287,32 +292,51 @@
 }
 
 /*
  * 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)
 {
         boolean_t rv;
 
         /* 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 write 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);
 }
 
 /*
  * Get the name of the current DC (if any)
  * Does NOT block.
  */
 void
 smb_domain_current_dc(smb_dcinfo_t *dci)
 {
-        (void) smb_dcache_getdc(dci);
+        (void) smb_dcache_getdc(dci, B_FALSE);
 }
 
 /*
  * Transfer the cache to updating state.
  * In this state any request for reading the cache would

@@ -332,10 +356,22 @@
 {
         smb_dcache_ready();
 }
 
 /*
+ * Mark the current domain controller (DC) info invalid
+ * until the DC locator call 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.
  *
  * Before adding the new entries existing entries of type

@@ -482,10 +518,11 @@
     char *forest, char *guid, smb_domain_t *di)
 {
         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,
             UUID_PRINTABLE_STRING_LENGTH);
 }

@@ -498,10 +535,11 @@
         smb_domain_trust_t *ti;
 
         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);
         ti->dti_trust_direction = trust_dir;
         ti->dti_trust_type = trust_type;

@@ -538,10 +576,11 @@
         if (smb_getnetbiosname(hostname, NETBIOS_NAME_SZ) != 0) {
                 free(lsidstr);
                 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);
         (void) smb_domain_add(SMB_DOMAIN_LOCAL, &di);
 

@@ -570,10 +609,11 @@
 
         rc = smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_name, NETBIOS_NAME_SZ);
         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);
         return (SMB_DOMAIN_SUCCESS);
 }

@@ -594,10 +634,11 @@
         list_create(&smb_dcache.dc_cache, sizeof (smb_domain_t),
             offsetof(smb_domain_t, di_lnd));
 
         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);
 }
 
 /*

@@ -650,10 +691,11 @@
 {
         (void) mutex_lock(&smb_dcache.dc_mtx);
         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);
 
         case SMB_DCACHE_STATE_UPDATING:
                 if (mode == SMB_DCACHE_RDLOCK) {

@@ -664,12 +706,13 @@
                         if (!smb_dcache_wait()) {
                                 (void) mutex_unlock(&smb_dcache.dc_mtx);
                                 return (SMB_DOMAIN_INTERNAL_ERR);
                         }
                 }
+                /* FALLTHROUGH */
 
-        default:
+        case SMB_DCACHE_STATE_READY:
                 smb_dcache.dc_nops++;
                 break;
         }
         (void) mutex_unlock(&smb_dcache.dc_mtx);
 

@@ -730,23 +773,44 @@
 static void
 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);
 }
 
 /*
  * Waits for SMB_DCACHE_UPDATE_WAIT seconds if cache is in
  * UPDATING state. Upon wake up returns true if cache is

@@ -756,14 +820,16 @@
 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;
         }