Two patches to add self-checks, part 1 of 7

Dave Collier-Brown davec-b at rogers.com
Fri Apr 11 23:04:31 GMT 2003


  I haven't died, I just got additional responsibilities
at work...
  The following is a reprise of the self-check code
I did for 2.x, retargeted to head/3.x.  To get it
past the automated sentinal, I'll send it in a series of
individual messages, the first two containing
patches, then the patched files, then the Makefile
to test the changes... .as there's nothing worse than 
a test that hasn't been tested (;-))

--dave
-- 
David Collier-Brown,  | Always do right. This will gratify
Americas Customer Eng | some people and astonish the rest.
at Sun Canada         |                      -- Mark Twain
(905) 415-2849        | davecb at spamcop.net
-------------- next part --------------
--- loadparm.old.c	Sat Apr  5 10:17:26 2003
+++ loadparm.c	Sun Apr  6 19:32:20 2003
@@ -537,6 +537,8 @@ static BOOL handle_copy(const char *pszP
 static BOOL handle_vfs_object(const char *pszParmValue, char **ptr);
 static BOOL handle_source_env(const char *pszParmValue, char **ptr);
 static BOOL handle_netbios_name(const char *pszParmValue, char **ptr);
+static BOOL handle_password_server(char *pszParmValue, char **ptr);
+static BOOL validate_netbios_name(const char *pszParmValue);
 static BOOL handle_winbind_uid(const char *pszParmValue, char **ptr);
 static BOOL handle_winbind_gid(const char *pszParmValue, char **ptr);
 static BOOL handle_non_unix_account_range(const char *pszParmValue, char **ptr);
@@ -1889,6 +1891,7 @@ static int getservicebyname(const char *
 			    service * pserviceDest);
 static void copy_service(service * pserviceDest,
 			 service * pserviceSource, BOOL *pcopymapDest);
+static BOOL globals_ok(void);
 static BOOL service_ok(int iService);
 static BOOL do_parameter(const char *pszParmName, const char *pszParmValue);
 static BOOL do_section(const char *pszSectionName);
@@ -2256,6 +2259,210 @@ static void copy_service(service * pserv
 }
 
 /***************************************************************************
+ Check globals for consistency. Return False if something fatal will happen.
+ The messages use ERROR, WARNING and NOTICE the same way service_ok does.
+ This replaces do_global_checks in utils/testparm.c, and should be called at
+ least once, and possibly each time the smb.conf file is reread.
+ ***************************************************************************/
+ static BOOL globals_ok(void)
+ {
+    BOOL bRetval = True;
+    SMB_STRUCT_STAT st;
+    static BOOL filesChecked = False;
+ 
+    /* Look for inconstancies in roughly decreasing order of severity, */
+ 
+    /* Make sure we have a lock directory, but only once. */
+    if (filesChecked == False) {
+       /* Check lock directory. Self-test 1. */
+       if (!directory_exist(lp_lockdir(), &st)) {
+          DEBUG(0,("ERROR: lock directory \"%s\" does not exist\n",
+                  lp_lockdir()));
+          bRetval = False;
+       }
+       /* Self-test 2. */
+       else if ((st.st_mode & 0777) != 0755) {
+          DEBUG(0,("WARNING: lock directory \"%s\" should have "
+            "permissions 0755 for browsing and locks to work\n", 
+            lp_lockdir()));
+       }
+	if (!directory_exist(lp_piddir(), &st)) {
+		/* Self-test 3. */
+		DEBUG(0,("ERROR: pid directory %s does not exist\n",
+		       lp_piddir()));
+ 		bRetval = False;
+ 	}
+	else if ((st.st_mode & 0777) != 0755) {
+		/* Self-test 4. */
+		DEBUG(0,("WARNING: pid directory %s should have "
+			"permissions 0755 for browsing to work\n",
+			 lp_piddir()));
+	}
+       filesChecked = True;
+    }
+ 
+    if (lp_security() == SEC_DOMAIN && !lp_encrypted_passwords()) {
+	/* Self-test 5. */
+	DEBUG(0,("ERROR: in 'security=domain' mode the 'encrypt passwords' "
+	"parameter must always be set to 'true'.\n"));
+	bRetval = False;
+    }
+
+#ifdef undef
+    if (lp_wins_support() && lp_wins_server_list()) {
+	/* This is actually wrong: Andrew Bartlett noticed that */
+        /* one could have oneself in the list.  I'm unsure of */
+	/* how to write a good test for that. --davecb */
+	DEBUG(0,("ERROR: 'wins support = true' and 'wins server = "
+		 "<server list>' cannot both be true.\n"));
+	bRetval = False;
+    }
+#endif /* undef */
+
+    /* Self-test 6. */
+    if (*lp_hosts_equiv() && !lp_hostname_lookups()) {
+	DEBUG(0,("ERROR: The setting 'hosts equiv = %s' requires that "
+		 "'hostname lookups = yes'.\n", lp_hosts_equiv()));
+	bRetval = False;
+    }
+
+    /* Password chat sanity checks. */
+    if (lp_security() == SEC_USER && lp_unix_password_sync()) {
+	/* Check that we have a valid lp_passwd_program() if not using pam. */
+#ifdef WITH_PAM
+	if (!lp_pam_password_change()) {
+#endif
+	    if (lp_passwd_program() == NULL) {
+		/* Self-test 7. */
+		DEBUG(0,("ERROR: the 'unix password sync' parameter "
+			 "is set but there is no valid 'passwd "
+			 "program' parameter.\n"));
+		bRetval = False;
+	    } 
+	    else {
+		pstring passwd_prog;
+		pstring truncated_prog;
+		char *p;
+
+		pstrcpy( passwd_prog, lp_passwd_program());
+		p = passwd_prog;
+		*truncated_prog = '\0';
+		next_token(&p, truncated_prog, NULL, sizeof(pstring));
+
+		if (access(truncated_prog, F_OK) == -1) {
+			/* Self-test 7 redux. */
+			DEBUG(0,("ERROR: the 'unix password sync' parameter "
+				 "is set and the 'passwd program' (%s) "
+				"cannot be executed (error was %s).\n",
+				 truncated_prog, strerror(errno)));
+			bRetval = False;
+ 		}
+	    }
+
+#ifdef WITH_PAM
+ 	}
+#endif
+    }
+	if (strlen(lp_winbind_separator()) != 1) {
+		/* Self-test 8. */
+		DEBUG(0,("ERROR: the 'winbind separator' parameter must "
+			"be a single character.\n"));
+		bRetval = False;
+ 	}
+	if (*lp_winbind_separator() == '+') {
+		/* Self-test 9 */
+		DEBUG(0,("WARNING: winbind separator = + may cause "
+			 "problems with group membership.\n"));
+	}
+
+ 
+    /* Password server should be a netbios name or IP address. */
+    if (lp_passwordserver != NULL) {
+ 	if (strchr(lp_passwordserver(),'.') != NULL &&
+ 	    !isdigit(*lp_passwordserver())) {
+		/* Self-test 10. */
+ 		DEBUG(0,("ERROR: password server \"%s\" is not a legal "
+ 			 "NetBIOS name or IP address, logons will fail.\n",
+ 			 lp_passwordserver()));
+ 		bRetval = False;
+	}
+ 	else if (strlen(lp_passwordserver()) >= 15) {
+		/* Self-test 11. */
+ 		DEBUG(0,("ERROR: password server \"%s\" is too long to "
+ 			 "be a legal NetBIOS name, logons will fail.\n",
+ 			 lp_passwordserver()));
+ 		bRetval = False;
+ 	}
+    }
+    /* Be sure update encrypted is done with NON-encrypted passwords. */
+    if (lp_update_encrypted() && lp_encrypted_passwords()) {
+       /* Self-test 12. */
+       DEBUG(0,("WARNING: update encrypted = yes requires encrypt "
+          "passwords = yes.\n"));
+    }
+    /* Er, should unix password sync be automatic if a smb */
+    /* password file exists? */
+    /* If unix password sync, check the prerequisites.*/
+    if (lp_unix_password_sync()) {
+       /* See if the program is executable. Self-test 13.*/
+       {  pstring passwd_prog;
+                char *p;
+          pstrcpy(passwd_prog, lp_passwd_program());
+         if ((p = strchr(passwd_prog, ' ')) != NULL) {
+             *p = '\0';
+         }
+          if (access(passwd_prog, X_OK) == -1) {
+	     /* Self-test 13. */
+             DEBUG(0,("WARNING: the unix password sync program \"%s\" "
+               "can't be executed, errno was %d (\"%s\").\n",
+               lp_passwd_program(),
+                errno, strerror(errno)));
+         }
+       }
+       /* There is a password chat value to use. */
+	if (lp_passwd_chat() == NULL || *lp_passwd_chat() == NULL) {
+		/* Self-test 14. */
+		DEBUG(0,("ERROR: the 'unix password sync' parameter is set "
+		      "but there is no valid 'passwd chat' parameter.\n"));
+		bRetval = False;
+	}
+
+       /* And the passwd chat isn't expecting the old password. */
+       if (strstr(lp_passwd_chat(), "%o") != 0) {
+	  /* Self-test 15. */
+          DEBUG(0,("WARNING: the 'passwd chat' script [%s] depends on "
+            "getting the old password via the %%o substitution. With "
+            "encrypted passwords and NT clients this will not work.\n",
+             lp_passwd_chat()));
+       }
+ 
+    }
+ 
+    /* Check for announcing as something other than NT, which can */
+    /* interefere with serving browse lists. */
+    if (lp_announce_as() != ANNOUNCE_AS_NT_SERVER) {
+       /* Self-test 16. */
+       DEBUG(0,( "WARNING: announce as not set to \"NT\", this may "
+          "interfere with browsing.\n"));
+    }
+
+    /* The following tests existed previously, they just moved here. */
+    if (lp_algorithmic_rid_base() < BASE_RID) {
+	/* Try to prevent admin foot-shooting, we can't put */
+	/* algorithmic rids below 1000, that's the 'well known RIDs' on NT */
+       /* Self-test 17. */
+	DEBUG(0,( "ERROR: The 'algorithmic rid base' mustbe equal to or above %lu\n", BASE_RID));
+     }
+     if (lp_algorithmic_rid_base() & 1) {
+       /* Self-test 17b. */
+        DEBUG(0,( "ERROR: The 'algorithmic rid base' must be even.\n"));
+     }
+     /* Self-tests 18-19 reserved for future globals_ok tests. */
+
+    return True;
+ } 
+
+/***************************************************************************
 Check a service for consistency. Return False if the service is in any way
 incomplete or faulty, else True.
 ***************************************************************************/
@@ -2263,41 +2470,113 @@ incomplete or faulty, else True.
 static BOOL service_ok(int iService)
 {
 	BOOL bRetval;
+    struct stat buf;
+    service *s = ServicePtrs[iService]; 
+ 
+    /* Look for inconstancies in roughly decreasing order of severity, */
+    /* Things that are inherently dangerous: WARNINGs. */
+ 
+    /* Test for path not pointing to a dir. */
+    /* This has to be macro-expanded for stat, as noted by Andrew Bartlett. */
+    if (s->szPath != NULL && s->szPath[0] != '\0') {
+       if (stat(lp_pathname(iService),&buf) == -1) {
+	  /* Self-test 20. */
+          DEBUG(0,("WARNING: can't stat path \"%s\" in service [%s].\n",
+                lp_pathname(iService), s->szService));
+          DEBUGADD(0,("errno = %d (%s).\n", errno, strerror(errno)));
+       }
+       else if ((buf.st_mode & S_IFDIR) != S_IFDIR) {
+	  /* Self-test 21. */
+          DEBUG(0,("WARNING: Path %s in service [%s] isn't a directory.\n",
+                lp_pathname(iService), s->szService));
+       }
+    }
+    
+    /* Look for map archive forced to fail by bad create mask. */
+    /* Ditto map system, map hidden. */
+    if (s->bMap_archive == True && (s->iCreate_mask & 0100) == 0) {
+	/* Self-test 22. */
+        DEBUG(0,( "WARNING: map archive = yes in share [%s], but create "
+                 "mask doesn't allow setting archive bit (100 octal).\n",
+                 s->szService));
+      }
+   
+     if (s->bMap_system == True && (s->iCreate_mask & 0010) ==  0) {
+	/* Self-test 22b. */
+        DEBUG(0,( "WARNING: map system = yes in share [%s], but create "
+                 "mask doesn't allow setting system bit (010 octal).\n",
+                 s->szService));
+     }
+ 
+    if (s->bMap_hidden == True && (s->iCreate_mask & 0001) ==  0) {
+	/* Self-test 22c. */
+        DEBUG(0,( "WARNING: map hidden = yes in share [%s], but create "
+                 "mask doesn't allow setting hidden bit (001 octal).\n",
+                 s->szService));
+    }
+ 
+    /* And see if force create mode sets any of the same three bits */
+    if ((s->iCreate_force_mode & 0100) != 0) {
+	/* Self-test 23. */
+        DEBUG(0,( "WARNING: force create mode forces archive bit on "
+                 "on all files in share [%s].\n", 
+                 s->szService));
+    }
+    if ((s->iCreate_force_mode & 0010) != 0) {
+	/* Self-test 23b. */
+        DEBUG(0,( "WARNING: force create mode forces system bit on "
+                 "on all files in share [%s].\n", 
+                 s->szService));
+    }
+    if ((s->iCreate_force_mode & 0001) != 0) {
+	/* Self-test 23c. */
+        DEBUG(0,( "WARNING: force create mode forces hidden bit on "
+                 "on all file in share [%s].\n",
+                 s->szService));
+    }
+    /* Implausible overrides and missing prerequisites: WARNINGs. */
+    /* If a service is flagged unavailable, log the fact at level 0. */
+    if (!s->bAvailable) {
+       /* Self-test 24. */
+       DEBUG(1,( "NOTICE: Service [%s] is marked available = no.\n",
+             s->szService));
+    }
+ 
+    /* If it's unbrowsable but we're serving browse lists, log that too. */
+    if (s->bBrowseable == False && Globals.bBrowseList == True
+      && strwicmp(s->szService,HOMES_NAME) != 0) {
+	/* Self-test 25. */
+       DEBUG(0,( "NOTICE: Service [%s] is unbrowsable, but browse "
+            "lists are being served.\n", s->szService));
+    }
+ 
+    /* If we're syncing always, we need strict sync on too. */
+    if (s->bSyncAlways == True && s->bStrictSync == False) {
+	/* Self-test 26. */
+       DEBUG(0,("NOTICE: In service [%s], sync always = yes, which requires "
+         "strict sync = yes.\n", s->szService));
+    }
+ 
+    /* We need oplocks for level2 oplocks. */
+    if (s->bLevel2OpLocks == True && s->bOpLocks == False) {
+	/* Self-test 27. */
+       DEBUG(0,("NOTICE: In service [%s], level 2 oplocks = yes, which "
+          "requires oplocks = yes as well.\n", s->szService));
+    }
+ 
+    /* Depending on defaults for proper execution: NOTICEs.*/
+    /* Test for missing path, substitute the default if unset. */
+    if (s->szPath != NULL && s->szPath[0] == '\0' 
+      && strwicmp(s->szService,HOMES_NAME) != 0) {
+	/* Self-test 24. */
+       DEBUG(0,("NOTICE: No path in service [%s], using %s\n",
+               s->szService,tmpdir()));
+       string_set(&s->szPath,tmpdir());      
+   }
+   
+      return (bRetval);
+ }
 
-	bRetval = True;
-	if (ServicePtrs[iService]->szService[0] == '\0') {
-		DEBUG(0, ("The following message indicates an internal error:\n"));
-		DEBUG(0, ("No service name in service entry.\n"));
-		bRetval = False;
-	}
-
-	/* The [printers] entry MUST be printable. I'm all for flexibility, but */
-	/* I can't see why you'd want a non-printable printer service...        */
-	if (strwicmp(ServicePtrs[iService]->szService, PRINTERS_NAME) == 0) {
-		if (!ServicePtrs[iService]->bPrint_ok) {
-			DEBUG(0, ("WARNING: [%s] service MUST be printable!\n",
-			       ServicePtrs[iService]->szService));
-			ServicePtrs[iService]->bPrint_ok = True;
-		}
-		/* [printers] service must also be non-browsable. */
-		if (ServicePtrs[iService]->bBrowseable)
-			ServicePtrs[iService]->bBrowseable = False;
-	}
-
-	if (ServicePtrs[iService]->szPath[0] == '\0' &&
-	    strwicmp(ServicePtrs[iService]->szService, HOMES_NAME) != 0) {
-		DEBUG(0, ("No path in service %s - using %s\n",
-		       ServicePtrs[iService]->szService, tmpdir()));
-		string_set(&ServicePtrs[iService]->szPath, tmpdir());
-	}
-
-	/* If a service is flagged unavailable, log the fact at level 0. */
-	if (!ServicePtrs[iService]->bAvailable)
-		DEBUG(1, ("NOTE: Service %s is flagged unavailable.\n",
-			  ServicePtrs[iService]->szService));
-
-	return (bRetval);
-}
 
 static struct file_lists {
 	struct file_lists *next;
@@ -2391,11 +2670,11 @@ static BOOL handle_netbios_name(const ch
 	BOOL ret;
 	pstring netbios_name;
 
-	pstrcpy(netbios_name, pszParmValue);
 
+ 	if (validate_netbios_name(pszParmValue) == False)
+ 		return False;
+	pstrcpy(netbios_name, pszParmValue);
 	standard_sub_basic(current_user_info.smb_name, netbios_name,sizeof(netbios_name));
-
-
 	ret = set_global_myname(netbios_name);
 	string_set(&Globals.szNetbiosName,global_myname());
 	
@@ -2405,6 +2684,95 @@ static BOOL handle_netbios_name(const ch
 	return ret;
 }
 
+ 
+ 
+ /***************************************************************************
+ handle_password_server -- validate and insert multiple netbios-name
+ parameters. It's a pstring global.
+ ***************************************************************************/
+ static BOOL handle_password_server(char *pszParmValue, char **parm_ptr) {
+    char *p;
+    pstring buf;
+  
+    *buf = '\0';
+    if (lp_security() != SEC_SERVER && lp_security() != SEC_DOMAIN) {
+        DEBUG(0,("WARNING: password server set to \"%s\", ",pszParmValue));
+        DEBUGADD(0,("but security is neither server nor domain.\n"
+         "password server value ignored\n"));
+        return True;
+    }
+ 
+    /* A "*" by itself means search for Primary or Backup Domain controllers */
+    if (lp_security() == SEC_DOMAIN && *pszParmValue == '*') {
+        pstrcpy(buf,pszParmValue); 
+    }
+    else {
+        for (p=strtok(pszParmValue, " \t"); p != NULL; p=strtok(NULL," \t")) {
+           if (validate_netbios_name(p) == False)
+              return False;
+           pstrcat(buf,p);
+           pstrcat(buf," ");
+        }
+        buf[MIN(strlen(buf)-1,sizeof(buf))] = '\0';
+    }
+ 
+    /* I've treated it here as an uppercase pstring. P_USTRING --davecb */
+    string_set(parm_ptr,buf);
+    strupper(*(char **)parm_ptr);
+    return True;
+ }
+ 
+/**************************************************************************
+Validate a single netbios-name, and render it legal if possible.
+under control of #define FIX
+**************************************************************************/
+static char *legalNetbiosChars = "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
+
+/* #define FIX */
+static BOOL validate_netbios_name(const char *pszParmValue) {
+    int len, i;
+    char *p;
+ 
+    /* Alternative 1a: manage FQDN by removing domain */
+    if ((p = strchr(pszParmValue, '.')) != NULL) {
+       DEBUG(0,("WARNING: netbios name \"%s\" contained a dot,\n",
+               pszParmValue));
+#ifdef FIX
+       *p = '\0';
+       DEBUGADD(0,("which is only legal in DNS domain names.\n"));
+       DEBUGADD(0,("Name has been truncated to \"%s\".\n",
+                pszParmValue));
+#else
+	return False;
+#endif
+    }
+ 
+    /* Alternative 1b: manage over-long name by truncating it */
+    len = strlen(pszParmValue);
+    if (len > 15) {
+#ifdef FIX
+       pszParmValue[15] = '\0';
+       len = 15;
+       DEBUG(0,("netbios name is longer than 15 characters, "
+               "and has been truncated to \"%s\".\n", pszParmValue));
+#else
+	DEBUG(0,("netbios name is longer than 15 characters\n", pszParmValue));
+	return False;
+#endif
+    }
+
+    /* Alternative 2: fail non-alphanumerics. */
+    if ((i = strspn(pszParmValue,legalNetbiosChars)) < len) {
+       DEBUG(0,("netbios name \"%s\" contains non-alphanumeric character "
+               "\"%c\", and cannot be set.\n", pszParmValue, 
+               pszParmValue[i]));
+       return False;
+    }
+    return True;
+ }
+
+
 static BOOL handle_workgroup(const char *pszParmValue, char **ptr)
 {
 	BOOL ret;
@@ -3697,6 +4065,9 @@ BOOL lp_load(const char *pszFname, BOOL 
 	}
 
 	init_iconv();
+
+ 	/* If we get here, check globals. */
+ 	bRetval = globals_ok();
 
 	return (bRetval);
 }


More information about the samba-technical mailing list