Support REQUIRETLS
[exim.git] / src / src / spool_in.c
index a5b14959fa4c2fcc347125bcc0f58994100f9c3d..0b3490b226f7634123496b4ee4ed08aedbc438c1 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for reading spool files. When compiling for a utility (eximon),
@@ -57,9 +57,17 @@ for (i = 0; i < 2; i++)
   fname = spool_fname(US"input", message_subdir, id, US"-D");
   DEBUG(D_deliver) debug_printf("Trying spool file %s\n", fname);
 
+  /* We protect against symlink attacks both in not propagating the
+   * file-descriptor to other processes as we exec, and also ensuring that we
+   * don't even open symlinks.
+   * No -D file inside the spool area should be a symlink.
+   */
   if ((fd = Uopen(fname,
 #ifdef O_CLOEXEC
                      O_CLOEXEC |
+#endif
+#ifdef O_NOFOLLOW
+                     O_NOFOLLOW |
 #endif
                      O_RDWR | O_APPEND, 0)) >= 0)
     break;
@@ -206,49 +214,13 @@ return TRUE;
 
 
 
-/*************************************************
-*             Read spool header file             *
-*************************************************/
-
-/* This function reads a spool header file and places the data into the
-appropriate global variables. The header portion is always read, but header
-structures are built only if read_headers is set true. It isn't, for example,
-while generating -bp output.
-
-It may be possible for blocks of nulls (binary zeroes) to get written on the
-end of a file if there is a system crash during writing. It was observed on an
-earlier version of Exim that omitted to fsync() the files - this is thought to
-have been the cause of that incident, but in any case, this code must be robust
-against such an event, and if such a file is encountered, it must be treated as
-malformed.
-
-As called from deliver_message() (at least) we are running as root.
-
-Arguments:
-  name          name of the header file, including the -H
-  read_headers  TRUE if in-store header structures are to be built
-  subdir_set    TRUE is message_subdir is already set
-
-Returns:        spool_read_OK        success
-                spool_read_notopen   open failed
-                spool_read_enverror  error in the envelope portion
-                spool_read_hdrerror  error in the header portion
-*/
-
-int
-spool_read_header(uschar *name, BOOL read_headers, BOOL subdir_set)
-{
-FILE *f = NULL;
-int n;
-int rcount = 0;
-long int uid, gid;
-BOOL inheader = FALSE;
-uschar *p;
-
 /* Reset all the global variables to their default values. However, there is
 one exception. DO NOT change the default value of dont_deliver, because it may
 be forced by an external setting. */
 
+void
+spool_clear_header_globals(void)
+{
 acl_var_c = acl_var_m = NULL;
 authenticated_id = NULL;
 authenticated_sender = NULL;
@@ -267,7 +239,9 @@ host_lookup_failed = FALSE;
 interface_address = NULL;
 interface_port = 0;
 local_error_message = FALSE;
+#ifdef HAVE_LOCAL_SCAN
 local_scan_data = NULL;
+#endif
 max_received_linelength = 0;
 message_linecount = 0;
 received_protocol = NULL;
@@ -297,12 +271,12 @@ bmi_verdicts = NULL;
 #ifndef DISABLE_DKIM
 dkim_signers = NULL;
 dkim_disable_verify = FALSE;
-dkim_collect_input = FALSE;
+dkim_collect_input = 0;
 #endif
 
 #ifdef SUPPORT_TLS
 tls_in.certificate_verified = FALSE;
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
 tls_in.dane_verified = FALSE;
 # endif
 tls_in.cipher = NULL;
@@ -313,6 +287,9 @@ tls_free_cert(&tls_in.peercert);
 tls_in.peerdn = NULL;
 tls_in.sni = NULL;
 tls_in.ocsp = OCSP_NOT_REQ;
+# if defined(EXPERIMENTAL_REQUIRETLS) && !defined(COMPILE_UTILITY)
+tls_requiretls = 0;
+# endif
 #endif
 
 #ifdef WITH_CONTENT_SCAN
@@ -328,6 +305,53 @@ message_utf8_downconvert = 0;
 
 dsn_ret = 0;
 dsn_envid = NULL;
+}
+
+
+/*************************************************
+*             Read spool header file             *
+*************************************************/
+
+/* This function reads a spool header file and places the data into the
+appropriate global variables. The header portion is always read, but header
+structures are built only if read_headers is set true. It isn't, for example,
+while generating -bp output.
+
+It may be possible for blocks of nulls (binary zeroes) to get written on the
+end of a file if there is a system crash during writing. It was observed on an
+earlier version of Exim that omitted to fsync() the files - this is thought to
+have been the cause of that incident, but in any case, this code must be robust
+against such an event, and if such a file is encountered, it must be treated as
+malformed.
+
+As called from deliver_message() (at least) we are running as root.
+
+Arguments:
+  name          name of the header file, including the -H
+  read_headers  TRUE if in-store header structures are to be built
+  subdir_set    TRUE is message_subdir is already set
+
+Returns:        spool_read_OK        success
+                spool_read_notopen   open failed
+                spool_read_enverror  error in the envelope portion
+                spool_read_hdrerror  error in the header portion
+*/
+
+int
+spool_read_header(uschar *name, BOOL read_headers, BOOL subdir_set)
+{
+FILE *f = NULL;
+int n;
+int rcount = 0;
+long int uid, gid;
+BOOL inheader = FALSE;
+uschar *p;
+
+/* Reset all the global variables to their default values. However, there is
+one exception. DO NOT change the default value of dont_deliver, because it may
+be forced by an external setting. */
+
+spool_clear_header_globals();
 
 /* Generate the full name and open the file. If message_subdir is already
 set, just look in the given directory. Otherwise, look in both the split
@@ -459,8 +483,8 @@ for (;;)
       tree_node *node;
       endptr = Ustrchr(big_buffer + 6, ' ');
       if (endptr == NULL) goto SPOOL_FORMAT_ERROR;
-      name = string_sprintf("%c%.*s", big_buffer[4], endptr - big_buffer - 6,
-        big_buffer + 6);
+      name = string_sprintf("%c%.*s", big_buffer[4],
+        (int)(endptr - big_buffer - 6), big_buffer + 6);
       if (sscanf(CS endptr, " %d", &count) != 1) goto SPOOL_FORMAT_ERROR;
       node = acl_var_create(name);
       node->data.ptr = store_get(count + 1);
@@ -578,8 +602,10 @@ for (;;)
       sender_local = TRUE;
     else if (Ustrcmp(big_buffer, "-localerror") == 0)
       local_error_message = TRUE;
+#ifdef HAVE_LOCAL_SCAN
     else if (Ustrncmp(p, "ocal_scan ", 10) == 0)
       local_scan_data = string_copy(big_buffer + 12);
+#endif
     break;
 
     case 'm':
@@ -626,22 +652,30 @@ for (;;)
 
 #ifdef SUPPORT_TLS
     case 't':
-    if (Ustrncmp(p, "ls_certificate_verified", 23) == 0)
-      tls_in.certificate_verified = TRUE;
-    else if (Ustrncmp(p, "ls_cipher", 9) == 0)
-      tls_in.cipher = string_copy(big_buffer + 12);
+    if (Ustrncmp(p, "ls_", 3) == 0)
+      {
+      uschar * q = p + 3;
+      if (Ustrncmp(q, "certificate_verified", 20) == 0)
+       tls_in.certificate_verified = TRUE;
+      else if (Ustrncmp(q, "cipher", 6) == 0)
+       tls_in.cipher = string_copy(big_buffer + 12);
 # ifndef COMPILE_UTILITY       /* tls support fns not built in */
-    else if (Ustrncmp(p, "ls_ourcert", 10) == 0)
-      (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert);
-    else if (Ustrncmp(p, "ls_peercert", 11) == 0)
-      (void) tls_import_cert(big_buffer + 14, &tls_in.peercert);
+      else if (Ustrncmp(q, "ourcert", 7) == 0)
+       (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert);
+      else if (Ustrncmp(q, "peercert", 8) == 0)
+       (void) tls_import_cert(big_buffer + 14, &tls_in.peercert);
 # endif
-    else if (Ustrncmp(p, "ls_peerdn", 9) == 0)
-      tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
-    else if (Ustrncmp(p, "ls_sni", 6) == 0)
-      tls_in.sni = string_unprinting(string_copy(big_buffer + 9));
-    else if (Ustrncmp(p, "ls_ocsp", 7) == 0)
-      tls_in.ocsp = big_buffer[10] - '0';
+      else if (Ustrncmp(q, "peerdn", 6) == 0)
+       tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
+      else if (Ustrncmp(q, "sni", 3) == 0)
+       tls_in.sni = string_unprinting(string_copy(big_buffer + 9));
+      else if (Ustrncmp(q, "ocsp", 4) == 0)
+       tls_in.ocsp = big_buffer[10] - '0';
+# if defined(EXPERIMENTAL_REQUIRETLS) && !defined(COMPILE_UTILITY)
+      else if (Ustrncmp(q, "requiretls", 10) == 0)
+       tls_requiretls = strtol(CS big_buffer+16, NULL, 0);
+# endif
+      }
     break;
 #endif