Two diffs to add paramater self-checking

David Collier-Brown davecb at canada.sun.com
Fri Jul 12 11:46:58 GMT 2002


These are for param/loadparm.c and utils/testparm.c, respectively,
to put in the self-checking that's been languishing... 

After these go in, I'll start tracking smb.conf changes
again, so I can support them, any you can delete 
samba-patches 298 (Self-check patches for 2.2.0 alpha 3,
11 Apr 2001)

--dave 
-- 
David Collier-Brown,           | Always do right. This will gratify 
Performance & Engineering      | some people and astonish the rest.
Americas Customer Engineering, |                      -- Mark Twain
(905) 415-2849                 | davecb at canada.sun.com
-------------- next part --------------
--- loadparm.c	2002/07/11 00:46:17	1.1
+++ loadparm.c	2002/07/11 23:54:35
@@ -557,6 +557,9 @@
 static BOOL handle_vfs_object(char *pszParmValue, char **ptr);
 static BOOL handle_source_env(char *pszParmValue, char **ptr);
 static BOOL handle_netbios_name(char *pszParmValue, char **ptr);
+static BOOL handle_netbios_aliases(char *pszParmValue,char **ptr);
+static BOOL handle_password_server(char *pszParmValue, char **ptr);
+static BOOL validate_netbios_name(char *pszParmValue);
 static BOOL handle_winbind_uid(char *pszParmValue, char **ptr);
 static BOOL handle_winbind_gid(char *pszParmValue, char **ptr);
 static BOOL handle_wins_server_list(char *pszParmValue, char **ptr);
@@ -1813,6 +1816,7 @@
 			 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(char *pszParmName, char *pszParmValue);
 static BOOL do_section(char *pszSectionName);
@@ -2182,52 +2186,245 @@
 	}
 }
 
+
+
 /***************************************************************************
-Check a service for consistency. Return False if the service is in any way
-incomplete or faulty, else True.
-***************************************************************************/
-static BOOL service_ok(int iService)
-{
-	BOOL bRetval;
+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;
+   char *wins;
+   static BOOL filesChecked = False;
+
+   /* Look for inconstancies in roughly decreasing order of severity, */
+
+   /* Check for both wins server and wins support, make sure server wins. */
+   /* DCB: The data structure changes in 2.0.7, requiring this check. */
+   wins = Globals.szWINSserver;
+   if (Globals.bWINSsupport == True && wins != NULL
+     && ! (wins[0] == '\0' || strwicmp(wins, "127.0.0.1") == 0)) {
+       DEBUG(0,( "ERROR: both wins server = yes and wins support == yes, "
+         "using wins server \"%s\".\n", wins));
+         Globals.bWINSsupport = False;
+   }
+
+   /* Make sure we have a lock directory, but only once. */
+   if (filesChecked == False) {
+      if (!directory_exist(Globals.szLockDir, &st)) {
+         DEBUG(0,("ERROR: lock directory \"%s\" does not exist\n",
+                 Globals.szLockDir));
+         bRetval = False;
+      }
+      else if ((st.st_mode & 0777) != 0755) {
+         DEBUG(0,("WARNING: lock directory \"%s\" should have "
+           "permissions 0755 for browsing and locks to work\n", 
+           Globals.szLockDir));
+      }
+      filesChecked = True;
+   }
+
+   /* Security = (server or domain) requires password server to be set.  */
+   if ((Globals.szPasswordServer == NULL || Globals.szPasswordServer[0] == '\0'
+)
+     && (Globals.security == SEC_SERVER || Globals.security ==
+SEC_DOMAIN)) {
+      DEBUG(0,("ERROR: security = server and security = domain "
+         "both require a password server.\n"));
+      bRetval = False;
+   }
+
+   /* Password server should be a netbios name. */
+   if (Globals.szPasswordServer != NULL 
+     && (strchr(Globals.szPasswordServer,'.') != NULL 
+       || strlen(Globals.szPasswordServer) >= 15)) {
+       DEBUG(0,("ERROR: password server \"%s\" is not a legal "
+                "NetBIOS name, logons will fail.\n",
+                Globals.szPasswordServer));
+       bRetval = False;
+   }
+
+   /* Check unix password sync prerequisites. */
+   if (Globals.bUnixPasswdSync) {
+      if (Globals.security != SEC_USER) {
+         DEBUG(0,("WARNING: unix password sync = yes requires "
+           "security = user.\n"));
+      }
+      if (Globals.bEncryptPasswords == False) {
+        DEBUG(0,("WARNING: unix password sync = yes requires "
+           "encrypt passwords = yes.\n"));
+      }
+   }
+
+   /* Be sure update encrypted is done with NON-encrypted passwords. */
+   if (Globals.bUpdateEncrypt && Globals.bEncryptPasswords == False) {
+      DEBUG(0,("WARNING: update encrypted = yes requires encrypt "
+         "passwords = no.\n"));
+   }
 
-	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;
-	}
+   /* Er, should unix password sync be automatic if smb password file exists?
+*/
 
