Add TRUSTED_CONFIG_PREFIX_FILE option
[exim.git] / src / src / exim.c
index a68a06227e1b5bca2d31193e323d553052b14a27..9db61e2a9e30f135c4cde7483f5ca7b1e7fced56 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.69 2010/06/06 02:46:13 pdp Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.71 2010/06/07 00:12:42 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -905,7 +905,7 @@ if (fixed_never_users[0] > 0)
   fprintf(f, "%d\n", (unsigned int)fixed_never_users[i]);
   }
 
-fprintf(f, "Size of off_t: %d\n", sizeof(off_t));
+fprintf(f, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t));
 
 /* This runtime check is to help diagnose library linkage mismatches which
 result in segfaults and the like; as such, it's left until the end,
@@ -1570,7 +1570,7 @@ for (i = 1; i < argc; i++)
     else if (Ustrcmp(argrest, "version") == 0)
       {
       switchchar = 'b';
-      argrest = "V";
+      argrest = US"V";
       }
     }
 
@@ -1845,6 +1845,100 @@ for (i = 1; i < argc; i++)
           }
         }
       #endif
+      if (real_uid != root_uid)
+        {
+        #ifdef TRUSTED_CONFIG_PREFIX_LIST
+
+       if (Ustrstr(argrest, "/../"))
+          trusted_config = FALSE;
+        else
+          {
+          FILE *trust_list = Ufopen(TRUSTED_CONFIG_PREFIX_LIST, "rb");
+          if (trust_list)
+            {
+            struct stat statbuf;
+
+            if (fstat(fileno(trust_list), &statbuf) != 0 ||
+                (statbuf.st_uid != root_uid        /* owner not root */
+                 #ifdef CONFIGURE_OWNER
+                 && statbuf.st_uid != config_uid   /* owner not the special one */
+                 #endif
+                   ) ||                            /* or */
+                (statbuf.st_gid != root_gid        /* group not root */
+                 #ifdef CONFIGURE_GROUP
+                 && statbuf.st_gid != config_gid   /* group not the special one */
+                 #endif
+                 && (statbuf.st_mode & 020) != 0   /* group writeable */
+                   ) ||                            /* or */
+                (statbuf.st_mode & 2) != 0)        /* world writeable */
+              {
+              trusted_config = FALSE;
+              fclose(trust_list);
+              }
+           else
+              {
+              /* Well, the trust list at least is up to scratch... */
+              void *reset_point = store_get(0);
+              uschar *trusted_prefixes[32];
+              int nr_prefixes = 0;
+              int i = 0;
+
+              while (Ufgets(big_buffer, big_buffer_size, trust_list))
+                {
+                uschar *start = big_buffer, *nl;
+                while (*start && isspace(*start))
+                start++;
+                if (*start == '#')
+                  continue;
+                nl = Ustrchr(start, '\n');
+                if (nl)
+                  *nl = 0;
+                trusted_prefixes[nr_prefixes++] = string_copy(start);
+                if (nr_prefixes == 32)
+                  break;
+                }
+              fclose(trust_list);
+
+              if (nr_prefixes)
+                {
+                int sep = 0;
+                uschar *list = argrest;
+                uschar *filename;
+                while (trusted_config && (filename = string_nextinlist(&list,
+                        &sep, big_buffer, big_buffer_size)) != NULL)
+                  {
+                  for (i=0; i < nr_prefixes; i++)
+                    {
+                    int len = Ustrlen(trusted_prefixes[i]);
+                    if (Ustrlen(filename) >= len &&
+                        Ustrncmp(filename, trusted_prefixes[i], len) == 0)
+                      break;
+                    }
+                  if (i == nr_prefixes)
+                    {
+                    trusted_config = FALSE;
+                    break;
+                    }
+                  }
+                }
+              else
+                {
+                /* No valid prefixes found in trust_list file. */
+                trusted_config = FALSE;
+                }
+              }
+           }
+          else
+            {
+            /* Could not open trust_list file. */
+            trusted_config = FALSE;
+            }
+          }
+      #else
+        /* Not root; don't trust config */
+        trusted_config = FALSE;
+      #endif
+        }
 
       config_main_filelist = argrest;
       config_changed = TRUE;
