summaryrefslogtreecommitdiffstats
path: root/src/global/verify_sender_addr.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:46:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:46:30 +0000
commitb5896ba9f6047e7031e2bdee0622d543e11a6734 (patch)
treefd7b460593a2fee1be579bec5697e6d887ea3421 /src/global/verify_sender_addr.c
parentInitial commit. (diff)
downloadpostfix-upstream.tar.xz
postfix-upstream.zip
Adding upstream version 3.4.23.upstream/3.4.23upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/global/verify_sender_addr.c')
-rw-r--r--src/global/verify_sender_addr.c341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/global/verify_sender_addr.c b/src/global/verify_sender_addr.c
new file mode 100644
index 0000000..e818f90
--- /dev/null
+++ b/src/global/verify_sender_addr.c
@@ -0,0 +1,341 @@
+/*++
+/* NAME
+/* verify_sender_addr 3
+/* SUMMARY
+/* time-dependent probe sender addresses
+/* SYNOPSIS
+/* #include <verify_sender_addr.h>
+/*
+/* char *var_verify_sender;
+/* int var_verify_sender_ttl;
+/*
+/* const char *make_verify_sender_addr()
+/*
+/* const char *valid_verify_sender_addr(addr)
+/* const char *addr;
+/* DESCRIPTION
+/* This module computes or verifies a constant or time-dependent
+/* sender address for an address verification probe. The
+/* time-dependent portion is appended to the address localpart
+/* specified with the address_verify_sender parameter.
+/*
+/* When the address_verify_sender parameter is empty or <>,
+/* the sender address is always the empty address (i.e. always
+/* time-independent).
+/*
+/* The caller must initialize the address_verify_sender and
+/* address_verify_sender_ttl parameter values.
+/*
+/* make_verify_sender_addr() generates an envelope sender
+/* address for an address verification probe.
+/*
+/* valid_verify_sender_addr() verifies that the given address
+/* is a valid sender address for address verification probes.
+/* When probe sender addresses are configured to be time-dependent,
+/* the given address is allowed to differ by +/-1 TTL unit
+/* from the expected address. The result is a null pointer
+/* when no match is found. Otherwise, the result is the sender
+/* address without the time-dependent portion; this is the
+/* address that should be used for further delivery.
+/* DIAGNOSTICS
+/* Fatal errors: malformed address_verify_sender value; out
+/* of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <events.h>
+#include <stringops.h>
+
+/* Global library */
+
+#include <mail_params.h>
+#include <rewrite_clnt.h>
+#include <safe_ultostr.h>
+#include <verify_sender_addr.h>
+
+/* Application-specific. */
+
+ /*
+ * We convert the time-dependent portion to a safe string (no vowels) in a
+ * reversible manner, so that we can check an incoming address against the
+ * current and +/-1 TTL time slot. This allows for some time slippage
+ * between multiple MTAs that handle mail for the same site. We use base 31
+ * so that the time stamp contains B-Z0-9. This simplifies regression tests.
+ */
+#define VERIFY_BASE 31
+
+ /*
+ * We append the time-dependent portion to the localpart of the the address
+ * verification probe sender address, so that the result has the form
+ * ``fixed1variable@fixed2''. There is no delimiter between ``fixed1'' and
+ * ``variable'', because that could make "old" time stamps valid depending
+ * on how the recipient_delimiter feature is configured. The fixed text is
+ * taken from var_verify_sender with perhaps domain information appended
+ * during address canonicalization. The variable part of the address changes
+ * every var_verify_sender_ttl seconds.
+ */
+char *var_verify_sender; /* "bare" probe sender address */
+int var_verify_sender_ttl; /* time between address changes */
+
+ /*
+ * Scaffolding for stand-alone testing.
+ */
+#ifdef TEST
+#undef event_time
+#define event_time() verify_time
+static unsigned long verify_time;
+
+#endif
+
+#define VERIFY_SENDER_ADDR_EPOCH() (event_time() / var_verify_sender_ttl)
+
+ /*
+ * SLMs.
+ */
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* make_verify_sender_addr - generate address_verify_sender address */
+
+const char *make_verify_sender_addr(void)
+{
+ static VSTRING *verify_sender_buf; /* the complete sender address */
+ static VSTRING *my_epoch_buf; /* scratch space */
+ char *my_at_domain;
+
+ /*
+ * The null sender is always time-independent.
+ */
+ if (*var_verify_sender == 0 || strcmp(var_verify_sender, "<>") == 0)
+ return ("");
+
+ /*
+ * Sanity check.
+ */
+ if (*var_verify_sender == '@')
+ msg_fatal("parameter %s: value \"%s\" must not start with '@'",
+ VAR_VERIFY_SENDER, var_verify_sender);
+ if ((my_at_domain = strchr(var_verify_sender, '@')) != 0 && my_at_domain[1] == 0)
+ msg_fatal("parameter %s: value \"%s\" must not end with '@'",
+ VAR_VERIFY_SENDER, var_verify_sender);
+
+ /*
+ * One-time initialization.
+ */
+ if (verify_sender_buf == 0) {
+ verify_sender_buf = vstring_alloc(10);
+ my_epoch_buf = vstring_alloc(10);
+ }
+
+ /*
+ * Start with the bare sender address.
+ */
+ vstring_strcpy(verify_sender_buf, var_verify_sender);
+
+ /*
+ * Append the time stamp to the address localpart, encoded in some
+ * non-decimal form for obscurity.
+ *
+ * XXX It would be nice to have safe_ultostr() append-only support.
+ */
+ if (var_verify_sender_ttl > 0) {
+ /* Strip the @domain portion, if applicable. */
+ if (my_at_domain != 0)
+ vstring_truncate(verify_sender_buf,
+ (ssize_t) (my_at_domain - var_verify_sender));
+ /* Append the time stamp to the address localpart. */
+ vstring_sprintf_append(verify_sender_buf, "%s",
+ safe_ultostr(my_epoch_buf,
+ VERIFY_SENDER_ADDR_EPOCH(),
+ VERIFY_BASE, 0, 0));
+ /* Add back the @domain, if applicable. */
+ if (my_at_domain != 0)
+ vstring_sprintf_append(verify_sender_buf, "%s", my_at_domain);
+ }
+
+ /*
+ * Rewrite the address to canonical form.
+ */
+ rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, STR(verify_sender_buf),
+ verify_sender_buf);
+
+ return (STR(verify_sender_buf));
+}
+
+/* valid_verify_sender_addr - decide if address matches time window +/-1 */
+
+const char *valid_verify_sender_addr(const char *their_addr)
+{
+ static VSTRING *time_indep_sender_buf; /* sender without time stamp */
+ ssize_t base_len;
+ unsigned long my_epoch;
+ unsigned long their_epoch;
+ char *my_at_domain;
+ char *their_at_domain;
+ char *cp;
+
+ /*
+ * The null address is always time-independent.
+ */
+ if (*var_verify_sender == 0 || strcmp(var_verify_sender, "<>") == 0)
+ return (*their_addr ? 0 : "");
+
+ /*
+ * One-time initialization. Generate the time-independent address that we
+ * will return if the match is successful. This address is also used as a
+ * matching template.
+ */
+ if (time_indep_sender_buf == 0) {
+ time_indep_sender_buf = vstring_alloc(10);
+ vstring_strcpy(time_indep_sender_buf, var_verify_sender);
+ rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, STR(time_indep_sender_buf),
+ time_indep_sender_buf);
+ }
+
+ /*
+ * Check the time-independent sender localpart.
+ */
+ if ((my_at_domain = strchr(STR(time_indep_sender_buf), '@')) != 0)
+ base_len = my_at_domain - STR(time_indep_sender_buf);
+ else
+ base_len = LEN(time_indep_sender_buf);
+ if (strncasecmp_utf8(STR(time_indep_sender_buf), their_addr, base_len) != 0)
+ return (0); /* sender localpart mis-match */
+
+ /*
+ * Check the time-independent domain.
+ */
+ if ((their_at_domain = strchr(their_addr, '@')) == 0 && my_at_domain != 0)
+ return (0); /* sender domain mis-match */
+ if (their_at_domain != 0
+ && (my_at_domain == 0
+ || strcasecmp_utf8(their_at_domain, my_at_domain) != 0))
+ return (0); /* sender domain mis-match */
+
+ /*
+ * Check the time-dependent portion.
+ */
+ if (var_verify_sender_ttl > 0) {
+ their_epoch = safe_strtoul(their_addr + base_len, &cp, VERIFY_BASE);
+ if ((*cp != '@' && *cp != 0)
+ || (their_epoch == ULONG_MAX && errno == ERANGE))
+ return (0); /* malformed time stamp */
+ my_epoch = VERIFY_SENDER_ADDR_EPOCH();
+ if (their_epoch < my_epoch - 1 || their_epoch > my_epoch + 1)
+ return (0); /* outside time window */
+ }
+
+ /*
+ * No time-dependent portion.
+ */
+ else {
+ if (their_addr[base_len] != '@' && their_addr[base_len] != 0)
+ return (0); /* garbage after sender base */
+ }
+ return (STR(time_indep_sender_buf));
+}
+
+ /*
+ * Proof-of-concept test program. Read test address_verify_sender and
+ * address_verify_sender_ttl values from stdin, and report results that we
+ * would get on stdout.
+ */
+#ifdef TEST
+
+#include <stdlib.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <vstring_vstream.h>
+#include <mail_conf.h>
+#include <conv_time.h>
+
+int main(int argc, char **argv)
+{
+ const char *verify_sender;
+ const char *valid_sender;
+
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+
+ /*
+ * Prepare to talk to the address rewriting service.
+ */
+ mail_conf_read();
+ vstream_printf("using config files in %s\n", var_config_dir);
+ if (chdir(var_queue_dir) < 0)
+ msg_fatal("chdir %s: %m", var_queue_dir);
+
+ /*
+ * Parse JCL.
+ */
+ if (argc != 3)
+ msg_fatal("usage: %s address_verify_sender address_verify_sender_ttl",
+ argv[0]);
+ var_verify_sender = argv[1];
+ if (conv_time(argv[2], &var_verify_sender_ttl, 's') == 0)
+ msg_fatal("bad time value: %s", argv[2]);
+ verify_time = time((time_t *) 0);
+
+ /*
+ * Compute the current probe sender address.
+ */
+ verify_sender = make_verify_sender_addr();
+
+ /*
+ * Check two past time slots.
+ */
+ if (var_verify_sender_ttl > 0) {
+ verify_time -= 2 * var_verify_sender_ttl;
+ vstream_printf("\"%s\" matches prev2: \"%s\"\n", verify_sender,
+ (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ?
+ valid_sender : "nope");
+ verify_time += var_verify_sender_ttl;
+ vstream_printf("\"%s\" matches prev1: \"%s\"\n", verify_sender,
+ (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ?
+ valid_sender : "nope");
+ verify_time += var_verify_sender_ttl;
+ }
+
+ /*
+ * Check the current time slot.
+ */
+ vstream_printf("\"%s\" matches self: \"%s\"\n", verify_sender,
+ (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ?
+ valid_sender : "nope");
+
+ /*
+ * Check two future time slots.
+ */
+ if (var_verify_sender_ttl > 0) {
+ verify_time += var_verify_sender_ttl;
+ vstream_printf("\"%s\" matches next1: \"%s\"\n", verify_sender,
+ (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ?
+ valid_sender : "nope");
+ verify_time += var_verify_sender_ttl;
+ vstream_printf("\"%s\" matches next2: \"%s\"\n", verify_sender,
+ (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ?
+ valid_sender : "nope");
+ }
+ vstream_fflush(VSTREAM_OUT);
+ exit(0);
+}
+
+#endif