2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 13:48:06 +00:00

postfix-3.6-20201101

This commit is contained in:
Wietse Venema
2020-11-01 00:00:00 -05:00
committed by Viktor Dukhovni
parent 5671f91c4d
commit 225d645d4e
9 changed files with 210 additions and 35 deletions

View File

@@ -25251,4 +25251,10 @@ Apologies for any names omitted.
Cleanup: changed the postdrop numerical UID prefix from "#" Cleanup: changed the postdrop numerical UID prefix from "#"
to "uid:", and tweaked some local_login_sender_maps to "uid:", and tweaked some local_login_sender_maps
documentatin. Files: proto/postconf.proto, postdrop/postdrop.c. documentation. Files: proto/postconf.proto, postdrop/postdrop.c.
20201031
Cleanup: don't split a space-comma separated address list
on on space or comma inside a quoted string. Files:
util/mystrtok.c, util/mystetok.ref, global/login_sender_match.c.

View File

@@ -196,16 +196,16 @@ int login_sender_match(LOGIN_SENDER_MATCH *lsm, const char *login_name,
/* flags= */ 0)) != 0) { /* flags= */ 0)) != 0) {
/* /*
* Match the sender. TODO: don't break a sender pattern on a * Match the sender. Don't break a sender pattern between double
* comma/space inside a quoted localpart. * quotes.
*/ */
cp = saved_sender_patterns = mystrdup(sender_patterns); cp = saved_sender_patterns = mystrdup(sender_patterns);
while (found_or_error == LSM_STAT_NOTFOUND while (found_or_error == LSM_STAT_NOTFOUND
&& (sender_pattern = mystrtok(&cp, CHARS_COMMA_SP)) != 0) { && (sender_pattern = mystrtokdq(&cp, CHARS_COMMA_SP)) != 0) {
/* Special pattern: @domain. */ /* Special pattern: @domain. */
if (*sender_pattern == '@') { if (*sender_pattern == '@') {
if ((at_sender_domain = strrchr(sender_addr, '@')) != 0 if ((at_sender_domain = strrchr(sender_addr, '@')) != 0
&& strcasecmp_utf8(sender_pattern, at_sender_domain) == 0) && strcasecmp_utf8(sender_pattern, at_sender_domain) == 0)
found_or_error = LSM_STAT_FOUND; found_or_error = LSM_STAT_FOUND;
} }
/* Special pattern: wildcard. */ /* Special pattern: wildcard. */
@@ -218,13 +218,15 @@ int login_sender_match(LOGIN_SENDER_MATCH *lsm, const char *login_name,
found_or_error = LSM_STAT_FOUND; found_or_error = LSM_STAT_FOUND;
} }
/* Literal pattern: match the stripped and externalized sender. */ /* Literal pattern: match the stripped and externalized sender. */
if (ext_stripped_sender == 0) else {
ext_stripped_sender = if (ext_stripped_sender == 0)
STR(strip_externalize_addr(lsm->ext_stripped_sender, ext_stripped_sender =
sender_addr, STR(strip_externalize_addr(lsm->ext_stripped_sender,
lsm->ext_delimiters)); sender_addr,
if (strcasecmp_utf8(sender_pattern, ext_stripped_sender) == 0) lsm->ext_delimiters));
found_or_error = LSM_STAT_FOUND; if (strcasecmp_utf8(sender_pattern, ext_stripped_sender) == 0)
found_or_error = LSM_STAT_FOUND;
}
} }
myfree(saved_sender_patterns); myfree(saved_sender_patterns);
} else { } else {
@@ -304,6 +306,18 @@ int main(int argc, char **argv)
"inline:{root=*, {uid:12345 = foo,foo@example.com}, bar=<>}", "inline:{root=*, {uid:12345 = foo,foo@example.com}, bar=<>}",
"+-", "<>", "*", "uid:12345", "foo", LSM_STAT_FOUND "+-", "<>", "*", "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; struct testcase *tp;
int act_return; int act_return;

View File

@@ -27,3 +27,9 @@ unknown: RUN test case 12 unknown uid:number
unknown: PASS test 12 unknown: PASS test 12
unknown: RUN test case 13 known uid:number unknown: RUN test case 13 known uid:number
unknown: PASS test 13 unknown: PASS test 13
unknown: RUN test case 14 unknown "other last"
unknown: PASS test 14
unknown: RUN test case 15 bare "first last"
unknown: PASS test 15
unknown: RUN test case 16 "first last"@domain
unknown: PASS test 16

View File

@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20201026" #define MAIL_RELEASE_DATE "20201101"
#define MAIL_VERSION_NUMBER "3.6" #define MAIL_VERSION_NUMBER "3.6"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

@@ -261,8 +261,10 @@ static int check_login_sender_acl(uid_t uid, VSTRING *sender_buf,
/* /*
* Optimization. * Optimization.
*/ */
#ifndef SNAPSHOT
if (strcmp(var_local_login_snd_maps, DEF_LOCAL_LOGIN_SND_MAPS) == 0) if (strcmp(var_local_login_snd_maps, DEF_LOCAL_LOGIN_SND_MAPS) == 0)
return (CLEANUP_STAT_OK); return (CLEANUP_STAT_OK);
#endif
/* /*
* Get the username. * Get the username.

View File

@@ -559,7 +559,8 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
miss_endif_pcre_test miss_endif_regexp_test split_qnameval_test \ miss_endif_pcre_test miss_endif_regexp_test split_qnameval_test \
vstring_test vstream_test dict_pcre_file_test dict_regexp_file_test \ vstring_test vstream_test dict_pcre_file_test dict_regexp_file_test \
dict_cidr_file_test dict_static_file_test dict_random_test \ dict_cidr_file_test dict_static_file_test dict_random_test \
dict_random_file_test dict_inline_file_test byte_mask_tests dict_random_file_test dict_inline_file_test byte_mask_tests \
mystrtok_test
root_tests: root_tests:
@@ -957,6 +958,11 @@ vstream_test: vstream vstream_test.in vstream_test.ref
diff vstream_test.ref vstream_test.tmp diff vstream_test.ref vstream_test.tmp
rm -f vstream_test.tmp rm -f vstream_test.tmp
mystrtok_test: mystrtok mystrtok.ref
$(SHLIB_ENV) ${VALGRIND} ./mystrtok >mystrtok.tmp 2>&1
diff mystrtok.ref mystrtok.tmp
rm -f mystrtok.tmp
depend: $(MAKES) depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \ (sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \ set -e; for i in [a-z][a-z0-9]*.c; do \

View File

@@ -14,6 +14,10 @@
/* char **bufp; /* char **bufp;
/* const char *delimiters; /* const char *delimiters;
/* const char *parens; /* const char *parens;
/*
/* char *mystrtokdq(bufp, delimiters)
/* char **bufp;
/* const char *delimiters;
/* DESCRIPTION /* DESCRIPTION
/* mystrtok() splits a buffer on the specified \fIdelimiters\fR. /* mystrtok() splits a buffer on the specified \fIdelimiters\fR.
/* Tokens are delimited by runs of delimiters, so this routine /* Tokens are delimited by runs of delimiters, so this routine
@@ -24,6 +28,11 @@
/* opening and closing parenthesis (one of each). The set of /* opening and closing parenthesis (one of each). The set of
/* \fIparens\fR must be distinct from the set of \fIdelimiters\fR. /* \fIparens\fR must be distinct from the set of \fIdelimiters\fR.
/* /*
/* mystrtokdq() is like mystrtok() but will not split text
/* between double quotes. The backslash character may be used
/* to escape characters. The double quote and backslash
/* character must not appear in the set of \fIdelimiters\fR.
/*
/* The \fIbufp\fR argument specifies the start of the search; it /* The \fIbufp\fR argument specifies the start of the search; it
/* is updated with each call. The input is destroyed. /* is updated with each call. The input is destroyed.
/* /*
@@ -80,7 +89,7 @@ char *mystrtok(char **src, const char *sep)
char *mystrtokq(char **src, const char *sep, const char *parens) char *mystrtokq(char **src, const char *sep, const char *parens)
{ {
char *start = *src; char *start = *src;
static char *cp; static char *cp;
int ch; int ch;
int level; int level;
@@ -99,7 +108,7 @@ char *mystrtokq(char **src, const char *sep, const char *parens)
for (level = 0, cp = start; (ch = *(unsigned char *) cp) != 0; cp++) { for (level = 0, cp = start; (ch = *(unsigned char *) cp) != 0; cp++) {
if (ch == parens[0]) { if (ch == parens[0]) {
level++; level++;
} else if (level > 0 && ch == parens[1]) { } else if (level > 0 && ch == parens[1]) {
level--; level--;
} else if (level == 0 && strchr(sep, ch) != 0) { } else if (level == 0 && strchr(sep, ch) != 0) {
*cp++ = 0; *cp++ = 0;
@@ -110,34 +119,135 @@ char *mystrtokq(char **src, const char *sep, const char *parens)
return (start); return (start);
} }
/* mystrtokdq - safe tokenizer, double quote and backslash support */
char *mystrtokdq(char **src, const char *sep)
{
char *cp = *src;
char *start;
/*
* Skip leading delimiters.
*/
cp += strspn(cp, sep);
/*
* Skip to next unquoted space or comma.
*/
if (*cp == 0) {
start = 0;
} else {
int in_quotes;
for (in_quotes = 0, start = cp; *cp; cp++) {
if (*cp == '\\') {
if (*++cp == 0)
break;
} else if (*cp == '"') {
in_quotes = !in_quotes;
} else if (!in_quotes && strchr(sep, *(unsigned char *) cp) != 0) {
*cp++ = 0;
break;
}
}
}
*src = cp;
return (start);
}
#ifdef TEST #ifdef TEST
/* /*
* Test program: read lines from stdin, split on whitespace. * Test program.
*/ */
#include "vstring.h" #include "msg.h"
#include "vstream.h" #include "mymalloc.h"
#include "vstring_vstream.h"
/*
* The following needs to be large enough to include a null terminator in
* every testcase.expected field.
*/
#define EXPECT_SIZE 5
struct testcase {
const char *action;
const char *input;
const char *expected[EXPECT_SIZE];
};
static const struct testcase testcases[] = {
{"mystrtok", ""},
{"mystrtok", " foo ", {"foo"}},
{"mystrtok", " foo bar ", {"foo", "bar"}},
{"mystrtokq", ""},
{"mystrtokq", "foo bar", {"foo", "bar"}},
{"mystrtokq", "{ bar } ", {"{ bar }"}},
{"mystrtokq", "foo { bar } baz", {"foo", "{ bar }", "baz"}},
{"mystrtokq", "foo{ bar } baz", {"foo{ bar }", "baz"}},
{"mystrtokq", "foo { bar }baz", {"foo", "{ bar }baz"}},
{"mystrtokdq", ""},
{"mystrtokdq", " foo ", {"foo"}},
{"mystrtokdq", " foo bar ", {"foo", "bar"}},
{"mystrtokdq", " foo\\ bar ", {"foo\\ bar"}},
{"mystrtokdq", " foo \\\" bar", {"foo", "\\\"", "bar"}},
{"mystrtokdq", " foo \" bar baz\" ", {"foo", "\" bar baz\""}},
};
int main(void) int main(void)
{ {
VSTRING *vp = vstring_alloc(100); const struct testcase *tp;
char *start; char *actual;
char *str; int pass;
int fail;
int match;
int n;
while (vstring_fgets(vp, VSTREAM_IN) && VSTRING_LEN(vp) > 0) { #define NUM_TESTS sizeof(testcases)/sizeof(testcases[0])
start = vstring_str(vp); #define STR_OR_NULL(s) ((s) ? (s) : "null")
if (strchr(start, CHARS_BRACE[0]) == 0) {
while ((str = mystrtok(&start, CHARS_SPACE)) != 0) for (pass = fail = 0, tp = testcases; tp < testcases + NUM_TESTS; tp++) {
vstream_printf(">%s<\n", str); char *saved_input = mystrdup(tp->input);
} else { char *cp = saved_input;
while ((str = mystrtokq(&start, CHARS_SPACE, CHARS_BRACE)) != 0)
vstream_printf(">%s<\n", str); msg_info("RUN test case %ld %s >%s<",
(long) (tp - testcases), tp->action, tp->input);
#if 0
msg_info("action=%s", tp->action);
msg_info("input=%s", tp->input);
for (n = 0; tp->expected[n]; tp++)
msg_info("expected[%d]=%s", n, tp->expected[n]);
#endif
for (n = 0; n < EXPECT_SIZE; n++) {
if (strcmp(tp->action, "mystrtok") == 0) {
actual = mystrtok(&cp, CHARS_SPACE);
} else if (strcmp(tp->action, "mystrtokq") == 0) {
actual = mystrtokq(&cp, CHARS_SPACE, CHARS_BRACE);
} else if (strcmp(tp->action, "mystrtokdq") == 0) {
actual = mystrtokdq(&cp, CHARS_SPACE);
} else {
msg_panic("invalid command: %s", tp->action);
}
if ((match = (actual && tp->expected[n]) ?
(strcmp(actual, tp->expected[n]) == 0) :
(actual == tp->expected[n])) != 0) {
if (actual == 0) {
msg_info("PASS test %ld", (long) (tp - testcases));
pass++;
break;
}
} else {
msg_warn("expected: >%s<, got: >%s<",
STR_OR_NULL(tp->expected[n]), STR_OR_NULL(actual));
msg_info("FAIL test %ld", (long) (tp - testcases));
fail++;
break;
}
} }
vstream_fflush(VSTREAM_OUT); if (n >= EXPECT_SIZE)
msg_panic("need to increase EXPECT_SIZE");
myfree(saved_input);
} }
vstring_free(vp); return (fail > 0);
return (0);
} }
#endif #endif

View File

@@ -0,0 +1,30 @@
unknown: RUN test case 0 mystrtok ><
unknown: PASS test 0
unknown: RUN test case 1 mystrtok > foo <
unknown: PASS test 1
unknown: RUN test case 2 mystrtok > foo bar <
unknown: PASS test 2
unknown: RUN test case 3 mystrtokq ><
unknown: PASS test 3
unknown: RUN test case 4 mystrtokq >foo bar<
unknown: PASS test 4
unknown: RUN test case 5 mystrtokq >{ bar } <
unknown: PASS test 5
unknown: RUN test case 6 mystrtokq >foo { bar } baz<
unknown: PASS test 6
unknown: RUN test case 7 mystrtokq >foo{ bar } baz<
unknown: PASS test 7
unknown: RUN test case 8 mystrtokq >foo { bar }baz<
unknown: PASS test 8
unknown: RUN test case 9 mystrtokdq ><
unknown: PASS test 9
unknown: RUN test case 10 mystrtokdq > foo <
unknown: PASS test 10
unknown: RUN test case 11 mystrtokdq > foo bar <
unknown: PASS test 11
unknown: RUN test case 12 mystrtokdq > foo\ bar <
unknown: PASS test 12
unknown: RUN test case 13 mystrtokdq > foo \" bar<
unknown: PASS test 13
unknown: RUN test case 14 mystrtokdq > foo " bar baz" <
unknown: PASS test 14

View File

@@ -30,6 +30,7 @@ extern char *trimblanks(char *, ssize_t);
extern char *concatenate(const char *,...); extern char *concatenate(const char *,...);
extern char *mystrtok(char **, const char *); extern char *mystrtok(char **, const char *);
extern char *mystrtokq(char **, const char *, const char *); extern char *mystrtokq(char **, const char *, const char *);
extern char *mystrtokdq(char **, const char *);
extern char *translit(char *, const char *, const char *); extern char *translit(char *, const char *, const char *);
#define printable(string, replacement) \ #define printable(string, replacement) \