X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/41313d92e0f157dacfa758993e7fc76e291b0415..64b67b658a37dd780cc1b2fd0ef87febe461a0ba:/src/src/spool_in.c diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 8e3099d88..33890c516 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -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), @@ -25,6 +25,8 @@ fact it won't be written to. Just in case there's a major disaster (e.g. overwriting some other file descriptor with the value of this one), open it with append. +As called by deliver_message() (at least) we are operating as root. + Argument: the id of the message Returns: fd if file successfully opened and locked, else -1 @@ -50,21 +52,29 @@ for (i = 0; i < 2; i++) { uschar * fname; int save_errno; - message_subdir[0] = split_spool_directory == (i == 0) ? id[5] : 0; + + message_subdir[0] = split_spool_directory == i ? '\0' : id[5]; fname = spool_fname(US"input", message_subdir, id, US"-D"); DEBUG(D_deliver) debug_printf("Trying spool file %s\n", fname); - if ((fd = Uopen(fname, O_RDWR | O_APPEND, 0)) >= 0) + if ((fd = Uopen(fname, +#ifdef O_CLOEXEC + O_CLOEXEC | +#endif + O_RDWR | O_APPEND, 0)) >= 0) break; save_errno = errno; if (errno == ENOENT) { if (i == 0) continue; if (!queue_running) - log_write(0, LOG_MAIN, "Spool file %s-D not found", id); + log_write(0, LOG_MAIN, "Spool%s%s file %s-D not found", + *queue_name ? US" Q=" : US"", + *queue_name ? queue_name : US"", + id); } - else log_write(0, LOG_MAIN, "Spool error for %s: %s", fname, - strerror(errno)); + else + log_write(0, LOG_MAIN, "Spool error for %s: %s", fname, strerror(errno)); errno = save_errno; return -1; } @@ -77,8 +87,9 @@ an open file descriptor (at least, I think that's the Cygwin story). On real Unix systems it doesn't make any difference as long as Exim is consistent in what it locks. */ -(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | - FD_CLOEXEC); +#ifndef O_CLOEXEC +(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); +#endif lock_data.l_type = F_WRLCK; lock_data.l_whence = SEEK_SET; @@ -195,47 +206,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. - -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_hdrdrror 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; @@ -254,7 +231,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; @@ -271,6 +250,9 @@ sender_ident = NULL; sender_local = FALSE; sender_set_untrusted = FALSE; smtp_active_hostname = primary_hostname; +#ifndef COMPILE_UTILITY +spool_file_wireformat = FALSE; +#endif tree_nonrecipients = NULL; #ifdef EXPERIMENTAL_BRIGHTMAIL @@ -281,12 +263,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; @@ -312,6 +294,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 @@ -381,10 +410,11 @@ sender_address[n-3] = 0; /* time */ if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR; -if (sscanf(CS big_buffer, "%d %d", &received_time, &warning_count) != 2) +if (sscanf(CS big_buffer, TIME_T_FMT " %d", &received_time.tv_sec, &warning_count) != 2) goto SPOOL_FORMAT_ERROR; +received_time.tv_usec = 0; -message_age = time(NULL) - received_time; +message_age = time(NULL) - received_time.tv_sec; #ifndef COMPILE_UTILITY DEBUG(D_deliver) debug_printf("user=%s uid=%ld gid=%ld sender=%s\n", @@ -442,8 +472,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); @@ -472,21 +502,24 @@ for (;;) else if (Ustrncmp(p, "cl ", 3) == 0) { - int index, count; - uschar name[20]; /* Need plenty of space for %d format */ - tree_node *node; - if ( sscanf(CS big_buffer + 5, "%d %d", &index, &count) != 2 + unsigned index, count; + uschar name[20]; /* Need plenty of space for %u format */ + tree_node * node; + if ( sscanf(CS big_buffer + 5, "%u %u", &index, &count) != 2 || index >= 20 + || count > 16384 /* arbitrary limit on variable size */ ) goto SPOOL_FORMAT_ERROR; if (index < 10) - (void) string_format(name, sizeof(name), "%c%d", 'c', index); + (void) string_format(name, sizeof(name), "%c%u", 'c', index); else - (void) string_format(name, sizeof(name), "%c%d", 'm', index - 10); + (void) string_format(name, sizeof(name), "%c%u", 'm', index - 10); node = acl_var_create(name); node->data.ptr = store_get(count + 1); + /* We sanity-checked the count, so disable the Coverity error */ + /* coverity[tainted_data] */ if (fread(node->data.ptr, 1, count+1, f) < count) goto SPOOL_READ_ERROR; - ((uschar*)node->data.ptr)[count] = 0; + (US node->data.ptr)[count] = '\0'; } break; @@ -554,11 +587,14 @@ for (;;) break; case 'l': - if (Ustrcmp(p, "ocal") == 0) sender_local = TRUE; + if (Ustrcmp(p, "ocal") == 0) + 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': @@ -574,6 +610,12 @@ for (;;) case 'r': if (Ustrncmp(p, "eceived_protocol", 16) == 0) received_protocol = string_copy(big_buffer + 19); + else if (Ustrncmp(p, "eceived_time_usec", 17) == 0) + { + unsigned usec; + if (sscanf(CS big_buffer + 21, "%u", &usec) == 1) + received_time.tv_usec = usec; + } break; case 's': @@ -587,6 +629,10 @@ for (;;) else if (Ustrncmp(p, "pam_score_int ", 14) == 0) spam_score_int = string_copy(big_buffer + 16); #endif +#ifndef COMPILE_UTILITY + else if (Ustrncmp(p, "pool_file_wireformat", 20) == 0) + spool_file_wireformat = TRUE; +#endif #if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY) else if (Ustrncmp(p, "mtputf8", 7) == 0) message_smtputf8 = TRUE; @@ -656,10 +702,12 @@ DEBUG(D_deliver) #endif /* COMPILE_UTILITY */ /* After reading the tree, the next line has not yet been read into the -buffer. It contains the count of recipients which follow on separate lines. */ +buffer. It contains the count of recipients which follow on separate lines. +Apply an arbitrary sanity check.*/ if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR; -if (sscanf(CS big_buffer, "%d", &rcount) != 1) goto SPOOL_FORMAT_ERROR; +if (sscanf(CS big_buffer, "%d", &rcount) != 1 || rcount > 16384) + goto SPOOL_FORMAT_ERROR; #ifndef COMPILE_UTILITY DEBUG(D_deliver) debug_printf("recipients_count=%d\n", rcount); @@ -668,6 +716,10 @@ DEBUG(D_deliver) debug_printf("recipients_count=%d\n", rcount); recipients_list_max = rcount; recipients_list = store_get(rcount * sizeof(recipient_item)); +/* We sanitised the count and know we have enough memory, so disable +the Coverity error on recipients_count */ +/* coverity[tainted_data] */ + for (recipients_count = 0; recipients_count < rcount; recipients_count++) { int nn;