@@ -3030,11 +3124,11 @@ if (setgroups(0, NULL) != 0)
 
 /* If the configuration file name has been altered by an argument on the
 command line (either a new file name or a macro definition) and the caller is
-not root or the exim user, or if this is a filter testing run, remove any
-setuid privilege the program has, and run as the underlying user.
+not root, or if this is a filter testing run, remove any setuid privilege the
+program has and run as the underlying user.
 
-If ALT_CONFIG_ROOT_ONLY is defined, the exim user is locked out of this, which
-severely restricts the use of -C for some purposes.
+The exim user is locked out of this, which severely restricts the use of -C
+for some purposes.
 
 Otherwise, set the real ids to the effective values (should be root unless run
 from inetd, which it can either be root or the exim uid, if one is configured).
@@ -3046,11 +3140,8 @@ values (such as the path name). If running in the test harness, pretend that
 configuration file changes and macro definitions haven't happened. */
 
 if ((                                            /* EITHER */
-    (config_changed || macros != NULL) &&        /* Config changed, and */
+    (!trusted_config || macros != NULL) &&       /* Config changed, and */
     real_uid != root_uid &&                      /* Not root, and */
-    #ifndef ALT_CONFIG_ROOT_ONLY                 /* (when not locked out) */
-    real_uid != exim_uid &&                      /* Not exim, and */
-    #endif
     !running_in_test_harness                     /* Not fudged */
     ) ||                                         /*   OR   */
     expansion_test                               /* expansion testing */
@@ -3238,15 +3329,12 @@ else
   }
 
 /* Handle the case when we have removed the setuid privilege because of -C or
--D. This means that the caller of Exim was not root, and, provided that
-ALT_CONFIG_ROOT_ONLY is not defined, was not the Exim user that is built into
-the binary.
+-D. This means that the caller of Exim was not root.
 
-If ALT_CONFIG_ROOT_ONLY is not defined, there is a problem if it turns out we
-were running as the exim user defined in the configuration file (different to
-the one in the binary). The sysadmin may expect this case to retain privilege
-because "the binary was called by the Exim user", but it hasn't, because of the
-order in which it handles this stuff. There are two possibilities:
+There is a problem if we were running as the Exim user. The sysadmin may
+expect this case to retain privilege because "the binary was called by the
+Exim user", but it hasn't, because either the -D option set macros, or the
+-C option set a non-trusted configuration file. There are two possibilities:
 
   (1) If deliver_drop_privilege is set, Exim is not going to re-exec in order
       to do message deliveries. Thus, the fact that it is running as a
@@ -3258,27 +3346,18 @@ order in which it handles this stuff. There are two possibilities:
 
   (2) If deliver_drop_privilege is not set, the configuration won't work as
       apparently intended, and so we log a panic message. In order to retain
-      root for -C or -D, the caller must either be root or the Exim user
-      defined in the binary (when deliver_drop_ privilege is false).
-
-If ALT_CONFIG_ROOT_ONLY is defined, we don't know whether we were called by the
-built-in exim user or one defined in the configuration. In either event,
-re-enable log processing, assuming the sysadmin knows what they are doing. */
+      root for -C or -D, the caller must either be root or be invoking a
+      trusted configuration file (when deliver_drop_privilege is false). */
 
-if (removed_privilege && (config_changed || macros != NULL) &&
+if (removed_privilege && (!trusted_config || macros != NULL) &&
     real_uid == exim_uid)
   {
-  #ifdef ALT_CONFIG_ROOT_ONLY
-  really_exim = TRUE;   /* let logging work normally */
-  #else
-
   if (deliver_drop_privilege)
     really_exim = TRUE; /* let logging work normally */
   else
     log_write(0, LOG_MAIN|LOG_PANIC,
-      "exim user (uid=%d) is defined only at runtime; privilege lost for %s",
-      (int)exim_uid, config_changed? "-C" : "-D");
-  #endif
+      "exim user lost privilege for using %s option",
+      (int)exim_uid, trusted_config? "-D" : "-C");
   }
 
 /* Start up Perl interpreter if Perl support is configured and there is a
@@ -3623,6 +3702,7 @@ else setgid(exim_gid);
 /* Handle a request to scan a file for malware */
 if (malware_test_file)
   {
+#ifdef WITH_CONTENT_SCAN
   int result;
   set_process_info("scanning file for malware");
   result = malware_in_file(malware_test_file);
@@ -3640,6 +3720,9 @@ if (malware_test_file)
     printf("Malware found: %s\n", malware_name);
   else
     printf("Malware scan detected malware of unknown name.\n");
+#else
+  printf("Malware scanning not enabled at compile time.\n");
+#endif
   exit(EXIT_FAILURE);
   }