From a848231ae0f346dc7cc000973fbeb65b0894ee92 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 21:59:03 +0200 Subject: Adding upstream version 3.8.5. Signed-off-by: Daniel Baumann --- src/global/login_sender_match.c | 364 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 364 insertions(+) create mode 100644 src/global/login_sender_match.c (limited to 'src/global/login_sender_match.c') diff --git a/src/global/login_sender_match.c b/src/global/login_sender_match.c new file mode 100644 index 0000000..e263762 --- /dev/null +++ b/src/global/login_sender_match.c @@ -0,0 +1,364 @@ +/*++ +/* NAME +/* login_sender_match 3 +/* SUMMARY +/* match login and sender against (login, sender) patterns +/* SYNOPSIS +/* #include +/* +/* typedef LOGIN_SENDER_MATCH LOGIN_SENDER_MATCH; +/* +/* LOGIN_SENDER_MATCH *login_sender_create( +/* const char *title, +/* const char *map_names, +/* const char *ext_delimiters, +/* const char *null_sender, +/* const char *wildcard) +/* +/* void login_sender_free( +/* LOGIN_SENDER_MATCH *lsm) +/* +/* int login_sender_match( +/* LOGIN_SENDER_MATCH *lsm, +/* const char *login_name, +/* const char *sender_addr) +/* DESCRIPTION +/* This module determines if a login name and internal-form +/* sender address match a (login name, external-form sender +/* patterns) table entry. A login name matches if it matches +/* a lookup table key. A sender address matches the corresponding +/* table entry if it matches a sender pattern. A wildcard +/* sender pattern matches any sender address. A sender pattern +/* that starts with '@' matches the '@' and the domain portion +/* of a sender address. Otherwise, the matcher ignores the +/* extension part of a sender address, and requires a +/* case-insensitive match against a sender pattern. +/* +/* login_sender_create() creates a (login name, sender patterns) +/* matcher. +/* +/* login_sender_free() destroys the specified (login name, +/* sender patterns) matcher. +/* +/* login_sender_match() looks up an entry for the \fBlogin_name\fR +/* argument, and determines if the lookup result matches the +/* \fBsender_adddr\fR argument. +/* +/* Arguments: +/* .IP title +/* The name of the configuration parameter that specifies the +/* map_names value, used for error messages. +/* .IP map_names +r* The lookup table(s) with (login name, sender patterns) entries. +/* .IP ext_delimiters +/* The set of address extension delimiters. +/* .IP null_sender +/* If a sender pattern equals the null_sender pattern, then +/* the empty address is matched. +/* .IP wildcard +/* Null pointer, or non-empty string with a wildcard pattern. +/* If a sender pattern equals the wildcard pattern, then any +/* sender address is matched. +/* .IP login_name +/* The login name (for example, UNIX account, or SASL username) +/* that will be used as a search key to locate a list of senders. +/* .IP sender_addr +/* The sender email address (unquoted form) that will be matched +/* against a (login name, sender patterns) table entry. +/* DIAGNOSTICS +/* login_sender_match() returns LSM_STAT_FOUND if a +/* match was found, LOGIN_STAT_NOTFOUND if no match was found, +/* LSM_STAT_RETRY if the table lookup failed, or +/* LSM_STAT_CONFIG in case of a configuration error. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include +#include +#include + + /* + * Global library. + */ +#include +#include +#include +#include +#include + + /* + * Private data structure. + */ +struct LOGIN_SENDER_MATCH { + MAPS *maps; + VSTRING *ext_stripped_sender; + char *ext_delimiters; + char *null_sender; + char *wildcard; +}; + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) + +/* login_sender_create - create (login name, sender patterns) matcher */ + +LOGIN_SENDER_MATCH *login_sender_create(const char *title, + const char *map_names, + const char *ext_delimiters, + const char *null_sender, + const char *wildcard) +{ + LOGIN_SENDER_MATCH *lsm = mymalloc(sizeof *lsm); + + lsm->maps = maps_create(title, map_names, DICT_FLAG_LOCK + | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST); + lsm->ext_stripped_sender = vstring_alloc(100); + lsm->ext_delimiters = mystrdup(ext_delimiters); + if (null_sender == 0 || *null_sender == 0) + msg_panic("login_sender_create: null or empty null_sender"); + lsm->null_sender = mystrdup(null_sender); + lsm->wildcard = (wildcard && *wildcard) ? mystrdup(wildcard) : 0; + return (lsm); +} + +/* login_sender_free - destroy (login name, sender patterns) matcher */ + +void login_sender_free(LOGIN_SENDER_MATCH *lsm) +{ + maps_free(lsm->maps); + vstring_free(lsm->ext_stripped_sender); + myfree(lsm->ext_delimiters); + myfree(lsm->null_sender); + if (lsm->wildcard) + myfree(lsm->wildcard); + myfree((void *) lsm); +} + +/* strip_externalize_addr - strip address extension and externalize remainder */ + +static VSTRING *strip_externalize_addr(VSTRING *ext_addr, const char *int_addr, + const char *delims) +{ + char *int_stripped_addr; + + if ((int_stripped_addr = strip_addr_internal(int_addr, + /* extension= */ (char **) 0, + delims)) != 0) { + quote_822_local(ext_addr, int_stripped_addr); + myfree(int_stripped_addr); + return (ext_addr); + } else { + return quote_822_local(ext_addr, int_addr); + } +} + +/* login_sender_match - match login and sender against (login, senders) table */ + +int login_sender_match(LOGIN_SENDER_MATCH *lsm, const char *login_name, + const char *sender_addr) +{ + int found_or_error = LSM_STAT_NOTFOUND; + + /* Sender patterns and derived info */ + const char *sender_patterns; + char *saved_sender_patterns; + char *cp; + char *sender_pattern; + + /* Actual sender and derived info */ + const char *ext_stripped_sender = 0; + const char *at_sender_domain; + + /* + * Match the login. + */ + if ((sender_patterns = maps_find(lsm->maps, login_name, + /* flags= */ 0)) != 0) { + + /* + * Match the sender. Don't break a sender pattern between double + * quotes. + */ + cp = saved_sender_patterns = mystrdup(sender_patterns); + while (found_or_error == LSM_STAT_NOTFOUND + && (sender_pattern = mystrtokdq(&cp, CHARS_COMMA_SP)) != 0) { + /* Special pattern: @domain. */ + if (*sender_pattern == '@') { + if ((at_sender_domain = strrchr(sender_addr, '@')) != 0 + && strcasecmp_utf8(sender_pattern, at_sender_domain) == 0) + found_or_error = LSM_STAT_FOUND; + } + /* Special pattern: wildcard. */ + else if (strcasecmp(sender_pattern, lsm->wildcard) == 0) { + found_or_error = LSM_STAT_FOUND; + } + /* Special pattern: empty sender. */ + else if (strcasecmp(sender_pattern, lsm->null_sender) == 0) { + if (*sender_addr == 0) + found_or_error = LSM_STAT_FOUND; + } + /* Literal pattern: match the stripped and externalized sender. */ + else { + if (ext_stripped_sender == 0) + ext_stripped_sender = + STR(strip_externalize_addr(lsm->ext_stripped_sender, + sender_addr, + lsm->ext_delimiters)); + if (strcasecmp_utf8(sender_pattern, ext_stripped_sender) == 0) + found_or_error = LSM_STAT_FOUND; + } + } + myfree(saved_sender_patterns); + } else { + found_or_error = lsm->maps->error; + } + return (found_or_error); +} + +#ifdef TEST + +int main(int argc, char **argv) +{ + struct testcase { + const char *title; + const char *map_names; + const char *ext_delimiters; + const char *null_sender; + const char *wildcard; + const char *login_name; + const char *sender_addr; + int exp_return; + }; + struct testcase testcases[] = { + {"wildcard works", + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "root", "anything", LSM_STAT_FOUND + }, + {"unknown user", + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "toor", "anything", LSM_STAT_NOTFOUND + }, + {"bare user", + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "foo", LSM_STAT_FOUND + }, + {"user@domain", + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "foo@example.com", LSM_STAT_FOUND + }, + {"user+ext@domain", + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "foo+bar@example.com", LSM_STAT_FOUND + }, + {"wrong sender", + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "bar@example.com", LSM_STAT_NOTFOUND + }, + {"@domain", + "inline:{root=*, {foo = @example.com}, bar=<>}", + "+-", "<>", "*", "foo", "anyone@example.com", LSM_STAT_FOUND + }, + {"wrong @domain", + "inline:{root=*, {foo = @example.com}, bar=<>}", + "+-", "<>", "*", "foo", "anyone@example.org", LSM_STAT_NOTFOUND + }, + {"null sender", + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "bar", "", LSM_STAT_FOUND + }, + {"wrong null sender", + "inline:{root=*, {foo = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "baz", "", LSM_STAT_NOTFOUND + }, + {"error", + "inline:{root=*}, fail:sorry", + "+-", "<>", "*", "baz", "whatever", LSM_STAT_RETRY + }, + {"no error", + "inline:{root=*}, fail:sorry", + "+-", "<>", "*", "root", "whatever", LSM_STAT_FOUND + }, + {"unknown uid:number", + "inline:{root=*, {uid:12345 = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "uid:54321", "foo", LSM_STAT_NOTFOUND + }, + {"known uid:number", + "inline:{root=*, {uid:12345 = foo,foo@example.com}, bar=<>}", + "+-", "<>", "*", "uid:12345", "foo", LSM_STAT_FOUND + }, + {"unknown \"other last\"", + "inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "other last", LSM_STAT_NOTFOUND + }, + {"bare \"first last\"", + "inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "first last", LSM_STAT_FOUND + }, + {"\"first last\"@domain", + "inline:{root=*, {foo = \"first last\",\"first last\"@example.com}, bar=<>}", + "+-", "<>", "*", "foo", "first last@example.com", LSM_STAT_FOUND + }, + }; + struct testcase *tp; + int act_return; + int pass; + int fail; + LOGIN_SENDER_MATCH *lsm; + + /* + * Fake variable settings. + */ + var_double_bounce_sender = DEF_DOUBLE_BOUNCE; + var_ownreq_special = DEF_OWNREQ_SPECIAL; + +#define NUM_TESTS sizeof(testcases)/sizeof(testcases[0]) + + for (pass = fail = 0, tp = testcases; tp < testcases + NUM_TESTS; tp++) { + msg_info("RUN test case %ld %s", (long) (tp - testcases), tp->title); +#if 0 + msg_info("title=%s", tp->title); + msg_info("map_names=%s", tp->map_names); + msg_info("ext_delimiters=%s", tp->ext_delimiters); + msg_info("null_sender=%s", tp->null_sender); + msg_info("wildcard=%s", tp->wildcard); + msg_info("login_name=%s", tp->login_name); + msg_info("sender_addr=%s", tp->sender_addr); + msg_info("exp_return=%d", tp->exp_return); +#endif + lsm = login_sender_create("test map", tp->map_names, + tp->ext_delimiters, tp->null_sender, + tp->wildcard); + act_return = login_sender_match(lsm, tp->login_name, tp->sender_addr); + if (act_return == tp->exp_return) { + msg_info("PASS test %ld", (long) (tp - testcases)); + pass++; + } else { + msg_info("FAIL test %ld", (long) (tp - testcases)); + fail++; + } + login_sender_free(lsm); + } + return (fail > 0); +} + +#endif /* TEST */ -- cgit v1.2.3