From b7e726f6ae4c6f19e7efc4e6b10ec35e5b01368c Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Tue, 16 Mar 2021 23:44:29 +0100 Subject: tidy (cherry picked from commit 7973b58af7db0fb8fddb54b366dcf43c7ce131ec) --- src/src/daemon.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/src/daemon.c b/src/src/daemon.c index ca3dd2cf1..fc6c011d4 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1052,7 +1052,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 @@ -2385,27 +2385,23 @@ for (;;) accept_retry_errno = errno; accept_retry_select_failed = select_failed; } - else - { - if (errno != accept_retry_errno || - select_failed != accept_retry_select_failed || - accept_retry_count >= 50) + else if ( errno != accept_retry_errno + || select_failed != accept_retry_select_failed + || accept_retry_count >= 50) { - log_write(0, LOG_MAIN | ((accept_retry_count >= 50)? LOG_PANIC : 0), + log_write(0, LOG_MAIN | (accept_retry_count >= 50? LOG_PANIC : 0), "%d %s() failure%s: %s", accept_retry_count, accept_retry_select_failed? "select" : "accept", - (accept_retry_count == 1)? "" : "s", + accept_retry_count == 1 ? "" : "s", strerror(accept_retry_errno)); log_close_all(); accept_retry_count = 0; accept_retry_errno = errno; accept_retry_select_failed = select_failed; } - } accept_retry_count++; } - else { if (accept_retry_count > 0) -- cgit v1.2.1 From 0439d2e0566d64c84feaf1434e0e4a3fd8ce29b3 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Mon, 29 Mar 2021 18:17:55 +0200 Subject: testsuite: tidy logs/4520 and confs/4520 This fixed 4520 failure en-passant, but I'm sure it's a timing issue here (the order of the mainlog output lines didn't exactly match the logs/4520) (cherry picked from commit 95306ca61531d9d79c5dac808a5a571158acd29c) --- test/confs/4520 | 4 ++-- test/log/4520 | 38 +++++++++++++++++++------------------- test/log/4523 | 4 ++-- test/log/4524 | 4 ++-- test/log/4541 | 8 ++++---- test/log/4545 | 8 ++++---- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/test/confs/4520 b/test/confs/4520 index 1f9d75b80..d2f2f7022 100644 --- a/test/confs/4520 +++ b/test/confs/4520 @@ -10,9 +10,9 @@ primary_hostname = myhost.test.ex # ----- Main settings ----- -acl_smtp_rcpt = accept logwrite = rcpt acl: macro: _DKIM_SIGN_HEADERS +acl_smtp_rcpt = accept logwrite = rcpt_acl: macro: _DKIM_SIGN_HEADERS acl_smtp_dkim = accept logwrite = dkim_acl: signer: $dkim_cur_signer bits: $dkim_key_length h=$dkim_headernames -acl_smtp_data = accept logwrite = data acl: dkim status $dkim_verify_status +acl_smtp_data = accept logwrite = data_acl: dkim status $dkim_verify_status dkim_verify_signers = $dkim_signers dkim_verify_min_keysizes = rsa=512 ed25519=250 diff --git a/test/log/4520 b/test/log/4520 index 4ed718225..d1797e68c 100644 --- a/test/log/4520 +++ b/test/log/4520 @@ -36,67 +36,67 @@ ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmaZ-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From 1999-03-02 09:44:33 10HmaZ-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] -1999-03-02 09:44:33 10HmaZ-0005vi-00 data acl: dkim status pass +1999-03-02 09:44:33 10HmaZ-0005vi-00 data_acl: dkim status pass 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaY-0005vi-00@myhost.test.ex for a@test.ex 1999-03-02 09:44:33 10HmaZ-0005vi-00 => a R=server_store T=file 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmbB-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From:From 1999-03-02 09:44:33 10HmbB-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 t=T x=T+10 [verification succeeded] -1999-03-02 09:44:33 10HmbB-0005vi-00 data acl: dkim status pass +1999-03-02 09:44:33 10HmbB-0005vi-00 data_acl: dkim status pass 1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmbA-0005vi-00@myhost.test.ex for b@test.ex 1999-03-02 09:44:33 10HmbB-0005vi-00 => b R=server_store T=file 1999-03-02 09:44:33 10HmbB-0005vi-00 Completed -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmbD-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From 1999-03-02 09:44:33 10HmbD-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] -1999-03-02 09:44:33 10HmbD-0005vi-00 data acl: dkim status pass +1999-03-02 09:44:33 10HmbD-0005vi-00 data_acl: dkim status pass 1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmbC-0005vi-00@myhost.test.ex for b10@test.ex 1999-03-02 09:44:33 10HmbD-0005vi-00 => b10 R=server_store T=file 1999-03-02 09:44:33 10HmbD-0005vi-00 Completed -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmbF-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=X-mine:X-mine:From 1999-03-02 09:44:33 10HmbF-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] -1999-03-02 09:44:33 10HmbF-0005vi-00 data acl: dkim status pass +1999-03-02 09:44:33 10HmbF-0005vi-00 data_acl: dkim status pass 1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmbE-0005vi-00@myhost.test.ex for b12@test.ex 1999-03-02 09:44:33 10HmbF-0005vi-00 => b12 R=server_store T=file 1999-03-02 09:44:33 10HmbF-0005vi-00 Completed -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmbH-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=X-Mine 1999-03-02 09:44:33 10HmbH-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] -1999-03-02 09:44:33 10HmbH-0005vi-00 data acl: dkim status pass +1999-03-02 09:44:33 10HmbH-0005vi-00 data_acl: dkim status pass 1999-03-02 09:44:33 10HmbH-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmbG-0005vi-00@myhost.test.ex for b20@test.ex 1999-03-02 09:44:33 10HmbH-0005vi-00 => b20 R=server_store T=file 1999-03-02 09:44:33 10HmbH-0005vi-00 Completed -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmbJ-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=X-mine:X-mine:X-Mine 1999-03-02 09:44:33 10HmbJ-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] -1999-03-02 09:44:33 10HmbJ-0005vi-00 data acl: dkim status pass +1999-03-02 09:44:33 10HmbJ-0005vi-00 data_acl: dkim status pass 1999-03-02 09:44:33 10HmbJ-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmbI-0005vi-00@myhost.test.ex for b22@test.ex 1999-03-02 09:44:33 10HmbJ-0005vi-00 => b22 R=server_store T=file 1999-03-02 09:44:33 10HmbJ-0005vi-00 Completed -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmbL-0005vi-00 DKIM: d=test.ex s=sel_bad [failed key import] 1999-03-02 09:44:33 10HmbL-0005vi-00 dkim_acl: signer: test.ex bits: 0 h=From 1999-03-02 09:44:33 10HmbL-0005vi-00 DKIM: d=test.ex s=sel_bad c=relaxed/relaxed a=rsa-sha256 b=1024 [invalid - syntax error in public key record] -1999-03-02 09:44:33 10HmbL-0005vi-00 data acl: dkim status invalid +1999-03-02 09:44:33 10HmbL-0005vi-00 data_acl: dkim status invalid 1999-03-02 09:44:33 10HmbL-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmbK-0005vi-00@myhost.test.ex for d@test.ex 1999-03-02 09:44:33 10HmbL-0005vi-00 => d R=server_store T=file 1999-03-02 09:44:33 10HmbL-0005vi-00 Completed -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 SMTP connection from the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] lost while reading message data (header) -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive -1999-03-02 09:44:33 10HmbN-0005vi-00 data acl: dkim status +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 10HmbN-0005vi-00 data_acl: dkim status 1999-03-02 09:44:33 10HmbN-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmbM-0005vi-00@myhost.test.ex for e@test.ex 1999-03-02 09:44:33 10HmbN-0005vi-00 => e R=server_store T=file 1999-03-02 09:44:33 10HmbN-0005vi-00 Completed -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmbP-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From 1999-03-02 09:44:33 10HmbP-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] -1999-03-02 09:44:33 10HmbP-0005vi-00 data acl: dkim status pass +1999-03-02 09:44:33 10HmbP-0005vi-00 data_acl: dkim status pass 1999-03-02 09:44:33 10HmbP-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmbO-0005vi-00@myhost.test.ex for f@test.ex 1999-03-02 09:44:33 10HmbP-0005vi-00 => f R=server_store T=file 1999-03-02 09:44:33 10HmbP-0005vi-00 Completed diff --git a/test/log/4523 b/test/log/4523 index 0acaa08c9..cd1ba4fb9 100644 --- a/test/log/4523 +++ b/test/log/4523 @@ -4,10 +4,10 @@ ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmaY-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From:From 1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha512 b=1024 [verification succeeded] -1999-03-02 09:44:33 10HmaY-0005vi-00 data acl: dkim status pass +1999-03-02 09:44:33 10HmaY-0005vi-00 data_acl: dkim status pass 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex for a@test.ex 1999-03-02 09:44:33 10HmaY-0005vi-00 => a R=server_store T=file 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed diff --git a/test/log/4524 b/test/log/4524 index 062884fa8..398c4b8e1 100644 --- a/test/log/4524 +++ b/test/log/4524 @@ -4,12 +4,12 @@ ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmaY-0005vi-00 dkim_acl: signer: test.ex bits: 512 h=From:To:Subject 1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=ses c=relaxed/relaxed a=rsa-sha256 b=512 [verification succeeded] 1999-03-02 09:44:33 10HmaY-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From:To:Subject 1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] -1999-03-02 09:44:33 10HmaY-0005vi-00 data acl: dkim status pass:pass +1999-03-02 09:44:33 10HmaY-0005vi-00 data_acl: dkim status pass:pass 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex for c@test.ex 1999-03-02 09:44:33 10HmaY-0005vi-00 => c R=server_store T=file 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed diff --git a/test/log/4541 b/test/log/4541 index 40d9fd8e3..78e3b85b2 100644 --- a/test/log/4541 +++ b/test/log/4541 @@ -7,22 +7,22 @@ ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmaY-0005vi-00 dkim_acl: signer: test.ex bits: 253 h=From 1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=sed c=relaxed/relaxed a=ed25519-sha256 b=512 [verification succeeded] 1999-03-02 09:44:33 10HmaY-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From 1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] -1999-03-02 09:44:33 10HmaY-0005vi-00 data acl: dkim status pass:pass +1999-03-02 09:44:33 10HmaY-0005vi-00 data_acl: dkim status pass:pass 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex for a@test.ex 1999-03-02 09:44:33 10HmaY-0005vi-00 => a R=server_store T=file 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmbA-0005vi-00 dkim_acl: signer: test.ex bits: 253 h=From 1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=sed c=relaxed/relaxed a=ed25519-sha256 b=512 [verification succeeded] 1999-03-02 09:44:33 10HmbA-0005vi-00 dkim_acl: signer: test.ex bits: 0 h=From 1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [not verified] -1999-03-02 09:44:33 10HmbA-0005vi-00 data acl: dkim status pass:none +1999-03-02 09:44:33 10HmbA-0005vi-00 data_acl: dkim status pass:none 1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaZ-0005vi-00@myhost.test.ex for b@test.ex 1999-03-02 09:44:33 10HmbA-0005vi-00 => b R=server_store T=file 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed diff --git a/test/log/4545 b/test/log/4545 index 000c10916..bf44330f9 100644 --- a/test/log/4545 +++ b/test/log/4545 @@ -7,19 +7,19 @@ ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmaY-0005vi-00 dkim_acl: signer: test.ex bits: 253 h=From:To:Subject 1999-03-02 09:44:33 10HmaY-0005vi-00 DKIM: d=test.ex s=sed c=relaxed/relaxed a=ed25519-sha256 b=512 [verification succeeded] -1999-03-02 09:44:33 10HmaY-0005vi-00 data acl: dkim status pass +1999-03-02 09:44:33 10HmaY-0005vi-00 data_acl: dkim status pass 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex for a@test.ex 1999-03-02 09:44:33 10HmaY-0005vi-00 => a R=server_store T=file 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed -1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive +1999-03-02 09:44:33 rcpt_acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmbA-0005vi-00 dkim_acl: signer: test.ex bits: 253 h=From 1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=sed c=relaxed/relaxed a=ed25519-sha256 b=512 [verification succeeded] 1999-03-02 09:44:33 10HmbA-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From 1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] -1999-03-02 09:44:33 10HmbA-0005vi-00 data acl: dkim status pass:pass +1999-03-02 09:44:33 10HmbA-0005vi-00 data_acl: dkim status pass:pass 1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaZ-0005vi-00@myhost.test.ex for b@test.ex 1999-03-02 09:44:33 10HmbA-0005vi-00 => b R=server_store T=file 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed -- cgit v1.2.1 From 6b3d553c733475a1033c8b7a241e6506d7ed73b1 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Thu, 18 Mar 2021 07:59:21 +0100 Subject: Enforce pid_file_path start at "/" (cherry picked from commit 60f2a8e797d9ebaea1e3eac4ad28ff64e11bab40) --- doc/doc-txt/ChangeLog | 2 ++ src/src/daemon.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 0792062ba..6f3b44136 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -163,6 +163,8 @@ JH/44 Bug 2701: Fix list-expansion of dns_ipv4_lookup. Previously, it did dnssec_require_domains, dnssec_request_domains, srv_fail_domains, mx_fail_domains. +HS/01 Enforce absolute PID file path name. + Exim version 4.94 ----------------- diff --git a/src/src/daemon.c b/src/src/daemon.c index fc6c011d4..e0c6bc908 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -942,6 +942,9 @@ 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); } -- cgit v1.2.1 From f9c8211fb0ad0dd362f471978a5e0abc5dfa71b4 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Thu, 18 Mar 2021 07:56:59 +0100 Subject: Handle SIGINT as we do with SIGTERM (cherry picked from commit cdc5c672e1c309294626cd5ed90acdccb05baaa1) --- doc/doc-txt/ChangeLog | 2 ++ src/src/daemon.c | 9 +++++++++ test/stderr/0433 | 6 ++++++ test/stderr/0438 | 3 +++ test/stderr/0609 | 1 + test/stderr/1007 | 8 ++++++++ test/stderr/2201 | 1 + 7 files changed, 30 insertions(+) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 6f3b44136..bad2933cb 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -165,6 +165,8 @@ JH/44 Bug 2701: Fix list-expansion of dns_ipv4_lookup. Previously, it did HS/01 Enforce absolute PID file path name. +HS/02 Handle SIGINT as we handle SIGTERM: terminate the Exim process. + Exim version 4.94 ----------------- diff --git a/src/src/daemon.c b/src/src/daemon.c index e0c6bc908..7bb89640e 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -474,6 +474,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 @@ -700,6 +701,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) { @@ -989,6 +991,11 @@ daemon_die(void) { int pid; +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 + if (daemon_notifier_fd >= 0) { close(daemon_notifier_fd); @@ -1885,6 +1892,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. */ @@ -2169,6 +2177,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(). */ diff --git a/test/stderr/0433 b/test/stderr/0433 index 627968150..660043761 100644 --- a/test/stderr/0433 +++ b/test/stderr/0433 @@ -19,6 +19,7 @@ set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX +SIGTERM/SIGINT seen search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -41,6 +42,7 @@ set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX +SIGTERM/SIGINT seen search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -64,6 +66,7 @@ set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on [127.0 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; 127.0.0.1.1228 ; 0.0.0.0 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX +SIGTERM/SIGINT seen search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -87,6 +90,7 @@ set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; 0.0.0.0; 127.0.0.1.1228 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX +SIGTERM/SIGINT seen search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -111,6 +115,7 @@ set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on [127.0 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; 127.0.0.1.1228 ; 0.0.0.0 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX +SIGTERM/SIGINT seen search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -135,5 +140,6 @@ set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX +SIGTERM/SIGINT seen search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> diff --git a/test/stderr/0438 b/test/stderr/0438 index 4dc97dd84..040f4f959 100644 --- a/test/stderr/0438 +++ b/test/stderr/0438 @@ -20,6 +20,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> @@ -43,6 +44,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.anotherpid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> @@ -66,6 +68,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/mypidfile -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> diff --git a/test/stderr/0609 b/test/stderr/0609 index 54e45a7a6..5d6f97fd8 100644 --- a/test/stderr/0609 +++ b/test/stderr/0609 @@ -50,4 +50,5 @@ ppppp LOG: lost_incoming_connection MAIN ppppp unexpected disconnection while reading SMTP command from [127.0.0.1] D=qqs ppppp >>>>>>>>>>>>>>>> Exim pid=pppp (daemon-accept) terminating with rc=1 >>>>>>>>>>>>>>>> ppppp 1 SMTP accept process running +ppppp SIGTERM/SIGINT seen ppppp >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> diff --git a/test/stderr/1007 b/test/stderr/1007 index 098c83845..9ec794af5 100644 --- a/test/stderr/1007 +++ b/test/stderr/1007 @@ -19,6 +19,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 (IPv6 and IPv4) daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> @@ -43,6 +44,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 (IPv6 and IPv4) port 1226 (IPv6 and IPv4) daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> @@ -68,6 +70,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 (IPv6 and IPv4) port 1226 (IPv6 and IPv4) [127.0.0.1]:1228 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; ::0 ; 127.0.0.1.1228 ; 0.0.0.0 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> @@ -93,6 +96,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 (IPv6 and IPv4) port 1226 (IPv6 and IPv4) [127.0.0.1]:1228 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; 0.0.0.0; 127.0.0.1.1228 ; ::0 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> @@ -115,6 +119,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on [ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6]:{1225,1226} daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> @@ -140,6 +145,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1227 (IPv6 and IPv4) [127.0.0.1]:1228 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; ::0 ; 127.0.0.1.1228 ; 0.0.0.0 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> @@ -166,6 +172,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on [127.0.0.1]:{1227,1225} daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; ::0 ; 127.0.0.1.1228 ; 0.0.0.0 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> @@ -191,6 +198,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 (IPv6 and IPv4) port 1226 (IPv4) daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... +SIGTERM/SIGINT seen pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> diff --git a/test/stderr/2201 b/test/stderr/2201 index feee638b3..babea34a3 100644 --- a/test/stderr/2201 +++ b/test/stderr/2201 @@ -230,4 +230,5 @@ ppppp child ppppp ended: status=0x0 ppppp normal exit, 0 ppppp 0 SMTP accept processes now running ppppp Listening... +ppppp SIGTERM/SIGINT seen ppppp >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> -- cgit v1.2.1 From 346f96bad326893f9c1fa772a5b8ac35b1f8f7bd Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 22:14:45 -0400 Subject: Default config: reject on too many bad RCPT An example exploit failed against my system, because I had this sanity guard in place; it's not a real security fix since a careful attacker could find enough valid recipients to hit that problem, but it highlights that this is a useful enough pattern that we should encourage its wider use. (cherry picked from commit 2a636a39fff29b7c3da1798767a510dfed982a62) --- doc/doc-txt/ChangeLog | 2 ++ src/src/configure.default | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index bad2933cb..3dbae7f76 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -167,6 +167,8 @@ HS/01 Enforce absolute PID file path name. HS/02 Handle SIGINT as we handle SIGTERM: terminate the Exim process. +PP/01 Add a too-many-bad-recipients guard to the default config's RCPT ACL. + Exim version 4.94 ----------------- diff --git a/src/src/configure.default b/src/src/configure.default index 3423ee0af..d94c148c1 100644 --- a/src/src/configure.default +++ b/src/src/configure.default @@ -449,6 +449,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 -- cgit v1.2.1 From 48d8c54ecf9493c709d4305850877b6062f285a7 Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Thu, 24 Sep 2020 23:03:14 +0100 Subject: gen_pkcs3: Terminate string before calling BH_hex2bn() Signed-off-by: Phil Pennock (cherry picked from commit 1cf66e5872d517b620c308af634e4e26e3547f06) --- src/util/gen_pkcs3.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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; } -- cgit v1.2.1 From 0aafa26a5d3d528e79476c91537c28936154fe04 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Fri, 18 Sep 2020 10:25:42 -0400 Subject: Re-ran the conversion of all DH parameters I get different results now to those I got before. Now, using gen_pkcs3 linked against OpenSSL 1.1.1f-1ubuntu2 on Focal Fossa, I get the results below. The ffdhe2048 value now matches that at . I ran the same code yesterday for just the ffdhe2048 item and got code which seemed to me then to match what was already in the C file. Something hinky is going on, perhaps with my sanity. (the commit IDs changee because of heavy rebasing (heiko)) (cherry picked from commit 76ed8115182e2daaadb437ec9655df8000796ec5) --- doc/doc-txt/ChangeLog | 5 +++++ src/src/std-crypto.c | 42 ++++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 3dbae7f76..a27b63f6f 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -169,6 +169,11 @@ HS/02 Handle SIGINT as we handle SIGTERM: terminate the Exim process. PP/01 Add a too-many-bad-recipients guard to the default config's RCPT ACL. +PP/02 Bug 2643: Correct TLS DH constants. + A missing NUL termination in our code-generation tool had led to some + incorrect Diffie-Hellman constants in the Exim source. + Reported by kylon94, code-gen tool fix by Simon Arlott. + Exim version 4.94 ----------------- diff --git a/src/src/std-crypto.c b/src/src/std-crypto.c index a045f6cc6..db94f20f8 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"; /* ========================================================================= */ -- cgit v1.2.1 From a8bd24b96c2027fd839f95a9e6b3282453ae288e Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 18:11:35 -0400 Subject: SECURITY: length limits on many cmdline options We'll also now abort upon, rather than silently truncate, a driver name (router, transport, ACL, etc) encountered in the config which is longer than the 64-char limit. (cherry picked from commit ff8bef9ae2370db4a7873fe2ce573a607fe6999f) --- doc/doc-docbook/spec.xfpt | 19 ++++++++++ doc/doc-txt/ChangeLog | 6 +++ src/src/acl.c | 4 +- src/src/exim.c | 93 +++++++++++++++++++++++++++++++---------------- src/src/exim.h | 45 +++++++++++++++++++++++ src/src/macro_predef.c | 2 +- src/src/macros.h | 6 --- src/src/malware.c | 2 +- src/src/parse.c | 8 ++-- src/src/readconf.c | 30 ++++++++++----- src/src/transport.c | 9 +++-- 11 files changed, 165 insertions(+), 59 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index cf4953786..446871125 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -51,6 +51,8 @@ .set ACL "access control lists (ACLs)" .set I "    " +.set drivernamemax "64" + .macro copyyear 2020 .endmacro @@ -18590,6 +18592,11 @@ which the preconditions are tested. The order of expansion of the options that provide data for a transport is: &%errors_to%&, &%headers_add%&, &%headers_remove%&, &%transport%&. +.new +The name of a router is limited to be &drivernamemax; ASCII characters long; +prior to Exim 4.95 names would be silently truncated at this length, but now +it is enforced. +.wen .option address_data routers string&!! unset @@ -22132,6 +22139,12 @@ and &$original_domain$& is never set. .scindex IIDgenoptra1 "generic options" "transport" .scindex IIDgenoptra2 "options" "generic; for transports" .scindex IIDgenoptra3 "transport" "generic options for" +.new +The name of a transport is limited to be &drivernamemax; ASCII characters long; +prior to Exim 4.95 names would be silently truncated at this length, but now +it is enforced. +.wen + The following generic options apply to all transports: @@ -26904,6 +26917,12 @@ permitted to use it as a relay. SMTP authentication is not of relevance to the transfer of mail between servers that have no managerial connection with each other. +.new +The name of an authenticator is limited to be &drivernamemax; ASCII characters long; +prior to Exim 4.95 names would be silently truncated at this length, but now +it is enforced. +.wen + .cindex "AUTH" "description of" .cindex "ESMTP extensions" AUTH Very briefly, the way SMTP authentication works is as follows: diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index a27b63f6f..7dbccb788 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -174,6 +174,12 @@ PP/02 Bug 2643: Correct TLS DH constants. incorrect Diffie-Hellman constants in the Exim source. Reported by kylon94, code-gen tool fix by Simon Arlott. +PP/03 Fix Linux security issue CVE-2020-SLCWD and guard against PATH_MAX + better. Reported by Qualys. + +PP/04 Impose security length checks on various command-line options. + Fixes CVE-2020-SPRSS reported by Qualys. + Exim version 4.94 ----------------- diff --git a/src/src/acl.c b/src/src/acl.c index 90e1ce81d..9fe9e5145 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -480,7 +480,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 @@ -738,7 +738,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/exim.c b/src/src/exim.c index 630ac4038..1c03aa879 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -761,6 +761,25 @@ vfprintf(stderr, fmt, ap); exit(EXIT_FAILURE); } +/* fail if a length is too long */ +static 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", + len, maxlen, description) +exit(EXIT_FAILURE); +} + +/* only pass through the string item back to the caller if it's short enough */ +static 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 @@ -2132,10 +2151,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; @@ -2145,7 +2164,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; @@ -2602,7 +2621,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; @@ -2628,6 +2647,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 @@ -2724,9 +2744,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; @@ -2771,13 +2791,13 @@ 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; /* -MCG: set the queue name, to a non-default value */ - case 'G': if (++i < argc) queue_name = string_copy_taint(argv[i], TRUE); + case 'G': if (++i < argc) queue_name = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCG"), TRUE); else badarg = TRUE; break; @@ -2812,7 +2832,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; @@ -2824,13 +2844,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*/ @@ -2857,6 +2877,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 @@ -2892,7 +2913,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) { @@ -3105,27 +3126,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 */ @@ -3145,19 +3166,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 */ @@ -3207,10 +3228,11 @@ on the second character (the one after '-'), to save some effort. */ break; /* -oX : 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; /* Unknown -o argument */ @@ -3251,9 +3273,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"), TRUE); else { + (void) exim_str_fail_toolong(argrest, (EXIM_DRIVERNAME_MAX+1+EXIM_HOSTNAME_MAX), "-p:"); received_protocol = string_copyn_taint(argrest, hn - argrest, TRUE); sender_host_name = string_copy_taint(hn + 1, TRUE); } @@ -3309,6 +3332,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"); queue_name = string_copyn(argrest, i); argrest += i; if (*argrest == '/') argrest++; @@ -3362,15 +3386,18 @@ on the second character (the one after '-'), to save some effort. */ /* -R: Set string to match in addresses for forced queue run to pick out particular messages. */ + /* Avoid attacks from people providing very long strings, and do so before + we make copies. */ + const char *tainted_selectstr; if (*argrest) - deliver_selectstring = string_copy_taint(argrest, TRUE); + tainted_selectstr = argrest; else if (i+1 < argc) - deliver_selectstring = string_copy_taint(argv[++i], TRUE); + 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) */ @@ -3401,12 +3428,14 @@ on the second character (the one after '-'), to save some effort. */ /* -S: Set string to match in addresses for forced queue run to pick out particular messages. */ + const char *tainted_selectstr; if (*argrest) - deliver_selectstring_sender = string_copy_taint(argrest, TRUE); + tainted_selectstr = argrest; else if (i+1 < argc) - deliver_selectstring_sender = string_copy_taint(argv[++i], TRUE); + 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. @@ -3488,10 +3517,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; diff --git a/src/src/exim.h b/src/src/exim.h index 2cc2621c4..17c5cdc87 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -128,6 +128,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 #include #include diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index 2b3269d90..1c93a6e03 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 f78ae2e3d..cebbf4085 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -179,12 +179,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 7ed4d358d..ef85d3dc1 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/parse.c b/src/src/parse.c index 3ea758ac9..39f5aaec1 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -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; diff --git a/src/src/readconf.c b/src/src/readconf.c index 948fa2403..c052cda5f 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -416,7 +416,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); @@ -433,7 +433,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++) { @@ -678,7 +678,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; @@ -1172,15 +1172,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; } @@ -1347,7 +1357,7 @@ static BOOL * get_set_flag(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, @@ -1620,8 +1630,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. */ @@ -2080,7 +2090,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) @@ -2427,7 +2437,7 @@ void *value; uid_t *uidlist; gid_t *gidlist; uschar *s; -uschar name2[64]; +uschar name2[EXIM_DRIVERNAME_MAX]; if (!ol) { @@ -3698,7 +3708,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 @@ -4226,7 +4236,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/transport.c b/src/src/transport.c index 609fd128c..8ee89ed2f 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -958,9 +958,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); + uschar buffer[EXIM_EMAILADDR_MAX + 20]; + int n = string_format(CS buffer, sizeof(buffer), + "Return-path: <%.*s>\n", + EXIM_EMAILADDR_MAX, return_path); if (!write_chunk(tctx, buffer, n)) goto bad; } @@ -1730,7 +1731,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; } -- cgit v1.2.1 From 7a7136ba7f5c2db33c7e320ffd4675335c4557e5 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 18:40:37 -0400 Subject: SECURITY: pick up more argv length checks (cherry picked from commit f28a6a502c7973d8844d11d4b0990d4b0359fb3f) --- src/src/exim.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/src/exim.c b/src/src/exim.c index 1c03aa879..34159da93 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1750,6 +1750,9 @@ f.running_in_test_harness = if (f.running_in_test_harness) debug_store = TRUE; +/* Protect against abusive argv[0] */ +exim_len_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 @@ -4498,7 +4501,7 @@ if (test_retry_arg >= 0) 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 @@ -4514,13 +4517,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]; + 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) @@ -4622,11 +4625,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); } @@ -4671,6 +4674,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) @@ -4876,7 +4880,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); } @@ -4970,7 +4974,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; @@ -4988,7 +4992,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(); @@ -5005,10 +5009,10 @@ 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[SPOOL_NAME_LENGTH]; /* Not big_buffer; used in spool_read_header() */ if (!f.admin_user) exim_fail("exim: permission denied\n"); - message_id = argv[msg_action_arg]; + message_id = exim_str_fail_toolong(argv[msg_action_arg], MESSAGE_ID_LENGTH, "message-id"); (void)string_format(spoolname, sizeof(spoolname), "%s-H", message_id); if ((deliver_datafile = spool_open_datafile(message_id)) < 0) printf ("Failed to load message datafile %s\n", message_id); @@ -5048,7 +5052,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 */ @@ -5491,7 +5495,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 */ -- cgit v1.2.1 From 6d2cfb575c95c1b81597d6b9eb2904cd695d7e4a Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 11:47:58 -0400 Subject: SECURITY: fix Qualys CVE-2020-SLCWD (cherry picked from commit bf5f9d56fadf9be8d947f141d31f7e0e8fa63762) --- doc/doc-txt/ChangeLog | 8 ++++---- src/src/exim.c | 6 ++++++ src/src/macros.h | 14 +++++++++----- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 7dbccb788..7ed412ea9 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -174,12 +174,12 @@ PP/02 Bug 2643: Correct TLS DH constants. incorrect Diffie-Hellman constants in the Exim source. Reported by kylon94, code-gen tool fix by Simon Arlott. -PP/03 Fix Linux security issue CVE-2020-SLCWD and guard against PATH_MAX - better. Reported by Qualys. - -PP/04 Impose security length checks on various command-line options. +PP/03 Impose security length checks on various command-line options. Fixes CVE-2020-SPRSS reported by Qualys. +PP/04 Fix Linux security issue CVE-2020-SLCWD and guard against PATH_MAX + better. Reported by Qualys. + Exim version 4.94 ----------------- diff --git a/src/src/exim.c b/src/src/exim.c index 34159da93..067c47f80 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -3828,7 +3828,13 @@ 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. */ +errno = 0; 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 - diff --git a/src/src/macros.h b/src/src/macros.h index cebbf4085..f83ba1933 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -153,7 +153,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 */ @@ -162,10 +164,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 -- cgit v1.2.1 From 4e59a5d5c448e1fcdcbead268ffe6561adf0224d Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 19:00:51 -0400 Subject: SECURITY: fix Qualys CVE-2020-PFPSN (cherry picked from commit 93b6044e1636404f3463f3e1113098742e295542) --- doc/doc-txt/ChangeLog | 4 ++++ src/src/parse.c | 14 +++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 7ed412ea9..c8b295b6e 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -180,6 +180,10 @@ PP/03 Impose security length checks on various command-line options. PP/04 Fix Linux security issue CVE-2020-SLCWD and guard against PATH_MAX better. Reported by Qualys. +PP/05 Fix security issue CVE-2020-PFPSN and guard against cmdline invoker + providing a particularly obnoxious sender full name. + Reported by Qualys. + Exim version 4.94 ----------------- diff --git a/src/src/parse.c b/src/src/parse.c index 39f5aaec1..ba5489ba9 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -1124,9 +1124,17 @@ while (s < end) { if (ss >= end) ss--; *t++ = '('; - Ustrncpy(t, s, ss-s); - t += ss-s; - s = ss; + if (ss < s) + { + /* Someone has ended the string with "(". */ + ss = s; + } + else + { + Ustrncpy(t, s, ss-s); + t += ss-s; + s = ss; + } } } -- cgit v1.2.1 From 0a6a7a3fd8464bae9ce0cf889e8eeb0bf0bab756 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 20:42:40 -0400 Subject: SECURITY: fix Qualys CVE-2020-PFPZA (cherry picked from commit 29d7a8c25f182c91d5d30f124f9e296dce5c018e) --- doc/doc-txt/ChangeLog | 3 +++ src/src/parse.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index c8b295b6e..0c7a8ad52 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -184,6 +184,9 @@ PP/05 Fix security issue CVE-2020-PFPSN and guard against cmdline invoker providing a particularly obnoxious sender full name. Reported by Qualys. +pp/06 Fix CVE-2020-28016 (PFPZA): Heap out-of-bounds write in parse_fix_phrase() + + Exim version 4.94 ----------------- diff --git a/src/src/parse.c b/src/src/parse.c index ba5489ba9..c50c8156d 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -979,6 +979,11 @@ if (i < len) /* No non-printers; use the RFC 822 quoting rules */ +if (!len) + { + return string_copy_taint_function("", is_tainted(phrase)); + } + buffer = store_get(len*4, is_tainted(phrase)); s = phrase; -- cgit v1.2.1 From 6f5d7e5af8eff688c36f81334e4f063689561963 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 20:49:49 -0400 Subject: SECURITY: refuse too small store allocations Negative sizes are definitely bad. Optimistically, I'm saying that zero is bad too. But perhaps we have something doing that, expecting to be able to grow. In which case we'll have to amend this. (cherry picked from commit 1c9afcec0043e2fb72607b2addb0613763705549) --- doc/doc-txt/ChangeLog | 4 +++- src/src/store.c | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 0c7a8ad52..b6d5067ec 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -184,8 +184,10 @@ PP/05 Fix security issue CVE-2020-PFPSN and guard against cmdline invoker providing a particularly obnoxious sender full name. Reported by Qualys. -pp/06 Fix CVE-2020-28016 (PFPZA): Heap out-of-bounds write in parse_fix_phrase() +PP/06 Fix CVE-2020-28016 (PFPZA): Heap out-of-bounds write in parse_fix_phrase() +PP/07 Refuse to allocate too little memory, block negative/zero allocations. + Security guard. Exim version 4.94 diff --git a/src/src/store.c b/src/src/store.c index 7d08c9804..90da012fb 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -233,6 +233,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 is also suspect (but we might have to allow it if we find our API +expects it in some places). */ +if (size < 1) + { + 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 -- cgit v1.2.1 From 9f06dcd6848052f2524658bf871c60a8d48c7dbe Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 21:30:04 -0400 Subject: SECURITY: a second negative store guard (cherry picked from commit 706864e934c70941ce7a327f97b7649a1e5f5556) --- src/src/store.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/src/store.c b/src/src/store.c index 90da012fb..1e6259c18 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -390,6 +390,13 @@ int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool; int inc = newsize - oldsize; int rounded_oldsize = oldsize; +if (newsize < 0) + { + 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. */ -- cgit v1.2.1 From a6e1f69d82adcfd3caab8f228d96750dfddc8f07 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 21:38:25 -0400 Subject: SECURITY: default recipients_max to 50,000 A default of "unlimited" can have unfortunate consequences when people start putting many millions of recipients on a message. (cherry picked from commit 1d7780722a66cea8da5fa4ae0775e85d185fbf7e) --- doc/doc-docbook/spec.xfpt | 2 +- doc/doc-txt/ChangeLog | 2 ++ doc/doc-txt/OptionLists.txt | 2 +- src/src/globals.c | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 446871125..9c66f5cc9 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -17200,7 +17200,7 @@ or if the message was submitted locally (not using TCP/IP), and the &%-bnq%& option was not set. -.option recipients_max main integer 0 +.option recipients_max main integer 50000 .cindex "limit" "number of recipients" .cindex "recipient" "maximum number" If this option is set greater than zero, it specifies the maximum number of diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index b6d5067ec..055b1ace8 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -189,6 +189,8 @@ PP/06 Fix CVE-2020-28016 (PFPZA): Heap out-of-bounds write in parse_fix_phrase() PP/07 Refuse to allocate too little memory, block negative/zero allocations. Security guard. +PP/08 Change default for recipients_max from unlimited to 50,000. + Exim version 4.94 ----------------- diff --git a/doc/doc-txt/OptionLists.txt b/doc/doc-txt/OptionLists.txt index 8b2dee352..99e87067a 100644 --- a/doc/doc-txt/OptionLists.txt +++ b/doc/doc-txt/OptionLists.txt @@ -464,7 +464,7 @@ receive_timeout time 0s main received_header_text string* + main received_headers_max integer 30 main recipient_unqualified_hosts host list unset main 4.00 replacing receiver_unqualified_hosts -recipients_max integer 0 main 1.60 +recipients_max integer 50000 main 1.60 default changed in 4.95 (was 0) recipients_max_reject boolean false main 1.70 redirect_router string unset routers 4.00 remote_max_parallel integer 1 main diff --git a/src/src/globals.c b/src/src/globals.c index c34ac9ddd..532eed27f 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1272,7 +1272,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; -- cgit v1.2.1 From 3a54fcd1e303bf1cc49beca7ceac35d7448860a9 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 21:48:05 -0400 Subject: SECURITY: Avoid integer overflow on too many recipients (cherry picked from commit 323ff55e67b44e95f9d3cfaba155e385aa33c4bd) --- doc/doc-txt/ChangeLog | 5 +++++ src/src/receive.c | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 055b1ace8..3bb5326ce 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -191,6 +191,11 @@ PP/07 Refuse to allocate too little memory, block negative/zero allocations. PP/08 Change default for recipients_max from unlimited to 50,000. +PP/09 Fix security issue with too many recipients on a message (to remove a + known security problem if someone does set recipients_max to unlimited, + or if local additions add to the recipient list). + Fixes CVE-2020-RCPTL reported by Qualys. + Exim version 4.94 ----------------- diff --git a/src/src/receive.c b/src/src/receive.c index ec90e93cd..ba6a8d3a6 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -485,11 +485,18 @@ Returns: nothing void receive_add_recipient(uschar *recipient, int pno) { +/* XXX This is a math limit; we should consider a performance/sanity limit too. */ +const int safe_recipients_limit = INT_MAX / sizeof(recipient_item) - 1; + if (recipients_count >= recipients_list_max) { recipient_item *oldlist = recipients_list; int oldmax = recipients_list_max; recipients_list_max = recipients_list_max ? 2*recipients_list_max : 50; + if ((recipients_list_max >= safe_recipients_limit) || (recipients_list_max < 0)) + { + log_write(0, LOG_MAIN|LOG_PANIC, "Too many recipients needed: %d not satisfiable", recipients_list_max); + } recipients_list = store_get(recipients_list_max * sizeof(recipient_item), FALSE); if (oldlist != NULL) memcpy(recipients_list, oldlist, oldmax * sizeof(recipient_item)); -- cgit v1.2.1 From 467948de0c407bd2bbc2e84abbbf09f35b035538 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 22:40:59 -0400 Subject: SECURITY: fix SMTP verb option parsing A boundary case in looking for an opening quote before the closing quote could walk off the front of the buffer. (cherry picked from commit 515d8d43a18481d23d7cf410b8dc71b4e254ebb8) --- doc/doc-txt/ChangeLog | 3 +++ src/src/smtp_in.c | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 3bb5326ce..1c7c39e2c 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -196,6 +196,9 @@ PP/09 Fix security issue with too many recipients on a message (to remove a or if local additions add to the recipient list). Fixes CVE-2020-RCPTL reported by Qualys. +PP/10 Fix security issue in SMTP verb option parsing + Fixes CVE-2020-EXOPT reported by Qualys. + Exim version 4.94 ----------------- diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index f53c3cf65..a86e977ce 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1997,12 +1997,13 @@ extract_option(uschar **name, uschar **value) uschar *n; uschar *v = smtp_cmd_data + Ustrlen(smtp_cmd_data) - 1; while (isspace(*v)) v--; -v[1] = 0; +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 == '"') && (v > smtp_cmd_data + 1)) + do v--; while (*v != '"' && v > smtp_cmd_data+1); v--; } -- cgit v1.2.1 From 96fb195ebc2eb6790e6ad6dde46d478aee62198d Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 29 Oct 2020 23:21:36 -0400 Subject: SECURITY: rework BDAT receive function handling (cherry picked from commit dd1b9b753bb7c42df2b8f48d726b82928b67940b) --- doc/doc-txt/ChangeLog | 6 +++++ src/src/globals.c | 1 + src/src/smtp_in.c | 67 ++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 1c7c39e2c..87f1952f5 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -199,6 +199,12 @@ PP/09 Fix security issue with too many recipients on a message (to remove a PP/10 Fix security issue in SMTP verb option parsing Fixes CVE-2020-EXOPT reported by Qualys. +PP/11 Fix security issue in BDAT state confusion. + Ensure we reset known-good where we know we need to not be reading BDAT + data, as a general case fix, and move the places where we switch to BDAT + mode until after various protocol state checks. + Fixes CVE-2020-BDATA reported by Qualys. + Exim version 4.94 ----------------- diff --git a/src/src/globals.c b/src/src/globals.c index 532eed27f..fcb9cc0b5 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -221,6 +221,7 @@ struct global_flags f = .authentication_local = FALSE, .background_daemon = TRUE, + .bdat_readers_wanted = FALSE, .chunking_offered = FALSE, .config_changed = FALSE, diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index a86e977ce..28a79ba5e 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -624,9 +624,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 +728,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 +761,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 +771,35 @@ if (chunking_state != CHUNKING_LAST) } +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_ungetc = bdat_ungetc; +} + +void +bdat_pop_receive_functions(void) +{ +receive_getc = lwr_receive_getc; +receive_getbuf = lwr_receive_getbuf; +receive_ungetc = lwr_receive_ungetc; +lwr_receive_getc = lwr_receive_getbuf = lwr_receive_ungetc = NULL; +} /************************************************* * SMTP version of ungetc() * @@ -2565,6 +2587,7 @@ receive_ungetc = smtp_ungetc; receive_feof = smtp_feof; receive_ferror = smtp_ferror; receive_smtp_buffered = smtp_buffered; +lwr_receive_getc = lwr_receive_getbuf = lwr_receive_ungetc = NULL; smtp_inptr = smtp_inend = smtp_inbuffer; smtp_had_eof = smtp_had_error = 0; @@ -3946,6 +3969,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; @@ -5266,16 +5297,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; f.dot_ends = FALSE; goto DATA_BDAT; @@ -5284,6 +5306,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 @@ -5312,7 +5335,10 @@ while (done <= 0) : US"valid RCPT command must precede BDAT"); if (chunking_state > CHUNKING_OFFERED) + { + bdat_push_receive_functions(); bdat_flush_data(); + } break; } @@ -5351,6 +5377,9 @@ while (done <= 0) } } + if (f.bdat_readers_wanted) + bdat_push_receive_functions(); + if (user_msg) smtp_user_msg(US"354", user_msg); else -- cgit v1.2.1 From da683a61556bbbebdffcbebf2668da58da59f898 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sat, 31 Oct 2020 14:36:55 +0000 Subject: Fixes for compilation (cherry picked from commit 85a90771a373aaaced64b92d7176a8a310490b9e) --- src/exim_monitor/em_version.c | 11 +++ src/src/exim.c | 121 ++++++++++++----------- src/src/filter.c | 30 +++--- src/src/functions.h | 24 ++--- src/src/globals.h | 1 + src/src/moan.c | 3 +- src/src/parse.c | 222 +++++++++++++++++++++--------------------- src/src/readconf.c | 10 +- src/src/receive.c | 12 ++- src/src/rewrite.c | 49 +++++----- src/src/smtp_in.c | 54 ++++++---- src/src/store.c | 8 +- src/src/transport.c | 9 +- src/src/tree.c | 6 +- src/src/verify.c | 6 +- 15 files changed, 309 insertions(+), 257 deletions(-) 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/src/exim.c b/src/src/exim.c index 067c47f80..e1736d6e9 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -768,7 +768,7 @@ 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", - len, maxlen, description) + itemlen, maxlen, description); exit(EXIT_FAILURE); } @@ -1554,10 +1554,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'; @@ -1571,7 +1572,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); } @@ -1654,10 +1655,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; @@ -1751,7 +1752,7 @@ if (f.running_in_test_harness) debug_store = TRUE; /* Protect against abusive argv[0] */ -exim_len_fail_toolong(argv[0], PATH_MAX, "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 @@ -3235,7 +3236,7 @@ on the second character (the one after '-'), to save some effort. */ case 'X': if (*argrest) badarg = TRUE; - else override_local_interfaces = string_copy_taint(exim_str_fail_toolong(argv[++i], 1024, "-oX", TRUE); + else override_local_interfaces = string_copy_taint(exim_str_fail_toolong(argv[++i], 1024, "-oX"), TRUE); break; /* Unknown -o argument */ @@ -3365,7 +3366,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, @@ -3376,29 +3380,29 @@ 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. */ - /* Avoid attacks from people providing very long strings, and do so before - we make copies. */ - const char *tainted_selectstr; - 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); + /* 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) */ @@ -3407,7 +3411,10 @@ on the second character (the one after '-'), to save some effort. */ /* -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, @@ -3418,27 +3425,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. */ - const char *tainted_selectstr; - 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); + 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. @@ -4500,7 +4507,7 @@ 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) { @@ -4529,7 +4536,7 @@ if (test_retry_arg >= 0) if (test_retry_arg < argc) { - uschar *ss = exim_str_fail_toolong(argv[test_retry_arg], EXIM_DRIVERNAME_MAX, "-brt 3rd"); + 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) @@ -5015,11 +5022,15 @@ if (expansion_test) dns_init(FALSE, FALSE, FALSE); if (msg_action_arg > 0 && msg_action == MSG_LOAD) { - uschar spoolname[SPOOL_NAME_LENGTH]; /* Not big_buffer; used in spool_read_header() */ + uschar * spoolname; if (!f.admin_user) exim_fail("exim: permission denied\n"); - message_id = exim_str_fail_toolong(argv[msg_action_arg], MESSAGE_ID_LENGTH, "message-id"); - (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) @@ -5640,10 +5651,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/filter.c b/src/src/filter.c index 402ad6ae5..4df482d59 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; @@ -2019,7 +2018,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; @@ -2043,7 +2042,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; @@ -2126,7 +2125,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; @@ -2180,7 +2179,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 */ @@ -2368,8 +2367,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 51bb17a09..4ce0f093d 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -360,16 +360,16 @@ 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 int vaguely_random_number(int); #ifndef DISABLE_TLS extern int vaguely_random_number_fallback(int); @@ -397,7 +397,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); @@ -423,14 +423,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 **, @@ -591,9 +591,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.h b/src/src/globals.h index a4c1143b7..bb811553c 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -183,6 +183,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/moan.c b/src/src/moan.c index 4e7fbd607..7989a3ab1 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 c50c8156d..3f1ba222f 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; @@ -981,7 +981,7 @@ if (i < len) if (!len) { - return string_copy_taint_function("", is_tainted(phrase)); + return string_copy_taint(US"", is_tainted(phrase)); } buffer = store_get(len*4, is_tainted(phrase)); @@ -1590,7 +1590,7 @@ for (;;) else { int start, end, domain; - uschar *recipient = NULL; + const uschar *recipient = NULL; int save = s[len]; s[len] = 0; @@ -1686,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 @@ -1721,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; @@ -1762,8 +1762,7 @@ while (*id) id++; *id++ = 0; store_release_above(id); -str = skip_comment(str); -return str; +return skip_comment(str); } @@ -1781,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; } @@ -1807,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 @@ -1823,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); } @@ -1853,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 @@ -1876,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; @@ -1930,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 @@ -1966,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; @@ -2040,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] @@ -2053,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/readconf.c b/src/src/readconf.c index c052cda5f..f962f9029 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1315,7 +1315,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) @@ -1354,7 +1354,7 @@ 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[EXIM_DRIVERNAME_MAX]; @@ -2427,7 +2427,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; @@ -2737,7 +2737,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; @@ -2876,7 +2876,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); } diff --git a/src/src/receive.c b/src/src/receive.c index ba6a8d3a6..5c6420286 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2130,7 +2130,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)) { @@ -2510,7 +2511,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); } @@ -2797,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 @@ -2973,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 7bff8a273..db9a3ff07 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; /* Ensure that the flag matches the flags in the rule. */ @@ -185,7 +186,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); @@ -390,15 +391,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; } @@ -529,7 +531,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; @@ -552,7 +555,8 @@ while (*s) if ((existflags & flag) != 0) { 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; @@ -744,7 +748,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; @@ -761,8 +766,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) @@ -795,7 +800,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 28a79ba5e..04d6aa4ba 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -593,6 +593,11 @@ if (n > 0) } +/* Forward declarations */ +static void bdat_push_receive_functions(void); +static 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 @@ -771,7 +776,7 @@ if (chunking_state != CHUNKING_LAST) } -void +static void bdat_push_receive_functions(void) { /* push the current receive_* function on the "stack", and @@ -792,13 +797,15 @@ receive_getc = bdat_getc; receive_ungetc = bdat_ungetc; } -void +static void bdat_pop_receive_functions(void) { receive_getc = lwr_receive_getc; receive_getbuf = lwr_receive_getbuf; receive_ungetc = lwr_receive_ungetc; -lwr_receive_getc = lwr_receive_getbuf = lwr_receive_ungetc = NULL; +lwr_receive_getc = NULL; +lwr_receive_getbuf = NULL; +lwr_receive_ungetc = NULL; } /************************************************* @@ -2266,9 +2273,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 */ @@ -2288,7 +2297,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); } @@ -2327,7 +2337,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; @@ -2346,7 +2357,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 @@ -2587,7 +2599,9 @@ receive_ungetc = smtp_ungetc; receive_feof = smtp_feof; receive_ferror = smtp_ferror; receive_smtp_buffered = smtp_buffered; -lwr_receive_getc = lwr_receive_getbuf = lwr_receive_ungetc = NULL; +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; @@ -3858,7 +3872,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, @@ -4865,7 +4880,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; @@ -4927,7 +4943,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); } @@ -5119,7 +5136,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; @@ -5306,7 +5324,7 @@ while (done <= 0) case DATA_CMD: HAD(SCH_DATA); f.dot_ends = TRUE; - f.bdat_readers_wanted = FALSE + f.bdat_readers_wanted = FALSE; DATA_BDAT: /* Common code for DATA and BDAT */ #ifndef DISABLE_PIPE_CONNECT @@ -5377,9 +5395,6 @@ while (done <= 0) } } - if (f.bdat_readers_wanted) - bdat_push_receive_functions(); - if (user_msg) smtp_user_msg(US"354", user_msg); else @@ -5387,6 +5402,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/store.c b/src/src/store.c index 1e6259c18..305ae6334 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -235,9 +235,11 @@ 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 is also suspect (but we might have to allow it if we find our API -expects it in some places). */ -if (size < 1) +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) { log_write(0, LOG_MAIN|LOG_PANIC_DIE, "bad memory allocation requested (%d bytes) at %s %d", diff --git a/src/src/transport.c b/src/src/transport.c index 8ee89ed2f..a43d811ad 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -958,11 +958,10 @@ if (!(tctx->options & topt_no_headers)) if (tctx->options & topt_add_return_path) { - uschar buffer[EXIM_EMAILADDR_MAX + 20]; - int n = string_format(CS buffer, sizeof(buffer), - "Return-path: <%.*s>\n", - EXIM_EMAILADDR_MAX, 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 */ 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 43343a646..262a8d76a 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -1705,7 +1705,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) @@ -1720,7 +1721,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) { -- cgit v1.2.1 From 6ca5fcba34070f4495a0188f16eb2e4d78f3430a Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Mon, 2 Nov 2020 02:23:14 -0500 Subject: Inline four often-called new functions The BDAT state switchers should happen so often during SMTP reception that a compiler hint to inline seems wise. The length filter checks happen on every start-up, which for Exim is often enough that I think an inline these is warranted too. (cherry picked from commit 6e3d0e3f1c8228ef19a3d1ba61f131cef3172ceb) --- src/src/exim.c | 4 ++-- src/src/smtp_in.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/src/exim.c b/src/src/exim.c index e1736d6e9..3aa006d96 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -762,7 +762,7 @@ exit(EXIT_FAILURE); } /* fail if a length is too long */ -static void +static inline void exim_len_fail_toolong(int itemlen, int maxlen, const char *description) { if (itemlen <= maxlen) @@ -773,7 +773,7 @@ exit(EXIT_FAILURE); } /* only pass through the string item back to the caller if it's short enough */ -static const uschar * +static inline const uschar * exim_str_fail_toolong(const uschar *item, int maxlen, const char *description) { exim_len_fail_toolong(Ustrlen(item), maxlen, description); diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 04d6aa4ba..b48870d26 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -594,8 +594,8 @@ if (n > 0) /* Forward declarations */ -static void bdat_push_receive_functions(void); -static void bdat_pop_receive_functions(void); +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 @@ -776,7 +776,7 @@ if (chunking_state != CHUNKING_LAST) } -static void +static inline void bdat_push_receive_functions(void) { /* push the current receive_* function on the "stack", and @@ -797,7 +797,7 @@ receive_getc = bdat_getc; receive_ungetc = bdat_ungetc; } -static void +static inline void bdat_pop_receive_functions(void) { receive_getc = lwr_receive_getc; -- cgit v1.2.1 From 125f0d4afbc858cf514c29326a3016c2d9d7bdc1 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Mon, 2 Nov 2020 02:39:38 -0500 Subject: Start documenting the things we changed incompatibly. (cherry picked from commit 8dad4da53bad2ed3b29fa6a3b9ef59bfec73dc0e) --- src/README.UPDATING | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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 ----------------- -- cgit v1.2.1 From 4045cb01a590ec480f45f80967cd9c59fe23a5d0 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Sat, 21 Nov 2020 22:03:03 +0100 Subject: SECURITY: off-by-one in smtp transport (read response) Credits: Qualys 1/ In src/transports/smtp.c: 2281 int n = sizeof(sx->buffer); 2282 uschar * rsp = sx->buffer; 2283 2284 if (sx->esmtp_sent && (n = Ustrlen(sx->buffer)) < sizeof(sx->buffer)/2) 2285 { rsp = sx->buffer + n + 1; n = sizeof(sx->buffer) - n; } This should probably be either: rsp = sx->buffer + n + 1; n = sizeof(sx->buffer) - n - 1; or: rsp = sx->buffer + n; n = sizeof(sx->buffer) - n; (not sure which) to avoid an off-by-one. (cherry picked from commit d2c44ef5dd94f1f43ba1d1a02bc4594f4fba5e38) --- src/src/transports/smtp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 6540e4d2b..f26e2337a 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2359,8 +2359,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; -- cgit v1.2.1 From 2d9f1837bdd6c5946cb9cd997544eefc8cc14fc4 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Sat, 21 Nov 2020 22:18:56 +0100 Subject: SECURITY: Don't miss the very last byte when reading long lines from -H Credits: Qualys 2/ In src/spool_in.c: 462 while ( (len = Ustrlen(big_buffer)) == big_buffer_size-1 463 && big_buffer[len-1] != '\n' 464 ) 465 { /* buffer not big enough for line; certs make this possible */ 466 uschar * buf; 467 if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR; 468 buf = store_get_perm(big_buffer_size *= 2, FALSE); 469 memcpy(buf, big_buffer, --len); The --len in memcpy() chops off a useful byte (we know for sure that big_buffer[len-1] is not a '\n' because we entered the while loop). (cherry picked from commit 58454ea01c2e817481770954edf09ad82f3cd417) --- src/src/spool_in.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 35e44df26..1433123c3 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -466,7 +466,7 @@ for (;;) 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); + memcpy(buf, big_buffer, len); big_buffer = buf; if (Ufgets(big_buffer+len, big_buffer_size-len, fp) == NULL) goto SPOOL_READ_ERROR; -- cgit v1.2.1 From 1e9a340c05d7233969637095a8a6378b14de2976 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Sat, 21 Nov 2020 22:41:28 +0100 Subject: SECURITY: Fix safeguard against upward traversal in msglog files. Credits: Qualys 3/ In src/deliver.c: 333 static int 334 open_msglog_file(uschar *filename, int mode, uschar **error) 335 { 336 if (Ustrstr(filename, US"/../")) 337 log_write(0, LOG_MAIN|LOG_PANIC, 338 "Attempt to open msglog file path with upward-traversal: '%s'\n", filename); Should this be LOG_PANIC_DIE instead of LOG_PANIC? Right now it will log the /../ attempt but will open the file anyway. (cherry picked from commit 742c27f02d83792937dcb1719b380d3dde6228bf) --- doc/doc-txt/ChangeLog | 2 ++ src/src/deliver.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 87f1952f5..98c1b05e2 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -205,6 +205,8 @@ PP/11 Fix security issue in BDAT state confusion. mode until after various protocol state checks. Fixes CVE-2020-BDATA reported by Qualys. +HS/03 Die on "/../" in msglog file names + Exim version 4.94 ----------------- diff --git a/src/src/deliver.c b/src/src/deliver.c index d85edd70e..389f2fb4c 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--) -- cgit v1.2.1 From db96ca55137d7684a9afdf9d118feed9116906b7 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Wed, 25 Nov 2020 22:26:53 +0100 Subject: SECURITY: Check overrun rcpt_count integer Credits: Qualys 4/ In src/smtp_in.c: 4966 case RCPT_CMD: 4967 HAD(SCH_RCPT); 4968 rcpt_count++; .... 5123 if (rcpt_count > recipients_max && recipients_max > 0) In theory this recipients_max check can be bypassed, because the int rcpt_count can overflow (become negative). In practice this would either consume too much memory or generate too much network traffic, but maybe it should be fixed anyway. (cherry picked from commit 04139ca809fbe56d8fe9c55a77640ea9fa93b8f1) --- src/src/smtp_in.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index b48870d26..0b6733673 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -5014,6 +5014,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; @@ -5170,7 +5174,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) { -- cgit v1.2.1 From 8b39dd074e3ec70cbda70a52cef5b71ecbf69499 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Wed, 25 Nov 2020 22:58:58 +0100 Subject: SECURITY: Avoid decrement of dkim_collect_input if already at 0 Credits: Qualys 5/ receive_msg() calls dkim_exim_verify_finish(), which sets dkim_collect_input to 0 and calls pdkim_feed_finish(), which calls pdkim_header_complete(), which decreases dkim_collect_input to UINT_MAX, which reactivates the DKIM code. As a result, pdkim_feed() is called again (through receive_getc at the end of receive_msg()), but functions like pdkim_finish_bodyhash() and exim_sha_finish() have already been called (in pdkim_feed_finish()). This suggests a use-after-free. But it seems that a use-after-free would happen only with EVP_DigestFinal() (in exim_sha_finish()), which does not seem to be reachable via DKIM (no SHA3). But we checked OpenSSL only, not GnuTLS. Here is a proof of concept that triggers the bug (which came very close to a security vulnerability): (sleep 10; echo 'EHLO test'; sleep 3; echo 'MAIL FROM:<>'; sleep 3; echo 'RCPT TO:postmaster'; sleep 3; echo 'BDAT 42 LAST'; date >&2; sleep 30; printf 'not a valid header line\r\nDKIM-Signature:\r\nXXX'; sleep 30) | nc -n -v 192.168.56.102 25 (gdb) print &dkim_collect_input $2 = (unsigned int *) 0x55e180386d90 (gdb) watch *(unsigned int *) 0x55e180386d90 Hardware watchpoint 1: *(unsigned int *) 0x55e180386d90 Old value = 0 New value = 4294967295 #0 0x000055e18031f805 in pdkim_header_complete (ctx=ctx@entry=0x55e181b9e8e0) at pdkim.c:1006 #1 0x000055e18032106c in pdkim_feed_finish (ctx=0x55e181b9e8e0, return_signatures=0x55e180386d78 , err=err@entry=0x7ffe443e1d00) at pdkim.c:1490 #2 0x000055e1802a3280 in dkim_exim_verify_finish () at dkim.c:328 #3 0x000055e1802c9d1d in receive_msg (extract_recip=extract_recip@entry=0) at receive.c:3409 (cherry picked from commit e3674091056ac05eb7ef1c504accce790c434bd7) --- src/src/pdkim/pdkim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index f68324097..01cf5dd28 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -1003,7 +1003,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'; -- cgit v1.2.1 From 4e784efa28b25683c81a857b464777df593cabee Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Wed, 25 Nov 2020 23:19:57 +0100 Subject: SECURITY: Avoid memory corruption in dkim handling Credits: Qualys 6/ In src/pdkim/pdkim.c, pdkim_update_ctx_bodyhash() is sometimes called with a global orig_data and hence canon_data, and the following line can therefore modify data that should be constant: 773 canon_data->len = b->bodylength - b->signed_body_bytes; For example, the following proof of concept sets lineending.len to 0 (this should not be possible): (sleep 10; echo 'EHLO test'; sleep 3; echo 'MAIL FROM:<>'; sleep 3; echo 'RCPT TO:postmaster'; sleep 3; echo 'DATA'; date >&2; sleep 30; printf 'DKIM-Signature:a=rsa-sha1;c=simple/simple;l=0\r\n\r\n\r\nXXX\r\n.\r\n'; sleep 30) | nc -n -v 192.168.56.102 25 (gdb) print lineending $1 = {data = 0x55e18035b2ad "\r\n", len = 2} (gdb) print &lineending.len $3 = (size_t *) 0x55e180385948 (gdb) watch *(size_t *) 0x55e180385948 Hardware watchpoint 1: *(size_t *) 0x55e180385948 Old value = 2 New value = 0 (gdb) print lineending $5 = {data = 0x55e18035b2ad "\r\n", len = 0} (cherry picked from commit ea850e27714ccda2090d781ebe89b410bc38c2c6) --- src/src/pdkim/pdkim.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 01cf5dd28..da2ea7e49 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -722,7 +722,9 @@ 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) { -blob * canon_data = orig_data; +const blob const * canon_data = orig_data; +size_t len; + /* Defaults to simple canon (no further treatment necessary) */ if (b->canon_method == PDKIM_CANON_RELAXED) @@ -770,13 +772,13 @@ if (b->canon_method == PDKIM_CANON_RELAXED) if ( b->bodylength >= 0 && b->signed_body_bytes + (unsigned long)canon_data->len > b->bodylength ) - canon_data->len = b->bodylength - b->signed_body_bytes; + len = b->bodylength - b->signed_body_bytes; -if (canon_data->len > 0) +if (len > 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, len); + b->signed_body_bytes += len; + DEBUG(D_acl) pdkim_quoteprint(canon_data->data, len); } return relaxed_data; -- cgit v1.2.1 From 667fb25b8f0dc3fbac57bce4051e345555fa776a Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Thu, 26 Nov 2020 18:16:59 +0100 Subject: SECURITY: Avoid modification of constant data Credits: Qualys 6/ In src/pdkim/pdkim.c, pdkim_update_ctx_bodyhash() is sometimes called with a global orig_data and hence canon_data, and the following line can therefore modify data that should be constant: 773 canon_data->len = b->bodylength - b->signed_body_bytes; For example, the following proof of concept sets lineending.len to 0 (this should not be possible): (sleep 10; echo 'EHLO test'; sleep 3; echo 'MAIL FROM:<>'; sleep 3; echo 'RCPT TO:postmaster'; sleep 3; echo 'DATA'; date >&2; sleep 30; printf 'DKIM-Signature:a=rsa-sha1;c=simple/simple;l=0\r\n\r\n\r\nXXX\r\n.\r\n'; sleep 30) | nc -n -v 192.168.56.102 25 (gdb) print lineending $1 = {data = 0x55e18035b2ad "\r\n", len = 2} (gdb) print &lineending.len $3 = (size_t *) 0x55e180385948 (gdb) watch *(size_t *) 0x55e180385948 Hardware watchpoint 1: *(size_t *) 0x55e180385948 Old value = 2 New value = 0 (gdb) print lineending $5 = {data = 0x55e18035b2ad "\r\n", len = 0} (cherry picked from commit 9fce76f56459dde7489eb21ce1ff822e04e10f43) --- src/src/pdkim/pdkim.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index da2ea7e49..4c73d4fac 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -720,10 +720,10 @@ 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 const * orig_data, blob * relaxed_data) { const blob const * canon_data = orig_data; -size_t len; +size_t left; /* Defaults to simple canon (no further treatment necessary) */ @@ -769,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 + && b->signed_body_bytes + left > b->bodylength ) - len = b->bodylength - b->signed_body_bytes; + left = b->bodylength - b->signed_body_bytes; -if (len > 0) +if (left > 0) { - exim_sha_update(&b->body_hash_ctx, CUS canon_data->data, len); - b->signed_body_bytes += len; - DEBUG(D_acl) pdkim_quoteprint(canon_data->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; -- cgit v1.2.1 From 13f9998ebb937970d1d9d18f205a6e03e14105b4 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Wed, 2 Dec 2020 22:28:02 +0100 Subject: SECURITY: smtp_out: Leave a clean input buffer, even in case of read error Credits: Qualys 7/ In src/smtp_out.c, read_response_line(), inblock->ptr is not updated when -1 is returned. This does not seem to have bad consequences, but is maybe not the intended behavior. (cherry picked from commit f7ac5a7d1e817bf60f161e7a1d40b65d66da607f) --- src/src/smtp_out.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index c4c409677..d6dfba52e 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -425,7 +425,7 @@ if (ob->socks_proxy) { int sock = socks_sock_connect(sc->host, sc->host_af, port, sc->interface, sc->tblock, ob->connect_timeout); - + if (sock >= 0) { if (early_data && early_data->data && early_data->len) @@ -643,7 +643,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 @@ -684,6 +684,7 @@ for (;;) { *p = 0; /* Leave malformed line for error message */ errno = ERRNO_SMTPFORMAT; + inblock->ptr = ptr; return -1; } } -- cgit v1.2.1 From 99d057fad97a2def9f000ebccda83e4008112819 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Thu, 4 Mar 2021 22:19:08 +0100 Subject: CVE-2020-28019: Failure to reset function pointer after BDAT error Based on Phil Pennock's commits 4715403e and 151ffd72, and Jeremy Harris's commits aa171254 and 9aceb5c2. (cherry picked from commit 0a3fbb7e3be375bc93b8e359c6aff333c7c2d76f) --- src/src/smtp_in.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 0b6733673..190064eed 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -794,15 +794,22 @@ else } 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; @@ -5319,7 +5326,7 @@ while (done <= 0) DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n", (int)chunking_state, chunking_data_left); - f.bdat_readers_wanted = TRUE; + f.bdat_readers_wanted = TRUE; /* FIXME: redundant vs chunking_state? */ f.dot_ends = FALSE; goto DATA_BDAT; @@ -5369,6 +5376,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; } -- cgit v1.2.1 From b05dc3573f4cd476482374b0ac0393153d344338 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Sun, 14 Mar 2021 12:16:57 +0100 Subject: CVE-2020-28008: Assorted attacks in Exim's spool directory We patch dbfn_open() by introducing two functions priv_drop_temp() and priv_restore() (inspired by OpenSSH's functions temporarily_use_uid() and restore_uid()), which temporarily drop and restore root privileges thanks to seteuid(). This goes against Exim's developers' wishes ("Exim (the project) doesn't trust seteuid to work reliably") but, to the best of our knowledge, seteuid() works everywhere and is the only way to securely fix dbfn_open(). (cherry picked from commit 18da59151dbafa89be61c63580bdb295db36e374) --- doc/doc-txt/ChangeLog | 3 ++ src/src/dbfn.c | 113 +++++++++++++++++++++++++++++--------------------- test/stderr/0275 | 2 +- test/stderr/0278 | 2 +- test/stderr/0386 | 2 +- test/stderr/0388 | 2 +- test/stderr/0402 | 2 +- test/stderr/0403 | 2 +- test/stderr/0404 | 2 +- test/stderr/0408 | 2 +- test/stderr/0487 | 2 +- 11 files changed, 77 insertions(+), 57 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 98c1b05e2..636fdf71e 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -207,6 +207,9 @@ PP/11 Fix security issue in BDAT state confusion. HS/03 Die on "/../" in msglog file names +QS/01 Creation of (database) files in $spool_dir: only uid=0 or the euid of + the Exim runtime user are allowed to create files. + Exim version 4.94 ----------------- diff --git a/src/src/dbfn.c b/src/src/dbfn.c index bbf20a1d5..dceb5f140 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -60,6 +60,66 @@ log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg); +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! */ + +static void +priv_drop_temp(const uid_t temp_uid, const gid_t temp_gid) +{ +if (priv_state != PRIV_RESTORED) _exit(EXIT_FAILURE); +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) _exit(EXIT_FAILURE); + + if (priv_ngroups > 0 && setgroups(1, &temp_gid) != 0) _exit(EXIT_FAILURE); + if (setegid(temp_gid) != 0) _exit(EXIT_FAILURE); + if (seteuid(temp_uid) != 0) _exit(EXIT_FAILURE); + + if (geteuid() != temp_uid) _exit(EXIT_FAILURE); + if (getegid() != temp_gid) _exit(EXIT_FAILURE); + } + +priv_state = PRIV_DROPPED; +} + +/* Inspired by OpenSSH's restore_uid(). Thanks! */ + +static void +priv_restore(void) +{ +if (priv_state != PRIV_DROPPED) _exit(EXIT_FAILURE); +priv_state = PRIV_RESTORING; + +if (priv_euid == root_uid) + { + if (seteuid(priv_euid) != 0) _exit(EXIT_FAILURE); + if (setegid(priv_egid) != 0) _exit(EXIT_FAILURE); + if (priv_ngroups > 0 && setgroups(priv_ngroups, priv_groups) != 0) _exit(EXIT_FAILURE); + + if (geteuid() != priv_euid) _exit(EXIT_FAILURE); + if (getegid() != priv_egid) _exit(EXIT_FAILURE); + } + +priv_state = PRIV_RESTORED; +} + + + + /************************************************* * Open and lock a database file * *************************************************/ @@ -91,7 +151,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[256], filename[256]; @@ -113,12 +172,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) { @@ -167,60 +227,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 *lastname = Ustrrchr(filename, '/') + 1; - int namelen = Ustrlen(name); - - *lastname = 0; - - if ((dd = exim_opendir(filename))) - 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(filename, &statbuf) >= 0 && statbuf.st_uid != exim_uid) - { - DEBUG(D_hints_lookup) - debug_printf_indent("ensuring %s is owned by exim\n", filename); - if (exim_chown(filename, exim_uid, exim_gid)) - DEBUG(D_hints_lookup) - debug_printf_indent("failed setting %s to owned by exim\n", filename); - } - } - - 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/test/stderr/0275 b/test/stderr/0275 index 35f1e4442..47c9c9e28 100644 --- a/test/stderr/0275 +++ b/test/stderr/0275 @@ -172,7 +172,7 @@ Delivery address list: EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim - failed to open DB file TESTSUITE/spool/db/retry.lockfile: No such file or directory + failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Considering: userx@test.ex diff --git a/test/stderr/0278 b/test/stderr/0278 index 23b0ba9c6..c09920b94 100644 --- a/test/stderr/0278 +++ b/test/stderr/0278 @@ -131,7 +131,7 @@ Delivery address list: EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim - failed to open DB file TESTSUITE/spool/db/retry.lockfile: No such file or directory + failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Considering: CALLER@test.ex diff --git a/test/stderr/0386 b/test/stderr/0386 index b136548b7..7bc56d9b7 100644 --- a/test/stderr/0386 +++ b/test/stderr/0386 @@ -273,7 +273,7 @@ Delivery address list: EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim - failed to open DB file TESTSUITE/spool/db/retry.lockfile: No such file or directory + failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Considering: 2@b diff --git a/test/stderr/0388 b/test/stderr/0388 index 468eae32d..7819fe907 100644 --- a/test/stderr/0388 +++ b/test/stderr/0388 @@ -11,7 +11,7 @@ set_process_info: pppp delivering 10HmaX-0005vi-00 EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim - failed to open DB file TESTSUITE/spool/db/retry.lockfile: No such file or directory + failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Considering: x@y diff --git a/test/stderr/0402 b/test/stderr/0402 index 1f6bf19c7..ec4b9ea72 100644 --- a/test/stderr/0402 +++ b/test/stderr/0402 @@ -214,7 +214,7 @@ Delivery address list: EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim - failed to open DB file TESTSUITE/spool/db/retry.lockfile: No such file or directory + failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Considering: CALLER@test.ex diff --git a/test/stderr/0403 b/test/stderr/0403 index 0c22cf8f5..9d759694c 100644 --- a/test/stderr/0403 +++ b/test/stderr/0403 @@ -71,7 +71,7 @@ Delivery address list: EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim - failed to open DB file TESTSUITE/spool/db/retry.lockfile: No such file or directory + failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Considering: userx@test.ex diff --git a/test/stderr/0404 b/test/stderr/0404 index b1a78c9fa..f0c1c355b 100644 --- a/test/stderr/0404 +++ b/test/stderr/0404 @@ -172,7 +172,7 @@ Delivery address list: EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim - failed to open DB file TESTSUITE/spool/db/retry.lockfile: No such file or directory + failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Considering: userx@test.ex diff --git a/test/stderr/0408 b/test/stderr/0408 index 3fbd0f2a6..50b853183 100644 --- a/test/stderr/0408 +++ b/test/stderr/0408 @@ -71,7 +71,7 @@ Delivery address list: EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim - failed to open DB file TESTSUITE/spool/db/retry.lockfile: No such file or directory + failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Considering: userx@test.ex diff --git a/test/stderr/0487 b/test/stderr/0487 index 83d243a54..579b1bd5b 100644 --- a/test/stderr/0487 +++ b/test/stderr/0487 @@ -99,7 +99,7 @@ Delivery address list: EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim - failed to open DB file TESTSUITE/spool/db/retry.lockfile: No such file or directory + failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Considering: userx@test.ex -- cgit v1.2.1 From be31ef213f118abe5fc68732f5492b6b16d28b87 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Wed, 10 Mar 2021 23:37:29 +0100 Subject: Add priv.c: reworked version of priv dropping code (cherry picked from commit 82b545236e6dc82b7af34528c532811bfc74ea19) --- src/OS/Makefile-Base | 3 +- src/scripts/MakeLinks | 2 +- src/src/dbfn.c | 62 ----------------------------------------- src/src/functions.h | 2 ++ src/src/priv.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ test/stderr/0275 | 1 - test/stderr/0278 | 1 - test/stderr/0386 | 1 - test/stderr/0388 | 1 - test/stderr/0402 | 1 - test/stderr/0403 | 1 - test/stderr/0404 | 1 - test/stderr/0408 | 1 - test/stderr/0487 | 1 - 14 files changed, 81 insertions(+), 73 deletions(-) create mode 100644 src/src/priv.c diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 9eed7b0b9..c7bb116ab 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 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 \ @@ -792,6 +792,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/scripts/MakeLinks b/src/scripts/MakeLinks index 784247d99..0bc7bb2a8 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 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/dbfn.c b/src/src/dbfn.c index dceb5f140..e9a0fc27c 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -58,68 +58,6 @@ log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg); #endif - - -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! */ - -static void -priv_drop_temp(const uid_t temp_uid, const gid_t temp_gid) -{ -if (priv_state != PRIV_RESTORED) _exit(EXIT_FAILURE); -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) _exit(EXIT_FAILURE); - - if (priv_ngroups > 0 && setgroups(1, &temp_gid) != 0) _exit(EXIT_FAILURE); - if (setegid(temp_gid) != 0) _exit(EXIT_FAILURE); - if (seteuid(temp_uid) != 0) _exit(EXIT_FAILURE); - - if (geteuid() != temp_uid) _exit(EXIT_FAILURE); - if (getegid() != temp_gid) _exit(EXIT_FAILURE); - } - -priv_state = PRIV_DROPPED; -} - -/* Inspired by OpenSSH's restore_uid(). Thanks! */ - -static void -priv_restore(void) -{ -if (priv_state != PRIV_DROPPED) _exit(EXIT_FAILURE); -priv_state = PRIV_RESTORING; - -if (priv_euid == root_uid) - { - if (seteuid(priv_euid) != 0) _exit(EXIT_FAILURE); - if (setegid(priv_egid) != 0) _exit(EXIT_FAILURE); - if (priv_ngroups > 0 && setgroups(priv_ngroups, priv_groups) != 0) _exit(EXIT_FAILURE); - - if (geteuid() != priv_euid) _exit(EXIT_FAILURE); - if (getegid() != priv_egid) _exit(EXIT_FAILURE); - } - -priv_state = PRIV_RESTORED; -} - - - - /************************************************* * Open and lock a database file * *************************************************/ diff --git a/src/src/functions.h b/src/src/functions.h index 4ce0f093d..fc4e0444d 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -370,6 +370,8 @@ extern const uschar *parse_fix_phrase(const uschar *, int); extern const uschar *parse_message_id(const uschar *, uschar **, uschar **); extern const uschar *parse_quote_2047(const uschar *, int, uschar *, BOOL); 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); 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 +#include +#include + +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/test/stderr/0275 b/test/stderr/0275 index 47c9c9e28..1d85b56c1 100644 --- a/test/stderr/0275 +++ b/test/stderr/0275 @@ -171,7 +171,6 @@ Delivery address list: locked TESTSUITE/spool/db/retry.lockfile EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) - ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/test/stderr/0278 b/test/stderr/0278 index c09920b94..5e9428da0 100644 --- a/test/stderr/0278 +++ b/test/stderr/0278 @@ -130,7 +130,6 @@ Delivery address list: locked TESTSUITE/spool/db/retry.lockfile EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) - ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/test/stderr/0386 b/test/stderr/0386 index 7bc56d9b7..608aa0402 100644 --- a/test/stderr/0386 +++ b/test/stderr/0386 @@ -272,7 +272,6 @@ Delivery address list: locked TESTSUITE/spool/db/retry.lockfile EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) - ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/test/stderr/0388 b/test/stderr/0388 index 7819fe907..91fcf830b 100644 --- a/test/stderr/0388 +++ b/test/stderr/0388 @@ -10,7 +10,6 @@ set_process_info: pppp delivering 10HmaX-0005vi-00 locked TESTSUITE/spool/db/retry.lockfile EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) - ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/test/stderr/0402 b/test/stderr/0402 index ec4b9ea72..e4ffc0ee5 100644 --- a/test/stderr/0402 +++ b/test/stderr/0402 @@ -213,7 +213,6 @@ Delivery address list: locked TESTSUITE/spool/db/retry.lockfile EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) - ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/test/stderr/0403 b/test/stderr/0403 index 9d759694c..5eeb2df0d 100644 --- a/test/stderr/0403 +++ b/test/stderr/0403 @@ -70,7 +70,6 @@ Delivery address list: locked TESTSUITE/spool/db/retry.lockfile EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) - ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/test/stderr/0404 b/test/stderr/0404 index f0c1c355b..f945de9b8 100644 --- a/test/stderr/0404 +++ b/test/stderr/0404 @@ -171,7 +171,6 @@ Delivery address list: locked TESTSUITE/spool/db/retry.lockfile EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) - ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/test/stderr/0408 b/test/stderr/0408 index 50b853183..48fc0aee1 100644 --- a/test/stderr/0408 +++ b/test/stderr/0408 @@ -70,7 +70,6 @@ Delivery address list: locked TESTSUITE/spool/db/retry.lockfile EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) - ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/test/stderr/0487 b/test/stderr/0487 index 579b1bd5b..834f4daf1 100644 --- a/test/stderr/0487 +++ b/test/stderr/0487 @@ -98,7 +98,6 @@ Delivery address list: locked TESTSUITE/spool/db/retry.lockfile EXIM_DBOPEN: file dir flags=O_RDONLY returned from EXIM_DBOPEN: (nil) - ensuring TESTSUITE/spool/db/retry.lockfile is owned by exim failed to open DB file TESTSUITE/spool/db/retry: No such file or directory no retry data available >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -- cgit v1.2.1 From 43c6f0b83200b7082353c50187ef75de3704580a Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Thu, 25 Mar 2021 22:48:09 +0100 Subject: CVE-2020-28014, CVE-2021-27216: PID file handling Arbitrary PID file creation, clobbering, and deletion. Patch provided by Qualys. (cherry picked from commit 974f32939a922512b27d9f0a8a1cb5dec60e7d37) --- doc/doc-txt/ChangeLog | 5 +- src/src/daemon.c | 171 +++++++++++++++++++++++++++++++++++++++----------- src/src/exim.c | 4 ++ test/stderr/0433 | 12 ++-- 4 files changed, 149 insertions(+), 43 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 636fdf71e..e2843e3b6 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -207,9 +207,12 @@ PP/11 Fix security issue in BDAT state confusion. HS/03 Die on "/../" in msglog file names -QS/01 Creation of (database) files in $spool_dir: only uid=0 or the euid of +QS/01 Creation of (database) files in $spool_dir: only uid=0 or the uid of the Exim runtime user are allowed to create files. +QS/02 PID file creation/deletion: only possible if uid=0 or uid is the Exim + runtime user. + Exim version 4.94 ----------------- diff --git a/src/src/daemon.c b/src/src/daemon.c index 7bb89640e..9245aaa59 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -935,7 +935,6 @@ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) } - static void set_pid_file_path(void) { @@ -950,35 +949,144 @@ if (pid_file_path[0] != '/') } -/* 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(errno, "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); } @@ -1839,23 +1947,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(errno, "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); diff --git a/src/src/exim.c b/src/src/exim.c index 3aa006d96..f7a45ff09 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -3207,6 +3207,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; diff --git a/test/stderr/0433 b/test/stderr/0433 index 660043761..3d0e9e9f8 100644 --- a/test/stderr/0433 +++ b/test/stderr/0433 @@ -18,8 +18,8 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... -pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX SIGTERM/SIGINT seen +pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -41,8 +41,8 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 port 1226 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... -pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX SIGTERM/SIGINT seen +pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -65,8 +65,8 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on [127.0.0.1]:1228 port 1225 (IPv4) port 1226 (IPv4) daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... -pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; 127.0.0.1.1228 ; 0.0.0.0 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX SIGTERM/SIGINT seen +pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; 127.0.0.1.1228 ; 0.0.0.0 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -89,8 +89,8 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 port 1226 [127.0.0.1]:1228 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... -pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; 0.0.0.0; 127.0.0.1.1228 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX SIGTERM/SIGINT seen +pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; 0.0.0.0; 127.0.0.1.1228 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -114,8 +114,8 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on [127.0.0.1]:1228 port 1227 (IPv4) daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... -pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; 127.0.0.1.1228 ; 0.0.0.0 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX SIGTERM/SIGINT seen +pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -DPORT=daemon_smtp_port=1225:1226 -DIFACE=local_interfaces = <; 127.0.0.1.1228 ; 0.0.0.0 -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -139,7 +139,7 @@ LOG: MAIN set_process_info: pppp daemon(x.yz): no queue runs, listening for SMTP on port 1225 port 1226 daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID Listening... -pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX SIGTERM/SIGINT seen +pppp exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -DSERVER=server -C TESTSUITE/test-config -d=0xf7795cfd -MCd daemon-del-pidfile -oP TESTSUITE/spool/exim-daemon.pid -oPX search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (daemon) terminating with rc=0 >>>>>>>>>>>>>>>> -- cgit v1.2.1 From 1c261b90f627f0489f7dfcf1e66b46cce67f477d Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Sun, 21 Feb 2021 18:54:16 -0800 Subject: CVE-2020-28025: Heap out-of-bounds read in pdkim_finish_bodyhash() (cherry picked from commit cad30cd3fb96196e908e0d66b1b45fdf377c850c) --- src/src/pdkim/pdkim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 4c73d4fac..4320ecd49 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -825,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); -- cgit v1.2.1 From a53a7fcfb8216764e4420d8d263356b4ed7d5cef Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Sun, 21 Feb 2021 19:05:56 -0800 Subject: CVE-2020-28018: Use-after-free in tls-openssl.c (cherry picked from commit 6290686dd59d8158d100c67e8f96df27158a6fc5) --- src/src/tls-openssl.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 054b23d0c..499384b50 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -3675,16 +3675,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) { -- cgit v1.2.1 From dbc3ab675c2e5e2a07ed13dc5ede4daa018600e7 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Mon, 29 Mar 2021 22:16:28 +0200 Subject: CVE-2020-28010: Heap out-of-bounds write in main() Based on Phil Pennock's 0f57feb4. Done by Qualys, modified by me. (cherry picked from commit b0982c2776048948ebae48574b70fa487684cb8c) --- src/src/exim.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/src/exim.c b/src/src/exim.c index f7a45ff09..975b39a58 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -3839,7 +3839,6 @@ 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. */ -errno = 0; initial_cwd = os_getcwd(NULL, 0); if (!initial_cwd && errno) exim_fail("exim: getting initial cwd failed: %s\n", strerror(errno)); @@ -4133,11 +4132,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); -- cgit v1.2.1 From 08102cbe8102f99b31655aa0e926c45b427efe6d Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Sun, 21 Feb 2021 19:22:33 -0800 Subject: CVE-2020-28011: Heap buffer overflow in queue_run() (cherry picked from commit 6e1fb878e95f8e6f838ffde5258c7a969c981865) --- src/src/queue.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/src/queue.c b/src/src/queue.c index 37d612482..a93a7a55f 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -393,12 +393,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) -- cgit v1.2.1 From 71585e8fcb8704a9f431f5a8d019280cccaad069 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Mon, 29 Mar 2021 22:44:47 +0200 Subject: CVE-2020-28013: Heap buffer overflow in parse_fix_phrase() Based on Phil Pennock's 8a50c88a, done by Qualys (cherry picked from commit 8161c16ec7320ac6164954bade23179a0ed095eb) --- src/src/parse.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/src/parse.c b/src/src/parse.c index 3f1ba222f..32b42cd29 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -1129,12 +1129,7 @@ while (s < end) { if (ss >= end) ss--; *t++ = '('; - if (ss < s) - { - /* Someone has ended the string with "(". */ - ss = s; - } - else + if (ss > s) { Ustrncpy(t, s, ss-s); t += ss-s; -- cgit v1.2.1 From f9c58fb385343b8e3fa13988efcbd30ae3285ea7 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Mon, 29 Mar 2021 23:02:34 +0200 Subject: SECURITY: Refuse negative and large store allocations Based on Phil Pennock's commits b34d3046 and e6c1606a. Done by Qualys. (cherry picked from commit 09d36bd64fc5bf71d8882af35c41ac4e8599acc1) --- src/src/store.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/src/store.c b/src/src/store.c index 305ae6334..123df70ee 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -239,12 +239,10 @@ 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) - { +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 @@ -392,12 +390,10 @@ int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool; int inc = newsize - oldsize; int rounded_oldsize = oldsize; -if (newsize < 0) - { +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. */ @@ -759,6 +755,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); @@ -789,6 +790,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); + if (size < 16) size = 16; if (!(yield = malloc((size_t)size))) -- cgit v1.2.1 From 605716b999a4ca6c7d5777ab7463058e9b055dc2 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Mon, 29 Mar 2021 23:05:58 +0200 Subject: CVE-2020-28017: Integer overflow in receive_add_recipient() Based on Phil Pennock's commit e3b441f7. (cherry picked from commit 18a19e18242edc5ab2082fa9c41cd6210d1b6087) --- src/src/receive.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/src/receive.c b/src/src/receive.c index 5c6420286..cf3ef4b96 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -485,18 +485,18 @@ Returns: nothing void receive_add_recipient(uschar *recipient, int pno) { -/* XXX This is a math limit; we should consider a performance/sanity limit too. */ -const int safe_recipients_limit = INT_MAX / sizeof(recipient_item) - 1; - if (recipients_count >= recipients_list_max) { recipient_item *oldlist = recipients_list; int oldmax = recipients_list_max; - recipients_list_max = recipients_list_max ? 2*recipients_list_max : 50; - if ((recipients_list_max >= safe_recipients_limit) || (recipients_list_max < 0)) + + 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, "Too many recipients needed: %d not satisfiable", recipients_list_max); + 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 != NULL) memcpy(recipients_list, oldlist, oldmax * sizeof(recipient_item)); -- cgit v1.2.1 From 33d4c87653ddbbea9fd8cb8eb2ff78c149850006 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Mon, 29 Mar 2021 23:12:02 +0200 Subject: CVE-2020-28022: Heap out-of-bounds read and write in extract_option() Based on Phil Pennock's commit c5017adf. (cherry picked from commit 9e941e1807b624b255c9ec0f41a0b3a89e144de3) --- src/src/smtp_in.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 190064eed..a8b92d0be 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2031,30 +2031,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--; -v[1] = '\0'; +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 == '"') && (v > smtp_cmd_data + 1)) - 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; -- cgit v1.2.1 From 4daba4bec729a57fb0863af786a1395e70794c76 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Tue, 30 Mar 2021 22:03:49 +0200 Subject: CVE-2020-28026: Line truncation and injection in spool_read_header() This also fixes: 2/ In src/spool_in.c: 462 while ( (len = Ustrlen(big_buffer)) == big_buffer_size-1 463 && big_buffer[len-1] != '\n' 464 ) 465 { /* buffer not big enough for line; certs make this possible */ 466 uschar * buf; 467 if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR; 468 buf = store_get_perm(big_buffer_size *= 2, FALSE); 469 memcpy(buf, big_buffer, --len); The --len in memcpy() chops off a useful byte (we know for sure that big_buffer[len-1] is not a '\n' because we entered the while loop). Based on a patch done by Qualys. (cherry picked from commit f0c307458e1ee81abbe7ed2d4a8d16b5cbd8a799) --- src/src/spool_in.c | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 1433123c3..e67d0f4f6 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 * @@ -452,26 +481,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); @@ -764,7 +780,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; -- cgit v1.2.1 From fcddccd650178ceeec3655c6c40f420164a8706e Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Sun, 21 Feb 2021 21:26:53 -0800 Subject: CVE-2020-28015+28021: New-line injection into spool header file (cherry picked from commit 31b1a42d0bd29cb05f85e56d3343b13bef20a2bd) --- src/src/spool_out.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 0851ce956..a0c69952c 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) { @@ -221,7 +233,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"); @@ -294,19 +306,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); } -- cgit v1.2.1 From 1241deaefb71c40436320af7d0bd04c7c9e54241 Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Sun, 21 Feb 2021 21:45:19 -0800 Subject: CVE-2020-28009: Integer overflow in get_stdinput() (cherry picked from commit bbf1bb10bee5a1d7cbcc97f178b348189219eb7d) --- src/src/string.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/src/string.c b/src/src/string.c index f91a6a428..27e030bd8 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1091,7 +1091,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 @@ -1140,6 +1149,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; @@ -1149,8 +1162,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 -- cgit v1.2.1 From 638f7ca75694bcbb70cfbe7db2ef52af4aca5c83 Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Sun, 21 Feb 2021 21:49:30 -0800 Subject: CVE-2020-28024: Heap buffer underflow in smtp_ungetc() (cherry picked from commit 998e5a9db121c3eff15cac16859bdffd7adcbe57) --- src/src/smtp_in.c | 3 +++ src/src/tls.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index a8b92d0be..258ec03e4 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -831,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; } diff --git a/src/src/tls.c b/src/src/tls.c index e5aabc6b4..d37a8f9ff 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -157,6 +157,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; } -- cgit v1.2.1 From 645a31d16195bb6b73f0a0d0c04b2251e5b28421 Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Sun, 21 Feb 2021 21:53:55 -0800 Subject: CVE-2020-28012: Missing close-on-exec flag for privileged pipe (cherry picked from commit 72dad1e64bb3d1ff387938f59678098cab1f60a3) --- doc/doc-txt/ChangeLog | 3 +++ src/src/rda.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index e2843e3b6..a3bf92816 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -213,6 +213,9 @@ QS/01 Creation of (database) files in $spool_dir: only uid=0 or the uid of QS/02 PID file creation/deletion: only possible if uid=0 or uid is the Exim runtime user. +QS/03 When reading the output from interpreted forward files we do not + pass the pipe between the parent and the interpreting process to + executed child processes (if any). Exim version 4.94 ----------------- diff --git a/src/src/rda.c b/src/src/rda.c index aed8abc24..ce6e7a36d 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 -- cgit v1.2.1 From 3b8c0ceb7339329188e19efb907da950dbe691d1 Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Sun, 21 Feb 2021 22:09:06 -0800 Subject: SECURITY: Always exit when LOG_PANIC_DIE is set (cherry picked from commit e20aa895b37f449d5c81c3e7b102fc534b5d23ba) --- doc/doc-txt/ChangeLog | 4 ++++ src/src/log.c | 1 + 2 files changed, 5 insertions(+) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index a3bf92816..ee508faab 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -217,6 +217,10 @@ QS/03 When reading the output from interpreted forward files we do not pass the pipe between the parent and the interpreting process to executed child processes (if any). +QS/04 Always die if requested from internal logging, even is logging is + disabled. + + Exim version 4.94 ----------------- diff --git a/src/src/log.c b/src/src/log.c index 99eba5f90..07bf2ce7f 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -900,6 +900,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; } -- cgit v1.2.1 From d600f6c4d0c5d33e3988dfbfee248ff6a1536673 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Tue, 30 Mar 2021 22:48:06 +0200 Subject: SECURITY: Leave a clean smtp_out input buffer even in case of read error Based on Heiko Schlittermann's commit 54895bc3. This fixes: 7/ In src/smtp_out.c, read_response_line(), inblock->ptr is not updated when -1 is returned. This does not seem to have bad consequences, but is maybe not the intended behavior. (cherry picked from commit 30f5d98786fb4e6ccfdd112fe65c153f0ee34c5f) --- src/src/smtp_out.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index d6dfba52e..d1f69024e 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -425,7 +425,7 @@ if (ob->socks_proxy) { int sock = socks_sock_connect(sc->host, sc->host_af, port, sc->interface, sc->tblock, ob->connect_timeout); - + if (sock >= 0) { if (early_data && early_data->data && early_data->len) @@ -710,6 +710,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; } -- cgit v1.2.1 From c5f2f5cf2a6b45ae7ba0ed15e04fbe014727b210 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Tue, 30 Mar 2021 22:59:25 +0200 Subject: SECURITY: Avoid modification of constant data in dkim handling Based on Heiko Schlittermann's commits f880c7f3 and c118c7f4. This fixes: 6/ In src/pdkim/pdkim.c, pdkim_update_ctx_bodyhash() is sometimes called with a global orig_data and hence canon_data, and the following line can therefore modify data that should be constant: 773 canon_data->len = b->bodylength - b->signed_body_bytes; For example, the following proof of concept sets lineending.len to 0 (this should not be possible): (sleep 10; echo 'EHLO test'; sleep 3; echo 'MAIL FROM:<>'; sleep 3; echo 'RCPT TO:postmaster'; sleep 3; echo 'DATA'; date >&2; sleep 30; printf 'DKIM-Signature:a=rsa-sha1;c=simple/simple;l=0\r\n\r\n\r\nXXX\r\n.\r\n'; sleep 30) | nc -n -v 192.168.56.102 25 (gdb) print lineending $1 = {data = 0x55e18035b2ad "\r\n", len = 2} (gdb) print &lineending.len $3 = (size_t *) 0x55e180385948 (gdb) watch *(size_t *) 0x55e180385948 Hardware watchpoint 1: *(size_t *) 0x55e180385948 Old value = 2 New value = 0 (gdb) print lineending $5 = {data = 0x55e18035b2ad "\r\n", len = 0} (cherry picked from commit 92359a62a0e31734ad8069c66f64b37f9eaaccbe) --- src/src/pdkim/pdkim.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 4320ecd49..36e666fd9 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,9 @@ 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, const blob const * orig_data, blob * relaxed_data) +pdkim_update_ctx_bodyhash(pdkim_bodyhash * b, const blob * orig_data, blob * relaxed_data) { -const blob const * canon_data = orig_data; +const blob * canon_data = orig_data; size_t left; /* Defaults to simple canon (no further treatment necessary) */ @@ -771,9 +771,9 @@ 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 + left > b->bodylength + && left > (unsigned long)b->bodylength - b->signed_body_bytes ) - left = b->bodylength - b->signed_body_bytes; + left = (unsigned long)b->bodylength - b->signed_body_bytes; if (left > 0) { -- cgit v1.2.1 From 8b1e9bc2cac17ee24d595c97dcf97d9b016f8a46 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Mon, 12 Apr 2021 23:05:44 +0200 Subject: CVE-2020-28016: Heap out-of-bounds write in parse_fix_phrase() Based on Phil Pennock's commit 76a1ce77. Modified by Qualys. (cherry picked from commit f218fef171cbe9e61d10f15399aab8fa6956535b) --- src/src/parse.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/src/parse.c b/src/src/parse.c index 32b42cd29..086b010c3 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -979,12 +979,12 @@ if (i < len) /* No non-printers; use the RFC 822 quoting rules */ -if (!len) +if (len <= 0 || len >= INT_MAX/4) { - return string_copy_taint(US"", is_tainted(phrase)); + return string_copy_taint(CUS"", is_tainted(phrase)); } -buffer = store_get(len*4, is_tainted(phrase)); +buffer = store_get((len+1)*4, is_tainted(phrase)); s = phrase; end = s + len; -- cgit v1.2.1 From 93e9a18fbf09deb59bd133986f4c89aeb2d2d86a Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Tue, 23 Feb 2021 08:33:03 -0800 Subject: CVE-2020-28007: Link attack in Exim's log directory We patch this vulnerability by opening (instead of just creating) the log file in an unprivileged (exim) child process, and by passing this file descriptor back to the privileged (root) parent process. The two functions log_send_fd() and log_recv_fd() are inspired by OpenSSH's functions mm_send_fd() and mm_receive_fd(); thanks! This patch also fixes: - a NULL-pointer dereference in usr1_handler() (this signal handler is installed before process_log_path is initialized); - a file-descriptor leak in dmarc_write_history_file() (two return paths did not close history_file_fd). Note: the use of log_open_as_exim() in dmarc_write_history_file() should be fine because the documentation explicitly states "Make sure the directory of this file is writable by the user exim runs as." (cherry picked from commit 2502cc41d1d92c1413eca6a4ba035c21162662bd) --- src/src/dmarc.c | 179 ++++++++++++++++++++++--------------------- src/src/exim.c | 14 +--- src/src/functions.h | 3 +- src/src/log.c | 214 +++++++++++++++++++++++++++++++++------------------- test/stderr/0397 | 6 +- 5 files changed, 234 insertions(+), 182 deletions(-) diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 2e43f846d..ef2b3197e 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -223,6 +223,97 @@ return NULL; } +static int +dmarc_write_history_file() +{ +int tmp_ans; +u_char **rua; /* aggregate report addressees */ +uschar *history_buffer = NULL; + +if (!dmarc_history_file) + { + DEBUG(D_receive) debug_printf("DMARC history file not set\n"); + return DMARC_HIST_DISABLED; + } + +/* Generate the contents of the history file */ +history_buffer = string_sprintf( + "job %s\nreporter %s\nreceived %ld\nipaddr %s\nfrom %s\nmfrom %s\n", + message_id, primary_hostname, time(NULL), sender_host_address, + header_from_sender, expand_string(US"$sender_address_domain")); + +if (spf_response) + history_buffer = string_sprintf("%sspf %d\n", history_buffer, dmarc_spf_ares_result); + /* history_buffer = string_sprintf("%sspf -1\n", history_buffer); */ + +history_buffer = string_sprintf( + "%s%spdomain %s\npolicy %d\n", + history_buffer, dkim_history_buffer, dmarc_used_domain, dmarc_policy); + +if ((rua = opendmarc_policy_fetch_rua(dmarc_pctx, NULL, 0, 1))) + for (tmp_ans = 0; rua[tmp_ans]; tmp_ans++) + history_buffer = string_sprintf("%srua %s\n", history_buffer, rua[tmp_ans]); +else + history_buffer = string_sprintf("%srua -\n", history_buffer); + +opendmarc_policy_fetch_pct(dmarc_pctx, &tmp_ans); +history_buffer = string_sprintf("%spct %d\n", history_buffer, tmp_ans); + +opendmarc_policy_fetch_adkim(dmarc_pctx, &tmp_ans); +history_buffer = string_sprintf("%sadkim %d\n", history_buffer, tmp_ans); + +opendmarc_policy_fetch_aspf(dmarc_pctx, &tmp_ans); +history_buffer = string_sprintf("%saspf %d\n", history_buffer, tmp_ans); + +opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans); +history_buffer = string_sprintf("%sp %d\n", history_buffer, tmp_ans); + +opendmarc_policy_fetch_sp(dmarc_pctx, &tmp_ans); +history_buffer = string_sprintf("%ssp %d\n", history_buffer, tmp_ans); + +history_buffer = string_sprintf( + "%salign_dkim %d\nalign_spf %d\naction %d\n", + history_buffer, da, sa, action); + +/* Write the contents to the history file */ +DEBUG(D_receive) + debug_printf("DMARC logging history data for opendmarc reporting%s\n", + (host_checking || f.running_in_test_harness) ? " (not really)" : ""); +if (host_checking || f.running_in_test_harness) + { + DEBUG(D_receive) + debug_printf("DMARC history data for debugging:\n%s", history_buffer); + } +else + { + ssize_t written_len; + const int history_file_fd = log_open_as_exim(dmarc_history_file); + + if (history_file_fd < 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "failure to create DMARC history file: %s", + dmarc_history_file); + return DMARC_HIST_FILE_ERR; + } + + written_len = write_to_fd_buf(history_file_fd, + history_buffer, + Ustrlen(history_buffer)); + + (void)close(history_file_fd); + + if (written_len <= 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "failure to write to DMARC history file: %s", + dmarc_history_file); + return DMARC_HIST_WRITE_ERR; + } + } +return DMARC_HIST_OK; +} + + +>>>>>>> a378ff99d... CVE-2020-28007: Link attack in Exim's log directory /* dmarc_process adds the envelope sender address to the existing context (if any), retrieves the result, sets up expansion strings and evaluates the condition outcome. */ @@ -514,94 +605,6 @@ if (!f.dmarc_disable_verify) return OK; } -static int -dmarc_write_history_file() -{ -int history_file_fd; -ssize_t written_len; -int tmp_ans; -u_char **rua; /* aggregate report addressees */ -uschar *history_buffer = NULL; - -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); - -if (history_file_fd < 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, "failure to create DMARC history file: %s", - dmarc_history_file); - return DMARC_HIST_FILE_ERR; - } - -/* Generate the contents of the history file */ -history_buffer = string_sprintf( - "job %s\nreporter %s\nreceived %ld\nipaddr %s\nfrom %s\nmfrom %s\n", - message_id, primary_hostname, time(NULL), sender_host_address, - header_from_sender, expand_string(US"$sender_address_domain")); - -if (spf_response) - history_buffer = string_sprintf("%sspf %d\n", history_buffer, dmarc_spf_ares_result); - /* history_buffer = string_sprintf("%sspf -1\n", history_buffer); */ - -history_buffer = string_sprintf( - "%s%spdomain %s\npolicy %d\n", - history_buffer, dkim_history_buffer, dmarc_used_domain, dmarc_policy); - -if ((rua = opendmarc_policy_fetch_rua(dmarc_pctx, NULL, 0, 1))) - for (tmp_ans = 0; rua[tmp_ans]; tmp_ans++) - history_buffer = string_sprintf("%srua %s\n", history_buffer, rua[tmp_ans]); -else - history_buffer = string_sprintf("%srua -\n", history_buffer); - -opendmarc_policy_fetch_pct(dmarc_pctx, &tmp_ans); -history_buffer = string_sprintf("%spct %d\n", history_buffer, tmp_ans); - -opendmarc_policy_fetch_adkim(dmarc_pctx, &tmp_ans); -history_buffer = string_sprintf("%sadkim %d\n", history_buffer, tmp_ans); - -opendmarc_policy_fetch_aspf(dmarc_pctx, &tmp_ans); -history_buffer = string_sprintf("%saspf %d\n", history_buffer, tmp_ans); - -opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans); -history_buffer = string_sprintf("%sp %d\n", history_buffer, tmp_ans); - -opendmarc_policy_fetch_sp(dmarc_pctx, &tmp_ans); -history_buffer = string_sprintf("%ssp %d\n", history_buffer, tmp_ans); - -history_buffer = string_sprintf( - "%salign_dkim %d\nalign_spf %d\naction %d\n", - history_buffer, da, sa, action); - -/* Write the contents to the history file */ -DEBUG(D_receive) - debug_printf("DMARC logging history data for opendmarc reporting%s\n", - (host_checking || f.running_in_test_harness) ? " (not really)" : ""); -if (host_checking || f.running_in_test_harness) - { - DEBUG(D_receive) - debug_printf("DMARC history data for debugging:\n%s", history_buffer); - } -else - { - written_len = write_to_fd_buf(history_file_fd, - history_buffer, - Ustrlen(history_buffer)); - if (written_len == 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, "failure to write to DMARC history file: %s", - dmarc_history_file); - return DMARC_HIST_WRITE_ERR; - } - (void)close(history_file_fd); - } -return DMARC_HIST_OK; -} - - uschar * dmarc_exim_expand_query(int what) { diff --git a/src/src/exim.c b/src/src/exim.c index 975b39a58..29e457fe2 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 diff --git a/src/src/functions.h b/src/src/functions.h index fc4e0444d..e22fd4f99 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -308,8 +308,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); diff --git a/src/src/log.c b/src/src/log.c index 07bf2ce7f..5d36b4983 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -264,14 +264,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. */ @@ -285,11 +290,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; @@ -297,6 +298,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 * *************************************************/ @@ -312,41 +388,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; } @@ -459,52 +554,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 diff --git a/test/stderr/0397 b/test/stderr/0397 index 82f143787..4d9dcaa47 100644 --- a/test/stderr/0397 +++ b/test/stderr/0397 @@ -1,7 +1,7 @@ -1999-03-02 09:44:33 Cannot open main log file "/non/existent/path/to/force/failure/main": No such file or directory: euid=uuuu egid=EXIM_GID +1999-03-02 09:44:33 Cannot open main log file "/non/existent/path/to/force/failure/main": Permission denied: euid=uuuu egid=EXIM_GID 1999-03-02 09:44:33 Start queue run: pid=pppp -1999-03-02 09:44:33 Cannot open main log file "/non/existent/path/to/force/failure/main": No such file or directory: euid=uuuu egid=EXIM_GID +1999-03-02 09:44:33 Cannot open main log file "/non/existent/path/to/force/failure/main": Permission denied: euid=uuuu egid=EXIM_GID SYSLOG: '2017-07-30 18:51:05 Start queue run: pid=pppp' -SYSLOG: '2017-07-30 18:51:05 Cannot open main log file "/non/existent/path/to/force/failure/main": No such file or directory: euid=uuuu egid=EXIM_GID' +SYSLOG: '2017-07-30 18:51:05 Cannot open main log file "/non/existent/path/to/force/failure/main": Permission denied: euid=uuuu egid=EXIM_GID' SYSLOG: 'exim: could not open panic log - aborting: see message(s) above' exim: could not open panic log - aborting: see message(s) above -- cgit v1.2.1 From fee1a06ec05e58e0cda8cf04f28240688736f945 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Wed, 21 Apr 2021 07:52:39 +0200 Subject: Update upgrade notes and source about use of seteuid() (cherry picked from commit bc13bbca6e07267dfe0c4d275bb0a2e9aabf1dfb) --- doc/doc-txt/Exim4.upgrade | 18 +++++++++++------- src/src/deliver.c | 6 +++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/doc/doc-txt/Exim4.upgrade b/doc/doc-txt/Exim4.upgrade index 528d94d9c..86d4a4dda 100644 --- a/doc/doc-txt/Exim4.upgrade +++ b/doc/doc-txt/Exim4.upgrade @@ -468,11 +468,12 @@ Generic Router Options . The way that require_files works has been changed. Each item in the list is now separately expanded as the test proceeds. The use of leading ! and + characters is unchanged. However, user and group checking is done differently. - Previously, seteuid() was used, but seteuid() is no longer used in Exim (see - "Security" below). Instead, Exim now scans along the components of the file - path and checks the access for the given uid and gid. It expects "x" access - on directories and "r" on the final file. This means that file access control - lists (on those operating systems that have them) are ignored. + Previously, seteuid() was used, but seteuid() is no longer used (see + "Security" below) for checking the files required by this option. Instead, + Exim now scans along the components of the file path and checks the access + for the given uid and gid. It expects "x" access on directories and "r" on + the final file. This means that file access control lists (on those + operating systems that have them) are ignored. Other Consequences of the Director/Router Merge @@ -1380,8 +1381,11 @@ Security -------- Exim 3 could be run in a variety of ways as far as security was concerned. This -has all been simplified in Exim 4. The security-conscious might like to know -that it no longer makes any use of the seteuid() function. +has all been simplified in Exim 4. Exim dropped the use of seteuid() in +most places. But recent (2020-10/2021-04) vulnerabilities forced us to +re-introduce seteuid() for opening the database files (hint files) as secure as +possible. For future (>= 4.95) versions we work on a solution that +does not need the seteuid call. . A UID and GID are required to be specified when Exim is compiled. They can be now specified by name as well as by number, so the relevant options are now diff --git a/src/src/deliver.c b/src/src/deliver.c index 389f2fb4c..f5f065e63 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2096,9 +2096,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. -- cgit v1.2.1 From b7ee9ba69039aca48704decc1c13b2618a8d96bc Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Wed, 21 Apr 2021 09:09:13 +0200 Subject: Remove merge artifact --- src/src/dmarc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/src/dmarc.c b/src/src/dmarc.c index ef2b3197e..8d734ddeb 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -313,7 +313,6 @@ return DMARC_HIST_OK; } ->>>>>>> a378ff99d... CVE-2020-28007: Link attack in Exim's log directory /* dmarc_process adds the envelope sender address to the existing context (if any), retrieves the result, sets up expansion strings and evaluates the condition outcome. */ -- cgit v1.2.1 From 611143d73ce446cb34c44e3dac2c7f321acaa225 Mon Sep 17 00:00:00 2001 From: Qualys Security Advisory Date: Sun, 21 Feb 2021 19:11:55 -0800 Subject: CVE-2020-28023: Out-of-bounds read in smtp_setup_msg() Extracted from Jeremy Harris's commit afaf5a50. --- src/src/acl.c | 3 ++- src/src/macros.h | 1 + src/src/smtp_in.c | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/src/acl.c b/src/src/acl.c index 9fe9e5145..7061230b4 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -4473,7 +4473,8 @@ switch (where) /* Drop cutthrough conns, and drop heldopen verify conns if the previous was not DATA */ { - uschar prev = smtp_connection_had[smtp_ch_index-2]; + uschar prev = + smtp_connection_had[SMTP_HBUFF_PREV(SMTP_HBUFF_PREV(smtp_ch_index))]; BOOL dropverify = !(prev == SCH_DATA || prev == SCH_BDAT); cancel_cutthrough_connection(dropverify, US"quit or conndrop"); diff --git a/src/src/macros.h b/src/src/macros.h index f83ba1933..b2f86ed53 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -150,6 +150,7 @@ enough to hold all the headers from a normal kind of message. */ /* The size of the circular buffer that remembers recent SMTP commands */ #define SMTP_HBUFF_SIZE 20 +#define SMTP_HBUFF_PREV(n) ((n) ? (n)-1 : SMTP_HBUFF_SIZE-1) /* 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 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 258ec03e4..e57059a51 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -5364,10 +5364,10 @@ while (done <= 0) } if (f.smtp_in_pipelining_advertised && last_was_rcpt) smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE, - smtp_names[smtp_connection_had[smtp_ch_index-1]]); + smtp_names[smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)]]); else done = synprot_error(L_smtp_protocol_error, 503, NULL, - smtp_connection_had[smtp_ch_index-1] == SCH_DATA + smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)] == SCH_DATA ? US"valid RCPT command must precede DATA" : US"valid RCPT command must precede BDAT"); -- cgit v1.2.1 From 39d83bf19fc0c4364e0a665360b14194c62e4ab4 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Mon, 19 Apr 2021 22:23:14 +0200 Subject: Honour the outcome of parse_extract_address(), testsuite 471 --- src/src/exim.c | 9 +++-- src/src/receive.c | 9 +++-- src/src/rewrite.c | 15 +++++++- test/log/0471 | 1 + test/scripts/0000-Basic/0471 | 4 ++ test/stderr/0471 | 89 +++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 111 insertions(+), 16 deletions(-) diff --git a/src/src/exim.c b/src/src/exim.c index 29e457fe2..ee75739ec 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -5540,11 +5540,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) { diff --git a/src/src/receive.c b/src/src/receive.c index cf3ef4b96..6eec73e2b 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2562,11 +2562,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 diff --git a/src/src/rewrite.c b/src/src/rewrite.c index db9a3ff07..781a2a33c 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -471,8 +471,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; @@ -483,16 +484,26 @@ while (*s) *ss = 0; recipient = parse_extract_address(s,&errmess,&start,&end,&domain,FALSE); + *ss = terminator; sprev = s; s = ss + (terminator? 1:0); 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, they 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 (rewrite_rules || routed_old) + { + log_write(0, LOG_MAIN, "rewrite: %s", errmess); + exim_exit(EXIT_FAILURE); + } loop_reset_point = store_reset(loop_reset_point); continue; } diff --git a/test/log/0471 b/test/log/0471 index fc09b4692..96d495748 100644 --- a/test/log/0471 +++ b/test/log/0471 @@ -1,3 +1,4 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for r1@test.ex 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for r2@test.ex 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for r3@test.ex +1999-03-02 09:44:33 10HmbA-0005vi-00 rewrite: address is ridiculously long: localpart_with_256_chars_567890123456789012345678901234567890123... diff --git a/test/scripts/0000-Basic/0471 b/test/scripts/0000-Basic/0471 index e01ca28d2..9cf52943f 100644 --- a/test/scripts/0000-Basic/0471 +++ b/test/scripts/0000-Basic/0471 @@ -167,6 +167,10 @@ exim -d -odq r2@test.ex To: localpart_with_056_chars_56789012345678901234567890123456@test.example **** exim -d -odq r3@test.ex +To: localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example +**** +1 +exim -d -odq r4@test.ex To: localpart_with_256_chars_5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example **** no_msglog_check diff --git a/test/stderr/0471 b/test/stderr/0471 index 8fd0a8660..574cb02db 100644 --- a/test/stderr/0471 +++ b/test/stderr/0471 @@ -25882,7 +25882,7 @@ Recipients: r3@test.ex search_tidyup called >>Headers received: -To: localpart_with_256_chars_5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example +To: localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example address match test: subject=r3@test.ex pattern=^.{40,}@* r3@test.ex in "^.{40,}@*"? no (end of list) @@ -25937,11 +25937,11 @@ CALLER@myhost.test.ex in "*@*"? yes (matched "*@*") lookup failed rewritten sender = CALLER@myhost.test.ex rewrite_one_header: type=T: - To: localpart_with_256_chars_5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example -address match test: subject=localpart_with_256_chars_5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example pattern=^.{40,}@* -localpart_with_256_chars_5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example in "^.{40,}@*"? yes (matched "^.{40,}@*") + To: localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example +address match test: subject=localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example pattern=^.{40,}@* +localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example in "^.{40,}@*"? yes (matched "^.{40,}@*") LOG: address_rewrite MAIN - "localpart_with_256_chars_5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example" from to: rewritten as "deny_me@test.example" by rule 1 + "localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example" from to: rewritten as "deny_me@test.example" by rule 1 address match test: subject=deny_me@test.example pattern=*@* test.example in "*"? yes (matched "*") deny_me@test.example in "*@*"? yes (matched "*@*") @@ -25995,7 +25995,7 @@ CALLER@myhost.test.ex in "*@*"? yes (matched "*@*") lookup failed search_tidyup called >>Headers after rewriting and local additions: -* To: localpart_with_256_chars_5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example +* To: localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example T To: deny_me@rwtest.example I Message-Id: F From: CALLER_NAME @@ -26016,3 +26016,80 @@ LOG: MAIN <= CALLER@myhost.test.ex U=CALLER P=local S=sss search_tidyup called >>>>>>>>>>>>>>>> Exim pid=pppp (fresh-exec) terminating with rc=0 >>>>>>>>>>>>>>>> +Exim version x.yz .... +changed uid/gid: forcing real = effective + uid=uuuu gid=CALLER_GID pid=pppp +configuration file is TESTSUITE/test-config +admin user +changed uid/gid: privilege not needed + uid=EXIM_UID gid=EXIM_GID pid=pppp +originator: uid=CALLER_UID gid=CALLER_GID login=CALLER name=CALLER_NAME +sender address = CALLER@myhost.test.ex +set_process_info: pppp accepting a local non-SMTP message from +spool directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 msg_size = 0 +log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 +Sender: CALLER@myhost.test.ex +Recipients: + r4@test.ex +search_tidyup called +>>Headers received: +To: localpart_with_256_chars_5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example + +address match test: subject=r4@test.ex pattern=^.{40,}@* +r4@test.ex in "^.{40,}@*"? no (end of list) +address match test: subject=r4@test.ex pattern=*@* +test.ex in "*"? yes (matched "*") +r4@test.ex in "*@*"? yes (matched "*@*") + search_open: lsearch "TESTSUITE/aux-fixed/0471.rw" + search_find: file="TESTSUITE/aux-fixed/0471.rw" + key="test.ex" partial=2 affix=*. starflags=0 opts=NULL + LRU list: + 0TESTSUITE/aux-fixed/0471.rw + End + internal_search_find: file="TESTSUITE/aux-fixed/0471.rw" + type=lsearch key="test.ex" opts=NULL + file lookup required for test.ex + in TESTSUITE/aux-fixed/0471.rw + lookup failed + trying partial match *.test.ex + internal_search_find: file="TESTSUITE/aux-fixed/0471.rw" + type=lsearch key="*.test.ex" opts=NULL + file lookup required for *.test.ex + in TESTSUITE/aux-fixed/0471.rw + lookup failed +address match test: subject=CALLER@myhost.test.ex pattern=^.{40,}@* +CALLER@myhost.test.ex in "^.{40,}@*"? no (end of list) +address match test: subject=CALLER@myhost.test.ex pattern=*@* +myhost.test.ex in "*"? yes (matched "*") +CALLER@myhost.test.ex in "*@*"? yes (matched "*@*") + search_open: lsearch "TESTSUITE/aux-fixed/0471.rw" + cached open + search_find: file="TESTSUITE/aux-fixed/0471.rw" + key="myhost.test.ex" partial=2 affix=*. starflags=0 opts=NULL + LRU list: + 0TESTSUITE/aux-fixed/0471.rw + End + internal_search_find: file="TESTSUITE/aux-fixed/0471.rw" + type=lsearch key="myhost.test.ex" opts=NULL + file lookup required for myhost.test.ex + in TESTSUITE/aux-fixed/0471.rw + lookup failed + trying partial match *.myhost.test.ex + internal_search_find: file="TESTSUITE/aux-fixed/0471.rw" + type=lsearch key="*.myhost.test.ex" opts=NULL + file lookup required for *.myhost.test.ex + in TESTSUITE/aux-fixed/0471.rw + lookup failed + trying partial match *.test.ex + internal_search_find: file="TESTSUITE/aux-fixed/0471.rw" + type=lsearch key="*.test.ex" opts=NULL + cached data used for lookup of *.test.ex + in TESTSUITE/aux-fixed/0471.rw + lookup failed +rewritten sender = CALLER@myhost.test.ex +rewrite_one_header: type=T: + To: localpart_with_256_chars_5678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example +LOG: MAIN + rewrite: address is ridiculously long: localpart_with_256_chars_567890123456789012345678901234567890123... +search_tidyup called +>>>>>>>>>>>>>>>> Exim pid=pppp (fresh-exec) terminating with rc=1 >>>>>>>>>>>>>>>> -- cgit v1.2.1 From 506286c62b8786a926dafb5bb05d3103492b86bc Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Mon, 26 Apr 2021 16:16:49 +0200 Subject: rewrite: revert to unchecked result of parse_extract_address() Now it breaks 471, and overlong addresses won't make it into the rewrite process, as they are handled as empty. --- src/src/rewrite.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 781a2a33c..f720f99e2 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -492,18 +492,34 @@ while (*s) /* There isn't much we can do for syntactic disasters at this stage. Pro tem (possibly for ever) ignore them. - If we got nothing, they there was any sort of error: non-parsable address, + 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; } -- cgit v1.2.1 From 6429b0fc79595f120703c022ae99aa10d698f909 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Mon, 26 Apr 2021 18:54:28 +0200 Subject: Cleanup docs on cve-2020-qualys, point to the Exim website --- doc/doc-txt/cve-2020-qualys | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 doc/doc-txt/cve-2020-qualys diff --git a/doc/doc-txt/cve-2020-qualys b/doc/doc-txt/cve-2020-qualys new file mode 100644 index 000000000..d5716444d --- /dev/null +++ b/doc/doc-txt/cve-2020-qualys @@ -0,0 +1,2 @@ +For the vulnerabilites reported by Qualys in October 2020 please see the +Exim Website: https://exim.org/static/doc/security/CVE-2020-qualys/ -- cgit v1.2.1