-	/* 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 unix password sync, check the prerequisites.*/
+   if (Globals.bUnixPasswdSync) {
+      /* See if the program is executable. */
+      {  pstring passwd_prog;
+               char *p;
+         pstrcpy(passwd_prog, Globals.szPasswdProgram);
+        if ((p = strchr(passwd_prog, ' ')) != NULL) {
+            *p = '\0';
+        }
+         if (access(passwd_prog, X_OK) == -1) {
+            DEBUG(0,("WARNING: the unix password sync program \"%s\" "
+              "can't be executed, errno was %d (\"%s\").\n",
+              Globals.szPasswdProgram,
+               errno, strerror(errno)));
+        }
+      }
 
-	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());
-	}
+      /* And the passwd chat isn't expecting the old password. */
+      if (strstr(Globals.szPasswdChat, "%o") != 0) {
+         DEBUG(0,("WARNING: the 'passwd chat' script [%s] depends on "
+           "getting the old password via the %%o substitution. With "
+           "encrypted passwords this is not possible.\n",
+            Globals.szPasswdChat));
+      }
+
+   }
+
+   /* Check for announcing as something other than NT, which can */
+   /* interefere with serving browse lists. */
+   if (Globals.announce_as != ANNOUNCE_AS_NT_SERVER) {
+      DEBUG(0,( "WARNING: announce as not set to \"NT\", this may "
+         "interfere with browsing.\n"));
+   }
+   return True;
+} 
 
-	/* 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);
+/***************************************************************************
+Check a service for consistency. Return False if the service is in any way
+incomplete or faulty, else True.
+***************************************************************************/
+static BOOL service_ok(int iService)
+{
+   BOOL bRetval = True;
+   struct stat buf;
+   service *s = ServicePtrs[iService]; 
+
+   /* Look for inconstancies in roughly decreasing order of severity, */
+   /* Test for a missing service name. */
+   if (s->szService == NULL || s->szService[0] == '\0') {
+        DEBUG(0,( "The following message indicates an internal error:\n"));
+        DEBUGADD(0,( "ERROR: No service name in service entry.\n"));
+        bRetval = False;
+     }
+  
+   /* Test for path not pointing to a dir (other tests follow). */
+   if (s->szPath != NULL && s->szPath[0] != '\0') {
+      if (stat(s->szPath,&buf) == -1) {
+         DEBUG(0,("ERROR: can't stat path %s in service [%s].\n",
+               s->szPath, s->szService));
+         DEBUGADD(0,("errno = %d (%s).\n", errno, strerror(errno)));
+      }
+      else if ((buf.st_mode & S_IFDIR) != S_IFDIR) {
+         DEBUG(0,("ERROR: Path %s in service [%s] isn't a directory.\n",
+               s->szPath, s->szService));
+      }
+   }
+
+   /* Contradictions in terms: */
+   /* 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(s->szService,PRINTERS_NAME) == 0) {
+      if (!s->bPrint_ok) {
+         DEBUG(0,( "ERROR: printer service [%s] MUST be printable!\n",
+               s->szService));
+        s->bPrint_ok = True;
+        }
+   }
+  
+   /* Things that are inherently dangerous: WARNINGs. */
+   
+   /* 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) {
+       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) {
+       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) {
+       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) {
+       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) {
+       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) {
+       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) {
+      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) {
+      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) {
+      DEBUG(0,("NOTICE: In service [%s], sync always = yes, which requires "
+        "strict sync = yes.\n", s->szService));
+   }
+
+   /* We ned oplocks for level2 oplocks. */
+   if (s->bLevel2OpLocks == True && s->bOpLocks == False) {
+      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) {
+      DEBUG(0,("NOTICE: No path in service [%s], using %s\n",
+              s->szService,tmpdir()));
+      string_set(&s->szPath,tmpdir());      
+  }
+  
+     return (bRetval);
 }
 
 static struct file_lists
@@ -2318,6 +2515,9 @@
 {
 	pstring netbios_name;
 
+	if (validate_netbios_name(pszParmValue) == False)
+		return False;
+
 	pstrcpy(netbios_name, pszParmValue);
 
 	standard_sub_basic(netbios_name);
@@ -2338,6 +2538,109 @@
 }
 
 /***************************************************************************
+handle_netbios_aliases -- validate and insert multiple netbios-name
+parameters. It's a pstring global, with the DOS_STRING attribute.
+***************************************************************************/
+static BOOL handle_netbios_aliases(char *pszParmValue, char **parm_ptr) {
+   char *p;
+   pstring buf;
+
+   *buf = '\0';
+   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);
+  unix_to_dos(*(char **)parm_ptr);
+  strupper(*(char **)parm_ptr);
+  return True;
+}
+
+/***************************************************************************
+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 (Globals.security != SEC_SERVER && Globals.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 (Globals.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);
+   unix_to_dos(*(char **)parm_ptr);
+   strupper(*(char **)parm_ptr);
+   return True;
+}
+
+/**************************************************************************
+validate a single netbios-name
+**************************************************************************/
+static char *legalNetbiosChars = "abcdefghijklmnopqrstuvwxyz"
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
+
+static BOOL validate_netbios_name(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));
+      *p = '\0';
+      DEBUGADD(0,("which is only legal in DNS domain names.\n"));
+      DEBUGADD(0,("Name has been truncated to \"%s\".\n",
+               pszParmValue));
+   }
+
+   /* Alternative 1b: manage over-long name by truncating it */
+   len = strlen(pszParmValue);
+   if (len > 15) {
+      pszParmValue[15] = '\0';
+      len = 15;
+      DEBUG(0,("netbios name is longer than 15 characters, "
+              "and has been truncated to \"%s\".\n", pszParmValue));
+   }
+
+   /* 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;
+}
+
+
+
+/***************************************************************************
  Do the work of sourcing in environment variable/value pairs.
 ***************************************************************************/
 
