result. Files: postlink, socketmap_table, dict_sockmap.[hc],
global/mail_params.[hc].
+20260716
+
+ Documentation for the new socketmap_max_query_size feature.
+ File: socketmap_table.
+
+ Bug (defect introduced: Postfix 1,1, date: 20010524): the
+ postsuper command under-read a very short queue filename. No
+ crash, information leak, or privilege escalation. Reported by
+ Qualys, assisted by Claude Mythos Preview. File: postsuper.c.
+
+20260717
+
+ Bug (defect introduced: Postfix 2.9, date: 20120114): a
+ lock file descriptor was not closed after failure to open a
+ DBM database. Reported by Qualys, assisted by Claude Mythos
+ Preview. File: dict_dbm.c.
+
+ Bug (defect introduced: Postfix-beta, date: 19990119): the
+ pipe(8) delivery agent deleted a command-line argument if
+ the argument contained $user AND $user expanded to an empty
+ string, breaking the positional order of arguments. This
+ was a workaround for a problem that hopefully no longer
+ exists. Reported by Qualys, assisted by Claude Mythos
+ Preview. File: pipe.c.
+
+20260720:
+
+ Bug (defect introduced: Postfix 1,1, date: 20010524): the
+ postsuper command over-read a very short queue filename.
+ No crash, information leak, or privilege escalation. Reported
+ by Qualys, assisted by Claude Mythos Preview. File:
+ mail_queue.h.
+
+ Code health: In the non-Berkeley DB migration client, connect
+ at least once. Qualys, assisted by Mythos Preview. File:
+ nbdb_clnt.c.
+
+ Bug (defect introduced: Postfix 1.0, date: 20000928): the
+ fast flush server, used by the SMTP command "ETRN", and by
+ the commands "postqueue -s site" and "postqueue -i queue_id"
+ (and their sendmail(1) equivalents), used the wrong duplicate
+ suppression API, resulting in too many queue scans by the
+ queue manager. Reported by Qualys, assisted by Claude Mythos
+ Preview. File: flush.c.
+
+ Fixed a mock vstream_fopen_as() error message. File:
+ mock_open_as.c.
+
+ Bug (introduced: Postfix 2.11, date: 20130422): the
+ posttls-finger test program falsely reported failure to
+ make a UNIX_domain connection. Reported by Qualys, assisted
+ by Claude Mythos Preview. File: posttls-finger.c.
+
+ Hardened the database parent directory permission checks
+ for automatic re-indexing with the non-Berkeley-DB migration
+ service. Files: nbdb_safe.c, nbdb_safe.h, nbdb_safe_test.c,
+ nbdb_process_test.c.
+
TODO
Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc.
able via <b>native</b> lookups).
<b>unix:</b><i>pathname</i>
- Connect to the UNIX-domain socket at <i>pathname</i>. LMTP only.
+ Connect to the UNIX-domain socket at <i>pathname</i>. This feature
+ requires "<b>-S</b>".
<b>match ...</b>
With no match arguments specified, certificate peername matching
uses the compiled-in default strategies for each security level.
- If you specify one or more arguments, these will be used as the
- list of certificate or public-key digests to match for the <b>fin-</b>
+ If you specify one or more arguments, these will be used as the
+ list of certificate or public-key digests to match for the <b>fin-</b>
<b>gerprint</b> level, or as the list of DNS names to match in the cer-
tificate at the <b>verify</b> and <b>secure</b> levels. If the security level
is <b>dane</b>, or <b>dane-only</b> the match names are ignored, and <b>hostname,</b>
neither the connection nor the server are authenticated.
<b><a name="configuration_parameters">CONFIGURATION PARAMETERS</a></b>
+ <b><a href="postconf.5.html#socketmap_max_query_size">socketmap_max_query_size</a> (10000)</b>
+ The maximum allowed query size for a socketmap client, not
+ including the socketmap and netstring encapsulation.
+
<b><a href="postconf.5.html#socketmap_max_reply_size">socketmap_max_reply_size</a> (100000)</b>
- The maximum allowed reply size from a socketmap server, not
+ The maximum allowed reply size from a socketmap server, not
including the netstring encapsulation.
<b><a name="see_also">SEE ALSO</a></b>
(these don't support \fBdane\fR or \fBdane\-only\fR as no DNSSEC
validation information is available via \fBnative\fR lookups).
.IP "\fBunix:\fIpathname\fR"
-Connect to the UNIX\-domain socket at \fIpathname\fR. LMTP only.
+Connect to the UNIX\-domain socket at \fIpathname\fR. This feature
+requires "\fB\-S\fR".
.IP "\fBmatch ...\fR"
With no match arguments specified, certificate peername matching uses
the compiled\-in default strategies for each security level. If you
.nf
.ad
.fi
+.IP "\fBsocketmap_max_query_size (10000)\fR"
+The maximum allowed query size for a socketmap client, not
+including the socketmap and netstring encapsulation.
.IP "\fBsocketmap_max_reply_size (100000)\fR"
The maximum allowed reply size from a socketmap server, not
including the netstring encapsulation.
# CONFIGURATION PARAMETERS
# .ad
# .fi
+# .IP "\fBsocketmap_max_query_size (10000)\fR"
+# The maximum allowed query size for a socketmap client, not
+# including the socketmap and netstring encapsulation.
# .IP "\fBsocketmap_max_reply_size (100000)\fR"
# The maximum allowed reply size from a socketmap server, not
# including the netstring encapsulation.
optimizers
resloop
tokenized
+fortra
void void defer_ctx
To undo a ptest_defer call call the function with a
charset charset
+nbdb_safe_for_uid nbdb_safe_for_uid owned by the user or root not group or other writable
+ Qualys Mythos 202606 int unsigned unsigned is non transitive
are not printable or whitespace Files smtpd smtpd c
support File smtpd smtpd c
sendmail sendmail c
+ postqueue postqueue c
continue;
}
if (dup_filter->used >= FLUSH_DUP_FILTER_SIZE
- || htable_find(dup_filter, STR(queue_id)) == 0) {
+ || htable_locate(dup_filter, STR(queue_id)) == 0) {
if (msg_verbose)
msg_info("%s: logfile %s: update queue file %s time stamps",
myname, path, STR(queue_id));
if (((long_form) = MQID_FIND_LG_INUM_SEPARATOR(_cp, (path))) != 0) { \
MQID_LG_DECODE_INUM(_cp + 1, (inum), (error)); \
} else { \
- MQID_SH_DECODE_INUM((path) + MQID_SH_USEC_PAD, (inum), (error)); \
+ if (strlen(path) > MQID_SH_USEC_PAD) \
+ MQID_SH_DECODE_INUM((path) + MQID_SH_USEC_PAD, (inum), (error)); \
+ else { (errno) = EINVAL; error = 1; } \
} \
} while (0)
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20260716"
+#define MAIL_RELEASE_DATE "20260720"
#define MAIL_VERSION_NUMBER "3.12"
#ifdef SNAPSHOT
int status;
int count;
+ /* 202607 Qualys+Mythos: try at least once. */
+ if (max_try < 1)
+ msg_panic("%s: bad max_try: %d", __func__, max_try);
+
/*
* A custom client endpoint that tries only a few times and that may be
* called by:
test_nbdb_sniffer: nbdb_sniffer_test
$(SHLIB_ENV) $(VALGRIND) ./nbdb_sniffer_test
-SAFE_TEST_OBJ = nbdb_safe.o
+SAFE_TEST_OBJ = nbdb_safe.o $(TESTLIB)
nbdb_safe_test: nbdb_safe_test.o $(SAFE_TEST_OBJ) $(LIBS)
$(CC) $(CFLAGS) -o $@ $@.o $(SAFE_TEST_OBJ) $(LIBS) $(SYSLIBS)
nbdb_safe.o: ../../include/check_arg.h
nbdb_safe.o: ../../include/mail_conf.h
nbdb_safe.o: ../../include/mail_params.h
+nbdb_safe.o: ../../include/msg.h
+nbdb_safe.o: ../../include/mymalloc.h
nbdb_safe.o: ../../include/sys_defs.h
nbdb_safe.o: ../../include/vbuf.h
nbdb_safe.o: ../../include/vstring.h
nbdb_safe_test.o: ../../include/check_arg.h
nbdb_safe_test.o: ../../include/mail_conf.h
nbdb_safe_test.o: ../../include/mail_params.h
+nbdb_safe_test.o: ../../include/mock_stat.h
nbdb_safe_test.o: ../../include/msg.h
nbdb_safe_test.o: ../../include/msg_vstream.h
nbdb_safe_test.o: ../../include/stringops.h
MOCK_STAT_REQ mock_stat_source;
MOCK_STAT_REQ mock_stat_leg_idx;
MOCK_STAT_REQ mock_stat_parent;
+ MOCK_STAT_REQ mock_stat_slash_path; /* "/path" */
+ MOCK_STAT_REQ mock_stat_slash; /* "/" */
MOCK_STAT_REQ mock_stat_new_idx;
MOCK_OPEN_AS_REQ mock_open;
MOCK_SPAWN_CMD_REQ mock_spawn;
nbdb_util_init(tp->params.migr_level);
setup_mock_cdb("{{x = x}}");
setup_mock_lmdb("{{x = x}}");
+ teardown_mock_stat();
if (nbdb_level >= NBDB_LEV_CODE_REINDEX) {
var_nbdb_allow_root_pfxs = (char *) tp->params.allow_root_pfxs;
var_nbdb_allow_user_pfxs = (char *) tp->params.allow_user_pfxs;
setup_mock_stat(&tp->mock_stat_leg_idx);
setup_mock_stat(&tp->mock_stat_parent);
setup_mock_stat(&tp->mock_stat_new_idx);
+ setup_mock_stat(&tp->mock_stat_slash_path);
+ setup_mock_stat(&tp->mock_stat_slash);
setup_mock_vstream_fopen_as(&tp->mock_open);
setup_mock_spawn_command(&tp->mock_spawn);
}
.out_errno = 0,
.out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
},
+ .mock_stat_slash_path = {
+ .want_path = "/path",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
+ .mock_stat_slash = {
+ .want_path = "/",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
.mock_stat_new_idx = {
.want_path = "/path/to/file.cdb",
.out_errno = ENOENT,
.out_errno = 0,
.out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
},
+ .mock_stat_slash_path = {
+ .want_path = "/path",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
+ .mock_stat_slash = {
+ .want_path = "/",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
.mock_stat_new_idx = {
.want_path = "/path/to/file.cdb",
.out_errno = ENOENT,
.out_errno = 0,
.out_st = {.st_mode = S_IWGRP,.st_uid = 0,.st_gid = 0,},
},
+ .mock_stat_slash_path = {
+ .want_path = "/path",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
+ .mock_stat_slash = {
+ .want_path = "/",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
.mock_stat_new_idx = {
.want_path = "/path/to/file.cdb",
.out_errno = ENOENT,
.out_errno = 0,
.out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
},
+ .mock_stat_slash_path = {
+ .want_path = "/path",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
+ .mock_stat_slash = {
+ .want_path = "/",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
.mock_stat_new_idx = {
.want_path = "/path/to/file.cdb",
.out_errno = ENOENT,
.st_mode = S_IRWXU,.st_uid = 1,.st_gid = 0,
},
},
+ .mock_stat_slash_path = {
+ .want_path = "/path",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
+ .mock_stat_slash = {
+ .want_path = "/",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
.mock_stat_new_idx = {
.want_path = "/path/to/file.cdb",
.out_errno = ENOENT,
.out_errno = 0,
.out_st = {.st_mode = S_IRWXU,.st_uid = 1,.st_gid = 0,},
},
+ .mock_stat_slash_path = {
+ .want_path = "/path",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
+ .mock_stat_slash = {
+ .want_path = "/",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
.mock_stat_new_idx = {
.want_path = "/path/to/file.cdb",
.out_errno = ENOENT,
.out_errno = 0,
.out_st = {.st_mode = S_IRWXU,.st_uid = 1,.st_gid = 0,},
},
+ .mock_stat_slash_path = {
+ .want_path = "/path",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
+ .mock_stat_slash = {
+ .want_path = "/",
+ .out_errno = 0,
+ .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+ },
.mock_stat_new_idx = {
.want_path = "/path/to/file.cdb",
.out_errno = ENOENT,
},
.mock_open = {
.want_path = "/path/to/file",
- .want_uid = 0,
+ .want_uid = 1,
.want_gid = 0,
.out_errno = 0,
.out_data = "foo bar\nfoo :bar",
/* uid_t uid,
/* const struct stat *st)
/*
+/* bool nbdb_safe_parent_for_uid(
+/* uid_t uid,
+/* const char *parent_dir,
+/* const struct stat *parent_dir_st)
+/*
/* bool nbdb_safe_to_index_as_legacy_index_owner(
/* const char *source_path,
/* const struct stat *source_st,
/* the safety of other pathname components. It is a good idea to
/* trust only a limited number of pathname prefixes.
/*
+/* nbdb_safe_parent_for_uid() implements nbdb_safe_for_uid()
+/* for the directories in the specified pathname. When a pathname
+/* element contains a symlink, this function will examine the
+/* symlink target but not the target's parent directories.
+/*
/* nbdb_safe_to_index_as_legacy_index_owner() determines
/* if a Berkeley DB source file and parent directory are 'safe'
/* for the uid of the Berkeley DB indexed file.
*/
#include <sys_defs.h>
#include <sys/stat.h>
+#include <string.h>
+#include <libgen.h>
/*
* Utility library.
#include <allowed_prefix.h>
#include <mail_conf.h>
#include <mail_params.h>
+#include <msg.h>
+#include <mymalloc.h>
+#include <wrap_stat.h>
/*
* Application-specific.
#include <nbdb_safe.h>
#include <nbdb_reindexd.h>
+/* nbdb_safe_parent_for_uid - simple pathname safety */
+
+bool nbdb_safe_parent_for_uid(uid_t uid, const char *path,
+ const struct stat * path_st)
+{
+ bool ret;
+
+ /*
+ * See also the comment in nbdb_safe_for_uid().
+ */
+ if (!nbdb_safe_for_uid(uid, path_st)) {
+ ret = false;
+ } else if (strcmp(path, "/") == 0) {
+ ret = true;
+ } else {
+ char *saved_path = mystrdup(path);
+ char *parent = dirname(saved_path);
+ struct stat parent_st;
+
+ if (stat(parent, &parent_st) < 0) {
+ ret = false;
+ msg_warn("look up status for directory '%s': %m", parent);
+ } else {
+ ret = nbdb_safe_parent_for_uid(uid, parent, &parent_st);
+ }
+ myfree(saved_path);
+ }
+ return (ret);
+}
+
/* nbdb_safe_for_uid - owned by the user or root, not group or other writable */
bool nbdb_safe_for_uid(uid_t uid, const struct stat * st)
leg_idx_path, (int) runas_uid, source_path);
return (false);
}
- if (!nbdb_safe_for_uid(runas_uid, parent_dir_st)) {
+ if (!nbdb_safe_parent_for_uid(runas_uid, parent_dir, parent_dir_st)) {
vstring_sprintf(why, "legacy indexed file '%s' is owned by "
"uid '%d', but parent directory '%s' is "
"owned or writable by other user; to allow "
* Internal API.
*/
extern bool nbdb_safe_for_uid(uid_t, const struct stat *);
+extern bool nbdb_safe_parent_for_uid(uid_t, const char *, const struct stat *);
extern bool nbdb_safe_to_index_as_legacy_index_owner(
const char *, const struct stat *,
const char *, const struct stat *,
* System library.
*/
#include <sys_defs.h>
+#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <mail_conf.h>
#include <mail_params.h>
+ /*
+ * Test library.
+ */
+#include <mock_stat.h>
+
/*
* Application-specific.
*/
struct stat source_st;
const char *leg_idx_path;
struct stat leg_idx_st;
- const char *parent_dir;
+ const char *parent_dir; /* "/etc/postfix" */
struct stat parent_dir_st;
+ MOCK_STAT_REQ slash_etc_dir; /* "/etc" */
+ MOCK_STAT_REQ slash_dir; /* "/" */
+ struct stat slash_dir_st;
const char *allow_root_pfxs;
const char *allow_user_pfxs;
const char *want_why;
var_nbdb_allow_user_pfxs = (char *) tp->allow_user_pfxs;
parsed_allow_root_pfxs = allowed_prefix_create(var_nbdb_allow_root_pfxs);
parsed_allow_user_pfxs = allowed_prefix_create(var_nbdb_allow_user_pfxs);
+ teardown_mock_stat();
+ setup_mock_stat(&tp->slash_etc_dir);
+ setup_mock_stat(&tp->slash_dir);
got_bool = nbdb_safe_to_index_as_legacy_index_owner(
tp->source_path, &tp->source_st,
tp->leg_idx_path, &tp->leg_idx_st,
.leg_idx_st = {.st_uid = 0,},
.parent_dir = "/etc/postfix",
.parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+ .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+ .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
.allow_root_pfxs = "/etc/postfix",
.allow_user_pfxs = "",
.want_bool = true,
.leg_idx_st = {.st_uid = 0,},
.parent_dir = "/etc/postfix",
.parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+ .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+ .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
.allow_root_pfxs = "/etc/posttfix",
.allow_user_pfxs = "",
.want_bool = false,
.leg_idx_st = {.st_uid = 0,},
.parent_dir = "/etc/postfix",
.parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+ .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+ .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
.allow_root_pfxs = "/etc/postfix",
.allow_user_pfxs = "",
.want_bool = false,
.leg_idx_st = {.st_uid = 0,},
.parent_dir = "/etc/postfix",
.parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+ .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+ .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
.allow_root_pfxs = "/etc/postfix",
.allow_user_pfxs = "",
.want_bool = false,
.leg_idx_st = {.st_uid = 0,},
.parent_dir = "/etc/postfix",
.parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+ .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+ .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
.allow_root_pfxs = "/etc/postfix",
.allow_user_pfxs = "",
.want_bool = false,
.want_why = "'/etc/postfix/access' is owned or writable by other user",
},
+ {.label = "safe_to_index_as_root:bad_root_owner",
+ .action = test_nbdb_safe_to_index_as_legacy_index_owner,
+ .source_path = "/etc/postfix/access",
+ .source_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+ .leg_idx_path = "/etc/postfix/access.db",
+ .leg_idx_st = {.st_uid = 0,},
+ .parent_dir = "/etc/postfix",
+ .parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+ .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+ .slash_dir = {"/", {.st_uid = 1,.st_mode = S_IRWXU,}, 0,},
+ .allow_root_pfxs = "/etc/postfix",
+ .allow_user_pfxs = "",
+ .want_bool = false,
+ .want_why = "",
+ },
+ {.label = "safe_to_index_as_root:bad_root_perms",
+ .action = test_nbdb_safe_to_index_as_legacy_index_owner,
+ .source_path = "/etc/postfix/access",
+ .source_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+ .leg_idx_path = "/etc/postfix/access.db",
+ .leg_idx_st = {.st_uid = 0,},
+ .parent_dir = "/etc/postfix",
+ .parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+ .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+ .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IWOTH,}, 0,},
+ .allow_root_pfxs = "/etc/postfix",
+ .allow_user_pfxs = "",
+ .want_bool = false,
+ .want_why = "",
+ },
{.label = "safe_to_index_as_root:bad_parent_owner",
.action = test_nbdb_safe_to_index_as_legacy_index_owner,
.source_path = "/etc/postfix/access",
.leg_idx_st = {.st_uid = 0,},
.parent_dir = "/etc/postfix",
.parent_dir_st = {.st_uid = 1,.st_mode = S_IRWXU,},
+ .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+ .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
.allow_root_pfxs = "/etc/postfix",
.allow_user_pfxs = "",
.want_bool = false,
.leg_idx_st = {.st_uid = 0,},
.parent_dir = "/etc/postfix",
.parent_dir_st = {.st_uid = 0,.st_mode = S_IWGRP,},
+ .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+ .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
.allow_root_pfxs = "/etc/postfix",
.allow_user_pfxs = "",
.want_bool = false,
.leg_idx_st = {.st_uid = 0,},
.parent_dir = "/etc/postfix",
.parent_dir_st = {.st_uid = 0,.st_mode = S_IWOTH,},
+ .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+ .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
.allow_root_pfxs = "/etc/postfix",
.allow_user_pfxs = "",
.want_bool = false,
* Either anything to the left of the extension delimiter or,
* in absence of the latter, anything to the left of the
* rightmost @.
- *
- * Beware: if the user name is blank (e.g. +user@host), the
- * argument is suppressed. This is necessary to allow for
- * cyrus bulletin-board (global mailbox) delivery. XXX But,
- * skipping empty user parts will also prevent other
- * expansions of this specific command-line argument.
*/
if (state.expand_flag & PIPE_FLAG_USER) {
morph_recipient(buf, rcpt_list->info[i].address,
rcpt_list->info[i].address);
if (*var_rcpt_delim)
split_addr(STR(buf), var_rcpt_delim);
- if (*STR(buf) == 0)
- continue;
+ /* 202607 Qualys+Mythos: don't skip arg if $user is "".*/
dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf));
}
* number.
*/
if ((action & ACTION_STRUCT)
+ /* 202607 Qualys+Mythos: skip short paths. */
+ && strlen(path) > SUFFIX_LEN
&& strcmp(path + (strlen(path) - SUFFIX_LEN), SUFFIX) == 0) {
path[strlen(path) - SUFFIX_LEN] = 0; /* XXX */
if (!mail_queue_id_ok(path)) {
/* (these don't support \fBdane\fR or \fBdane-only\fR as no DNSSEC
/* validation information is available via \fBnative\fR lookups).
/* .IP "\fBunix:\fIpathname\fR"
-/* Connect to the UNIX-domain socket at \fIpathname\fR. LMTP only.
+/* Connect to the UNIX-domain socket at \fIpathname\fR. This feature
+/* requires "\fB-S\fR".
/* .IP "\fBmatch ...\fR"
/* With no match arguments specified, certificate peername matching uses
/* the compiled-in default strategies for each security level. If you
*/
if (state->smtp == 0) {
if (strncmp(dest, "unix:", 5) == 0) {
+ /* 202607 Qualys+Mythos: handle unix-domain success. */
state->stream = connect_unix(state, dest + 5);
- if (!state->stream)
+ if (state->stream) {
+ return (0);
+ } else {
msg_info("Failed to establish session to %s: %s",
dest, vstring_str(state->why->reason));
- return (1);
+ return (1);
+ }
}
if (strncmp(dest, "inet:", 5) == 0)
dest += 5;
/* TODO(wietse) replace with match_str() and match_decimal(). */
if (strcmp(path, mock_req.want_path) != 0)
- msg_fatal("vstream_fopen: got path '%s', want '%s'",
+ msg_fatal("vstream_fopen_as: got path '%s', want '%s'",
path, mock_req.want_path);
if (uid != mock_req.want_uid)
- msg_fatal("vstream_fopen: got uid '%d', want '%d'",
+ msg_fatal("vstream_fopen_as: got uid '%d', want '%d'",
(int) uid, (int) mock_req.want_uid);
if (gid != mock_req.want_gid)
- msg_fatal("vstream_fopen: got gid '%d', want '%d'",
+ msg_fatal("vstream_fopen_as: got gid '%d', want '%d'",
(int) gid, (int) mock_req.want_gid);
if (mock_req.out_errno != 0) {
struct stat st;
DBM *dbm;
char *dbm_path = 0;
- int lock_fd;
+ int lock_fd = -1;
/*
* Let the optimizer worry about eliminating redundant code.
DICT *__d = (d); \
if (dbm_path != 0) \
myfree(dbm_path); \
+ if (lock_fd >= 0) \
+ (void) close(lock_fd); \
return (__d); \
} while (0)
msg_fatal("unlock database %s for open: %m", dbm_path);
if (close(lock_fd) < 0)
msg_fatal("close database %s: %m", dbm_path);
+ lock_fd = -1;
}
dict_dbm = (DICT_DBM *) dict_alloc(DICT_TYPE_DBM, path, sizeof(*dict_dbm));
dict_dbm->dict.lookup = dict_dbm_lookup;