Security: Berkeley DB 2 and later try to read settings from
a file DB_CONFIG in the current directory. This undocumented
- feature may introduce undisclosed vulnerabilities resulting in
- privilege escalation with Postfix set-gid programs (postdrop,
- postqueue) before they chdir to the Postfix queue directory,
- and with the postmap and postalias commands depending on whether
- the user's current directory is writable by other users. This
- fix does not change Postfix behavior for Berkeley DB < 3.
- File: util/dict_db.c.
+ feature may introduce undisclosed vulnerabilities resulting
+ in privilege escalation with Postfix set-gid programs
+ (postdrop, postqueue) before they chdir to the Postfix queue
+ directory, and with the postmap and postalias commands
+ depending on whether the user's current directory is writable
+ by other users. This fix does not change Postfix behavior
+ for Berkeley DB < 3, but reduces file create performance
+ for Berkeley DB 3 .. 4.6. File: util/dict_db.c.
+
+20170617
+
+ Cleanup: the postconf command warns about unknown parameter
+ names in a database configuration file, specified as an
+ absolute pathname (for example, ldap:/path/to/file). This
+ code was mostly written in January 2017, and it still is a
+ partial implementation. Files: postconf/postconf_dbms.c,
+ postconf/Makefile.in, postconf/test66.ref.
+
+20170618
+
+ Cleanup: added missing "defined(__GLIBC__)" guards for
+ GLIBC version tests. File: util/sys_defs.h.
+
+20170620
+
+ Bugfix (introduced: Postfix 3.2) extension propagation was
+ broken with "recipient_delimiter = .", because of code that
+ was too clever by half. Files: global/mail_adr_crunch.c,
+ global/mail_addr_crunch.ref.
+
+20170704
+
+ Typos (introduced: Postfix 2.10): in comments about
+ IPv4-in-IPv6 addresses, replace :ffff::1.2.3.4 with the
+ correct form :ffff::1.2.3.4. Incorrect or misleading comments
+ are worse than no comments. Files: smtpd/smtpd_haproxy.c,
+ postscreen/postscreen_haproxy.c.
Disable -DSNAPSHOT and -DNONPROD in makedefs.
+ Add postwhite as a postscreen-related project.
+ https://github.com/stevejenkins/postwhite/blob/master/README.md
+
+ Decide whether to deprecate database configuration pathnames
+ that start with ".", for example, ldap:./file/name. These forms
+ are documented for ldap:, memcache:, mysql:, pgsql:, and sqlite:
+ maps. Postfix daemon processes will look up files relative to the
+ queue directory, but with postmap command-line processes it would
+ be more natural to interpret relative pathnames relative to the
+ current directory of the calling process (it would be a surprise
+ if "postmap hash:./foo" would access "/var/spool/postfix/foo",
+ or if "postmap hash:foo" and or "postmap hash:./foo" would access
+ different files).
+
Convert postalias(1) to store external-form keys, and convert
aliases(5) to perform external-first lookup with fallback to
internal form, to make it consistent with the rest of Postfix.
tok822_externalize(extern_addr, tpp[0]->head, TOK822_STR_DEFL);
canon_addr_external(canon_addr, STR(extern_addr));
unquote_822_local(intern_addr, STR(canon_addr));
- if (extension && strchr(STR(intern_addr), *extension) == 0) {
+ if (extension) {
VSTRING_SPACE(intern_addr, extlen + 1);
if ((ratsign = strrchr(STR(intern_addr), '@')) == 0) {
vstring_strcat(intern_addr, extension);
==== external to internal, with extension
|foo+extension@example.com|
|foo bar+extension@example.com|
-|foo+ext@example.com|
+|foo+ext+extension@example.com|
==== external to internal, without extension
|foo@example.com|
|foo bar@example.com|
==== external to external, with extension
|foo+extension@example.com|
|"foo bar+extension"@example.com|
-|foo+ext@example.com|
+|foo+ext+extension@example.com|
==== external to external, without extension
|foo@example.com|
|"foo bar"@example.com|
|foo+ext@example.com|
==== internal to internal, with extension
|foo+extension@example.com|
-|foo+ext@example.com|
+|foo+ext+extension@example.com|
==== internal to internal, without extension
|foo@example.com|
|foo+ext@example.com|
#define DO_PROPAGATE_UNMATCHED_EXTENSION 1
#define NO_RECIPIENT_DELIMITER ""
#define PLUS_RECIPIENT_DELIMITER "+"
+#define DOT_RECIPIENT_DELIMITER "."
/*
* All these tests must pass, so that we know that mail_addr_map_opt() works
"a@a@example.com",
{"\"a@a\"@example.net"}, 1,
},
+ {
+ "12 external -external-> external, extension, propagation",
+ "inline:{ aa@example.com=bb@example.com }",
+ DO_PROPAGATE_UNMATCHED_EXTENSION, DOT_RECIPIENT_DELIMITER,
+ MA_FORM_EXTERNAL, MA_FORM_EXTERNAL, MA_FORM_EXTERNAL,
+ "aa.ext@example.com",
+ {"bb.ext@example.com"}, 1,
+ },
0,
};
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20170612"
+#define MAIL_RELEASE_DATE "20170716"
#define MAIL_VERSION_NUMBER "3.3"
#ifdef SNAPSHOT
test31 test32 test33 test34 test35 test36 test37 test39 test40 test41 \
test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 \
test52 test53 test54 test55 test56 test57 test58 test59 test60 test61 \
- test62 test63 test64 test65
+ test62 test63 test64 test65 test66
root_tests:
diff test65.ref test65.tmp
rm -f main.cf master.cf test65.tmp
+# unknown parameters in database configuration file (absolute pathname).
+
+test66: $(PROG) test66.ref
+ rm -f main.cf master.cf
+ touch master.cf
+ echo alias_maps = ldap:`pwd`/test66.cf >> main.cf
+ echo " " mysql:`pwd`/test66.cf >> main.cf
+ echo " " pgsql:`pwd`/test66.cf >> main.cf
+ echo " " sqlite:`pwd`/test66.cf >> main.cf
+ echo " " memcache:`pwd`/test66.cf >> main.cf
+ echo junk = junk >> test66.cf
+ touch -t 197101010000 main.cf
+ $(SHLIB_ENV) ./$(PROG) -c. 2>test66.tmp >/dev/null
+ sed "s;PWD;`pwd`;" test66.ref | diff - test66.tmp
+ rm -f main.cf master.cf test66.tmp test66.cf
+
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
postconf_dbms.o: ../../include/argv.h
postconf_dbms.o: ../../include/check_arg.h
postconf_dbms.o: ../../include/dict.h
+postconf_dbms.o: ../../include/dict_ht.h
postconf_dbms.o: ../../include/dict_ldap.h
postconf_dbms.o: ../../include/dict_memcache.h
postconf_dbms.o: ../../include/dict_mysql.h
/* When a database type is found that supports legacy-style
/* configuration, the table name is combined with each of the
/* database-defined suffixes to generate candidate parameter
-/* names for that database type.
+/* names for that database type; if the table name specifies
+/* a client configuration file, that file is scanned for unused
+/* parameter settings.
/* .IP flag_parameter
/* A function that takes as arguments a candidate parameter
/* name, parameter flags, and a PCF_MASTER_ENT pointer. The
/* System library. */
#include <sys_defs.h>
+#include <errno.h>
#include <string.h>
/* Utility library. */
#include <mail_conf.h>
#include <mail_params.h>
+#include <dict_ht.h>
#include <dict_proxy.h>
#include <dict_ldap.h>
#include <dict_mysql.h>
0,
};
+/* pcf_check_dbms_client - look for unused names in client configuration */
+
+static void pcf_check_dbms_client(const PCF_DBMS_INFO *dp, const char *cf_file)
+{
+ DICT *dict;
+ VSTREAM *fp;
+ const char **cpp;
+ const char *name;
+ const char *value;
+ char *dict_spec;
+ int dir;
+
+ /*
+ * We read each database client configuration file into its own
+ * dictionary, and nag only the first time that a file is visited.
+ */
+ dict_spec = concatenate(dp->db_type, ":", cf_file, (char *) 0);
+ if ((dict = dict_handle(dict_spec)) == 0) {
+
+ /*
+ * Populate the dictionary with settings in this database client
+ * configuration file. Don't die if a file can't be opened - some
+ * files may contain passwords and should not be world-readable.
+ * Note: dict_load_fp() nags about duplicate pameter settings.
+ */
+ dict = dict_ht_open(dict_spec, O_CREAT | O_RDWR, 0);
+ dict_register(dict_spec, dict);
+ if ((fp = vstream_fopen(cf_file, O_RDONLY, 0)) == 0
+ && errno != EACCES) {
+ msg_warn("open \"%s\" configuration \"%s\": %m",
+ dp->db_type, cf_file);
+ myfree(dict_spec);
+ return;
+ }
+ dict_load_fp(dict_spec, fp);
+ if (vstream_fclose(fp)) {
+ msg_warn("read \"%s\" configuration \"%s\": %m",
+ dp->db_type, cf_file);
+ myfree(dict_spec);
+ return;
+ }
+
+ /*
+ * Remove all known database client parameters from this dictionary,
+ * then report the remaining ones as "unused". We use ad-hoc logging
+ * code, because a database client parameter namespace is unlike the
+ * parameter namespaces in main.cf or master.cf.
+ */
+ for (cpp = dp->db_suffixes; *cpp; cpp++)
+ (void) dict_del(dict, *cpp);
+ for (dir = DICT_SEQ_FUN_FIRST;
+ dict->sequence(dict, dir, &name, &value) == DICT_STAT_SUCCESS;
+ dir = DICT_SEQ_FUN_NEXT)
+ msg_warn("%s: unused parameter: %s=%s", dict_spec, name, value);
+ }
+ myfree(dict_spec);
+}
+
/* pcf_register_dbms_helper - parse one possible database type:name */
static void pcf_register_dbms_helper(char *str_value,
&& strcmp(db_type, DICT_TYPE_PROXY) == 0)
db_type = prefix;
+ if (prefix == 0)
+ continue;
+
+ /*
+ * Look for database:prefix where the prefix is an absolute pathname.
+ * Then, report unknown database client configuration parameters.
+ *
+ * XXX What about a pathname beginning with '.'? This supposedly is
+ * relative to the queue directory, which is the default directory
+ * for all Postfix daemon processes. This would also have to handle
+ * the case that the queue is not yet created.
+ */
+ if (*prefix == '/') {
+ for (dp = pcf_dbms_info; dp->db_type != 0; dp++) {
+ if (strcmp(db_type, dp->db_type) == 0) {
+ pcf_check_dbms_client(dp, prefix);
+ break;
+ }
+ }
+ continue;
+ }
+
/*
* Look for database:prefix where the prefix is not a pathname and
* the database is a known type. Synthesize candidate parameter names
* list, and see if those parameters have a "name=value" entry in the
* local or global namespace.
*/
- if (prefix != 0 && *prefix != '/' && *prefix != '.') {
+ if (*prefix != '.') {
if (*prefix == CHARS_BRACE[0]) {
if ((err = extpar(&prefix, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) {
/* XXX Encapsulate this in pcf_warn() function. */
--- /dev/null
+./postconf: warning: ldap:PWD/test66.cf: unused parameter: junk=junk
+./postconf: warning: mysql:PWD/test66.cf: unused parameter: junk=junk
+./postconf: warning: pgsql:PWD/test66.cf: unused parameter: junk=junk
+./postconf: warning: sqlite:PWD/test66.cf: unused parameter: junk=junk
+./postconf: warning: memcache:PWD/test66.cf: unused parameter: junk=junk
/*
* Parse the haproxy line. Note: the haproxy_srvr_parse() routine
* performs address protocol checks, address and port syntax checks, and
- * converts IPv4-in-IPv6 address string syntax (:ffff::1.2.3.4) to IPv4
+ * converts IPv4-in-IPv6 address string syntax (::ffff:1.2.3.4) to IPv4
* syntax where permitted by the main.cf:inet_protocols setting.
*/
if (status == 0 && last_char == '\n') {
/*
* Note: the haproxy_srvr_parse() routine performs address protocol
* checks, address and port syntax checks, and converts IPv4-in-IPv6
- * address string syntax (:ffff::1.2.3.4) to IPv4 syntax where permitted
+ * address string syntax (::ffff:1.2.3.4) to IPv4 syntax where permitted
* by the main.cf:inet_protocols setting, but logs no warnings.
*/
#define ENABLE_DEADLINE 1
#define HAVE_GLIBC_API_VERSION_SUPPORT(maj, min) __GLIBC_PREREQ(maj, min)
#else
#define HAVE_GLIBC_API_VERSION_SUPPORT(maj, min) \
- ((__GLIBC__ << 16) + __GLIBC_MINOR__ >= ((maj) << 16) + (min))
+ (defined(__GLIBC__) && \
+ ((__GLIBC__ << 16) + __GLIBC_MINOR__ >= ((maj) << 16) + (min)))
#endif
#if HAVE_GLIBC_API_VERSION_SUPPORT(2, 1)
#define SOCKADDR_SIZE socklen_t
#define KERNEL_VERSION(a,b,c) (LINUX_VERSION_CODE + 1)
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)) \
- || (__GLIBC__ < 2)
+ || (defined(__GLIBC__) && __GLIBC__ < 2)
#define CANT_USE_SEND_RECV_MSG
#define DEF_SMTP_CACHE_DEMAND 0
#else