@@ -3495,6 +3798,8 @@
 		string_set(&Globals.szWINSserver, "127.0.0.1");
 
 	}
+	/* If we get here, check globals. */
+	bRetval = globals_ok();
 
 	return (bRetval);
 }
-------------- next part --------------
--- testparm.c.old	Fri Jul 12 14:07:35 2002
+++ testparm.c	Fri Jul 12 14:07:43 2002
@@ -42,11 +42,133 @@
 extern FILE *dbf;
 
 /***********************************************
-Here we formerly did a set of 'hard coded' checks for bad
-configuration settings, called do_global_checks(void).
-This is now done in loadparm, in globals_ok() -- Dave C-B
+ Here we do a set of 'hard coded' checks for bad
+ configuration settings.
 ************************************************/
 
+static int do_global_checks(void)
+{
+	int ret = 0;
+	SMB_STRUCT_STAT st;
+
+	if (lp_security() == SEC_DOMAIN && !lp_encrypted_passwords()) {
+		printf("ERROR: in 'security=domain' mode the 'encrypt passwords' parameter must also be set to 'true'.\n");
+		ret = 1;
+	}
+
+	if (lp_wins_support() && *lp_wins_server()) {
+		printf("ERROR: both 'wins support = true' and 'wins server = <server>' \
+cannot be set in the smb.conf file. nmbd will abort with this setting.\n");
+		ret = 1;
+	}
+
+	if (!directory_exist(lp_lockdir(), &st)) {
+		printf("ERROR: lock directory %s does not exist\n",
+		       lp_lockdir());
+		ret = 1;
+	} else if ((st.st_mode & 0777) != 0755) {
+		printf("WARNING: lock directory %s should have permissions 0755 for browsing to work\n",
+		       lp_lockdir());
+		ret = 1;
+	}
+
+	if (!directory_exist(lp_piddir(), &st)) {
+		printf("ERROR: pid directory %s does not exist\n",
+		       lp_piddir());
+		ret = 1;
+	}
+
+	/*
+	 * Password server sanity checks.
+	 */
+
+	if((lp_security() == SEC_SERVER || lp_security() == SEC_DOMAIN) && !lp_passwordserver()) {
+		pstring sec_setting;
+		if(lp_security() == SEC_SERVER)
+			pstrcpy(sec_setting, "server");
+		else if(lp_security() == SEC_DOMAIN)
+			pstrcpy(sec_setting, "domain");
+
+		printf("ERROR: The setting 'security=%s' requires the 'password server' parameter be set \
+to a valid password server.\n", sec_setting );
+		ret = 1;
+	}
+
+	/*
+	 * 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) {
+				printf("ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd program' \
+parameter.\n" );
+				ret = 1;
+			} 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) {
+					printf("ERROR: the 'unix password sync' parameter is set and the 'passwd program' (%s) \
+cannot be executed (error was %s).\n", truncated_prog, strerror(errno) );
+					ret = 1;
+				}
+			}
+
+#ifdef WITH_PAM
+		}
+#endif
+
+		if(lp_passwd_chat() == NULL) {
+			printf("ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd chat' \
+parameter.\n");
+			ret = 1;
+		}
+
+		/*
+		 * Check that we have a valid script and that it hasn't
+		 * been written to expect the old password.
+		 */
+
+		if(lp_encrypted_passwords()) {
+			if(strstr( lp_passwd_chat(), "%o")!=NULL) {
+				printf("ERROR: the 'passwd chat' script [%s] expects to use the old plaintext password \
+via the %%o substitution. With encrypted passwords this is not possible.\n", lp_passwd_chat() );
+				ret = 1;
+			}
+		}
+	}
+
+	if (lp_status(-1) && lp_max_smbd_processes()) {
+		printf("ERROR: the 'max smbd processes' parameter is set and the 'status' parameter is set to 'no'.\n");
+		ret = 1;
+	}
+
+	if (strlen(lp_winbind_separator()) != 1) {
+		printf("ERROR: the 'winbind separator' parameter must be a single character.\n");
+		ret = 1;
+	}
+
+	if (*lp_winbind_separator() == '+') {
+		printf("'winbind separator = +' might cause problems with group membership.\n");
+	}
+
+	return ret;
+}   
 
 static void usage(char *pname)
 {
@@ -124,6 +246,8 @@
   }
 
   printf("Loaded services file OK.\n");
+
+  ret = do_global_checks();
 
   for (s=0;s<1000;s++) {
     if (VALID_SNUM(s))


More information about the samba-technical mailing list