summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>2021-05-27 23:18:04 +0200
committerHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>2021-05-27 23:18:04 +0200
commit31f5b3492bde6a055c0c349a3d46718bd5a7e4f0 (patch)
tree588b31ed9b8aaf8a267cecb9ff9586cd63cf4927 /src
parenteae427c645a7d0e2051b0600d2e4235789740132 (diff)
parent7242147951e127e0db14f9edc070251e110fedea (diff)
Merge branch 'qualys-2020'
- all Qualys patches from 4.94.2 - all fixes from 4.94.2+fixes if not applied yet
Diffstat (limited to 'src')
-rw-r--r--src/OS/Makefile-Base3
-rw-r--r--src/README.UPDATING18
-rw-r--r--src/exim_monitor/em_version.c11
-rwxr-xr-xsrc/scripts/MakeLinks2
-rw-r--r--src/src/acl.c4
-rw-r--r--src/src/configure.default14
-rw-r--r--src/src/daemon.c182
-rw-r--r--src/src/dbfn.c58
-rw-r--r--src/src/deliver.c8
-rw-r--r--src/src/dmarc.c6
-rw-r--r--src/src/exim.c252
-rw-r--r--src/src/exim.h45
-rw-r--r--src/src/filter.c30
-rw-r--r--src/src/functions.h29
-rw-r--r--src/src/globals.c3
-rw-r--r--src/src/globals.h1
-rw-r--r--src/src/log.c215
-rw-r--r--src/src/macro_predef.c2
-rw-r--r--src/src/macros.h20
-rw-r--r--src/src/malware.c2
-rw-r--r--src/src/moan.c3
-rw-r--r--src/src/parse.c244
-rw-r--r--src/src/pdkim/pdkim.c25
-rw-r--r--src/src/priv.c76
-rw-r--r--src/src/queue.c14
-rw-r--r--src/src/rda.c5
-rw-r--r--src/src/readconf.c40
-rw-r--r--src/src/receive.c28
-rw-r--r--src/src/rewrite.c79
-rw-r--r--src/src/smtp_in.c148
-rw-r--r--src/src/smtp_out.c4
-rw-r--r--src/src/spool_in.c48
-rw-r--r--src/src/spool_out.c23
-rw-r--r--src/src/std-crypto.c42
-rw-r--r--src/src/store.c26
-rw-r--r--src/src/string.c19
-rw-r--r--src/src/tls-openssl.c4
-rw-r--r--src/src/tls.c3
-rw-r--r--src/src/transport.c10
-rw-r--r--src/src/transports/smtp.c4
-rw-r--r--src/src/tree.c6
-rw-r--r--src/src/verify.c6
-rw-r--r--src/util/gen_pkcs3.c11
43 files changed, 1166 insertions, 607 deletions
diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base
index 77fb34742..bc5fbc42c 100644
--- a/src/OS/Makefile-Base
+++ b/src/OS/Makefile-Base
@@ -486,7 +486,7 @@ OBJ_EXIM = acl.o base64.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \
filtertest.o globals.o dkim.o dkim_transport.o dnsbl.o hash.o \
header.o host.o ip.o log.o lss.o match.o md5.o moan.o \
- os.o parse.o queue.o \
+ os.o parse.o priv.o queue.o \
rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \
route.o search.o sieve.o smtp_in.o smtp_out.o spool_in.o spool_out.o \
std-crypto.o store.o string.o tls.o tod.o transport.o tree.o verify.o \
@@ -793,6 +793,7 @@ md5.o: $(HDRS) md5.c
moan.o: $(HDRS) moan.c
os.o: $(HDRS) $(OS_C_INCLUDES) os.c
parse.o: $(HDRS) parse.c
+priv.o: $(HDRS) priv.c
queue.o: $(HDRS) queue.c
rda.o: $(HDRS) rda.c
readconf.o: $(HDRS) readconf.c
diff --git a/src/README.UPDATING b/src/README.UPDATING
index 708027f2c..72bc97021 100644
--- a/src/README.UPDATING
+++ b/src/README.UPDATING
@@ -26,6 +26,24 @@ The rest of this document contains information about changes in 4.xx releases
that might affect a running system.
+Exim version 4.95
+-----------------
+
+Various length limits have been applied to Exim's parsing of its command-line.
+These are all set to be at least as long as any valid input, so we do not believe
+that any real use-cases have been affected by this.
+
+The names of various drivers (authenticators, routers, transports, ...) have
+always been limited to 64 characters, but before this release the names were
+silently truncated, inviting problems. Now the length limit should be enforced.
+If this affects you, then please rename to use shorter names.
+
+The default maximum number of recipients of a single email has changed from
+"unlimited" (ie: as much as CPU and memory will allow, until something breaks
+badly) to 50,000. You can raise or lower this as you see fit, but we strongly
+caution against using zero/unlimited.
+
+
Exim version 4.94
-----------------
diff --git a/src/exim_monitor/em_version.c b/src/exim_monitor/em_version.c
index c5931fc8d..336f2ae97 100644
--- a/src/exim_monitor/em_version.c
+++ b/src/exim_monitor/em_version.c
@@ -8,6 +8,17 @@
#define EM_VERSION_C
+/* Needed by macros.h */
+/* Some systems have PATH_MAX and some have MAX_PATH_LEN. */
+
+#ifndef PATH_MAX
+# ifdef MAX_PATH_LEN
+# define PATH_MAX MAX_PATH_LEN
+# else
+# define PATH_MAX 1024
+# endif
+#endif
+
#include "mytypes.h"
#include "store.h"
#include "macros.h"
diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks
index 54ca7fb61..3e16ae13a 100755
--- a/src/scripts/MakeLinks
+++ b/src/scripts/MakeLinks
@@ -103,7 +103,7 @@ for f in blob.h dbfunctions.h dbstuff.h exim.h functions.h globals.h \
deliver.c directory.c dns.c dnsbl.c drtables.c dummies.c enq.c exim.c \
exim_dbmbuild.c exim_dbutil.c exim_lock.c expand.c filter.c filtertest.c \
globals.c hash.c header.c host.c ip.c log.c lss.c match.c md5.c moan.c \
- parse.c perl.c queue.c rda.c readconf.c receive.c retry.c rewrite.c \
+ parse.c perl.c priv.c queue.c rda.c readconf.c receive.c retry.c rewrite.c \
rfc2047.c route.c search.c setenv.c environment.c \
sieve.c smtp_in.c smtp_out.c spool_in.c spool_out.c std-crypto.c store.c \
string.c tls.c tlscert-gnu.c tlscert-openssl.c tls-cipher-stdname.c \
diff --git a/src/src/acl.c b/src/src/acl.c
index a6c3d4cbe..f358516a1 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -486,7 +486,7 @@ static control_def controls_list[] = {
{ US"no_delay_flush", FALSE,
ACL_BIT_NOTSMTP | ACL_BIT_NOTSMTP_START
},
-
+
[CONTROL_NO_ENFORCE_SYNC] =
{ US"no_enforce_sync", FALSE,
ACL_BIT_NOTSMTP | ACL_BIT_NOTSMTP_START
@@ -744,7 +744,7 @@ while ((s = (*func)()))
int v, c;
BOOL negated = FALSE;
uschar *saveline = s;
- uschar name[64];
+ uschar name[EXIM_DRIVERNAME_MAX];
/* Conditions (but not verbs) are allowed to be negated by an initial
exclamation mark. */
diff --git a/src/src/configure.default b/src/src/configure.default
index 6127a9bf0..87f255aa9 100644
--- a/src/src/configure.default
+++ b/src/src/configure.default
@@ -458,6 +458,20 @@ acl_check_rcpt:
require verify = sender
+ # Reject all RCPT commands after too many bad recipients
+ # This is partly a defense against spam abuse and partly attacker abuse.
+ # Real senders should manage, by the time they get to 10 RCPT directives,
+ # to have had at least half of them be real addresses.
+ #
+ # This is a lightweight check and can protect you against repeated
+ # invocations of more heavy-weight checks which would come after it.
+
+ deny condition = ${if and {\
+ {>{$rcpt_count}{10}}\
+ {<{$recipients_count}{${eval:$rcpt_count/2}}} }}
+ message = Rejected for too many bad recipients
+ logwrite = REJECT [$sender_host_address]: bad recipient count high [${eval:$rcpt_count-$recipients_count}]
+
# Accept if the message comes from one of the hosts for which we are an
# outgoing relay. It is assumed that such hosts are most likely to be MUAs,
# so we set control=submission to make Exim treat the message as a
diff --git a/src/src/daemon.c b/src/src/daemon.c
index 52e6dcab1..f40a58c75 100644
--- a/src/src/daemon.c
+++ b/src/src/daemon.c
@@ -471,6 +471,7 @@ if (pid == 0)
signal(SIGCHLD, SIG_IGN);
#endif
signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
/* Attempt to get an id from the sending machine via the RFC 1413
protocol. We do this in the sub-process in order not to hold up the
@@ -697,6 +698,7 @@ if (pid == 0)
signal(SIGHUP, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
if (geteuid() != root_uid && !deliver_drop_privilege)
{
@@ -930,7 +932,6 @@ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
}
-
static void
set_pid_file_path(void)
{
@@ -939,37 +940,150 @@ if (override_pid_file_path)
if (!*pid_file_path)
pid_file_path = string_sprintf("%s/exim-daemon.pid", spool_directory);
+
+if (pid_file_path[0] != '/')
+ log_write(0, LOG_PANIC_DIE, "pid file path %s must be absolute\n", pid_file_path);
}
-/* Remove the daemon's pidfile. Note: runs with root privilege,
-as a direct child of the daemon. Does not return. */
+enum pid_op { PID_WRITE, PID_CHECK, PID_DELETE };
-void
-delete_pid_file(void)
+/* Do various pid file operations as safe as possible. Ideally we'd just
+drop the privileges for creation of the pid file and not care at all about removal of
+the file. FIXME.
+Returns: true on success, false + errno==EACCES otherwise
+*/
+static BOOL
+operate_on_pid_file(const enum pid_op operation, const pid_t pid)
{
-uschar * daemon_pid = string_sprintf("%d\n", (int)getppid());
-FILE * f;
+char pid_line[sizeof(int) * 3 + 2];
+const int pid_len = snprintf(pid_line, sizeof(pid_line), "%d\n", (int)pid);
+BOOL lines_match = FALSE;
+
+char * path = NULL;
+char * base = NULL;
+char * dir = NULL;
+
+const int dir_flags = O_RDONLY | O_NONBLOCK;
+const int base_flags = O_NOFOLLOW | O_NONBLOCK;
+const mode_t base_mode = 0644;
+struct stat sb;
+
+int cwd_fd = -1;
+int dir_fd = -1;
+int base_fd = -1;
+
+BOOL success = FALSE;
+errno = EACCES;
set_pid_file_path();
-if ((f = Ufopen(pid_file_path, "rb")))
+if (!f.running_in_test_harness && real_uid != root_uid && real_uid != exim_uid) goto cleanup;
+if (pid_len < 2 || pid_len >= (int)sizeof(pid_line)) goto cleanup;
+
+path = CS string_copy(pid_file_path);
+if ((base = Ustrrchr(path, '/')) == NULL) /* should not happen, but who knows */
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "pid file path \"%s\" does not contain a '/'", pid_file_path);
+
+dir = (base != path) ? path : "/";
+*base++ = '\0';
+
+if (!dir || !*dir || *dir != '/') goto cleanup;
+if (!base || !*base || strchr(base, '/') != NULL) goto cleanup;
+
+cwd_fd = open(".", dir_flags);
+if (cwd_fd < 0 || fstat(cwd_fd, &sb) != 0 || !S_ISDIR(sb.st_mode)) goto cleanup;
+dir_fd = open(dir, dir_flags);
+if (dir_fd < 0 || fstat(dir_fd, &sb) != 0 || !S_ISDIR(sb.st_mode)) goto cleanup;
+
+/* emulate openat */
+if (fchdir(dir_fd) != 0) goto cleanup;
+base_fd = open(base, O_RDONLY | base_flags);
+if (fchdir(cwd_fd) != 0)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "can't return to previous working dir: %s", strerror(errno));
+
+if (base_fd >= 0)
{
- if ( fgets(CS big_buffer, big_buffer_size, f)
- && Ustrcmp(daemon_pid, big_buffer) == 0
- )
- if (Uunlink(pid_file_path) == 0)
+ char line[sizeof(pid_line)];
+ ssize_t len = -1;
+
+ if (fstat(base_fd, &sb) != 0 || !S_ISREG(sb.st_mode)) goto cleanup;
+ if ((sb.st_mode & 07777) != base_mode || sb.st_nlink != 1) goto cleanup;
+ if (sb.st_size < 2 || sb.st_size >= (off_t)sizeof(line)) goto cleanup;
+
+ len = read(base_fd, line, sizeof(line));
+ if (len != (ssize_t)sb.st_size) goto cleanup;
+ line[len] = '\0';
+
+ if (strspn(line, "0123456789") != (size_t)len-1) goto cleanup;
+ if (line[len-1] != '\n') goto cleanup;
+ lines_match = (len == pid_len && strcmp(line, pid_line) == 0);
+ }
+
+if (operation == PID_WRITE)
+ {
+ if (!lines_match)
+ {
+ if (base_fd >= 0)
{
- DEBUG(D_any)
- debug_printf("%s unlink: %s\n", pid_file_path, strerror(errno));
- }
- else
- DEBUG(D_any)
- debug_printf("unlinked %s\n", pid_file_path);
- fclose(f);
+ int error = -1;
+ /* emulate unlinkat */
+ if (fchdir(dir_fd) != 0) goto cleanup;
+ error = unlink(base);
+ if (fchdir(cwd_fd) != 0)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "can't return to previous working dir: %s", strerror(errno));
+ if (error) goto cleanup;
+ (void)close(base_fd);
+ base_fd = -1;
+ }
+ /* emulate openat */
+ if (fchdir(dir_fd) != 0) goto cleanup;
+ base_fd = open(base, O_WRONLY | O_CREAT | O_EXCL | base_flags, base_mode);
+ if (fchdir(cwd_fd) != 0)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "can't return to previous working dir: %s", strerror(errno));
+ if (base_fd < 0) goto cleanup;
+ if (fchmod(base_fd, base_mode) != 0) goto cleanup;
+ if (write(base_fd, pid_line, pid_len) != pid_len) goto cleanup;
+ DEBUG(D_any) debug_printf("pid written to %s\n", pid_file_path);
+ }
}
else
- DEBUG(D_any)
- debug_printf("%s\n", string_open_failed("pid file %s", pid_file_path));
+ {
+ if (!lines_match) goto cleanup;
+ if (operation == PID_DELETE)
+ {
+ int error = -1;
+ /* emulate unlinkat */
+ if (fchdir(dir_fd) != 0) goto cleanup;
+ error = unlink(base);
+ if (fchdir(cwd_fd) != 0)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "can't return to previous working dir: %s", strerror(errno));
+ if (error) goto cleanup;
+ }
+ }
+
+success = TRUE;
+errno = 0;
+
+cleanup:
+if (cwd_fd >= 0) (void)close(cwd_fd);
+if (dir_fd >= 0) (void)close(dir_fd);
+if (base_fd >= 0) (void)close(base_fd);
+return success;
+}
+
+
+/* Remove the daemon's pidfile. Note: runs with root privilege,
+as a direct child of the daemon. Does not return. */
+
+void
+delete_pid_file(void)
+{
+const BOOL success = operate_on_pid_file(PID_DELETE, getppid());
+
+DEBUG(D_any)
+ debug_printf("delete pid file %s %s: %s\n", pid_file_path,
+ success ? "success" : "failure", strerror(errno));
+
exim_exit(EXIT_SUCCESS);
}
@@ -982,7 +1096,7 @@ daemon_die(void)
{
int pid;
-DEBUG(D_any) debug_printf("SIGTERM seen\n");
+DEBUG(D_any) debug_printf("SIGTERM/SIGINT seen\n");
#if !defined(DISABLE_TLS) && (defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT))
tls_watch_invalidate();
#endif
@@ -1058,7 +1172,7 @@ len = offsetof(struct sockaddr_un, sun_path) + 1
DEBUG(D_any) debug_printf(" @%s\n", sa_un.sun_path+1);
#else /* filesystem-visible and persistent; will neeed removal */
len = offsetof(struct sockaddr_un, sun_path)
- + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s",
+ + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s",
expand_string(notifier_socket));
DEBUG(D_any) debug_printf(" %s\n", sa_un.sun_path);
#endif
@@ -1836,22 +1950,14 @@ The variable daemon_write_pid is used to control this. */
if (f.running_in_test_harness || write_pid)
{
- FILE *f;
-
- set_pid_file_path();
- if ((f = modefopen(pid_file_path, "wb", 0644)))
- {
- (void)fprintf(f, "%d\n", (int)getpid());
- (void)fclose(f);
- DEBUG(D_any) debug_printf("pid written to %s\n", pid_file_path);
- }
- else
- DEBUG(D_any)
- debug_printf("%s\n", string_open_failed("pid file %s", pid_file_path));
+ const enum pid_op operation = (f.running_in_test_harness
+ || real_uid == root_uid
+ || (real_uid == exim_uid && !override_pid_file_path)) ? PID_WRITE : PID_CHECK;
+ if (!operate_on_pid_file(operation, getpid()))
+ DEBUG(D_any) debug_printf("%s pid file %s: %s\n", (operation == PID_WRITE) ? "write" : "check", pid_file_path, strerror(errno));
}
/* Set up the handler for SIGHUP, which causes a restart of the daemon. */
-
sighup_seen = FALSE;
signal(SIGHUP, sighup_handler);
@@ -1888,6 +1994,7 @@ os_non_restarting_signal(SIGCHLD, main_sigchld_handler);
sigterm_seen = FALSE;
os_non_restarting_signal(SIGTERM, main_sigterm_handler);
+os_non_restarting_signal(SIGINT, main_sigterm_handler);
/* If we are to run the queue periodically, pretend the alarm has just gone
off. This will cause the first queue-runner to get kicked off straight away. */
@@ -2175,6 +2282,7 @@ for (;;)
signal(SIGHUP, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
/* Re-exec if privilege has been given up, unless deliver_drop_
privilege is set. Reset SIGALRM before exec(). */
@@ -2415,7 +2523,7 @@ for (;;)
accept_retry_errno = errno;
accept_retry_select_failed = select_failed;
}
- else if ( errno != accept_retry_errno
+ else if ( errno != accept_retry_errno
|| select_failed != accept_retry_select_failed
|| accept_retry_count >= 50)
{
diff --git a/src/src/dbfn.c b/src/src/dbfn.c
index 0f56ad5a6..5cbe10c1f 100644
--- a/src/src/dbfn.c
+++ b/src/src/dbfn.c
@@ -63,8 +63,6 @@ log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg);
#endif
-
-
/*************************************************
* Open and lock a database file *
*************************************************/
@@ -96,7 +94,6 @@ dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof, BOOL panic)
{
int rc, save_errno;
BOOL read_only = flags == O_RDONLY;
-BOOL created = FALSE;
flock_t lock_data;
uschar dirname[PATHLEN], filename[PATHLEN];
@@ -118,12 +115,13 @@ exists, there is no error. */
snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name);
+priv_drop_temp(exim_uid, exim_gid);
if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0)
{
- created = TRUE;
(void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic);
dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE);
}
+priv_restore();
if (dbblock->lockfd < 0)
{
@@ -172,63 +170,17 @@ it easy to pin this down, there are now debug statements on either side of the
open call. */
snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name);
-EXIM_DBOPEN(filename, dirname, flags, EXIMDB_MODE, &(dbblock->dbptr));
+priv_drop_temp(exim_uid, exim_gid);
+EXIM_DBOPEN(filename, dirname, flags, EXIMDB_MODE, &(dbblock->dbptr));
if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
{
DEBUG(D_hints_lookup)
debug_printf_indent("%s appears not to exist: trying to create\n", filename);
- created = TRUE;
EXIM_DBOPEN(filename, dirname, flags|O_CREAT, EXIMDB_MODE, &(dbblock->dbptr));
}
-
save_errno = errno;
-
-/* If we are running as root and this is the first access to the database, its
-files will be owned by root. We want them to be owned by exim. We detect this
-situation by noting above when we had to create the lock file or the database
-itself. Because the different dbm libraries use different extensions for their
-files, I don't know of any easier way of arranging this than scanning the
-directory for files with the appropriate base name. At least this deals with
-the lock file at the same time. Also, the directory will typically have only
-half a dozen files, so the scan will be quick.
-
-This code is placed here, before the test for successful opening, because there
-was a case when a file was created, but the DBM library still returned NULL
-because of some problem. It also sorts out the lock file if that was created
-but creation of the database file failed. */
-
-if (created && geteuid() == root_uid)
- {
- DIR * dd;
- uschar path[PATHLEN];
- uschar *lastname;
- int namelen = Ustrlen(name);
-
- Ustrcpy(path, filename);
- lastname = Ustrrchr(path, '/') + 1;
- *lastname = 0;
-
- if ((dd = exim_opendir(path)))
- for (struct dirent *ent; ent = readdir(dd); )
- if (Ustrncmp(ent->d_name, name, namelen) == 0)
- {
- struct stat statbuf;
- /* Filenames from readdir() are trusted,
- so use a taint-nonchecking copy */
- strcpy(CS lastname, CCS ent->d_name);
- if (Ustat(path, &statbuf) >= 0 && statbuf.st_uid != exim_uid)
- {
- DEBUG(D_hints_lookup)
- debug_printf_indent("ensuring %s is owned by exim\n", path);
- if (exim_chown(path, exim_uid, exim_gid))
- DEBUG(D_hints_lookup)
- debug_printf_indent("failed setting %s to owned by exim\n", path);
- }
- }
-
- closedir(dd);
- }
+priv_restore();
/* If the open has failed, return NULL, leaving errno set. If lof is TRUE,
log the event - also for debugging - but debug only if the file just doesn't
diff --git a/src/src/deliver.c b/src/src/deliver.c
index ba2948dfd..4e472ebe6 100644
--- a/src/src/deliver.c
+++ b/src/src/deliver.c
@@ -334,7 +334,7 @@ static int
open_msglog_file(uschar *filename, int mode, uschar **error)
{
if (Ustrstr(filename, US"/../"))
- log_write(0, LOG_MAIN|LOG_PANIC,
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
"Attempt to open msglog file path with upward-traversal: '%s'\n", filename);
for (int i = 2; i > 0; i--)
@@ -2097,9 +2097,9 @@ return FALSE;
/* Each local delivery is performed in a separate process which sets its
uid and gid as specified. This is a safer way than simply changing and
-restoring using seteuid(); there is a body of opinion that seteuid() cannot be
-used safely. From release 4, Exim no longer makes any use of it. Besides, not
-all systems have seteuid().
+restoring using seteuid(); there is a body of opinion that seteuid()
+cannot be used safely. From release 4, Exim no longer makes any use of
+it for delivery. Besides, not all systems have seteuid().
If the uid/gid are specified in the transport_instance, they are used; the
transport initialization must ensure that either both or neither are set.
diff --git a/src/src/dmarc.c b/src/src/dmarc.c
index 599ecc454..750f9197f 100644
--- a/src/src/dmarc.c
+++ b/src/src/dmarc.c
@@ -53,9 +53,9 @@ static dmarc_exim_p dmarc_policy_description[] = {
};
-void
+void
dmarc_version_report(FILE *f)
-{
+{
const char *implementation, *version;
fprintf(f, "Library version: dmarc: Compile: %d.%d.%d.%d\n",
@@ -254,7 +254,7 @@ if (!dmarc_history_file)
DEBUG(D_receive) debug_printf("DMARC history file not set\n");
return DMARC_HIST_DISABLED;
}
-history_file_fd = log_create(dmarc_history_file);
+history_file_fd = log_create_as_exim(dmarc_history_file);
if (history_file_fd < 0)
{
diff --git a/src/src/exim.c b/src/src/exim.c
index fd1d87362..6a9a1477f 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -233,18 +233,8 @@ int fd;
os_restarting_signal(sig, usr1_handler);
-if ((fd = Uopen(process_log_path, O_APPEND|O_WRONLY, LOG_MODE)) < 0)
- {
- /* If we are already running as the Exim user, try to create it in the
- current process (assuming spool_directory exists). Otherwise, if we are
- root, do the creation in an exim:exim subprocess. */
-
- int euid = geteuid();
- if (euid == exim_uid)
- fd = Uopen(process_log_path, O_CREAT|O_APPEND|O_WRONLY, LOG_MODE);
- else if (euid == root_uid)
- fd = log_create_as_exim(process_log_path);
- }
+if (!process_log_path) return;
+fd = log_open_as_exim(process_log_path);
/* If we are neither exim nor root, or if we failed to create the log file,
give up. There is not much useful we can do with errors, since we don't want
@@ -760,6 +750,25 @@ vfprintf(stderr, fmt, ap);
exit(EXIT_FAILURE);
}
+/* fail if a length is too long */
+static inline void
+exim_len_fail_toolong(int itemlen, int maxlen, const char *description)
+{
+if (itemlen <= maxlen)
+ return;
+fprintf(stderr, "exim: length limit exceeded (%d > %d) for: %s\n",
+ itemlen, maxlen, description);
+exit(EXIT_FAILURE);
+}
+
+/* only pass through the string item back to the caller if it's short enough */
+static inline const uschar *
+exim_str_fail_toolong(const uschar *item, int maxlen, const char *description)
+{
+exim_len_fail_toolong(Ustrlen(item), maxlen, description);
+return item;
+}
+
/* exim_chown_failure() called from exim_chown()/exim_fchown() on failure
of chown()/fchown(). See src/functions.h for more explanation */
int
@@ -1540,10 +1549,11 @@ Arguments:
*/
static void
-expansion_test_line(uschar * line)
+expansion_test_line(const uschar * line)
{
int len;
BOOL dummy_macexp;
+uschar * s;
Ustrncpy(big_buffer, line, big_buffer_size);
big_buffer[big_buffer_size-1] = '\0';
@@ -1557,7 +1567,7 @@ if (isupper(big_buffer[0]))
printf("Defined macro '%s'\n", mlast->name);
}
else
- if ((line = expand_string(big_buffer))) printf("%s\n", CS line);
+ if ((s = expand_string(big_buffer))) printf("%s\n", CS s);
else printf("Failed: %s\n", expand_string_message);
}
@@ -1641,10 +1651,10 @@ uschar *cmdline_syslog_name = NULL;
uschar *start_queue_run_id = NULL;
uschar *stop_queue_run_id = NULL;
uschar *expansion_test_message = NULL;
-uschar *ftest_domain = NULL;
-uschar *ftest_localpart = NULL;
-uschar *ftest_prefix = NULL;
-uschar *ftest_suffix = NULL;
+const uschar *ftest_domain = NULL;
+const uschar *ftest_localpart = NULL;
+const uschar *ftest_prefix = NULL;
+const uschar *ftest_suffix = NULL;
uschar *log_oneline = NULL;
uschar *malware_test_file = NULL;
uschar *real_sender_address;
@@ -1739,6 +1749,9 @@ f.running_in_test_harness =
if (f.running_in_test_harness)
debug_store = TRUE;
+/* Protect against abusive argv[0] */
+exim_str_fail_toolong(argv[0], PATH_MAX, "argv[0]");
+
/* The C standard says that the equivalent of setlocale(LC_ALL, "C") is obeyed
at the start of a program; however, it seems that some environments do not
follow this. A "strange" locale can affect the formatting of timestamps, so we
@@ -2140,10 +2153,10 @@ on the second character (the one after '-'), to save some effort. */
{
if (++i >= argc)
exim_fail("exim: string expected after %s\n", arg);
- if (Ustrcmp(argrest, "d") == 0) ftest_domain = argv[i];
- else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = argv[i];
- else if (Ustrcmp(argrest, "p") == 0) ftest_prefix = argv[i];
- else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = argv[i];
+ if (Ustrcmp(argrest, "d") == 0) ftest_domain = exim_str_fail_toolong(argv[i], EXIM_DOMAINNAME_MAX, "-bfd");
+ else if (Ustrcmp(argrest, "l") == 0) ftest_localpart = exim_str_fail_toolong(argv[i], EXIM_LOCALPART_MAX, "-bfl");
+ else if (Ustrcmp(argrest, "p") == 0) ftest_prefix = exim_str_fail_toolong(argv[i], EXIM_LOCALPART_MAX, "-bfp");
+ else if (Ustrcmp(argrest, "s") == 0) ftest_suffix = exim_str_fail_toolong(argv[i], EXIM_LOCALPART_MAX, "-bfs");
else badarg = TRUE;
}
break;
@@ -2153,7 +2166,7 @@ on the second character (the one after '-'), to save some effort. */
if (!*argrest || Ustrcmp(argrest, "c") == 0)
{
if (++i >= argc) { badarg = TRUE; break; }
- sender_host_address = string_copy_taint(argv[i], TRUE);
+ sender_host_address = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-bh"), TRUE);
host_checking = checking = f.log_testing_mode = TRUE;
f.host_checking_callout = *argrest == 'c';
message_logs = FALSE;
@@ -2611,7 +2624,7 @@ on the second character (the one after '-'), to save some effort. */
case 'F':
if (!*argrest)
if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; }
- originator_name = string_copy_taint(argrest, TRUE);
+ originator_name = string_copy_taint(exim_str_fail_toolong(argrest, EXIM_HUMANNAME_MAX, "-F"), TRUE);
f.sender_name_forced = TRUE;
break;
@@ -2637,6 +2650,7 @@ on the second character (the one after '-'), to save some effort. */
uschar *errmess;
if (!*argrest)
if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; }
+ (void) exim_str_fail_toolong(argrest, EXIM_DISPLAYMAIL_MAX, "-f");
if (!*argrest)
*(sender_address = store_get(1, FALSE)) = '\0'; /* Ensure writeable memory */
else
@@ -2733,9 +2747,9 @@ on the second character (the one after '-'), to save some effort. */
if (msg_action_arg >= 0)
exim_fail("exim: incompatible arguments\n");
- continue_transport = string_copy_taint(argv[++i], TRUE);
- continue_hostname = string_copy_taint(argv[++i], TRUE);
- continue_host_address = string_copy_taint(argv[++i], TRUE);
+ continue_transport = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-C internal transport"), TRUE);
+ continue_hostname = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-C internal hostname"), TRUE);
+ continue_host_address = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-C internal hostaddr"), TRUE);
continue_sequence = Uatoi(argv[++i]);
msg_action = MSG_DELIVER;
msg_action_arg = ++i;
@@ -2780,7 +2794,7 @@ on the second character (the one after '-'), to save some effort. */
/* -MCd: for debug, set a process-purpose string */
case 'd': if (++i < argc)
- process_purpose = string_copy_taint(argv[i], TRUE);
+ process_purpose = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCd"), TRUE);
else badarg = TRUE;
break;
@@ -2788,7 +2802,7 @@ on the second character (the one after '-'), to save some effort. */
from the commandline should be tainted - but we will need an untainted
value for the spoolfile when doing a -odi delivery process. */
- case 'G': if (++i < argc) queue_name = string_copy_taint(argv[i], FALSE);
+ case 'G': if (++i < argc) queue_name = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCG"), FALSE);
else badarg = TRUE;
break;
@@ -2861,7 +2875,7 @@ on the second character (the one after '-'), to save some effort. */
case 'r':
case 's': if (++i < argc)
{
- continue_proxy_sni = string_copy_taint(argv[i], TRUE);
+ continue_proxy_sni = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_HOSTNAME_MAX, "-MCr/-MCs"), TRUE);
if (argrest[1] == 'r') continue_proxy_dane = TRUE;
}
else badarg = TRUE;
@@ -2873,13 +2887,13 @@ on the second character (the one after '-'), to save some effort. */
and the TLS cipher. */
case 't': if (++i < argc)
- sending_ip_address = string_copy_taint(argv[i], TRUE);
+ sending_ip_address = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-MCt IP"), TRUE);
else badarg = TRUE;
if (++i < argc)
sending_port = (int)(Uatol(argv[i]));
else badarg = TRUE;
if (++i < argc)
- continue_proxy_cipher = string_copy_taint(argv[i], TRUE);
+ continue_proxy_cipher = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_CIPHERNAME_MAX, "-MCt cipher"), TRUE);
else badarg = TRUE;
/*FALLTHROUGH*/
@@ -2906,6 +2920,7 @@ on the second character (the one after '-'), to save some effort. */
following options which are followed by a single message id, and which
act on that message. Some of them use the "recipient" addresses as well.
-Mar add recipient(s)
+ -MG move to a different queue
-Mmad mark all recipients delivered
-Mmd mark recipients(s) delivered
-Mes edit sender
@@ -2941,7 +2956,7 @@ on the second character (the one after '-'), to save some effort. */
else if (Ustrcmp(argrest, "G") == 0)
{
msg_action = MSG_SETQUEUE;
- queue_name_dest = string_copy_taint(argv[++i], TRUE);
+ queue_name_dest = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-MG"), TRUE);
}
else if (Ustrcmp(argrest, "mad") == 0)
{
@@ -3154,27 +3169,27 @@ on the second character (the one after '-'), to save some effort. */
/* -oMa: Set sender host address */
if (Ustrcmp(argrest, "a") == 0)
- sender_host_address = string_copy_taint(argv[++i], TRUE);
+ sender_host_address = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMa"), TRUE);
/* -oMaa: Set authenticator name */
else if (Ustrcmp(argrest, "aa") == 0)
- sender_host_authenticated = string_copy_taint(argv[++i], TRUE);
+ sender_host_authenticated = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMaa"), TRUE);
/* -oMas: setting authenticated sender */
else if (Ustrcmp(argrest, "as") == 0)
- authenticated_sender = string_copy_taint(argv[++i], TRUE);
+ authenticated_sender = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), TRUE);
/* -oMai: setting authenticated id */
else if (Ustrcmp(argrest, "ai") == 0)
- authenticated_id = string_copy_taint(argv[++i], TRUE);
+ authenticated_id = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), TRUE);
/* -oMi: Set incoming interface address */
else if (Ustrcmp(argrest, "i") == 0)
- interface_address = string_copy_taint(argv[++i], TRUE);
+ interface_address = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMi"), TRUE);
/* -oMm: Message reference */
@@ -3194,19 +3209,19 @@ on the second character (the one after '-'), to save some effort. */
if (received_protocol)
exim_fail("received_protocol is set already\n");
else
- received_protocol = string_copy_taint(argv[++i], TRUE);
+ received_protocol = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMr"), TRUE);
/* -oMs: Set sender host name */
else if (Ustrcmp(argrest, "s") == 0)
- sender_host_name = string_copy_taint(argv[++i], TRUE);
+ sender_host_name = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-oMs"), TRUE);
/* -oMt: Set sender ident */
else if (Ustrcmp(argrest, "t") == 0)
{
sender_ident_set = TRUE;
- sender_ident = string_copy_taint(argv[++i], TRUE);
+ sender_ident = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IDENTUSER_MAX, "-oMt"), TRUE);
}
/* Else a bad argument */
@@ -3231,6 +3246,10 @@ on the second character (the one after '-'), to save some effort. */
-oPX: delete pid file of daemon */
case 'P':
+ if (!f.running_in_test_harness && real_uid != root_uid && real_uid != exim_uid)
+ exim_fail("exim: only uid=%d or uid=%d can use -oP and -oPX "
+ "(uid=%d euid=%d | %d)\n",
+ root_uid, exim_uid, getuid(), geteuid(), real_uid);
if (!*argrest) override_pid_file_path = argv[++i];
else if (Ustrcmp(argrest, "X") == 0) delete_pid_file();
else badarg = TRUE;
@@ -3256,10 +3275,11 @@ on the second character (the one after '-'), to save some effort. */
break;
/* -oX <list>: Override local_interfaces and/or default daemon ports */
+ /* Limits: Is there a real limit we want here? 1024 is very arbitrary. */
case 'X':
if (*argrest) badarg = TRUE;
- else override_local_interfaces = string_copy_taint(argv[++i], TRUE);
+ else override_local_interfaces = string_copy_taint(exim_str_fail_toolong(argv[++i], 1024, "-oX"), TRUE);
break;
/* -oY: Override creation of daemon notifier socket */
@@ -3307,9 +3327,10 @@ on the second character (the one after '-'), to save some effort. */
exim_fail("received_protocol is set already\n");
if (!hn)
- received_protocol = string_copy_taint(argrest, TRUE);
+ received_protocol = string_copy_taint(exim_str_fail_toolong(argrest, EXIM_DRIVERNAME_MAX, "-p<protocol>"), TRUE);
else
{
+ (void) exim_str_fail_toolong(argrest, (EXIM_DRIVERNAME_MAX+1+EXIM_HOSTNAME_MAX), "-p<protocol>:<host>");
received_protocol = string_copyn_taint(argrest, hn - argrest, TRUE);
sender_host_name = string_copy_taint(hn + 1, TRUE);
}
@@ -3365,6 +3386,7 @@ on the second character (the one after '-'), to save some effort. */
{
int i;
for (argrest++, i = 0; argrest[i] && argrest[i] != '/'; ) i++;
+ exim_len_fail_toolong(i, EXIM_DRIVERNAME_MAX, "-q*G<name>");
queue_name = string_copyn(argrest, i);
argrest += i;
if (*argrest == '/') argrest++;
@@ -3394,7 +3416,10 @@ on the second character (the one after '-'), to save some effort. */
case 'R': /* Synonymous with -qR... */
- receiving_message = FALSE;
+ {
+ const uschar *tainted_selectstr;
+
+ receiving_message = FALSE;
/* -Rf: As -R (below) but force all deliveries,
-Rff: Ditto, but also thaw all frozen messages,
@@ -3405,35 +3430,41 @@ on the second character (the one after '-'), to save some effort. */
in all cases provided there are no further characters in this
argument. */
- if (*argrest)
- for (int i = 0; i < nelem(rsopts); i++)
- if (Ustrcmp(argrest, rsopts[i]) == 0)
- {
- if (i != 2) f.queue_run_force = TRUE;
- if (i >= 2) f.deliver_selectstring_regex = TRUE;
- if (i == 1 || i == 4) f.deliver_force_thaw = TRUE;
- argrest += Ustrlen(rsopts[i]);
- }
+ if (*argrest)
+ for (int i = 0; i < nelem(rsopts); i++)
+ if (Ustrcmp(argrest, rsopts[i]) == 0)
+ {
+ if (i != 2) f.queue_run_force = TRUE;
+ if (i >= 2) f.deliver_selectstring_regex = TRUE;
+ if (i == 1 || i == 4) f.deliver_force_thaw = TRUE;
+ argrest += Ustrlen(rsopts[i]);
+ }
/* -R: Set string to match in addresses for forced queue run to
pick out particular messages. */
- if (*argrest)
- deliver_selectstring = string_copy_taint(argrest, TRUE);
- else if (i+1 < argc)
- deliver_selectstring = string_copy_taint(argv[++i], TRUE);
- else
- exim_fail("exim: string expected after -R\n");
+ /* Avoid attacks from people providing very long strings, and do so before
+ we make copies. */
+ if (*argrest)
+ tainted_selectstr = argrest;
+ else if (i+1 < argc)
+ tainted_selectstr = argv[++i];
+ else
+ exim_fail("exim: string expected after -R\n");
+ deliver_selectstring = string_copy_taint(exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-R"), TRUE);
+ }
break;
-
/* -r: an obsolete synonym for -f (see above) */
/* -S: Like -R but works on sender. */
case 'S': /* Synonymous with -qS... */
- receiving_message = FALSE;
+ {
+ const uschar *tainted_selectstr;
+
+ receiving_message = FALSE;
/* -Sf: As -S (below) but force all deliveries,
-Sff: Ditto, but also thaw all frozen messages,
@@ -3444,25 +3475,27 @@ on the second character (the one after '-'), to save some effort. */
in all cases provided there are no further characters in this
argument. */
- if (*argrest)
- for (int i = 0; i < nelem(rsopts); i++)
- if (Ustrcmp(argrest, rsopts[i]) == 0)
- {
- if (i != 2) f.queue_run_force = TRUE;
- if (i >= 2) f.deliver_selectstring_sender_regex = TRUE;
- if (i == 1 || i == 4) f.deliver_force_thaw = TRUE;
- argrest += Ustrlen(rsopts[i]);
- }
+ if (*argrest)
+ for (int i = 0; i < nelem(rsopts); i++)
+ if (Ustrcmp(argrest, rsopts[i]) == 0)
+ {
+ if (i != 2) f.queue_run_force = TRUE;
+ if (i >= 2) f.deliver_selectstring_sender_regex = TRUE;
+ if (i == 1 || i == 4) f.deliver_force_thaw = TRUE;
+ argrest += Ustrlen(rsopts[i]);
+ }
/* -S: Set string to match in addresses for forced queue run to
pick out particular messages. */
- if (*argrest)
- deliver_selectstring_sender = string_copy_taint(argrest, TRUE);
- else if (i+1 < argc)
- deliver_selectstring_sender = string_copy_taint(argv[++i], TRUE);
- else
- exim_fail("exim: string expected after -S\n");
+ if (*argrest)
+ tainted_selectstr = argrest;
+ else if (i+1 < argc)
+ tainted_selectstr = argv[++i];
+ else
+ exim_fail("exim: string expected after -S\n");
+ deliver_selectstring_sender = string_copy_taint(exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-S"), TRUE);
+ }
break;
/* -Tqt is an option that is exclusively for use by the testing suite.
@@ -3544,10 +3577,12 @@ on the second character (the one after '-'), to save some effort. */
exim_fail("exim: string expected after -X\n");
break;
+ /* -z: a line of text to log */
+
case 'z':
if (!*argrest)
if (++i < argc)
- log_oneline = string_copy_taint(argv[i], TRUE);
+ log_oneline = string_copy_taint(exim_str_fail_toolong(argv[i], 2048, "-z logtext"), TRUE);
else
exim_fail("exim: file name expected after %s\n", argv[i-1]);
break;
@@ -3837,6 +3872,11 @@ during readconf_main() some expansion takes place already. */
/* Store the initial cwd before we change directories. Can be NULL if the
dir has already been unlinked. */
initial_cwd = os_getcwd(NULL, 0);
+if (!initial_cwd && errno)
+ exim_fail("exim: getting initial cwd failed: %s\n", strerror(errno));
+
+if (initial_cwd && (strlen(CCS initial_cwd) >= BIG_BUFFER_SIZE))
+ exim_fail("exim: initial cwd is far too long (%d)\n", Ustrlen(CCS initial_cwd));
/* checking:
-be[m] expansion test -
@@ -4124,11 +4164,9 @@ if ( (debug_selector & D_any || LOGGING(arguments))
p += 13;
else
{
- Ustrncpy(p + 4, initial_cwd, big_buffer_size-5);
- p += 4 + Ustrlen(initial_cwd);
- /* in case p is near the end and we don't provide enough space for
- * string_format to be willing to write. */
- *p = '\0';
+ p += 4;
+ snprintf(CS p, big_buffer_size - (p - big_buffer), "%s", CCS initial_cwd);
+ p += Ustrlen(CCS p);
}
(void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc);
@@ -4515,14 +4553,14 @@ if (test_retry_arg >= 0)
retry_config *yield;
int basic_errno = 0;
int more_errno = 0;
- uschar *s1, *s2;
+ const uschar *s1, *s2;
if (test_retry_arg >= argc)
{
printf("-brt needs a domain or address argument\n");
exim_exit(EXIT_FAILURE);
}
- s1 = argv[test_retry_arg++];
+ s1 = exim_str_fail_toolong(argv[test_retry_arg++], EXIM_EMAILADDR_MAX, "-brt");
s2 = NULL;
/* If the first argument contains no @ and no . it might be a local user
@@ -4538,13 +4576,13 @@ if (test_retry_arg >= 0)
/* There may be an optional second domain arg. */
if (test_retry_arg < argc && Ustrchr(argv[test_retry_arg], '.') != NULL)
- s2 = argv[test_retry_arg++];
+ s2 = exim_str_fail_toolong(argv[test_retry_arg++], EXIM_DOMAINNAME_MAX, "-brt 2nd");
/* The final arg is an error name */
if (test_retry_arg < argc)
{
- uschar *ss = argv[test_retry_arg];
+ const uschar *ss = exim_str_fail_toolong(argv[test_retry_arg], EXIM_DRIVERNAME_MAX, "-brt 3rd");
uschar *error =
readconf_retry_error(ss, ss + Ustrlen(ss), &basic_errno, &more_errno);
if (error != NULL)
@@ -4646,11 +4684,11 @@ if (list_options)
Ustrcmp(argv[i], "macro") == 0 ||
Ustrcmp(argv[i], "environment") == 0))
{
- fail |= !readconf_print(argv[i+1], argv[i], flag_n);
+ fail |= !readconf_print(exim_str_fail_toolong(argv[i+1], EXIM_DRIVERNAME_MAX, "-bP name"), argv[i], flag_n);
i++;
}
else
- fail = !readconf_print(argv[i], NULL, flag_n);
+ fail = !readconf_print(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-bP item"), NULL, flag_n);
}
exim_exit(fail ? EXIT_FAILURE : EXIT_SUCCESS);
}
@@ -4695,6 +4733,7 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD)
pid_t pid;
/*XXX This use of argv[i] for msg_id should really be tainted, but doing
that runs into a later copy into the untainted global message_id[] */
+ /*XXX Do we need a length limit check here? */
if (i == argc - 1)
(void)deliver_message(argv[i], forced_delivery, deliver_give_up);
else if ((pid = exim_fork(US"cmdline-delivery")) == 0)
@@ -4900,7 +4939,7 @@ if (test_rewrite_arg >= 0)
printf("-brw needs an address argument\n");
exim_exit(EXIT_FAILURE);
}
- rewrite_test(argv[test_rewrite_arg]);
+ rewrite_test(exim_str_fail_toolong(argv[test_rewrite_arg], EXIM_EMAILADDR_MAX, "-brw"));
exim_exit(EXIT_SUCCESS);
}
@@ -4993,7 +5032,7 @@ if (verify_address_mode || f.address_test_mode)
while (recipients_arg < argc)
{
/* Supplied addresses are tainted since they come from a user */
- uschar * s = string_copy_taint(argv[recipients_arg++], TRUE);
+ uschar * s = string_copy_taint(exim_str_fail_toolong(argv[recipients_arg++], EXIM_DISPLAYMAIL_MAX, "address verification"), TRUE);
while (*s)
{
BOOL finished = FALSE;
@@ -5010,7 +5049,7 @@ if (verify_address_mode || f.address_test_mode)
{
uschar * s = get_stdinput(NULL, NULL);
if (!s) break;
- test_address(string_copy_taint(s, TRUE), flags, &exit_value);
+ test_address(string_copy_taint(exim_str_fail_toolong(s, EXIM_DISPLAYMAIL_MAX, "address verification (stdin)"), TRUE), flags, &exit_value);
}
route_tidyup();
@@ -5027,11 +5066,15 @@ if (expansion_test)
dns_init(FALSE, FALSE, FALSE);
if (msg_action_arg > 0 && msg_action == MSG_LOAD)
{
- uschar spoolname[256]; /* Not big_buffer; used in spool_read_header() */
+ uschar * spoolname;
if (!f.admin_user)
exim_fail("exim: permission denied\n");
- message_id = argv[msg_action_arg];
- (void)string_format(spoolname, sizeof(spoolname), "%s-H", message_id);
+ message_id = US exim_str_fail_toolong(argv[msg_action_arg], MESSAGE_ID_LENGTH, "message-id");
+ /* Checking the length of the ID is sufficient to validate it.
+ Get an untainted version so file opens can be done. */
+ message_id = string_copy_taint(message_id, FALSE);
+
+ spoolname = string_sprintf("%s-H", message_id);
if ((deliver_datafile = spool_open_datafile(message_id)) < 0)
printf ("Failed to load message datafile %s\n", message_id);
if (spool_read_header(spoolname, TRUE, FALSE) != spool_read_OK)
@@ -5070,7 +5113,7 @@ if (expansion_test)
if (recipients_arg < argc)
while (recipients_arg < argc)
- expansion_test_line(argv[recipients_arg++]);
+ expansion_test_line(exim_str_fail_toolong(argv[recipients_arg++], EXIM_EMAILADDR_MAX, "recipient"));
/* Read stdin */
@@ -5513,7 +5556,9 @@ while (more)
{
int start, end, domain;
uschar * errmess;
- uschar * s = string_copy_taint(list[i], TRUE);
+ /* There can be multiple addresses, so EXIM_DISPLAYMAIL_MAX (tuned for 1) is too short.
+ * We'll still want to cap it to something, just in case. */
+ uschar * s = string_copy_taint(exim_str_fail_toolong(list[i], BIG_BUFFER_SIZE, "address argument"), TRUE);
/* Loop for each comma-separated address */
@@ -5548,11 +5593,12 @@ while (more)
parse_extract_address(s, &errmess, &start, &end, &domain, FALSE);
#ifdef SUPPORT_I18N
- if (string_is_utf8(recipient))
- message_smtputf8 = TRUE;
- else
- allow_utf8_domains = b;
+ if (recipient)
+ if (string_is_utf8(recipient)) message_smtputf8 = TRUE;
+ else allow_utf8_domains = b;
}
+#else
+ ;
#endif
if (domain == 0 && !f.allow_unqualified_recipient)
{
@@ -5650,10 +5696,10 @@ while (more)
{
deliver_domain = ftest_domain ? ftest_domain : qualify_domain_recipient;
deliver_domain_orig = deliver_domain;
- deliver_localpart = ftest_localpart ? ftest_localpart : originator_login;
+ deliver_localpart = ftest_localpart ? US ftest_localpart : originator_login;
deliver_localpart_orig = deliver_localpart;
- deliver_localpart_prefix = ftest_prefix;
- deliver_localpart_suffix = ftest_suffix;
+ deliver_localpart_prefix = US ftest_prefix;
+ deliver_localpart_suffix = US ftest_suffix;
deliver_home = originator_home;
if (!return_path)
diff --git a/src/src/exim.h b/src/src/exim.h
index bb8e4f6e2..8bbeecb4d 100644
--- a/src/src/exim.h
+++ b/src/src/exim.h
@@ -135,6 +135,51 @@ making unique names. */
# endif
#endif
+/* RFC 5321 specifies that the maximum length of a local-part is 64 octets
+and the maximum length of a domain is 255 octets, but then also defines
+the maximum length of a forward/reverse path as 256 not 64+1+255.
+For an IP address, the maximum is 45 without a scope and we don't work
+with scoped addresses, so go with that. (IPv6 with mapped IPv4).
+
+A hostname maximum length is in practice the same as the domainname, for
+the same core reasons (maximum length of a DNS name), but the semantics
+are different and seeing "DOMAIN" in source is confusing when talking about
+hostnames; so we define a second macro. We'll use RFC 2181 as the reference
+for this one.
+
+There is no known (to me) specification on the maximum length of a human name
+in email addresses and we should be careful about imposing such a limit on
+received email, but in terms of limiting what untrusted callers specify, or
+local generation, having a limit makes sense. Err on the side of generosity.
+
+For a display mail address, we have a human name, an email in brackets,
+possibly some (Comments), so it needs to be at least 512+3 and some more to
+avoid extraneous errors.
+Since the sane SMTP line length limit is 998, constraining such parameters to
+be 1024 seems generous and unlikely to spuriously reject legitimate
+invocations.
+
+The driver name is a name of a router/transport/authenticator etc in the
+configuration file. We also use this for some other short strings, such
+as queue names.
+Also TLS ciphersuite name (no real known limit since the protocols use
+integers, but max seen in reality is 45 octets).
+
+RFC 1413 gives us the 512 limit on IDENT protocol userids.
+*/
+
+#define EXIM_EMAILADDR_MAX 256
+#define EXIM_LOCALPART_MAX 64
+#define EXIM_DOMAINNAME_MAX 255
+#define EXIM_IPADDR_MAX 45
+#define EXIM_HOSTNAME_MAX 255
+#define EXIM_HUMANNAME_MAX 256
+#define EXIM_DISPLAYMAIL_MAX 1024
+#define EXIM_DRIVERNAME_MAX 64
+#define EXIM_CIPHERNAME_MAX 64
+#define EXIM_IDENTUSER_MAX 512
+
+
#include <sys/types.h>
#include <sys/file.h>
#include <dirent.h>
diff --git a/src/src/filter.c b/src/src/filter.c
index 3897ae0f9..3f9f750b6 100644
--- a/src/src/filter.c
+++ b/src/src/filter.c
@@ -51,7 +51,7 @@ typedef struct condition_block {
/* Miscellaneous other declarations */
static uschar **error_pointer;
-static uschar *log_filename;
+static const uschar *log_filename;
static int filter_options;
static int line_number;
static int expect_endif;
@@ -1668,7 +1668,7 @@ Returns: FF_DELIVERED success, a significant action was taken
static int
interpret_commands(filter_cmd *commands, address_item **generated)
{
-uschar *s;
+const uschar *s;
int mode;
address_item *addr;
BOOL condition_value;
@@ -1677,7 +1677,7 @@ while (commands)
{
int ff_ret;
uschar *fmsg, *ff_name;
- uschar *expargs[MAILARGS_STRING_COUNT];
+ const uschar *expargs[MAILARGS_STRING_COUNT];
int i, n[2];
@@ -1709,7 +1709,7 @@ while (commands)
case add_command:
for (i = 0; i < 2; i++)
{
- uschar *ss = expargs[i];
+ const uschar *ss = expargs[i];
uschar *end;
if (i == 1 && (*ss++ != 'n' || ss[1] != 0))
@@ -1806,9 +1806,8 @@ while (commands)
af_ignore_error flag if necessary, and the errors address, which can be
set in a system filter and to the local address in user filters. */
- addr = deliver_make_addr(expargs[0], TRUE); /* TRUE => copy s */
- addr->prop.errors_address = (s == NULL)?
- s : string_copy(s); /* Default is NULL */
+ addr = deliver_make_addr(US expargs[0], TRUE); /* TRUE => copy s, so deconst ok */
+ addr->prop.errors_address = !s ? NULL : string_copy(s); /* Default is NULL */
if (commands->noerror) addr->prop.ignore_error = TRUE;
addr->next = *generated;
*generated = addr;
@@ -1848,7 +1847,7 @@ while (commands)
af_pfr and af_file flags, the af_ignore_error flag if necessary, and the
mode value. */
- addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */
+ addr = deliver_make_addr(US s, TRUE); /* TRUE => copy s, so deconst ok */
setflag(addr, af_pfr);
setflag(addr, af_file);
if (commands->noerror) addr->prop.ignore_error = TRUE;
@@ -1878,7 +1877,7 @@ while (commands)
each command argument is expanded in the transport after the command
has been split up into separate arguments. */
- addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */
+ addr = deliver_make_addr(US s, TRUE); /* TRUE => copy s, so deconst ok */
setflag(addr, af_pfr);
setflag(addr, af_expand_pipe);
if (commands->noerror) addr->prop.ignore_error = TRUE;
@@ -2016,7 +2015,7 @@ while (commands)
/* This setting lasts only while the filter is running; on exit, the
variable is reset to the previous value. */
- else headers_charset = s;
+ else headers_charset = s; /*XXX loses track of const */
}
break;
@@ -2040,7 +2039,7 @@ while (commands)
ff_ret = FF_FREEZE;
DEFERFREEZEFAIL:
- fmsg = expargs[0];
+ fmsg = expargs[0]; /*XXX loses track of const */
if (Ustrlen(fmsg) > 1024) Ustrcpy(fmsg + 1000, US" ... (truncated)");
fmsg = US string_printing(fmsg);
*error_pointer = fmsg;
@@ -2123,7 +2122,7 @@ while (commands)
for (i = 0; i < MAILARGS_STRING_COUNT; i++)
{
uschar *p;
- uschar *s = expargs[i];
+ const uschar *s = expargs[i];
if (s == NULL) continue;
@@ -2177,7 +2176,7 @@ while (commands)
/* The string is OK */
- commands->args[i].u = s;
+ commands->args[i].u = s; /*XXX loses track of const */
}
/* Proceed with mail or vacation command */
@@ -2365,8 +2364,9 @@ Returns: TRUE if the message is deemed to be personal
BOOL
filter_personal(string_item *aliases, BOOL scan_cc)
{
-uschar *self, *self_from, *self_to;
-uschar *psself = NULL, *psself_from = NULL, *psself_to = NULL;
+const uschar *self, *self_from, *self_to;
+uschar *psself = NULL;
+const uschar *psself_from = NULL, *psself_to = NULL;
rmark reset_point = store_mark();
BOOL yield;
header_line *h;
diff --git a/src/src/functions.h b/src/src/functions.h
index 7d6e3380e..84eb87397 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -316,8 +316,7 @@ extern int ip_streamsocket(const uschar *, uschar **, int, host_item *);
extern int ipv6_nmtoa(int *, uschar *);
extern uschar *local_part_quote(uschar *);
-extern int log_create(uschar *);
-extern int log_create_as_exim(uschar *);
+extern int log_open_as_exim(uschar *);
extern void log_close_all(void);
extern macro_item * macro_create(const uschar *, const uschar *, BOOL);
@@ -371,16 +370,18 @@ extern FILE *modefopen(const uschar *, const char *, mode_t);
extern int open_cutthrough_connection( address_item * addr );
-extern uschar *parse_extract_address(uschar *, uschar **, int *, int *, int *,
+extern uschar *parse_extract_address(const uschar *, uschar **, int *, int *, int *,
BOOL);
extern int parse_forward_list(uschar *, int, address_item **, uschar **,
const uschar *, uschar *, error_block **);
extern uschar *parse_find_address_end(uschar *, BOOL);
-extern uschar *parse_find_at(uschar *);
+extern const uschar *parse_find_at(const uschar *);
extern const uschar *parse_fix_phrase(const uschar *, int);
-extern uschar *parse_message_id(uschar *, uschar **, uschar **);
+extern const uschar *parse_message_id(const uschar *, uschar **, uschar **);
extern const uschar *parse_quote_2047(const uschar *, int, uschar *, BOOL);
-extern uschar *parse_date_time(uschar *str, time_t *t);
+extern const uschar *parse_date_time(const uschar *str, time_t *t);
+extern void priv_drop_temp(const uid_t, const gid_t);
+extern void priv_restore(void);
extern int vaguely_random_number(int);
#ifndef DISABLE_TLS
extern int vaguely_random_number_fallback(int);
@@ -408,7 +409,7 @@ extern void readconf_driver_init(uschar *, driver_instance **,
extern uschar *readconf_find_option(void *);
extern void readconf_main(BOOL);
extern void readconf_options_from_list(optionlist *, unsigned, const uschar *, uschar *);
-extern BOOL readconf_print(uschar *, uschar *, BOOL);
+extern BOOL readconf_print(const uschar *, uschar *, BOOL);
extern uschar *readconf_printtime(int);
extern uschar *readconf_readname(uschar *, int, uschar *);
extern int readconf_readtime(const uschar *, int, BOOL);
@@ -434,14 +435,14 @@ extern retry_config *retry_find_config(const uschar *, const uschar *, int, int)
extern BOOL retry_ultimate_address_timeout(uschar *, const uschar *,
dbdata_retry *, time_t);
extern void retry_update(address_item **, address_item **, address_item **);
-extern uschar *rewrite_address(uschar *, BOOL, BOOL, rewrite_rule *, int);
-extern uschar *rewrite_address_qualify(uschar *, BOOL);
+extern const uschar *rewrite_address(const uschar *, BOOL, BOOL, rewrite_rule *, int);
+extern const uschar *rewrite_address_qualify(const uschar *, BOOL);
extern header_line *rewrite_header(header_line *,
const uschar *, const uschar *,
rewrite_rule *, int, BOOL);
-extern uschar *rewrite_one(uschar *, int, BOOL *, BOOL, uschar *,
+extern const uschar *rewrite_one(const uschar *, int, BOOL *, BOOL, uschar *,
rewrite_rule *);
-extern void rewrite_test(uschar *);
+extern void rewrite_test(const uschar *);
extern uschar *rfc2047_decode2(uschar *, BOOL, uschar *, int, int *, int *,
uschar **);
extern int route_address(address_item *, address_item **, address_item **,
@@ -609,9 +610,9 @@ extern BOOL transport_headers_send(transport_ctx *,
BOOL (*)(transport_ctx *, uschar *, int));
extern gstring * transport_show_supported(gstring *);
extern BOOL transport_write_message(transport_ctx *, int);
-extern void tree_add_duplicate(uschar *, address_item *);
-extern void tree_add_nonrecipient(uschar *);
-extern void tree_add_unusable(host_item *);
+extern void tree_add_duplicate(const uschar *, address_item *);
+extern void tree_add_nonrecipient(const uschar *);
+extern void tree_add_unusable(const host_item *);
extern void tree_dup(tree_node **, tree_node *);
extern int tree_insertnode(tree_node **, tree_node *);
extern tree_node *tree_search(tree_node *, const uschar *);
diff --git a/src/src/globals.c b/src/src/globals.c
index c45e8a930..e1837b67d 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -227,6 +227,7 @@ struct global_flags f =
.authentication_local = FALSE,
.background_daemon = TRUE,
+ .bdat_readers_wanted = FALSE,
.chunking_offered = FALSE,
.config_changed = FALSE,
@@ -1295,7 +1296,7 @@ uschar *recipient_verify_failure = NULL;
int recipients_count = 0;
recipient_item *recipients_list = NULL;
int recipients_list_max = 0;
-int recipients_max = 0;
+int recipients_max = 50000;
const pcre *regex_AUTH = NULL;
const pcre *regex_check_dns_names = NULL;
const pcre *regex_From = NULL;
diff --git a/src/src/globals.h b/src/src/globals.h
index ed7cffb76..4beb9d07e 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -185,6 +185,7 @@ extern struct global_flags {
BOOL authentication_local :1; /* TRUE if non-smtp (implicit authentication) */
BOOL background_daemon :1; /* Set FALSE to keep in foreground */
+ BOOL bdat_readers_wanted :1; /* BDAT-handling to be pushed on readfunc stack */
BOOL chunking_offered :1;
BOOL config_changed :1; /* True if -C used */
diff --git a/src/src/log.c b/src/src/log.c
index 2cf578069..bb6902ea0 100644
--- a/src/src/log.c
+++ b/src/src/log.c
@@ -265,14 +265,19 @@ overwrite it temporarily if it is necessary to create the directory.
Returns: a file descriptor, or < 0 on failure (errno set)
*/
-int
-log_create(uschar *name)
+static int
+log_open_already_exim(uschar * const name)
{
-int fd = Uopen(name,
-#ifdef O_CLOEXEC
- O_CLOEXEC |
-#endif
- O_CREAT|O_APPEND|O_WRONLY, LOG_MODE);
+int fd = -1;
+const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
+
+if (geteuid() != exim_uid)
+ {
+ errno = EACCES;
+ return -1;
+ }
+
+fd = Uopen(name, flags, LOG_MODE);
/* If creation failed, attempt to build a log directory in case that is the
problem. */
@@ -286,11 +291,7 @@ if (fd < 0 && errno == ENOENT)
DEBUG(D_any) debug_printf("%s log directory %s\n",
created ? "created" : "failed to create", name);
*lastslash = '/';
- if (created) fd = Uopen(name,
-#ifdef O_CLOEXEC
- O_CLOEXEC |
-#endif
- O_CREAT|O_APPEND|O_WRONLY, LOG_MODE);
+ if (created) fd = Uopen(name, flags, LOG_MODE);
}
return fd;
@@ -298,6 +299,81 @@ return fd;
+/* Inspired by OpenSSH's mm_send_fd(). Thanks! */
+
+static int
+log_send_fd(const int sock, const int fd)
+{
+struct msghdr msg;
+union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int))];
+} cmsgbuf;
+struct cmsghdr *cmsg;
+struct iovec vec;
+char ch = 'A';
+ssize_t n;
+
+memset(&msg, 0, sizeof(msg));
+memset(&cmsgbuf, 0, sizeof(cmsgbuf));
+msg.msg_control = &cmsgbuf.buf;
+msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+cmsg = CMSG_FIRSTHDR(&msg);
+cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+cmsg->cmsg_level = SOL_SOCKET;
+cmsg->cmsg_type = SCM_RIGHTS;
+*(int *)CMSG_DATA(cmsg) = fd;
+
+vec.iov_base = &ch;
+vec.iov_len = 1;
+msg.msg_iov = &vec;
+msg.msg_iovlen = 1;
+
+while ((n = sendmsg(sock, &msg, 0)) == -1 && errno == EINTR);
+if (n != 1) return -1;
+return 0;
+}
+
+/* Inspired by OpenSSH's mm_receive_fd(). Thanks! */
+
+static int
+log_recv_fd(const int sock)
+{
+struct msghdr msg;
+union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int))];
+} cmsgbuf;
+struct cmsghdr *cmsg;
+struct iovec vec;
+ssize_t n;
+char ch = '\0';
+int fd = -1;
+
+memset(&msg, 0, sizeof(msg));
+vec.iov_base = &ch;
+vec.iov_len = 1;
+msg.msg_iov = &vec;
+msg.msg_iovlen = 1;
+
+memset(&cmsgbuf, 0, sizeof(cmsgbuf));
+msg.msg_control = &cmsgbuf.buf;
+msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+while ((n = recvmsg(sock, &msg, 0)) == -1 && errno == EINTR);
+if (n != 1 || ch != 'A') return -1;
+
+cmsg = CMSG_FIRSTHDR(&msg);
+if (cmsg == NULL) return -1;
+if (cmsg->cmsg_type != SCM_RIGHTS) return -1;
+fd = *(const int *)CMSG_DATA(cmsg);
+if (fd < 0) return -1;
+return fd;
+}
+
+
+
/*************************************************
* Create a log file as the exim user *
*************************************************/
@@ -313,41 +389,60 @@ Returns: a file descriptor, or < 0 on failure (errno set)
*/
int
-log_create_as_exim(uschar *name)
+log_open_as_exim(uschar * const name)
{
-pid_t pid = exim_fork(US"logfile-create");
-int status = 1;
int fd = -1;
+const uid_t euid = geteuid();
-/* In the subprocess, change uid/gid and do the creation. Return 0 from the
-subprocess on success. If we don't check for setuid failures, then the file
-can be created as root, so vulnerabilities which cause setuid to fail mean
-that the Exim user can use symlinks to cause a file to be opened/created as
-root. We always open for append, so can't nuke existing content but it would
-still be Rather Bad. */
-
-if (pid == 0)
+if (euid == exim_uid)
{
- if (setgid(exim_gid) < 0)
- die(US"exim: setgid for log-file creation failed, aborting",
- US"Unexpected log failure, please try later");
- if (setuid(exim_uid) < 0)
- die(US"exim: setuid for log-file creation failed, aborting",
- US"Unexpected log failure, please try later");
- _exit((log_create(name) < 0)? 1 : 0);
+ fd = log_open_already_exim(name);
}
+else if (euid == root_uid)
+ {
+ int sock[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == 0)
+ {
+ const pid_t pid = exim_fork(US"logfile-open");
+ if (pid == 0)
+ {
+ (void)close(sock[0]);
+ if (setgroups(1, &exim_gid) != 0) _exit(EXIT_FAILURE);
+ if (setgid(exim_gid) != 0) _exit(EXIT_FAILURE);
+ if (setuid(exim_uid) != 0) _exit(EXIT_FAILURE);
+
+ if (getuid() != exim_uid || geteuid() != exim_uid) _exit(EXIT_FAILURE);
+ if (getgid() != exim_gid || getegid() != exim_gid) _exit(EXIT_FAILURE);
+
+ fd = log_open_already_exim(name);
+ if (fd < 0) _exit(EXIT_FAILURE);
+ if (log_send_fd(sock[1], fd) != 0) _exit(EXIT_FAILURE);
+ (void)close(sock[1]);
+ _exit(EXIT_SUCCESS);
+ }
-/* If we created a subprocess, wait for it. If it succeeded, try the open. */
-
-while (pid > 0 && waitpid(pid, &status, 0) != pid);
-if (status == 0) fd = Uopen(name,
-#ifdef O_CLOEXEC
- O_CLOEXEC |
-#endif
- O_APPEND|O_WRONLY, LOG_MODE);
+ (void)close(sock[1]);
+ if (pid > 0)
+ {
+ fd = log_recv_fd(sock[0]);
+ while (waitpid(pid, NULL, 0) == -1 && errno == EINTR);
+ }
+ (void)close(sock[0]);
+ }
+ }
-/* If we failed to create a subprocess, we are in a bad way. We return
-with fd still < 0, and errno set, letting the caller handle the error. */
+if (fd >= 0)
+ {
+ int flags;
+ flags = fcntl(fd, F_GETFD);
+ if (flags != -1) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ flags = fcntl(fd, F_GETFL);
+ if (flags != -1) (void)fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+ }
+else
+ {
+ errno = EACCES;
+ }
return fd;
}
@@ -462,52 +557,17 @@ if (!ok)
die(US"exim: log file path too long: aborting",
US"Logging failure; please try later");
-/* We now have the file name. Try to open an existing file. After a successful
-open, arrange for automatic closure on exec(), and then return. */
+/* We now have the file name. After a successful open, return. */
-*fd = Uopen(buffer,
-#ifdef O_CLOEXEC
- O_CLOEXEC |
-#endif
- O_APPEND|O_WRONLY, LOG_MODE);
+*fd = log_open_as_exim(buffer);
if (*fd >= 0)
{
-#ifndef O_CLOEXEC
- (void)fcntl(*fd, F_SETFD, fcntl(*fd, F_GETFD) | FD_CLOEXEC);
-#endif
return;
}
-/* Open was not successful: try creating the file. If this is a root process,
-we must do the creating in a subprocess set to exim:exim in order to ensure
-that the file is created with the right ownership. Otherwise, there can be a
-race if another Exim process is trying to write to the log at the same time.
-The use of SIGUSR1 by the exiwhat utility can provoke a lot of simultaneous
-writing. */
-
euid = geteuid();
-/* If we are already running as the Exim user (even if that user is root),
-we can go ahead and create in the current process. */
-
-if (euid == exim_uid) *fd = log_create(buffer);
-
-/* Otherwise, if we are root, do the creation in an exim:exim subprocess. If we
-are neither exim nor root, creation is not attempted. */
-
-else if (euid == root_uid) *fd = log_create_as_exim(buffer);
-
-/* If we now have an open file, set the close-on-exec flag and return. */
-
-if (*fd >= 0)
- {
-#ifndef O_CLOEXEC
- (void)fcntl(*fd, F_SETFD, fcntl(*fd, F_GETFD) | FD_CLOEXEC);
-#endif
- return;
- }
-
/* Creation failed. There are some circumstances in which we get here when
the effective uid is not root or exim, which is the problem. (For example, a
non-setuid binary with log_arguments set, called in certain ways.) Rather than
@@ -903,6 +963,7 @@ if (!(flags & (LOG_MAIN|LOG_PANIC|LOG_REJECT)))
if (f.disable_logging)
{
DEBUG(D_any) debug_printf("log writing disabled\n");
+ if ((flags & LOG_PANIC_DIE) == LOG_PANIC_DIE) exim_exit(EXIT_FAILURE);
return;
}
diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c
index b0f57be19..5340c5af8 100644
--- a/src/src/macro_predef.c
+++ b/src/src/macro_predef.c
@@ -72,7 +72,7 @@ options_from_list(optionlist * opts, unsigned nopt,
const uschar * section, uschar * group)
{
const uschar * s;
-uschar buf[64];
+uschar buf[EXIM_DRIVERNAME_MAX];
/* The 'previously-defined-substring' rule for macros in config file
lines is done thus for these builtin macros: we know that the table
diff --git a/src/src/macros.h b/src/src/macros.h
index 43217807f..f8987d604 100644
--- a/src/src/macros.h
+++ b/src/src/macros.h
@@ -154,7 +154,9 @@ enough to hold all the headers from a normal kind of message. */
/* The initial size of a big buffer for use in various places. It gets put
into big_buffer_size and in some circumstances increased. It should be at least
-as long as the maximum path length. */
+as long as the maximum path length PLUS room for string additions.
+Let's go with "at least twice as large as maximum path length".
+*/
#ifdef AUTH_HEIMDAL_GSSAPI
/* RFC 4121 section 5.2, SHOULD support 64K input buffers */
@@ -163,10 +165,12 @@ as long as the maximum path length. */
# define __BIG_BUFFER_SIZE 16384
#endif
-#if defined PATH_MAX && PATH_MAX > __BIG_BUFFER_SIZE
-# define BIG_BUFFER_SIZE PATH_MAX
-#elif defined MAXPATHLEN && MAXPATHLEN > __BIG_BUFFER_SIZE
-# define BIG_BUFFER_SIZE MAXPATHLEN
+#ifndef PATH_MAX
+/* exim.h will have ensured this exists before including us. */
+# error headers confusion, PATH_MAX missing in macros.h
+#endif
+#if (PATH_MAX*2) > __BIG_BUFFER_SIZE
+# define BIG_BUFFER_SIZE (PATH_MAX*2)
#else
# define BIG_BUFFER_SIZE __BIG_BUFFER_SIZE
#endif
@@ -180,12 +184,6 @@ written on the spool, it gets read into big_buffer. */
#define LOCAL_SCAN_MAX_RETURN (BIG_BUFFER_SIZE - 24)
-/* A limit to the length of an address. RFC 2821 limits the local part to 64
-and the domain to 255, so this should be adequate, taking into account quotings
-etc. */
-
-#define ADDRESS_MAXLENGTH 512
-
/* The length of the base names of spool files, which consist of an internal
message id with a trailing "-H" or "-D" added. */
diff --git a/src/src/malware.c b/src/src/malware.c
index 831e9af1f..a6e354bc4 100644
--- a/src/src/malware.c
+++ b/src/src/malware.c
@@ -109,7 +109,7 @@ features_malware(void)
{
const uschar * s;
uschar * t;
-uschar buf[64];
+uschar buf[EXIM_DRIVERNAME_MAX];
spf(buf, sizeof(buf), US"_HAVE_MALWARE_");
diff --git a/src/src/moan.c b/src/src/moan.c
index bf5483ce3..1d9c5b9a5 100644
--- a/src/src/moan.c
+++ b/src/src/moan.c
@@ -86,7 +86,8 @@ if (h || message_id)
fprintf(fp, "References:");
if (h)
{
- uschar * s, * id, * error;
+ const uschar * s;
+ uschar * id, * error;
uschar * referenced_ids[12];
int reference_count = 0;
diff --git a/src/src/parse.c b/src/src/parse.c
index 885a01c0d..fa3395206 100644
--- a/src/src/parse.c
+++ b/src/src/parse.c
@@ -12,7 +12,7 @@
#include "exim.h"
-static uschar *last_comment_position;
+static const uschar *last_comment_position;
@@ -143,21 +143,21 @@ Argument: pointer to an address, possibly unqualified
Returns: pointer to the last @ in an address, or NULL if none
*/
-uschar *
-parse_find_at(uschar *s)
+const uschar *
+parse_find_at(const uschar *s)
{
-uschar *t = s + Ustrlen(s);
+const uschar * t = s + Ustrlen(s);
while (--t >= s)
- {
if (*t == '@')
{
int backslash_count = 0;
- uschar *tt = t - 1;
+ const uschar *tt = t - 1;
while (tt > s && *tt-- == '\\') backslash_count++;
if ((backslash_count & 1) == 0) return t;
}
- else if (*t == '\"') return NULL;
- }
+ else if (*t == '\"')
+ return NULL;
+
return NULL;
}
@@ -191,8 +191,8 @@ Argument: current character pointer
Returns: new character pointer
*/
-static uschar *
-skip_comment(uschar *s)
+static const uschar *
+skip_comment(const uschar *s)
{
last_comment_position = s;
while (*s)
@@ -232,8 +232,8 @@ Arguments:
Returns: new character pointer
*/
-static uschar *
-read_domain(uschar *s, uschar *t, uschar **errorptr)
+static const uschar *
+read_domain(const uschar *s, uschar *t, uschar **errorptr)
{
uschar *tt = t;
s = skip_comment(s);
@@ -406,8 +406,8 @@ Arguments:
Returns: new character pointer
*/
-static uschar *
-read_local_part(uschar *s, uschar *t, uschar **error, BOOL allow_null)
+static const uschar *
+read_local_part(const uschar *s, uschar *t, uschar **error, BOOL allow_null)
{
uschar *tt = t;
*error = NULL;
@@ -491,8 +491,8 @@ Arguments:
Returns: new character pointer
*/
-static uschar *
-read_route(uschar *s, uschar *t, uschar **errorptr)
+static const uschar *
+read_route(const uschar *s, uschar *t, uschar **errorptr)
{
BOOL commas = FALSE;
*errorptr = NULL;
@@ -545,8 +545,8 @@ Arguments:
Returns: new character pointer
*/
-static uschar *
-read_addr_spec(uschar *s, uschar *t, int term, uschar **errorptr,
+static const uschar *
+read_addr_spec(const uschar *s, uschar *t, int term, uschar **errorptr,
uschar **domainptr)
{
s = read_local_part(s, t, errorptr, FALSE);
@@ -616,12 +616,12 @@ Returns: points to the extracted address, or NULL on error
#define FAILED(s) { *errorptr = s; goto PARSE_FAILED; }
uschar *
-parse_extract_address(uschar *mailbox, uschar **errorptr, int *start, int *end,
+parse_extract_address(const uschar *mailbox, uschar **errorptr, int *start, int *end,
int *domain, BOOL allow_null)
{
uschar *yield = store_get(Ustrlen(mailbox) + 1, is_tainted(mailbox));
-uschar *startptr, *endptr;
-uschar *s = US mailbox;
+const uschar *startptr, *endptr;
+const uschar *s = US mailbox;
uschar *t = US yield;
*domain = 0;
@@ -808,11 +808,11 @@ while (isspace(endptr[-1])) endptr--;
*end = endptr - US mailbox;
/* Although this code has no limitation on the length of address extracted,
-other parts of Exim may have limits, and in any case, RFC 2821 limits local
-parts to 64 and domains to 255, so we do a check here, giving an error if the
-address is ridiculously long. */
+other parts of Exim may have limits, and in any case, RFC 5321 limits email
+addresses to 256, so we do a check here, giving an error if the address is
+ridiculously long. */
-if (*end - *start > ADDRESS_MAXLENGTH)
+if (*end - *start > EXIM_EMAILADDR_MAX)
{
*errorptr = string_sprintf("address is ridiculously long: %.64s...", yield);
return NULL;
@@ -984,7 +984,12 @@ if (i < len)
/* No non-printers; use the RFC 822 quoting rules */
-buffer = store_get(len*4, is_tainted(phrase));
+if (len <= 0 || len >= INT_MAX/4)
+ {
+ return string_copy_taint(CUS"", is_tainted(phrase));
+ }
+
+buffer = store_get((len+1)*4, is_tainted(phrase));
s = phrase;
end = s + len;
@@ -1129,9 +1134,12 @@ while (s < end)
{
if (ss >= end) ss--;
*t++ = '(';
- Ustrncpy(t, s, ss-s);
- t += ss-s;
- s = ss;
+ if (ss > s)
+ {
+ Ustrncpy(t, s, ss-s);
+ t += ss-s;
+ s = ss;
+ }
}
}
@@ -1582,7 +1590,7 @@ for (;;)
else
{
int start, end, domain;
- uschar *recipient = NULL;
+ const uschar *recipient = NULL;
int save = s[len];
s[len] = 0;
@@ -1678,8 +1686,8 @@ for (;;)
recipient = ((options & RDO_REWRITE) != 0)?
rewrite_address(recipient, TRUE, FALSE, global_rewrite_rules,
rewrite_existflags) :
- rewrite_address_qualify(recipient, TRUE);
- addr = deliver_make_addr(recipient, TRUE); /* TRUE => copy recipient */
+ rewrite_address_qualify(recipient, TRUE); /*XXX loses track of const */
+ addr = deliver_make_addr(US recipient, TRUE); /* TRUE => copy recipient, so deconst ok */
}
/* Restore the final character in the original data, and add to the
@@ -1713,8 +1721,8 @@ Arguments:
Returns: points after the processed message-id or NULL on error
*/
-uschar *
-parse_message_id(uschar *str, uschar **yield, uschar **error)
+const uschar *
+parse_message_id(const uschar *str, uschar **yield, uschar **error)
{
uschar *domain = NULL;
uschar *id;
@@ -1754,8 +1762,7 @@ while (*id) id++;
*id++ = 0;
store_release_above(id);
-str = skip_comment(str);
-return str;
+return skip_comment(str);
}
@@ -1773,16 +1780,16 @@ Arguments:
Returns: points after the processed date or NULL on error
*/
-static uschar *
-parse_number(uschar *str, int *n, int digits)
+static const uschar *
+parse_number(const uschar *str, int *n, int digits)
{
- *n=0;
- while (digits--)
+*n=0;
+while (digits--)
{
- if (*str<'0' || *str>'9') return NULL;
- *n=10*(*n)+(*str++-'0');
+ if (*str<'0' || *str>'9') return NULL;
+ *n=10*(*n)+(*str++-'0');
}
- return str;
+return str;
}
@@ -1799,8 +1806,8 @@ Arguments:
Returns: points after the parsed day or NULL on error
*/
-static uschar *
-parse_day_of_week(uschar *str)
+static const uschar *
+parse_day_of_week(const uschar * str)
{
/*
day-of-week = ([FWS] day-name) / obs-day-of-week
@@ -1815,17 +1822,16 @@ static const uschar *day_name[7]={ US"mon", US"tue", US"wed", US"thu", US"fri",
int i;
uschar day[4];
-str=skip_comment(str);
-for (i=0; i<3; ++i)
+str = skip_comment(str);
+for (i = 0; i < 3; ++i)
{
- if ((day[i]=tolower(*str))=='\0') return NULL;
+ if ((day[i] = tolower(*str)) == '\0') return NULL;
++str;
}
-day[3]='\0';
-for (i=0; i<7; ++i) if (Ustrcmp(day,day_name[i])==0) break;
-if (i==7) return NULL;
-str=skip_comment(str);
-return str;
+day[3] = '\0';
+for (i = 0; i<7; ++i) if (Ustrcmp(day,day_name[i]) == 0) break;
+if (i == 7) return NULL;
+return skip_comment(str);
}
@@ -1845,8 +1851,8 @@ Arguments:
Returns: points after the processed date or NULL on error
*/
-static uschar *
-parse_date(uschar *str, int *d, int *m, int *y)
+static const uschar *
+parse_date(const uschar *str, int *d, int *m, int *y)
{
/*
date = day month year
@@ -1868,36 +1874,39 @@ day = ([FWS] 1*2DIGIT) / obs-day
obs-day = [CFWS] 1*2DIGIT [CFWS]
*/
-uschar *c,*n;
+const uschar * s, * n;
static const uschar *month_name[]={ US"jan", US"feb", US"mar", US"apr", US"may", US"jun", US"jul", US"aug", US"sep", US"oct", US"nov", US"dec" };
int i;
uschar month[4];
-str=skip_comment(str);
-if ((str=parse_number(str,d,1))==NULL) return NULL;
-if (*str>='0' && *str<='9') *d=10*(*d)+(*str++-'0');
-c=skip_comment(str);
-if (c==str) return NULL;
-else str=c;
-for (i=0; i<3; ++i) if ((month[i]=tolower(*(str+i)))=='\0') return NULL;
-month[3]='\0';
-for (i=0; i<12; ++i) if (Ustrcmp(month,month_name[i])==0) break;
-if (i==12) return NULL;
+str = skip_comment(str);
+if ((str = parse_number(str,d,1)) == NULL) return NULL;
+
+if (*str>='0' && *str<='9') *d = 10*(*d)+(*str++-'0');
+s = skip_comment(str);
+if (s == str) return NULL;
+str = s;
+
+for (i = 0; i<3; ++i) if ((month[i]=tolower(*(str+i))) == '\0') return NULL;
+month[3] = '\0';
+for (i = 0; i<12; ++i) if (Ustrcmp(month,month_name[i]) == 0) break;
+if (i == 12) return NULL;
str+=3;
-*m=i;
-c=skip_comment(str);
-if (c==str) return NULL;
-else str=c;
-if ((n=parse_number(str,y,4)))
+*m = i;
+s = skip_comment(str);
+if (s == str) return NULL;
+str=s;
+
+if ((n = parse_number(str,y,4)))
{
- str=n;
+ str = n;
if (*y<1900) return NULL;
- *y=*y-1900;
+ *y = *y-1900;
}
-else if ((n=parse_number(str,y,2)))
+else if ((n = parse_number(str,y,2)))
{
- str=skip_comment(n);
- while (*(str-1)==' ' || *(str-1)=='\t') --str; /* match last FWS later */
+ str = skip_comment(n);
+ while (*(str-1) == ' ' || *(str-1) == '\t') --str; /* match last FWS later */
if (*y<50) *y+=100;
}
else return NULL;
@@ -1922,8 +1931,8 @@ Arguments:
Returns: points after the processed time or NULL on error
*/
-static uschar *
-parse_time(uschar *str, int *h, int *m, int *s, int *z)
+static const uschar *
+parse_time(const uschar *str, int *h, int *m, int *s, int *z)
{
/*
time = time-of-day FWS zone
@@ -1958,61 +1967,61 @@ obs-zone = "UT" / "GMT" / ; Universal Time
%d107-122 ; upper and lower case
*/
-uschar *c;
+const uschar * c;
-str=skip_comment(str);
-if ((str=parse_number(str,h,2))==NULL) return NULL;
-str=skip_comment(str);
+str = skip_comment(str);
+if ((str = parse_number(str,h,2)) == NULL) return NULL;
+str = skip_comment(str);
if (*str!=':') return NULL;
++str;
-str=skip_comment(str);
-if ((str=parse_number(str,m,2))==NULL) return NULL;
-c=skip_comment(str);
-if (*str==':')
+str = skip_comment(str);
+if ((str = parse_number(str,m,2)) == NULL) return NULL;
+c = skip_comment(str);
+if (*str == ':')
{
++str;
- str=skip_comment(str);
- if ((str=parse_number(str,s,2))==NULL) return NULL;
- c=skip_comment(str);
+ str = skip_comment(str);
+ if ((str = parse_number(str,s,2)) == NULL) return NULL;
+ c = skip_comment(str);
}
-if (c==str) return NULL;
+if (c == str) return NULL;
else str=c;
-if (*str=='+' || *str=='-')
+if (*str == '+' || *str == '-')
{
int neg;
- neg=(*str=='-');
+ neg = (*str == '-');
++str;
- if ((str=parse_number(str,z,4))==NULL) return NULL;
- *z=(*z/100)*3600+(*z%100)*60;
- if (neg) *z=-*z;
+ if ((str = parse_number(str,z,4)) == NULL) return NULL;
+ *z = (*z/100)*3600+(*z%100)*60;
+ if (neg) *z = -*z;
}
else
{
char zone[5];
- struct { const char *name; int off; } zone_name[10]=
+ struct { const char *name; int off; } zone_name[10] =
{ {"gmt",0}, {"ut",0}, {"est",-5}, {"edt",-4}, {"cst",-6}, {"cdt",-5}, {"mst",-7}, {"mdt",-6}, {"pst",-8}, {"pdt",-7}};
int i,j;
- for (i=0; i<4; ++i)
+ for (i = 0; i<4; ++i)
{
- zone[i]=tolower(*(str+i));
+ zone[i] = tolower(*(str+i));
if (zone[i]<'a' || zone[i]>'z') break;
}
- zone[i]='\0';
- for (j=0; j<10 && strcmp(zone,zone_name[j].name); ++j);
+ zone[i] = '\0';
+ for (j = 0; j<10 && strcmp(zone,zone_name[j].name); ++j);
/* Besides zones named in the grammar, RFC 2822 says other alphabetic */
/* time zones should be treated as unknown offsets. */
if (j<10)
{
- *z=zone_name[j].off*3600;
+ *z = zone_name[j].off*3600;
str+=i;
}
else if (zone[0]<'a' || zone[1]>'z') return 0;
else
{
while ((*str>='a' && *str<='z') || (*str>='A' && *str<='Z')) ++str;
- *z=0;
+ *z = 0;
}
}
return str;
@@ -2032,8 +2041,8 @@ Arguments:
Returns: points after the processed date-time or NULL on error
*/
-uschar *
-parse_date_time(uschar *str, time_t *t)
+const uschar *
+parse_date_time(const uschar *str, time_t *t)
{
/*
date-time = [ day-of-week "," ] date FWS time [CFWS]
@@ -2045,27 +2054,26 @@ extern char **environ;
char **old_environ;
static char gmt0[]="TZ=GMT0";
static char *gmt_env[]={ gmt0, (char*)0 };
-uschar *try;
+const uschar * try;
-if ((try=parse_day_of_week(str)))
+if ((try = parse_day_of_week(str)))
{
- str=try;
+ str = try;
if (*str!=',') return 0;
++str;
}
-if ((str=parse_date(str,&tm.tm_mday,&tm.tm_mon,&tm.tm_year))==NULL) return NULL;
+if ((str = parse_date(str,&tm.tm_mday,&tm.tm_mon,&tm.tm_year)) == NULL) return NULL;
if (*str!=' ' && *str!='\t') return NULL;
-while (*str==' ' || *str=='\t') ++str;
-if ((str=parse_time(str,&tm.tm_hour,&tm.tm_min,&tm.tm_sec,&zone))==NULL) return NULL;
-tm.tm_isdst=0;
-old_environ=environ;
-environ=gmt_env;
-*t=mktime(&tm);
-environ=old_environ;
-if (*t==-1) return NULL;
+while (*str == ' ' || *str == '\t') ++str;
+if ((str = parse_time(str,&tm.tm_hour,&tm.tm_min,&tm.tm_sec,&zone)) == NULL) return NULL;
+tm.tm_isdst = 0;
+old_environ = environ;
+environ = gmt_env;
+*t = mktime(&tm);
+environ = old_environ;
+if (*t == -1) return NULL;
*t-=zone;
-str=skip_comment(str);
-return str;
+return skip_comment(str);
}
diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c
index ca16e2b74..45fa2a104 100644
--- a/src/src/pdkim/pdkim.c
+++ b/src/src/pdkim/pdkim.c
@@ -107,7 +107,7 @@ pdkim_combined_canon_entry pdkim_combined_canons[] = {
};
-static blob lineending = {.data = US"\r\n", .len = 2};
+static const blob lineending = {.data = US"\r\n", .len = 2};
/* -------------------------------------------------------------------------- */
uschar *
@@ -720,9 +720,11 @@ return NULL;
If we have to relax the data for this sig, return our copy of it. */
static blob *
-pdkim_update_ctx_bodyhash(pdkim_bodyhash * b, blob * orig_data, blob * relaxed_data)
+pdkim_update_ctx_bodyhash(pdkim_bodyhash * b, const blob * orig_data, blob * relaxed_data)
{
-blob * canon_data = orig_data;
+const blob * canon_data = orig_data;
+size_t left;
+
/* Defaults to simple canon (no further treatment necessary) */
if (b->canon_method == PDKIM_CANON_RELAXED)
@@ -767,16 +769,17 @@ if (b->canon_method == PDKIM_CANON_RELAXED)
}
/* Make sure we don't exceed the to-be-signed body length */
+left = canon_data->len;
if ( b->bodylength >= 0
- && b->signed_body_bytes + (unsigned long)canon_data->len > b->bodylength
+ && left > (unsigned long)b->bodylength - b->signed_body_bytes
)
- canon_data->len = b->bodylength - b->signed_body_bytes;
+ left = (unsigned long)b->bodylength - b->signed_body_bytes;
-if (canon_data->len > 0)
+if (left > 0)
{
- exim_sha_update(&b->body_hash_ctx, CUS canon_data->data, canon_data->len);
- b->signed_body_bytes += canon_data->len;
- DEBUG(D_acl) pdkim_quoteprint(canon_data->data, canon_data->len);
+ exim_sha_update(&b->body_hash_ctx, CUS canon_data->data, left);
+ b->signed_body_bytes += left;
+ DEBUG(D_acl) pdkim_quoteprint(canon_data->data, left);
}
return relaxed_data;
@@ -822,7 +825,7 @@ for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next)
/* VERIFICATION --------------------------------------------------------- */
/* Be careful that the header sig included a bodyash */
- if ( sig->bodyhash.data
+ if (sig->bodyhash.data && sig->bodyhash.len == b->bh.len
&& memcmp(b->bh.data, sig->bodyhash.data, b->bh.len) == 0)
{
DEBUG(D_acl) debug_printf("DKIM [%s] Body hash compared OK\n", sig->domain);
@@ -1003,7 +1006,7 @@ else
last_sig->next = sig;
}
- if (--dkim_collect_input == 0)
+ if (dkim_collect_input && --dkim_collect_input == 0)
{
ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header->s);
ctx->cur_header->s[ctx->cur_header->ptr = 0] = '\0';
diff --git a/src/src/priv.c b/src/src/priv.c
new file mode 100644
index 000000000..94d425401
--- /dev/null
+++ b/src/src/priv.c
@@ -0,0 +1,76 @@
+#include "exim.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+
+static enum {
+ PRIV_DROPPING, PRIV_DROPPED,
+ PRIV_RESTORING, PRIV_RESTORED
+} priv_state = PRIV_RESTORED;
+
+
+static uid_t priv_euid;
+static gid_t priv_egid;
+static gid_t priv_groups[EXIM_GROUPLIST_SIZE + 1];
+static int priv_ngroups;
+
+/* Inspired by OpenSSH's temporarily_use_uid(). Thanks! */
+
+void
+priv_drop_temp(const uid_t temp_uid, const gid_t temp_gid)
+{
+if (priv_state != PRIV_RESTORED)
+ log_write(0, LOG_PANIC_DIE, "priv_drop_temp: unexpected priv_state %d != %d", priv_state, PRIV_RESTORED);
+
+priv_state = PRIV_DROPPING;
+
+priv_euid = geteuid();
+if (priv_euid == root_uid)
+ {
+ priv_egid = getegid();
+ priv_ngroups = getgroups(nelem(priv_groups), priv_groups);
+ if (priv_ngroups < 0)
+ log_write(0, LOG_PANIC_DIE, "getgroups: %s", strerror(errno));
+
+ if (priv_ngroups > 0 && setgroups(1, &temp_gid) != 0)
+ log_write(0, LOG_PANIC_DIE, "setgroups: %s", strerror(errno));
+ if (setegid(temp_gid) != 0)
+ log_write(0, LOG_PANIC_DIE, "setegid(%d): %s", temp_gid, strerror(errno));
+ if (seteuid(temp_uid) != 0)
+ log_write(0, LOG_PANIC_DIE, "seteuid(%d): %s", temp_uid, strerror(errno));
+
+ if (geteuid() != temp_uid)
+ log_write(0, LOG_PANIC_DIE, "getdeuid() != %d", temp_uid);
+ if (getegid() != temp_gid)
+ log_write(0, LOG_PANIC_DIE, "getegid() != %d", temp_gid);
+ }
+
+priv_state = PRIV_DROPPED;
+}
+
+/* Inspired by OpenSSH's restore_uid(). Thanks! */
+
+void
+priv_restore(void)
+{
+if (priv_state != PRIV_DROPPED)
+ log_write(0, LOG_PANIC_DIE, "priv_restore: unexpected priv_state %d != %d", priv_state, PRIV_DROPPED);
+priv_state = PRIV_RESTORING;
+
+if (priv_euid == root_uid)
+ {
+ if (seteuid(priv_euid) != 0)
+ log_write(0, LOG_PANIC_DIE, "seteuid(%d): %s", priv_euid, strerror(errno));
+ if (setegid(priv_egid) != 0)
+ log_write(0, LOG_PANIC_DIE, "setegid(%d): %s", priv_egid, strerror(errno));
+ if (priv_ngroups > 0 && setgroups(priv_ngroups, priv_groups) != 0)
+ log_write(0, LOG_PANIC_DIE, "setgroups: %s", strerror(errno));
+
+ if (geteuid() != priv_euid)
+ log_write(0, LOG_PANIC_DIE, "getdeuid() != %d", priv_euid);
+ if (getegid() != priv_egid)
+ log_write(0, LOG_PANIC_DIE, "getdegid() != %d", priv_egid);
+ }
+
+priv_state = PRIV_RESTORED;
+}
diff --git a/src/src/queue.c b/src/src/queue.c
index 4c93c1d7f..567784575 100644
--- a/src/src/queue.c
+++ b/src/src/queue.c
@@ -396,12 +396,18 @@ if (!recurse)
p += sprintf(CS p, " -q%s", extras);
if (deliver_selectstring)
- p += sprintf(CS p, " -R%s %s", f.deliver_selectstring_regex? "r" : "",
- deliver_selectstring);
+ {
+ snprintf(CS p, big_buffer_size - (p - big_buffer), " -R%s %s",
+ f.deliver_selectstring_regex? "r" : "", deliver_selectstring);
+ p += Ustrlen(CCS p);
+ }
if (deliver_selectstring_sender)
- p += sprintf(CS p, " -S%s %s", f.deliver_selectstring_sender_regex? "r" : "",
- deliver_selectstring_sender);
+ {
+ snprintf(CS p, big_buffer_size - (p - big_buffer), " -S%s %s",
+ f.deliver_selectstring_sender_regex? "r" : "", deliver_selectstring_sender);
+ p += Ustrlen(CCS p);
+ }
log_detail = string_copy(big_buffer);
if (*queue_name)
diff --git a/src/src/rda.c b/src/src/rda.c
index 5615b64d5..fb3714ea2 100644
--- a/src/src/rda.c
+++ b/src/src/rda.c
@@ -618,9 +618,14 @@ search_tidyup();
if ((pid = exim_fork(US"router-interpret")) == 0)
{
header_line *waslast = header_last; /* Save last header */
+ int fd_flags = -1;
fd = pfd[pipe_write];
(void)close(pfd[pipe_read]);
+
+ if ((fd_flags = fcntl(fd, F_GETFD)) == -1) goto bad;
+ if (fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC) == -1) goto bad;
+
exim_setugid(ugid->uid, ugid->gid, FALSE, rname);
/* Addresses can get rewritten in filters; if we are not root or the exim
diff --git a/src/src/readconf.c b/src/src/readconf.c
index e8e310beb..816133329 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -422,7 +422,7 @@ options_from_list(optionlist_config, nelem(optionlist_config), US"MAIN", NULL);
void
options_auths(void)
{
-uschar buf[64];
+uschar buf[EXIM_DRIVERNAME_MAX];
options_from_list(optionlist_auths, optionlist_auths_size, US"AUTHENTICATORS", NULL);
@@ -439,7 +439,7 @@ for (struct auth_info * ai = auths_available; ai->driver_name[0]; ai++)
void
options_logging(void)
{
-uschar buf[64];
+uschar buf[EXIM_DRIVERNAME_MAX];
for (bit_table * bp = log_options; bp < log_options + log_options_count; bp++)
{
@@ -684,7 +684,7 @@ Returns: FALSE iff fatal error
BOOL
macro_read_assignment(uschar *s)
{
-uschar name[64];
+uschar name[EXIM_DRIVERNAME_MAX];
int namelen = 0;
BOOL redef = FALSE;
macro_item *m;
@@ -1178,15 +1178,25 @@ uschar *
readconf_readname(uschar *name, int len, uschar *s)
{
int p = 0;
+BOOL broken = FALSE;
if (isalpha(Uskip_whitespace(&s)))
while (isalnum(*s) || *s == '_')
{
if (p < len-1) name[p++] = *s;
+ else {
+ broken = TRUE;
+ break;
+ }
s++;
}
name[p] = 0;
+if (broken) {
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "exim item name too long (>%d), unable to use \"%s\" (truncated)",
+ len, name);
+}
Uskip_whitespace(&s);
return s;
}
@@ -1311,7 +1321,7 @@ Returns: pointer to an option entry, or NULL if not found
*/
static optionlist *
-find_option(uschar *name, optionlist *ol, int last)
+find_option(const uschar *name, optionlist *ol, int last)
{
int first = 0;
while (last > first)
@@ -1350,10 +1360,10 @@ Returns: a pointer to the boolean flag.
*/
static BOOL *
-get_set_flag(uschar *name, optionlist *oltop, int last, void *data_block)
+get_set_flag(const uschar *name, optionlist *oltop, int last, void *data_block)
{
optionlist *ol;
-uschar name2[64];
+uschar name2[EXIM_DRIVERNAME_MAX];
sprintf(CS name2, "*set_%.50s", name);
if (!(ol = find_option(name2, oltop, last)))
log_write(0, LOG_MAIN|LOG_PANIC_DIE,
@@ -1626,8 +1636,8 @@ uschar *inttype = US"";
uschar *sptr;
uschar *s = buffer;
uschar **str_target;
-uschar name[64];
-uschar name2[64];
+uschar name[EXIM_DRIVERNAME_MAX];
+uschar name2[EXIM_DRIVERNAME_MAX];
/* There may be leading spaces; thereafter, we expect an option name starting
with a letter. */
@@ -2089,7 +2099,7 @@ switch (type)
case opt_bool_set:
if (*s != 0)
{
- s = readconf_readname(name2, 64, s);
+ s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s);
if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0)
boolvalue = TRUE;
else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0)
@@ -2426,7 +2436,7 @@ Returns: boolean success
*/
static BOOL
-print_ol(optionlist *ol, uschar *name, void *options_block,
+print_ol(optionlist *ol, const uschar *name, void *options_block,
optionlist *oltop, int last, BOOL no_labels)
{
struct passwd *pw;
@@ -2436,7 +2446,7 @@ void *value;
uid_t *uidlist;
gid_t *gidlist;
uschar *s;
-uschar name2[64];
+uschar name2[EXIM_DRIVERNAME_MAX];
if (!ol)
{
@@ -2736,7 +2746,7 @@ Returns: Boolean success
*/
BOOL
-readconf_print(uschar *name, uschar *type, BOOL no_labels)
+readconf_print(const uschar *name, uschar *type, BOOL no_labels)
{
BOOL names_only = FALSE;
optionlist *ol2 = NULL;
@@ -2875,7 +2885,7 @@ if (!type)
else
return print_ol(find_option(name,
- optionlist_config, nelem(optionlist_config)),
+ optionlist_config, nelem(optionlist_config)),
name, NULL, optionlist_config, nelem(optionlist_config), no_labels);
}
@@ -3706,7 +3716,7 @@ uschar *buffer;
while ((buffer = get_config_line()))
{
- uschar name[64];
+ uschar name[EXIM_DRIVERNAME_MAX];
uschar *s;
/* Read the first name on the line and test for the start of a new driver. A
@@ -4234,7 +4244,7 @@ acl_line = get_config_line();
while(acl_line)
{
- uschar name[64];
+ uschar name[EXIM_DRIVERNAME_MAX];
tree_node *node;
uschar *error;
diff --git a/src/src/receive.c b/src/src/receive.c
index b0dacbd68..2223d4645 100644
--- a/src/src/receive.c
+++ b/src/src/receive.c
@@ -490,6 +490,13 @@ if (recipients_count >= recipients_list_max)
{
recipient_item *oldlist = recipients_list;
int oldmax = recipients_list_max;
+
+ const int safe_recipients_limit = INT_MAX / 2 / sizeof(recipient_item);
+ if (recipients_list_max < 0 || recipients_list_max >= safe_recipients_limit)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Too many recipients: %d", recipients_list_max);
+ }
+
recipients_list_max = recipients_list_max ? 2*recipients_list_max : 50;
recipients_list = store_get(recipients_list_max * sizeof(recipient_item), FALSE);
if (oldlist)
@@ -2122,7 +2129,8 @@ OVERSIZE:
if (newsender)
{
if (domain == 0 && newsender[0] != 0)
- newsender = rewrite_address_qualify(newsender, FALSE);
+ /* deconst ok as newsender was not const */
+ newsender = US rewrite_address_qualify(newsender, FALSE);
if (filter_test != FTEST_NONE || receive_check_set_sender(newsender))
{
@@ -2502,7 +2510,7 @@ if (extract_recip)
{
while (recipients_count-- > 0)
{
- uschar *s = rewrite_address(recipients_list[recipients_count].address,
+ const uschar * s = rewrite_address(recipients_list[recipients_count].address,
TRUE, TRUE, global_rewrite_rules, rewrite_existflags);
tree_add_nonrecipient(s);
}
@@ -2553,11 +2561,12 @@ if (extract_recip)
&domain, FALSE);
#ifdef SUPPORT_I18N
- if (string_is_utf8(recipient))
- message_smtputf8 = TRUE;
- else
- allow_utf8_domains = b;
+ if (recipient)
+ if (string_is_utf8(recipient)) message_smtputf8 = TRUE;
+ else allow_utf8_domains = b;
}
+#else
+ ;
#endif
/* Keep a list of all the bad addresses so we can send a single
@@ -2789,8 +2798,8 @@ recipients will get here only if the conditions were right (allow_unqualified_
recipient is TRUE). */
for (int i = 0; i < recipients_count; i++)
- recipients_list[i].address =
- rewrite_address(recipients_list[i].address, TRUE, TRUE,
+ recipients_list[i].address = /* deconst ok as src was not cont */
+ US rewrite_address(recipients_list[i].address, TRUE, TRUE,
global_rewrite_rules, rewrite_existflags);
/* If there is no From: header, generate one for local (without
@@ -2965,7 +2974,8 @@ it has already been rewritten as part of verification for SMTP input. */
if (global_rewrite_rules && !sender_address_unrewritten && *sender_address)
{
- sender_address = rewrite_address(sender_address, FALSE, TRUE,
+ /* deconst ok as src was not const */
+ sender_address = US rewrite_address(sender_address, FALSE, TRUE,
global_rewrite_rules, rewrite_existflags);
DEBUG(D_receive|D_rewrite)
debug_printf("rewritten sender = %s\n", sender_address);
diff --git a/src/src/rewrite.c b/src/src/rewrite.c
index d003c6ce0..6a6da47df 100644
--- a/src/src/rewrite.c
+++ b/src/src/rewrite.c
@@ -59,12 +59,12 @@ Arguments:
Returns: fully-qualified address
*/
-uschar *
-rewrite_address_qualify(uschar *s, BOOL is_recipient)
+const uschar *
+rewrite_address_qualify(const uschar *s, BOOL is_recipient)
{
-return (parse_find_at(s) != NULL)? s :
- string_sprintf("%s@%s", s,
- is_recipient? qualify_domain_recipient : qualify_domain_sender);
+return parse_find_at(s)
+ ? s : string_sprintf("%s@%s", s,
+ is_recipient ? qualify_domain_recipient : qualify_domain_sender);
}
@@ -96,12 +96,12 @@ Returns: new address if rewritten; the input address if no change;
rewritten address is returned, not just the active bit.
*/
-uschar *
-rewrite_one(uschar *s, int flag, BOOL *whole, BOOL add_header, uschar *name,
+const uschar *
+rewrite_one(const uschar *s, int flag, BOOL *whole, BOOL add_header, uschar *name,
rewrite_rule *rewrite_rules)
{
-uschar *yield = s;
-uschar *subject = s;
+const uschar *yield = s;
+const uschar *subject = s;
uschar *domain = NULL;
BOOL done = FALSE;
int rule_number = 1;
@@ -119,7 +119,8 @@ for (rewrite_rule * rule = rewrite_rules;
int count = 0;
uschar *save_localpart;
const uschar *save_domain;
- uschar *error, *new, *newparsed;
+ uschar *error, *new;
+ const uschar * newparsed;
/* Come back here for a repeat after a successful rewrite. We do this
only so many times. */
@@ -181,7 +182,7 @@ for (rewrite_rule * rule = rewrite_rules;
set up as an expansion variable */
domain[-1] = 0;
- deliver_localpart = subject;
+ deliver_localpart = US subject;
deliver_domain = domain;
new = expand_string(rule->replacement);
@@ -386,15 +387,16 @@ Arguments:
Returns: possibly rewritten address
*/
-uschar *
-rewrite_address(uschar *s, BOOL is_recipient, BOOL add_header,
+const uschar *
+rewrite_address(const uschar *s, BOOL is_recipient, BOOL add_header,
rewrite_rule *rewrite_rules, int existflags)
{
-int flag = is_recipient? rewrite_envto : rewrite_envfrom;
+int flag = is_recipient ? rewrite_envto : rewrite_envfrom;
+
s = rewrite_address_qualify(s, is_recipient);
-if ((existflags & flag) != 0)
+if (existflags & flag)
{
- uschar *new = rewrite_one(s, flag, NULL, add_header, is_recipient?
+ const uschar *new = rewrite_one(s, flag, NULL, add_header, is_recipient?
US"original-recipient" : US"sender", rewrite_rules);
if (new != s) s = new;
}
@@ -466,8 +468,9 @@ while (*s)
{
uschar *sprev;
uschar *ss = parse_find_address_end(s, FALSE);
- uschar *recipient, *new, *errmess;
+ uschar *recipient, *new;
rmark loop_reset_point = store_mark();
+ uschar *errmess = NULL;
BOOL changed = FALSE;
int terminator = *ss;
int start, end, domain;
@@ -484,10 +487,35 @@ while (*s)
while (isspace(*s)) s++;
/* There isn't much we can do for syntactic disasters at this stage.
- Pro tem (possibly for ever) ignore them. */
+ Pro tem (possibly for ever) ignore them.
+ If we got nothing, then there was any sort of error: non-parsable address,
+ empty address, overlong addres. Sometimes the result matters, sometimes not.
+ It seems this function is called for *any* header we see. */
+
if (!recipient)
{
+#if 0
+ /* FIXME:
+ This was(!) an attempt tho handle empty rewrits, but seemingly it
+ needs more effort to decide if the returned empty address matters.
+ Now this will now break test 471 again.
+
+ 471 fails now because it uses an overlong address, for wich parse_extract_address()
+ returns an empty address (which was not expected).
+
+ Checking the output and exit if rewrite_rules or routed_old are present
+ isn't a good idea either: It's enough to have *any* rewrite rule
+ in the configuration plus "To: undisclosed recpients:;" to exit(), which
+ is not what we want.
+ */
+
+ if (rewrite_rules || routed_old)
+ {
+ log_write(0, LOG_MAIN, "rewrite: %s", errmess);
+ exim_exit(EXIT_FAILURE);
+ }
+#endif
loop_reset_point = store_reset(loop_reset_point);
continue;
}
@@ -526,7 +554,8 @@ while (*s)
{
BOOL is_recipient =
(flag & (rewrite_sender | rewrite_from | rewrite_replyto)) == 0;
- new = rewrite_address_qualify(recipient, is_recipient);
+ /* deconst ok as recipient was notconst */
+ new = US rewrite_address_qualify(recipient, is_recipient);
changed = (new != recipient);
recipient = new;
@@ -549,7 +578,8 @@ while (*s)
if (existflags & flag)
{
BOOL whole;
- new = rewrite_one(recipient, flag, &whole, FALSE, NULL, rewrite_rules);
+ /* deconst ok as recipient was notconst */
+ new = US rewrite_one(recipient, flag, &whole, FALSE, NULL, rewrite_rules);
if (new != recipient)
{
changed = TRUE;
@@ -741,7 +771,8 @@ Argument: the address to test
Returns: nothing
*/
-void rewrite_test(uschar *s)
+void
+rewrite_test(const uschar *s)
{
uschar *recipient, *error;
int start, end, domain;
@@ -758,8 +789,8 @@ pretending it is a sender. */
if ((rewrite_existflags & rewrite_smtp) != 0)
{
- uschar *new = rewrite_one(s, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE,
- US"", global_rewrite_rules);
+ const uschar * new = rewrite_one(s, rewrite_smtp|rewrite_smtp_sender, NULL,
+ FALSE, US"", global_rewrite_rules);
if (new != s)
{
if (*new == 0)
@@ -792,7 +823,7 @@ for (int i = 0; i < 8; i++)
{
BOOL whole = FALSE;
int flag = 1 << i;
- uschar *new = rewrite_one(recipient, flag, &whole, FALSE, US"",
+ const uschar * new = rewrite_one(recipient, flag, &whole, FALSE, US"",
global_rewrite_rules);
printf("%s: ", rrname[i]);
if (*new == 0)
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index d60e7d5c5..8e9b93ab3 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -593,6 +593,11 @@ if (n > 0)
}
+/* Forward declarations */
+static inline void bdat_push_receive_functions(void);
+static inline void bdat_pop_receive_functions(void);
+
+
/* Get a byte from the smtp input, in CHUNKING mode. Handle ack of the
previous BDAT chunk and getting new ones when we run out. Uses the
underlying smtp_getc or tls_getc both for that and for getting the
@@ -624,9 +629,7 @@ for(;;)
if (chunking_data_left > 0)
return lwr_receive_getc(chunking_data_left--);
- receive_getc = lwr_receive_getc;
- receive_getbuf = lwr_receive_getbuf;
- receive_ungetc = lwr_receive_ungetc;
+ bdat_pop_receive_functions();
#ifndef DISABLE_DKIM
dkim_save = dkim_collect_input;
dkim_collect_input = 0;
@@ -730,9 +733,7 @@ next_cmd:
goto repeat_until_rset;
}
- receive_getc = bdat_getc;
- receive_getbuf = bdat_getbuf; /* r~getbuf is never actually used */
- receive_ungetc = bdat_ungetc;
+ bdat_push_receive_functions();
#ifndef DISABLE_DKIM
dkim_collect_input = dkim_save;
#endif
@@ -765,9 +766,7 @@ while (chunking_data_left)
if (!bdat_getbuf(&n)) break;
}
-receive_getc = lwr_receive_getc;
-receive_getbuf = lwr_receive_getbuf;
-receive_ungetc = lwr_receive_ungetc;
+bdat_pop_receive_functions();
if (chunking_state != CHUNKING_LAST)
{
@@ -777,7 +776,44 @@ if (chunking_state != CHUNKING_LAST)
}
+static inline void
+bdat_push_receive_functions(void)
+{
+/* push the current receive_* function on the "stack", and
+replace them by bdat_getc(), which in turn will use the lwr_receive_*
+functions to do the dirty work. */
+if (lwr_receive_getc == NULL)
+ {
+ lwr_receive_getc = receive_getc;
+ lwr_receive_getbuf = receive_getbuf;
+ lwr_receive_ungetc = receive_ungetc;
+ }
+else
+ {
+ DEBUG(D_receive) debug_printf("chunking double-push receive functions\n");
+ }
+
+receive_getc = bdat_getc;
+receive_getbuf = bdat_getbuf;
+receive_ungetc = bdat_ungetc;
+}
+
+static inline void
+bdat_pop_receive_functions(void)
+{
+if (lwr_receive_getc == NULL)
+ {
+ DEBUG(D_receive) debug_printf("chunking double-pop receive functions\n");
+ return;
+ }
+receive_getc = lwr_receive_getc;
+receive_getbuf = lwr_receive_getbuf;
+receive_ungetc = lwr_receive_ungetc;
+lwr_receive_getc = NULL;
+lwr_receive_getbuf = NULL;
+lwr_receive_ungetc = NULL;
+}
/*************************************************
* SMTP version of ungetc() *
@@ -795,6 +831,9 @@ Returns: the character
int
smtp_ungetc(int ch)
{
+if (smtp_inptr <= smtp_inbuffer)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "buffer underflow in smtp_ungetc");
+
*--smtp_inptr = ch;
return ch;
}
@@ -804,6 +843,7 @@ int
bdat_ungetc(int ch)
{
chunking_data_left++;
+bdat_push_receive_functions(); /* we're not done yet, calling push is safe, because it checks the state before pushing anything */
return lwr_receive_ungetc(ch);
}
@@ -1967,29 +2007,35 @@ static BOOL
extract_option(uschar **name, uschar **value)
{
uschar *n;
-uschar *v = smtp_cmd_data + Ustrlen(smtp_cmd_data) - 1;
-while (isspace(*v)) v--;
+uschar *v;
+if (Ustrlen(smtp_cmd_data) <= 0) return FALSE;
+v = smtp_cmd_data + Ustrlen(smtp_cmd_data) - 1;
+while (v > smtp_cmd_data && isspace(*v)) v--;
v[1] = 0;
+
while (v > smtp_cmd_data && *v != '=' && !isspace(*v))
{
/* Take care to not stop at a space embedded in a quoted local-part */
-
- if (*v == '"') do v--; while (*v != '"' && v > smtp_cmd_data+1);
+ if (*v == '"')
+ {
+ do v--; while (v > smtp_cmd_data && *v != '"');
+ if (v <= smtp_cmd_data) return FALSE;
+ }
v--;
}
+if (v <= smtp_cmd_data) return FALSE;
n = v;
if (*v == '=')
{
- while(isalpha(n[-1])) n--;
+ while (n > smtp_cmd_data && isalpha(n[-1])) n--;
/* RFC says SP, but TAB seen in wild and other major MTAs accept it */
- if (!isspace(n[-1])) return FALSE;
+ if (n <= smtp_cmd_data || !isspace(n[-1])) return FALSE;
n[-1] = 0;
}
else
{
n++;
- if (v == smtp_cmd_data) return FALSE;
}
*v++ = 0;
*name = n;
@@ -2205,9 +2251,11 @@ while (done <= 0)
/* Apply SMTP rewrite */
- raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
- rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE,
- US"", global_rewrite_rules) : smtp_cmd_data;
+ raw_sender = rewrite_existflags & rewrite_smtp
+ /* deconst ok as smtp_cmd_data was not const */
+ ? US rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL,
+ FALSE, US"", global_rewrite_rules)
+ : smtp_cmd_data;
/* Extract the address; the TRUE flag allows <> as valid */
@@ -2227,7 +2275,8 @@ while (done <= 0)
&& sender_address[0] != 0 && sender_address[0] != '@')
if (f.allow_unqualified_sender)
{
- sender_address = rewrite_address_qualify(sender_address, FALSE);
+ /* deconst ok as sender_address was not const */
+ sender_address = US rewrite_address_qualify(sender_address, FALSE);
DEBUG(D_receive) debug_printf("unqualified address %s accepted "
"and rewritten\n", raw_sender);
}
@@ -2266,7 +2315,8 @@ while (done <= 0)
recipient address */
recipient = rewrite_existflags & rewrite_smtp
- ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
+ /* deconst ok as smtp_cmd_data was not const */
+ ? US rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
global_rewrite_rules)
: smtp_cmd_data;
@@ -2285,7 +2335,8 @@ while (done <= 0)
{
DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
recipient);
- recipient = rewrite_address_qualify(recipient, TRUE);
+ /* deconst ok as recipient was not const */
+ recipient = US rewrite_address_qualify(recipient, TRUE);
}
/* The function moan_smtp_batch() does not return. */
else
@@ -2527,6 +2578,9 @@ receive_ungetc = smtp_ungetc;
receive_feof = smtp_feof;
receive_ferror = smtp_ferror;
receive_smtp_buffered = smtp_buffered;
+lwr_receive_getc = NULL;
+lwr_receive_getbuf = NULL;
+lwr_receive_ungetc = NULL;
smtp_inptr = smtp_inend = smtp_inbuffer;
smtp_had_eof = smtp_had_error = 0;
@@ -3812,7 +3866,8 @@ if (f.allow_unqualified_recipient || strcmpic(*recipient, US"postmaster") == 0)
DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
*recipient);
rd = Ustrlen(recipient) + 1;
- *recipient = rewrite_address_qualify(*recipient, TRUE);
+ /* deconst ok as *recipient was not const */
+ *recipient = US rewrite_address_qualify(*recipient, TRUE);
return rd;
}
smtp_printf("501 %s: recipient address must contain a domain\r\n", FALSE,
@@ -3953,6 +4008,14 @@ cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE;
#endif
+if (lwr_receive_getc != NULL)
+ {
+ /* This should have already happened, but if we've gotten confused,
+ force a reset here. */
+ DEBUG(D_receive) debug_printf("WARNING: smtp_setup_msg had to restore receive functions to lowers\n");
+ bdat_pop_receive_functions();
+ }
+
/* Set the local signal handler for SIGTERM - it tries to end off tidily */
had_command_sigterm = 0;
@@ -4855,7 +4918,8 @@ while (done <= 0)
TRUE flag allows "<>" as a sender address. */
raw_sender = rewrite_existflags & rewrite_smtp
- ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
+ /* deconst ok as smtp_cmd_data was not const */
+ ? US rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
global_rewrite_rules)
: smtp_cmd_data;
@@ -4917,7 +4981,8 @@ while (done <= 0)
if (f.allow_unqualified_sender)
{
sender_domain = Ustrlen(sender_address) + 1;
- sender_address = rewrite_address_qualify(sender_address, FALSE);
+ /* deconst ok as sender_address was not const */
+ sender_address = US rewrite_address_qualify(sender_address, FALSE);
DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
raw_sender);
}
@@ -4987,6 +5052,10 @@ while (done <= 0)
case RCPT_CMD:
HAD(SCH_RCPT);
+ /* We got really to many recipients. A check against configured
+ limits is done later */
+ if (rcpt_count < 0 || rcpt_count >= INT_MAX/2)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Too many recipients: %d", rcpt_count);
rcpt_count++;
was_rcpt = fl.rcpt_in_progress = TRUE;
@@ -5109,7 +5178,8 @@ while (done <= 0)
as a recipient address */
recipient = rewrite_existflags & rewrite_smtp
- ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
+ /* deconst ok as smtp_cmd_data was not const */
+ ? US rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
global_rewrite_rules)
: smtp_cmd_data;
@@ -5142,7 +5212,7 @@ while (done <= 0)
/* Check maximum allowed */
- if (rcpt_count > recipients_max && recipients_max > 0)
+ if (rcpt_count+1 < 0 || rcpt_count > recipients_max && recipients_max > 0)
{
if (recipients_max_reject)
{
@@ -5287,16 +5357,7 @@ while (done <= 0)
DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n",
(int)chunking_state, chunking_data_left);
- /* push the current receive_* function on the "stack", and
- replace them by bdat_getc(), which in turn will use the lwr_receive_*
- functions to do the dirty work. */
- lwr_receive_getc = receive_getc;
- lwr_receive_getbuf = receive_getbuf;
- lwr_receive_ungetc = receive_ungetc;
-
- receive_getc = bdat_getc;
- receive_ungetc = bdat_ungetc;
-
+ f.bdat_readers_wanted = TRUE; /* FIXME: redundant vs chunking_state? */
f.dot_ends = FALSE;
goto DATA_BDAT;
@@ -5305,6 +5366,7 @@ while (done <= 0)
case DATA_CMD:
HAD(SCH_DATA);
f.dot_ends = TRUE;
+ f.bdat_readers_wanted = FALSE;
DATA_BDAT: /* Common code for DATA and BDAT */
#ifndef DISABLE_PIPE_CONNECT
@@ -5333,7 +5395,10 @@ while (done <= 0)
: US"valid RCPT command must precede BDAT");
if (chunking_state > CHUNKING_OFFERED)
+ {
+ bdat_push_receive_functions();
bdat_flush_data();
+ }
break;
}
@@ -5342,6 +5407,12 @@ while (done <= 0)
sender_address = NULL; /* This will allow a new MAIL without RSET */
sender_address_unrewritten = NULL;
smtp_printf("554 Too many recipients\r\n", FALSE);
+
+ if (chunking_state > CHUNKING_OFFERED)
+ {
+ bdat_push_receive_functions();
+ bdat_flush_data();
+ }
break;
}
@@ -5379,6 +5450,9 @@ while (done <= 0)
"354 Enter message, ending with \".\" on a line by itself\r\n", FALSE);
}
+ if (f.bdat_readers_wanted)
+ bdat_push_receive_functions();
+
#ifdef TCP_QUICKACK
if (smtp_in) /* all ACKs needed to ramp window up for bulk data */
(void) setsockopt(fileno(smtp_in), IPPROTO_TCP, TCP_QUICKACK,
diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c
index 2a4497488..f103c2752 100644
--- a/src/src/smtp_out.c
+++ b/src/src/smtp_out.c
@@ -692,7 +692,7 @@ Arguments:
timelimit deadline for reading the lime, seconds past epoch
Returns: length of a line that has been put in the buffer
- -1 otherwise, with errno set
+ -1 otherwise, with errno set, and inblock->ptr adjusted
*/
static int
@@ -733,6 +733,7 @@ for (;;)
{
*p = 0; /* Leave malformed line for error message */
errno = ERRNO_SMTPFORMAT;
+ inblock->ptr = ptr;
return -1;
}
}
@@ -758,6 +759,7 @@ for (;;)
/* Get here if there has been some kind of recv() error; errno is set, but we
ensure that the result buffer is empty before returning. */
+inblock->ptr = inblock->ptrend = inblock->buffer;
*buffer = 0;
return -1;
}
diff --git a/src/src/spool_in.c b/src/src/spool_in.c
index f64c52c5a..0bebcea97 100644
--- a/src/src/spool_in.c
+++ b/src/src/spool_in.c
@@ -304,6 +304,35 @@ dsn_ret = 0;
dsn_envid = NULL;
}
+static void *
+fgets_big_buffer(FILE *fp)
+{
+int len = 0;
+
+big_buffer[0] = 0;
+if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) return NULL;
+
+while ((len = Ustrlen(big_buffer)) == big_buffer_size-1
+ && big_buffer[len-1] != '\n')
+ {
+ uschar *newbuffer;
+ int newsize;
+
+ if (big_buffer_size >= BIG_BUFFER_SIZE * 4) return NULL;
+ newsize = big_buffer_size * 2;
+ newbuffer = store_get_perm(newsize, FALSE);
+ memcpy(newbuffer, big_buffer, len);
+
+ big_buffer = newbuffer;
+ big_buffer_size = newsize;
+ if (Ufgets(big_buffer + len, big_buffer_size - len, fp) == NULL) return NULL;
+ }
+
+if (len <= 0 || big_buffer[len-1] != '\n') return NULL;
+return big_buffer;
+}
+
+
/*************************************************
* Read spool header file *
@@ -454,26 +483,13 @@ If the line starts with "--" the content of the variable is tainted. */
for (;;)
{
- int len;
BOOL tainted;
uschar * var;
const uschar * p;
- if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR;
+ if (fgets_big_buffer(fp) == NULL) goto SPOOL_READ_ERROR;
if (big_buffer[0] != '-') break;
- while ( (len = Ustrlen(big_buffer)) == big_buffer_size-1
- && big_buffer[len-1] != '\n'
- )
- { /* buffer not big enough for line; certs make this possible */
- uschar * buf;
- if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR;
- buf = store_get_perm(big_buffer_size *= 2, FALSE);
- memcpy(buf, big_buffer, --len);
- big_buffer = buf;
- if (Ufgets(big_buffer+len, big_buffer_size-len, fp) == NULL)
- goto SPOOL_READ_ERROR;
- }
- big_buffer[len-1] = 0;
+ big_buffer[Ustrlen(big_buffer)-1] = 0;
tainted = big_buffer[1] == '-';
var = big_buffer + (tainted ? 2 : 1);
@@ -774,7 +790,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++)
uschar *errors_to = NULL;
uschar *p;
- if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR;
+ if (fgets_big_buffer(fp) == NULL) goto SPOOL_READ_ERROR;
nn = Ustrlen(big_buffer);
if (nn < 2) goto SPOOL_FORMAT_ERROR;
diff --git a/src/src/spool_out.c b/src/src/spool_out.c
index bbc798fb4..8531112c0 100644
--- a/src/src/spool_out.c
+++ b/src/src/spool_out.c
@@ -105,6 +105,18 @@ return fd;
+static const uschar *
+zap_newlines(const uschar *s)
+{
+uschar *z, *p;
+
+if (Ustrchr(s, '\n') == NULL) return s;
+
+p = z = string_copy(s);
+while ((p = Ustrchr(p, '\n')) != NULL) *p++ = ' ';
+return z;
+}
+
static void
spool_var_write(FILE * fp, const uschar * name, const uschar * val)
{
@@ -223,7 +235,7 @@ if (body_zerocount > 0) fprintf(fp, "-body_zerocount %d\n", body_zerocount);
if (authenticated_id)
spool_var_write(fp, US"auth_id", authenticated_id);
if (authenticated_sender)
- spool_var_write(fp, US"auth_sender", authenticated_sender);
+ spool_var_write(fp, US"auth_sender", zap_newlines(authenticated_sender));
if (f.allow_unqualified_recipient) fprintf(fp, "-allow_unqualified_recipient\n");
if (f.allow_unqualified_sender) fprintf(fp, "-allow_unqualified_sender\n");
@@ -296,19 +308,20 @@ fprintf(fp, "%d\n", recipients_count);
for (int i = 0; i < recipients_count; i++)
{
recipient_item *r = recipients_list + i;
+ const uschar *address = zap_newlines(r->address);
/* DEBUG(D_deliver) debug_printf("DSN: Flags: 0x%x\n", r->dsn_flags); */
if (r->pno < 0 && !r->errors_to && r->dsn_flags == 0)
- fprintf(fp, "%s\n", r->address);
+ fprintf(fp, "%s\n", address);
else
{
- uschar * errors_to = r->errors_to ? r->errors_to : US"";
+ const uschar *errors_to = r->errors_to ? zap_newlines(r->errors_to) : CUS"";
/* for DSN SUPPORT extend exim 4 spool in a compatible way by
adding new values upfront and add flag 0x02 */
- uschar * orcpt = r->orcpt ? r->orcpt : US"";
+ const uschar *orcpt = r->orcpt ? zap_newlines(r->orcpt) : CUS"";
- fprintf(fp, "%s %s %d,%d %s %d,%d#3\n", r->address, orcpt, Ustrlen(orcpt),
+ fprintf(fp, "%s %s %d,%d %s %d,%d#3\n", address, orcpt, Ustrlen(orcpt),
r->dsn_flags, errors_to, Ustrlen(errors_to), r->pno);
}
diff --git a/src/src/std-crypto.c b/src/src/std-crypto.c
index e4df56006..b462908ea 100644
--- a/src/src/std-crypto.c
+++ b/src/src/std-crypto.c
@@ -350,12 +350,12 @@ static const char dh_ike_18_pem[] =
*/
static const char dh_ike_22_pem[] =
"-----BEGIN DH PARAMETERS-----\n"
-"MIIBCAKBgQCxC4+WoIDgHd6S3l6uXVTsUsmfvPsGo8aaap3KUtI7YWBz4oZ1oj0Y\n"
+"MIIBDAKBgQCxC4+WoIDgHd6S3l6uXVTsUsmfvPsGo8aaap3KUtI7YWBz4oZ1oj0Y\n"
"mDjvHi7mUsAT7LSuqQYRIySXXDzUm4O/rMvdfZDEvXCYSI6cIZpzck7/1vrlZEc4\n"
"+qMaT/VbzMChUa9fDci0vUW/N982XBpl5oz9p21NpwjfH7K8LkpDcQKBgQCk0cvV\n"
"w/00EmdlpELvuZkF+BBN0lisUH/WQGz/FCZtMSZv6h5cQVZLd35pD1UE8hMWAhe0\n"
"sBuIal6RVH+eJ0n01/vX07mpLuGQnQ0iY/gKdqaiTAh6CR9THb8KAWm2oorWYqTR\n"
-"jnOvoy13nVkY0IvIhY9Nzvl8KiSFXm7rIrOy5Q==\n"
+"jnOvoy13nVkY0IvIhY9Nzvl8KiSFXm7rIrOy5QICAKA=\n"
"-----END DH PARAMETERS-----\n";
/* RFC 5114 IKE_id=23
@@ -396,7 +396,7 @@ static const char dh_ike_22_pem[] =
*/
static const char dh_ike_23_pem[] =
"-----BEGIN DH PARAMETERS-----\n"
-"MIICCgKCAQEArRB+HpEjqdDWYPqnlVnFH6INZOVoO5/RtUsVl7YdCnXm+hQd+VpW\n"
+"MIICDgKCAQEArRB+HpEjqdDWYPqnlVnFH6INZOVoO5/RtUsVl7YdCnXm+hQd+VpW\n"
"26+aPEB7od8V6z1oijCcGA4d5rhaEnSgpm0/gVKtasISkDfJ7e/aTfjZHo/vVbc5\n"
"S3rVt9C2wSIHyfmNEe002/bGugssi7wnvmoA4KC5xJcIs7+KMXCRiDaBKGEwvImF\n"
"2xYC5xRBXZMwJ4Jzx94x79xzEPcSH9WgdBWYfZrcCkhtzfk6zEQyg4cxXXXhmMZB\n"
@@ -406,7 +406,8 @@ static const char dh_ike_23_pem[] =
"kAGo1mrXwXZpEBmZAkr00CcnWsE0i7inYtBSG8mK4kcVBCLqHtQJk51U2nRgzbX2\n"
"xrJQcXy+8YDrNBGOmNEZUppF1vg0Vm4wJeMWozDvu3eobwwasVsFGuPUKMj4rLcK\n"
"gTcVC47rEOGD7dGZY93Z4mPkdwWJ72qiHn9fL/OBtTnM40CdE81Wavu0jWwBkYHh\n"
-"vP6UswJp7f5y/ptqpL17Wg8ccc//TBnEGOH27AF5gbwIfypwZbOEuJDTGR8r+g==\n"
+"vP6UswJp7f5y/ptqpL17Wg8ccc//TBnEGOH27AF5gbwIfypwZbOEuJDTGR8r+gIC\n"
+"AOA=\n"
"-----END DH PARAMETERS-----\n";
/* RFC 5114 IKE_id=24
@@ -447,7 +448,7 @@ static const char dh_ike_23_pem[] =
*/
static const char dh_ike_24_pem[] =
"-----BEGIN DH PARAMETERS-----\n"
-"MIICCQKCAQEAh6jmHbS2Zjz/u9GcZRlZmYzu9ghmDdDyXSzu1ENeOwDgDfjx1hlX\n"
+"MIICDQKCAQEAh6jmHbS2Zjz/u9GcZRlZmYzu9ghmDdDyXSzu1ENeOwDgDfjx1hlX\n"
"1Pr330VhsqowFsPZETQJb6o79Cltgw6afCCeDGSXUXq9WoqdMGvPZ+2R+eZyW0dY\n"
"wCLgse9Cdb97bFv8EdRfkIi5QfVOseWbuLw5oL8SMH9cT9twxYGyP3a2Osrhyqa3\n"
"kC1SUmc1SIoO8TxtmlG/pKs62DR3llJNjvahZ7WkGCXZZ+FE5RQFZCUcysuD5rSG\n"
@@ -457,7 +458,8 @@ static const char dh_ike_24_pem[] =
"+MKMuxilWuMTQQAKZQGW+THHelfy3fRj5ensFEt3feYqqrioYorDdtKC1u04ZOZ5\n"
"gkKOvIMdFDSPby+Rk7UEWvJ2cWTh38lnwfs/LlWkvRv/6DucgNBSuYXRguoK2yo7\n"
"cxPT/hTISEseBSWIubfSu9LfAWGZ7NBuFVfNCRWzNTu7ZODsN3/QKDcN+StSx4kU\n"
-"KM3GfrYYS1I9HbJGwy9jB4SQ8A741kfRSNR5VFFeIyfP75jFgmZLTA9sxBZZ\n"
+"KM3GfrYYS1I9HbJGwy9jB4SQ8A741kfRSNR5VFFeIyfP75jFgmZLTA9sxBZZAgIB\n"
+"AA==\n"
"-----END DH PARAMETERS-----\n";
/* ------------------------------------------------------------------------- */
@@ -509,12 +511,12 @@ A.1. ffdhe2048
*/
static const char dh_ffdhe2048_pem[] =
"-----BEGIN DH PARAMETERS-----\n"
-"MIH+AoH4DfhUWKK7Spqv3FYgJz088di5xYPOLTaVqeE2QRRkM/vMk53OJJs++X0v\n"
-"42NjDHXY9oGyAq7EYXrT3x7V1f1lYSQz9R9fBm7QhWNlVT3tGvO1VxNef1fJNZhP\n"
-"DHDg5ot34qaJ2vPv6HId8VihNq3nNTCsyk9IOnl6vAqxgrMk+2HRCKlLssjj+7lq\n"
-"2rdg1/RoHU9Co945TfSuVu3nY3K7GQsHp8juCm1wngL84c334uzANATNKDQvYZFy\n"
-"/pzphYP/jk8SMu7ygYPD/jsbTG+tczu1/LwuwiAFxY7xg30Wg7LG80omwbLv+ohr\n"
-"QjhhKFyX//////////8CAQI=\n"
+"MIIBDAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
+"+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n"
+"87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\n"
+"YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n"
+"7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD\n"
+"ssbzSibBsu/6iGtCOGEoXJf//////////wIBAgICB/8=\n"
"-----END DH PARAMETERS-----\n";
/*
@@ -574,7 +576,7 @@ A.2. ffdhe3072
*/
static const char dh_ffdhe3072_pem[] =
"-----BEGIN DH PARAMETERS-----\n"
-"MIIBiAKCAYEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
+"MIIBjAKCAYEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
"+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n"
"87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\n"
"YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n"
@@ -582,7 +584,7 @@ static const char dh_ffdhe3072_pem[] =
"ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3\n"
"7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32\n"
"nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZsYu\n"
-"N///////////AgEC\n"
+"N///////////AgECAgIL/w==\n"
"-----END DH PARAMETERS-----\n";
/*
@@ -654,7 +656,7 @@ A.3. ffdhe4096
*/
static const char dh_ffdhe4096_pem[] =
"-----BEGIN DH PARAMETERS-----\n"
-"MIICCAKCAgEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
+"MIICDAKCAgEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
"+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n"
"87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\n"
"YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n"
@@ -664,7 +666,7 @@ static const char dh_ffdhe4096_pem[] =
"nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZp4e\n"
"8W5vUsMWTfT7eTDp5OWIV7asfV9C1p9tGHdjzx1VA0AEh/VbpX4xzHpxNciG77Qx\n"
"iu1qHgEtnmgyqQdgCpGBMMRtx3j5ca0AOAkpmaMzy4t6Gh25PXFAADwqTs6p+Y0K\n"
-"zAqCkc3OyX3Pjsm1Wn+IpGtNtahR9EGC4caKAH5eZV9q//////////8CAQI=\n"
+"zAqCkc3OyX3Pjsm1Wn+IpGtNtahR9EGC4caKAH5eZV9q//////////8CAQICAg//\n"
"-----END DH PARAMETERS-----\n";
/*
@@ -756,7 +758,7 @@ A.4. ffdhe6144
*/
static const char dh_ffdhe6144_pem[] =
"-----BEGIN DH PARAMETERS-----\n"
-"MIIDCAKCAwEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
+"MIIDDAKCAwEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
"+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n"
"87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\n"
"YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n"
@@ -772,7 +774,7 @@ static const char dh_ffdhe6144_pem[] =
"w1h+ONoAd9m0dj5OS5Syu8GUxmUed8r5ku6qwCMqKBv2s6c5wSJhFoIK6NtYR6Z8\n"
"vvnJCRtGLVOM1ysDdGrnf15iKSwxFWKoRlBdyC24VDOK5J9SNclbkReMzy3Vys70\n"
"A+ydGBDGJysEWztx+dxrgNY/3UqOmtseaWKmlSbUMWHBpB1XDXk42tSkDjKc0OQO\n"
-"Zf//////////AgEC\n"
+"Zf//////////AgECAgIX/w==\n"
"-----END DH PARAMETERS-----\n";
/*
@@ -886,7 +888,7 @@ A.5. ffdhe8192
*/
static const char dh_ffdhe8192_pem[] =
"-----BEGIN DH PARAMETERS-----\n"
-"MIIECAKCBAEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
+"MIIEDAKCBAEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n"
"+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n"
"87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\n"
"YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n"
@@ -907,7 +909,7 @@ static const char dh_ffdhe8192_pem[] =
"UVQfxoychrAiu3CZh2pGDnRRqKkxCXA/7hwhfmw4JuUsUappHg5CPPyZ6eMWUMEh\n"
"e2JIFs2tmpX51bgBlIjZwKCh/jB1pXfiMYP4HUo/L6RXHvyM4LqKT+i2hV3+crCm\n"
"bt7S+6v75Yow+vq+HF1xqH4vdB74wf6G/qa7/eUwZ38Nl9EdSfeoRD0IIuUGqfRh\n"
-"TgEeKpSDj/iM1oyLt8XGQkz//////////wIBAg==\n"
+"TgEeKpSDj/iM1oyLt8XGQkz//////////wIBAgICH/8=\n"
"-----END DH PARAMETERS-----\n";
/* ========================================================================= */
diff --git a/src/src/store.c b/src/src/store.c
index 22615ea08..2a32e9b5c 100644
--- a/src/src/store.c
+++ b/src/src/store.c
@@ -268,6 +268,17 @@ store_get_3(int size, BOOL tainted, const char *func, int linenumber)
{
int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool;
+/* Ensure we've been asked to allocate memory.
+A negative size is a sign of a security problem.
+A zero size might be also suspect, but our internal usage deliberately
+does this to return a current watermark value for a later release of
+allocated store. */
+
+if (size < 0 || size >= INT_MAX/2)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "bad memory allocation requested (%d bytes) at %s %d",
+ size, func, linenumber);
+
/* Round up the size to a multiple of the alignment. Although this looks a
messy statement, because "alignment" is a constant expression, the compiler can
do a reasonable job of optimizing, especially if the value of "alignment" is a
@@ -417,6 +428,11 @@ int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool;
int inc = newsize - oldsize;
int rounded_oldsize = oldsize;
+if (oldsize < 0 || newsize < oldsize || newsize >= INT_MAX/2)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "bad memory extension requested (%d -> %d bytes) at %s %d",
+ oldsize, newsize, func, linenumber);
+
/* Check that the block being extended was already of the required taint status;
refuse to extend if not. */
@@ -784,6 +800,11 @@ if (is_tainted(block) != tainted)
die_tainted(US"store_newblock", CUS func, linenumber);
#endif
+if (len < 0 || len > newsize)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "bad memory extension requested (%d -> %d bytes) at %s %d",
+ len, newsize, func, linenumber);
+
newtext = store_get(newsize, tainted);
memcpy(newtext, block, len);
if (release_ok) store_release_3(block, pool, func, linenumber);
@@ -814,6 +835,11 @@ internal_store_malloc(int size, const char *func, int line)
{
void * yield;
+if (size < 0 || size >= INT_MAX/2)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "bad memory allocation requested (%d bytes) at %s %d",
+ size, func, line);
+
size += sizeof(int); /* space to store the size, used under debug */
if (size < 16) size = 16;
diff --git a/src/src/string.c b/src/src/string.c
index cb9132420..64d14439e 100644
--- a/src/src/string.c
+++ b/src/src/string.c
@@ -1094,7 +1094,16 @@ existing length of the string. */
unsigned inc = oldsize < 4096 ? 127 : 1023;
+if (g->ptr < 0 || g->ptr > g->size || g->size >= INT_MAX/2)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "internal error in gstring_grow (ptr %d size %d)", g->ptr, g->size);
+
if (count <= 0) return;
+
+if (count >= INT_MAX/2 - g->ptr)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "internal error in gstring_grow (ptr %d count %d)", g->ptr, count);
+
g->size = (p + count + inc + 1) & ~inc; /* one for a NUL */
/* Try to extend an existing allocation. If the result of calling
@@ -1143,6 +1152,10 @@ string_catn(gstring * g, const uschar *s, int count)
int p;
BOOL srctaint = is_tainted(s);
+if (count < 0)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "internal error in string_catn (count %d)", count);
+
if (!g)
{
unsigned inc = count < 4096 ? 127 : 1023;
@@ -1152,8 +1165,12 @@ if (!g)
else if (srctaint && !is_tainted(g->s))
gstring_rebuffer(g);
+if (g->ptr < 0 || g->ptr > g->size)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "internal error in string_catn (ptr %d size %d)", g->ptr, g->size);
+
p = g->ptr;
-if (p + count >= g->size)
+if (count >= g->size - p)
gstring_grow(g, count);
/* Because we always specify the exact number of characters to copy, we can
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 13b0c232f..eb18d64d3 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -4053,16 +4053,12 @@ if (more || corked)
{
if (!len) buff = US &error; /* dummy just so that string_catn is ok */
-#ifndef DISABLE_PIPE_CONNECT
int save_pool = store_pool;
store_pool = POOL_PERM;
-#endif
corked = string_catn(corked, buff, len);
-#ifndef DISABLE_PIPE_CONNECT
store_pool = save_pool;
-#endif
if (more)
{
diff --git a/src/src/tls.c b/src/src/tls.c
index ddee95de2..e073eadbe 100644
--- a/src/src/tls.c
+++ b/src/src/tls.c
@@ -457,6 +457,9 @@ Returns: the character
int
tls_ungetc(int ch)
{
+if (ssl_xfer_buffer_lwm <= 0)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "buffer underflow in tls_ungetc");
+
ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch;
return ch;
}
diff --git a/src/src/transport.c b/src/src/transport.c
index d8fd85855..3d86919de 100644
--- a/src/src/transport.c
+++ b/src/src/transport.c
@@ -959,10 +959,10 @@ if (!(tctx->options & topt_no_headers))
if (tctx->options & topt_add_return_path)
{
- uschar buffer[ADDRESS_MAXLENGTH + 20];
- int n = sprintf(CS buffer, "Return-path: <%.*s>\n", ADDRESS_MAXLENGTH,
- return_path);
- if (!write_chunk(tctx, buffer, n)) goto bad;
+ int n;
+ uschar * s = string_sprintf("Return-path: <%.*s>\n%n",
+ EXIM_EMAILADDR_MAX, return_path, &n);
+ if (!write_chunk(tctx, s, n)) goto bad;
}
/* Add envelope-to: if requested */
@@ -1729,7 +1729,7 @@ while (1)
{
msgq[i].bKeep = TRUE;
- Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH),
+ Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH),
MESSAGE_ID_LENGTH);
msgq[i].message_id[MESSAGE_ID_LENGTH] = 0;
}
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index 02a55f198..264ebc094 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -2475,8 +2475,8 @@ goto SEND_QUIT;
int n = sizeof(sx->buffer);
uschar * rsp = sx->buffer;
- if (sx->esmtp_sent && (n = Ustrlen(sx->buffer)) < sizeof(sx->buffer)/2)
- { rsp = sx->buffer + n + 1; n = sizeof(sx->buffer) - n; }
+ if (sx->esmtp_sent && (n = Ustrlen(sx->buffer) + 1) < sizeof(sx->buffer)/2)
+ { rsp = sx->buffer + n; n = sizeof(sx->buffer) - n; }
if (smtp_write_command(sx, SCMD_FLUSH, "HELO %s\r\n", sx->helo_data) < 0)
goto SEND_FAILED;
diff --git a/src/src/tree.c b/src/src/tree.c
index d5a409651..e16a8643c 100644
--- a/src/src/tree.c
+++ b/src/src/tree.c
@@ -27,7 +27,7 @@ Returns: nothing
*/
void
-tree_add_nonrecipient(uschar *s)
+tree_add_nonrecipient(const uschar *s)
{
rmark rpoint = store_mark();
tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(s));
@@ -52,7 +52,7 @@ Returns: nothing
*/
void
-tree_add_duplicate(uschar *s, address_item *addr)
+tree_add_duplicate(const uschar *s, address_item *addr)
{
rmark rpoint = store_mark();
tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(s));
@@ -74,7 +74,7 @@ Returns: nothing
*/
void
-tree_add_unusable(host_item *h)
+tree_add_unusable(const host_item *h)
{
rmark rpoint = store_mark();
tree_node *node;
diff --git a/src/src/verify.c b/src/src/verify.c
index 6ddff47a5..6e07566a7 100644
--- a/src/src/verify.c
+++ b/src/src/verify.c
@@ -1685,7 +1685,8 @@ if (parse_find_at(address) == NULL)
*failure_ptr = US"qualify";
return FAIL;
}
- address = rewrite_address_qualify(address, options & vopt_is_recipient);
+ /* deconst ok as address was not const */
+ address = US rewrite_address_qualify(address, options & vopt_is_recipient);
}
DEBUG(D_verify)
@@ -1700,7 +1701,8 @@ may have been set by domains and local part tests during an ACL. */
if (global_rewrite_rules)
{
uschar *old = address;
- address = rewrite_address(address, options & vopt_is_recipient, FALSE,
+ /* deconst ok as address was not const */
+ address = US rewrite_address(address, options & vopt_is_recipient, FALSE,
global_rewrite_rules, rewrite_existflags);
if (address != old)
{
diff --git a/src/util/gen_pkcs3.c b/src/util/gen_pkcs3.c
index 6a467e07a..5c4e42993 100644
--- a/src/util/gen_pkcs3.c
+++ b/src/util/gen_pkcs3.c
@@ -54,7 +54,6 @@ void __attribute__((__noreturn__))
die_openssl_err(const char *msg)
{
char err_string[250];
- unsigned long e;
ERR_error_string_n(ERR_get_error(), err_string, sizeof(err_string));
die("%s: %s", msg, err_string);
@@ -71,9 +70,9 @@ bn_from_text(const char *text)
int rc;
len = strlen(text);
- spaceless = malloc(len);
+ spaceless = malloc(len + 1);
if (!spaceless)
- die("malloc(%zu) failed: %s", len, strerror(errno));
+ die("malloc(%zu) failed: %s", len + 1, strerror(errno));
for (p = spaceless, q = text, end = text + len;
q < end;
@@ -81,13 +80,15 @@ bn_from_text(const char *text)
if (!isspace(*q))
*p++ = *q;
}
+ len = p - spaceless;
+ *p++ = '\0';
b = NULL;
rc = BN_hex2bn(&b, spaceless);
- if (rc != p - spaceless)
+ if (rc != (int)len)
die("BN_hex2bn did not convert entire input; took %d of %zu bytes",
- rc, p - spaceless);
+ rc, len);
return b;
}