From: Wietse Venema Date: Sun, 14 Aug 2011 05:00:00 +0000 (-0500) Subject: postfix-2.9-20110814 X-Git-Tag: v2.9.0-RC1~41 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=816198df55bbf8204771fa9ae4aa65415ab12c54;p=thirdparty%2Fpostfix.git postfix-2.9-20110814 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index e2dd02635..ccdbaa296 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -16850,3 +16850,34 @@ Apologies for any names omitted. permit_tls_all_clientcerts, and check_address_map (the last one is used in local_header_rewrite_clients only). File: smtpd/smtpd_check.c. + +20110729 + + Workaround: some getpwnam() and getpwuid() implementations + cause mail to bounce ("user unknown") after LDAP etc. lookup + error. Postfix now uses POSIX getpwnam_r() and getpwuid_r() + where available. Initially, this workaround supports FreeBSD, + Solaris and Linux. Files: makedefs, util/sys_defs.h, + global/mypwd.[hc], local/alias.c, local/dotforward.c, + local/include.c, local/mailbox.c, local/recipient.c. + +20110731 + + MacOS X 10.5 supports POSIX getpwnam_r() and getpwuid_r() + (source: MacOS manpages at www.freebsd.org). If MacOS turns + out to make a false promise, then we will undo this change. + Files: makedefs, util/sys_defs.h. + +20110810 + + Cleanup: optimize an optimization to avoid uid->name lookup + when all users are authorized with authorized_submit_users, + authorized_mailq_users, authorized_flush_users. File: + global/user_acl.c. + +20110811 + + Workaround: report a {client_connections} Milter macro value + of zero instead of garbage, when the remote SMTP client is + excluded from connection count limits. Problem reported by + Christian Roessner. File: smtpd/smtpd_state,c, diff --git a/postfix/WISHLIST b/postfix/WISHLIST index 0f11b8c6b..4c2ee9666 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -8,6 +8,8 @@ Wish list: Make the rules for how to use close-on-exec more explicit. + Add SASL / TLS note to BASIC_CONFIGURATION_README.html#relay_from. + Trick from amavisd: save listen socket/fifo/etc state, clear their close-on-exec flags, exec the same program file to re-initialize (with saved socket state on command line or diff --git a/postfix/html/postconf.1.html b/postfix/html/postconf.1.html index 1e351dcb6..ea7d04144 100644 --- a/postfix/html/postconf.1.html +++ b/postfix/html/postconf.1.html @@ -21,9 +21,9 @@ POSTCONF(1) POSTCONF(1) postconf [-btv] [-c config_dir] [template_file] DESCRIPTION - The postconf(1) command displays the actual values of con- - figuration parameters, changes configuration parameter - values, or displays other configuration information about + The postconf(1) command displays the values of configura- + tion parameters by default. It can also change parameter + values, or display other configuration information about the Postfix mail system. Options: @@ -88,8 +88,8 @@ POSTCONF(1) POSTCONF(1) With Postfix version 2.8 and later, the -e is no longer needed. - -h Show parameter values only, not the "name = " label - that normally precedes the value. + -h Show parameter values only; do not prepend the + "name = " label that normally precedes the value. -l List the names of all supported mailbox locking methods. Postfix supports the following methods: diff --git a/postfix/makedefs b/postfix/makedefs index bdfb150da..0f83e7275 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -48,6 +48,8 @@ # Do not build with PCRE support. # By default, PCRE support is compiled in when the \fBpcre-config\fR # utility is installed. +# .IP \fB-DNO_POSIX_GETPW_R\fR +# Disable support for POSIX getpwnam_r/getpwuid_r. # .IP \fB-DNO_SIGSETJMP\fR # Use setjmp()/longjmp() instead of sigsetjmp()/siglongjmp(). # By default, Postfix uses sigsetjmp()/siglongjmp() when they @@ -152,6 +154,8 @@ case "$SYSTEM.$RELEASE" in ;; OpenBSD.4*) SYSTYPE=OPENBSD4 ;; + OpenBSD.5*) SYSTYPE=OPENBSD5 + ;; ekkoBSD.1*) SYSTYPE=EKKOBSD1 ;; NetBSD.1*) SYSTYPE=NETBSD1 @@ -184,9 +188,9 @@ case "$SYSTEM.$RELEASE" in SYSLIBS="-lresolv -lsocket -lnsl" # Stock awk breaks with >10 files. test -x /usr/xpg4/bin/awk && AWK=/usr/xpg4/bin/awk - # Solaris 2.5 added usleep() and POSIX regular expressions + # Solaris 2.5 added usleep(), POSIX regexp, POSIX getpwnam/uid_r case $RELEASE in - 5.[0-4]) CCARGS="$CCARGS -DMISSING_USLEEP -DNO_POSIX_REGEXP";; + 5.[0-4]) CCARGS="$CCARGS -DMISSING_USLEEP -DNO_POSIX_REGEXP -DNO_POSIX_GETPW_R";; esac # Solaris 8 added IPv6 and /dev/poll case $RELEASE in @@ -454,6 +458,10 @@ ReliantUNIX-?.5.43) SYSTYPE=ReliantUnix543 [1-6].*) CCARGS="$CCARGS -DNO_IPV6";; *) CCARGS="$CCARGS -DBIND_8_COMPAT -DNO_NETINFO";; esac + # Darwin 9.0 (MacOS X 10.5) adds POSIX getpwnam_r/getpwuid_r + case $RELEASE in + [1-8].*) CCARGS="$CCARGS -DNO_POSIX_GETPW_R";; + esac # Darwin 10.3.0 no longer has . case $RELEASE in ?.*) CCARGS="$CCARGS -DRESOLVE_H_NEEDS_NAMESER8_COMPAT_H";; diff --git a/postfix/man/man1/postconf.1 b/postfix/man/man1/postconf.1 index 6e57e8d2e..eed34eb54 100644 --- a/postfix/man/man1/postconf.1 +++ b/postfix/man/man1/postconf.1 @@ -24,10 +24,10 @@ Postfix configuration utility .SH DESCRIPTION .ad .fi -The \fBpostconf\fR(1) command displays the actual values -of configuration parameters, changes configuration parameter -values, or displays other configuration information about -the Postfix mail system. +The \fBpostconf\fR(1) command displays the values of +configuration parameters by default. It can also change +parameter values, or display other configuration information +about the Postfix mail system. Options: .IP \fB-a\fR @@ -83,8 +83,8 @@ to protect shell metacharacters and whitespace. With Postfix version 2.8 and later, the \fB-e\fR is no longer needed. .IP \fB-h\fR -Show parameter values only, not the "\fIname = \fR" label -that normally precedes the value. +Show parameter values only; do not prepend the "\fIname = +\fR" label that normally precedes the value. .IP \fB-l\fR List the names of all supported mailbox locking methods. Postfix supports the following methods: diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 123944275..4e168bde9 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -2001,12 +2001,12 @@ trace.o: rcpt_print.h trace.o: recipient_list.h trace.o: trace.c trace.o: trace.h +user_acl.o: ../../include/dict_static.h user_acl.o: ../../include/match_list.h user_acl.o: ../../include/match_ops.h user_acl.o: ../../include/sys_defs.h user_acl.o: ../../include/vbuf.h user_acl.o: ../../include/vstring.h -user_acl.o: mail_params.h user_acl.o: mypwd.h user_acl.o: string_list.h user_acl.o: user_acl.c diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 697904f2a..fc1b0e90c 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20110706" +#define MAIL_RELEASE_DATE "20110814" #define MAIL_VERSION_NUMBER "2.9" #ifdef SNAPSHOT diff --git a/postfix/src/global/mypwd.c b/postfix/src/global/mypwd.c index 2b7821382..ef5928120 100644 --- a/postfix/src/global/mypwd.c +++ b/postfix/src/global/mypwd.c @@ -2,35 +2,53 @@ /* NAME /* mypwd 3 /* SUMMARY -/* caching getpwnam()/getpwuid() +/* caching getpwnam_r()/getpwuid_r() /* SYNOPSIS /* #include /* -/* struct mypasswd *mypwuid(uid) +/* int mypwuid_err(uid, pwd) /* uid_t uid; +/* struct mypasswd **pwd; /* -/* struct mypasswd *mypwnam(name) +/* int mypwnam_err(name, pwd) /* const char *name; +/* struct mypasswd **pwd; /* /* void mypwfree(pwd) /* struct mypasswd *pwd; +/* BACKWARDS COMPATIBILITY +/* struct mypasswd *mypwuid(uid) +/* uid_t uid; +/* +/* struct mypasswd *mypwnam(name) +/* const char *name; /* DESCRIPTION /* This module maintains a reference-counted cache of password -/* database lookup results. The idea is to avoid surprises by -/* getpwnam() or getpwuid() overwriting a previous result, while -/* at the same time avoiding duplicate copies of password -/* information in memory, and to avoid making repeated getpwxxx() -/* calls for the same information. +/* database lookup results. The idea is to avoid making repeated +/* getpw*() calls for the same information. +/* +/* mypwnam_err() and mypwuid_err() are wrappers that cache a +/* private copy of results from the getpwnam_r() and getpwuid_r() +/* library routines (on legacy systems: from getpwnam() and +/* getpwuid(). Note: cache updates are not protected by mutex. /* -/* mypwnam() and mypwuid() are wrappers that cache a private copy -/* of results from the getpwnam() and getpwuid() library routines. /* Results are shared between calls with the same \fIname\fR /* or \fIuid\fR argument, so changing results is verboten. /* -/* mypwfree() cleans up the result of mypwnam() and mypwuid(). +/* mypwnam() and mypwuid() are binary-compatibility wrappers +/* for legacy applications. +/* +/* mypwfree() cleans up the result of mypwnam*() and mypwuid*(). /* BUGS /* This module is security sensitive and complex at the same /* time, which is bad. +/* DIAGNOSTICS +/* mypwnam_err() and mypwuid_err() return a non-zero system +/* error code when the lookup could not be performed. They +/* return zero, plus a null struct mypasswd pointer, when the +/* requested information was not found. +/* +/* Fatal error: out of memory. /* LICENSE /* .ad /* .fi @@ -45,11 +63,12 @@ /* System library. */ #include -#include +#include #include #ifdef USE_PATHS_H #include #endif +#include /* Utility library. */ @@ -62,17 +81,52 @@ #include "mypwd.h" + /* + * Workaround: Solaris >= 2.5.1 provides two getpwnam_r() and getpwuid_r() + * implementations. The default variant is compatible with historical + * Solaris implementations. The non-default variant is POSIX-compliant. + * + * To get the POSIX-compliant variant, we include the file after + * defining _POSIX_PTHREAD_SEMANTICS. We do this after all other includes, + * so that we won't unexpectedly affect any other APIs. + * + * This happens to work because nothing above this includes , and + * because of the specific way that Solaris redefines getpwnam_r() and + * getpwuid_r() for POSIX compliance. We know the latter from peeking under + * the hood. What we do is only marginally better than directly invoking + * __posix_getpwnam_r() and __posix_getpwuid_r(). + */ +#ifdef GETPW_R_NEEDS_POSIX_PTHREAD_SEMANTICS +#define _POSIX_PTHREAD_SEMANTICS +#endif +#include + /* * The private cache. One for lookups by name, one for lookups by uid, and - * one for the last looked up result. + * one for the last looked up result. There is a minor problem: multiple + * cache entries may have the same uid value, but the cache that is indexed + * by uid can store only one entry per uid value. */ static HTABLE *mypwcache_name = 0; static BINHASH *mypwcache_uid = 0; static struct mypasswd *last_pwd; + /* + * XXX Solaris promises that we can determine the getpw*_r() buffer size by + * calling sysconf(_SC_GETPW_R_SIZE_MAX). Many systems promise that they + * will return an ERANGE error when the buffer is too small. However, not + * all systems make such promises. Therefore, we settle for the dumbest + * option: a large buffer. This is acceptable because the buffer is used + * only for short-term storage. + */ +#ifdef HAVE_POSIX_GETPW_R +#define GETPW_R_BUFSIZ 1024 +#endif +#define MYPWD_ERROR_DELAY (30) + /* mypwenter - enter password info into cache */ -static struct mypasswd *mypwenter(struct passwd * pwd) +static struct mypasswd *mypwenter(const struct passwd * pwd) { struct mypasswd *mypwd; @@ -92,15 +146,36 @@ static struct mypasswd *mypwenter(struct passwd * pwd) mypwd->pw_gecos = mystrdup(pwd->pw_gecos); mypwd->pw_dir = mystrdup(pwd->pw_dir); mypwd->pw_shell = mystrdup(*pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL); + + /* + * Avoid mypwcache_uid memory leak when multiple names have the same UID. + * This makes the lookup result dependent on program history. But, it was + * already history-dependent before we added this extra check. + */ htable_enter(mypwcache_name, mypwd->pw_name, (char *) mypwd); - binhash_enter(mypwcache_uid, (char *) &mypwd->pw_uid, - sizeof(mypwd->pw_uid), (char *) mypwd); + if (binhash_locate(mypwcache_uid, (char *) &mypwd->pw_uid, + sizeof(mypwd->pw_uid)) == 0) + binhash_enter(mypwcache_uid, (char *) &mypwd->pw_uid, + sizeof(mypwd->pw_uid), (char *) mypwd); return (mypwd); } /* mypwuid - caching getpwuid() */ struct mypasswd *mypwuid(uid_t uid) +{ + struct mypasswd *mypwd; + + while ((errno = mypwuid_err(uid, &mypwd)) != 0) { + msg_warn("getpwuid_r: %m"); + sleep(MYPWD_ERROR_DELAY); + } + return (mypwd); +} + +/* mypwuid_err - caching getpwuid_r(), minus thread safety */ + +int mypwuid_err(uid_t uid, struct mypasswd ** result) { struct passwd *pwd; struct mypasswd *mypwd; @@ -113,30 +188,58 @@ struct mypasswd *mypwuid(uid_t uid) mypwfree(last_pwd); last_pwd = 0; } else { - mypwd = last_pwd; + *result = mypwd = last_pwd; mypwd->refcount++; - return (mypwd); + return (0); } } /* - * Find the info in the cache or in the password database. Make a copy, - * so that repeated getpwnam() calls will not clobber our result. + * Find the info in the cache or in the password database. */ if ((mypwd = (struct mypasswd *) binhash_find(mypwcache_uid, (char *) &uid, sizeof(uid))) == 0) { - if ((pwd = getpwuid(uid)) == 0) +#ifdef HAVE_POSIX_GETPW_R + char pwstore[GETPW_R_BUFSIZ]; + struct passwd pwbuf; + int err; + + err = getpwuid_r(uid, &pwbuf, pwstore, sizeof(pwstore), &pwd); + if (err != 0) + return (err); + if (pwd == 0) { + *result = 0; + return (0); + } +#else + if ((pwd = getpwuid(uid)) == 0) { + *result = 0; return (0); + } +#endif mypwd = mypwenter(pwd); } - last_pwd = mypwd; + *result = last_pwd = mypwd; mypwd->refcount += 2; - return (mypwd); + return (0); } /* mypwnam - caching getpwnam() */ struct mypasswd *mypwnam(const char *name) +{ + struct mypasswd *mypwd; + + while ((errno = mypwnam_err(name, &mypwd)) != 0) { + msg_warn("getpwnam_r: %m"); + sleep(MYPWD_ERROR_DELAY); + } + return (mypwd); +} + +/* mypwnam_err - caching getpwnam_r(), minus thread safety */ + +int mypwnam_err(const char *name, struct mypasswd ** result) { struct passwd *pwd; struct mypasswd *mypwd; @@ -149,24 +252,39 @@ struct mypasswd *mypwnam(const char *name) mypwfree(last_pwd); last_pwd = 0; } else { - mypwd = last_pwd; + *result = mypwd = last_pwd; mypwd->refcount++; - return (mypwd); + return (0); } } /* - * Find the info in the cache or in the password database. Make a copy, - * so that repeated getpwnam() calls will not clobber our result. + * Find the info in the cache or in the password database. */ if ((mypwd = (struct mypasswd *) htable_find(mypwcache_name, name)) == 0) { - if ((pwd = getpwnam(name)) == 0) +#ifdef HAVE_POSIX_GETPW_R + char pwstore[GETPW_R_BUFSIZ]; + struct passwd pwbuf; + int err; + + err = getpwnam_r(name, &pwbuf, pwstore, sizeof(pwstore), &pwd); + if (err != 0) + return (err); + if (pwd == 0) { + *result = 0; + return (0); + } +#else + if ((pwd = getpwnam(name)) == 0) { + *result = 0; return (0); + } +#endif mypwd = mypwenter(pwd); } - last_pwd = mypwd; + *result = last_pwd = mypwd; mypwd->refcount += 2; - return (mypwd); + return (0); } /* mypwfree - destroy password info */ @@ -176,10 +294,15 @@ void mypwfree(struct mypasswd * mypwd) if (mypwd->refcount < 1) msg_panic("mypwfree: refcount %d", mypwd->refcount); + /* + * See mypwenter() for the reason behind the binhash_locate() test. + */ if (--mypwd->refcount == 0) { htable_delete(mypwcache_name, mypwd->pw_name, (void (*) (char *)) 0); - binhash_delete(mypwcache_uid, (char *) &mypwd->pw_uid, - sizeof(mypwd->pw_uid), (void (*) (char *)) 0); + if (binhash_locate(mypwcache_uid, (char *) &mypwd->pw_uid, + sizeof(mypwd->pw_uid))) + binhash_delete(mypwcache_uid, (char *) &mypwd->pw_uid, + sizeof(mypwd->pw_uid), (void (*) (char *)) 0); myfree(mypwd->pw_name); myfree(mypwd->pw_passwd); myfree(mypwd->pw_gecos); @@ -209,8 +332,11 @@ int main(int argc, char **argv) if (argc == 1) msg_fatal("usage: %s name or uid ...", argv[0]); - mypwd = (struct mypasswd **) mymalloc((argc + 1) * sizeof(*mypwd)); + mypwd = (struct mypasswd **) mymalloc((argc + 2) * sizeof(*mypwd)); + /* + * Do a sequence of lookups. + */ for (i = 1; i < argc; i++) { if (ISDIGIT(argv[i][0])) mypwd[i] = mypwuid(atoi(argv[i])); @@ -218,14 +344,23 @@ int main(int argc, char **argv) mypwd[i] = mypwnam(argv[i]); if (mypwd[i] == 0) msg_fatal("%s: not found", argv[i]); - msg_info("+ %s link=%d used=%d used=%d", argv[i], mypwd[i]->refcount, - mypwcache_name->used, mypwcache_uid->used); + msg_info("lookup %s %s/%d refcount=%d name_cache=%d uid_cache=%d", + argv[i], mypwd[i]->pw_name, mypwd[i]->pw_uid, + mypwd[i]->refcount, mypwcache_name->used, mypwcache_uid->used); } - for (i = 1; i < argc; i++) { - msg_info("- %s link=%d used=%d used=%d", argv[i], mypwd[i]->refcount, + mypwd[argc] = last_pwd; + + /* + * The following should free all entries. + */ + for (i = 1; i < argc + 1; i++) { + msg_info("free %s/%d refcount=%d name_cache=%d uid_cache=%d", + mypwd[i]->pw_name, mypwd[i]->pw_uid, mypwd[i]->refcount, mypwcache_name->used, mypwcache_uid->used); mypwfree(mypwd[i]); } + msg_info("name_cache=%d uid_cache=%d", + mypwcache_name->used, mypwcache_uid->used); myfree((char *) mypwd); return (0); diff --git a/postfix/src/global/mypwd.h b/postfix/src/global/mypwd.h index d5f8a892b..82f2cd37b 100644 --- a/postfix/src/global/mypwd.h +++ b/postfix/src/global/mypwd.h @@ -5,7 +5,7 @@ /* NAME /* mypwnam 3h /* SUMMARY -/* caching getpwnam()/getpwuid() +/* caching getpwnam_r()/getpwuid_r() /* SYNOPSIS /* #include /* DESCRIPTION @@ -25,6 +25,8 @@ struct mypasswd { char *pw_shell; }; +extern int mypwnam_err(const char *, struct mypasswd **); +extern int mypwuid_err(uid_t, struct mypasswd **); extern struct mypasswd *mypwnam(const char *); extern struct mypasswd *mypwuid(uid_t); extern void mypwfree(struct mypasswd *); diff --git a/postfix/src/global/user_acl.c b/postfix/src/global/user_acl.c index 0121c68dd..9dd19c0f7 100644 --- a/postfix/src/global/user_acl.c +++ b/postfix/src/global/user_acl.c @@ -46,12 +46,12 @@ /* Utility library. */ #include +#include /* Global library. */ #include #include -#include /* STATIC_ANYONE_ACL */ /* Application-specific. */ @@ -69,9 +69,10 @@ const char *check_user_acl_byuid(char *acl, uid_t uid) /* * Optimize for the most common case. This also makes Postfix a little - * more robust in the face of local infrastructure failures. + * more robust in the face of local infrastructure failures. Note that we + * only need to match the "static:" substring, not the result value. */ - if (strcmp(acl, STATIC_ANYONE_ACL) == 0) + if (strncmp(acl, DICT_TYPE_STATIC ":", sizeof(DICT_TYPE_STATIC)) == 0) return (0); /* @@ -89,6 +90,10 @@ const char *check_user_acl_byuid(char *acl, uid_t uid) * really suitable for mixing numerical and non-numerical user * information, because the numerical match is done in a separate pass * from the non-numerical match. This breaks when the ! operator is used. + * + * XXX To avoid waiting until the lookup completes (e.g., LDAP or NIS down) + * invoke mypwuid_err(), and either change the user_acl() API to + * propagate the error to the caller, or treat lookup errors as fatal. */ if ((mypwd = mypwuid(uid)) == 0) { name = "unknown"; diff --git a/postfix/src/local/Makefile.in b/postfix/src/local/Makefile.in index 291664b5a..503e502e6 100644 --- a/postfix/src/local/Makefile.in +++ b/postfix/src/local/Makefile.in @@ -191,6 +191,7 @@ dotforward.o: ../../include/argv.h dotforward.o: ../../include/attr.h dotforward.o: ../../include/been_here.h dotforward.o: ../../include/bounce.h +dotforward.o: ../../include/defer.h dotforward.o: ../../include/deliver_request.h dotforward.o: ../../include/delivered_hdr.h dotforward.o: ../../include/dict.h diff --git a/postfix/src/local/alias.c b/postfix/src/local/alias.c index 966a5dcb0..a2ead5795 100644 --- a/postfix/src/local/alias.c +++ b/postfix/src/local/alias.c @@ -63,6 +63,7 @@ #include #include #include +#include #ifdef STRCASECMP_IN_STRINGS_H #include @@ -218,8 +219,11 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, BOUNCE_ATTR(state.msg_attr)); return (YES); } - if ((alias_pwd = mypwuid(dict->owner.uid)) == 0) { - msg_warn("cannot find alias database owner for %s", *cpp); + if ((errno = mypwuid_err(dict->owner.uid, &alias_pwd)) != 0 + || alias_pwd == 0) { + msg_warn(errno ? + "cannot find alias database owner for %s: %m" : + "cannot find alias database owner for %s", *cpp); dsb_simple(state.msg_attr.why, "4.3.0", "cannot find alias database owner"); *statusp = defer_append(BOUNCE_FLAGS(state.request), diff --git a/postfix/src/local/dotforward.c b/postfix/src/local/dotforward.c index e26d241ff..7b7309e40 100644 --- a/postfix/src/local/dotforward.c +++ b/postfix/src/local/dotforward.c @@ -74,6 +74,7 @@ #include #include +#include #include #include #include @@ -126,7 +127,15 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) * Skip non-existing users. The mailbox delivery routine will catch the * error. */ - if ((mypwd = mypwnam(state.msg_attr.user)) == 0) + if ((errno = mypwnam_err(state.msg_attr.user, &mypwd)) != 0) { + msg_warn("error looking up passwd info for %s: %m", + state.msg_attr.user); + dsb_simple(state.msg_attr.why, "4.0.0", "user lookup error"); + *statusp = defer_append(BOUNCE_FLAGS(state.request), + BOUNCE_ATTR(state.msg_attr)); + return (YES); + } + if (mypwd == 0) return (NO); /* diff --git a/postfix/src/local/include.c b/postfix/src/local/include.c index 115883249..a213d3c08 100644 --- a/postfix/src/local/include.c +++ b/postfix/src/local/include.c @@ -49,6 +49,7 @@ #include #include #include +#include /* Utility library. */ @@ -156,8 +157,9 @@ int deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path) * that is owned by their victim. */ if (usr_attr.uid == 0) { - if ((file_pwd = mypwuid(st.st_uid)) == 0) { - msg_warn("cannot find username for uid %ld", (long) st.st_uid); + if ((errno = mypwuid_err(st.st_uid, &file_pwd)) != 0 || file_pwd == 0) { + msg_warn(errno ? "cannot find username for uid %ld: %m" : + "cannot find username for uid %ld", (long) st.st_uid); msg_warn("%s: cannot find :include: file owner username", path); dsb_simple(state.msg_attr.why, "4.3.5", "mail system configuration error"); diff --git a/postfix/src/local/mailbox.c b/postfix/src/local/mailbox.c index 58b01f79f..0cfb50e79 100644 --- a/postfix/src/local/mailbox.c +++ b/postfix/src/local/mailbox.c @@ -302,8 +302,16 @@ int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) /* * Skip delivery when this recipient does not exist. */ - if ((mbox_pwd = mypwnam(state.msg_attr.user)) == 0) - return (NO); + if ((errno = mypwnam_err(state.msg_attr.user, &mbox_pwd)) != 0) { + msg_warn("error looking up passwd info for %s: %m", + state.msg_attr.user); + dsb_simple(state.msg_attr.why, "4.0.0", "user lookup error"); + *statusp = defer_append(BOUNCE_FLAGS(state.request), + BOUNCE_ATTR(state.msg_attr)); + return (YES); + } + if (mbox_pwd == 0) + return (NO); /* * No early returns or we have a memory leak. diff --git a/postfix/src/local/recipient.c b/postfix/src/local/recipient.c index f6f34502c..192144493 100644 --- a/postfix/src/local/recipient.c +++ b/postfix/src/local/recipient.c @@ -63,6 +63,7 @@ #include #include #include +#include #ifdef STRCASECMP_IN_STRINGS_H #include @@ -179,14 +180,25 @@ static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr) * $HOME/.forward file, then mailbox delivery. Back off when the user's * home directory does not exist. */ + mypwd = 0; if (var_stat_home_dir - && (mypwd = mypwnam(state.msg_attr.user)) != 0 - && stat_as(mypwd->pw_dir, &st, mypwd->pw_uid, mypwd->pw_gid) < 0) { - dsb_simple(state.msg_attr.why, "4.3.0", - "cannot access home directory %s: %m", mypwd->pw_dir); + && (errno = mypwnam_err(state.msg_attr.user, &mypwd)) != 0) { + msg_warn("error looking up passwd info for %s: %m", + state.msg_attr.user); + dsb_simple(state.msg_attr.why, "4.0.0", "user lookup error"); return (defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr))); } + if (mypwd != 0) { + if (stat_as(mypwd->pw_dir, &st, mypwd->pw_uid, mypwd->pw_gid) < 0) { + dsb_simple(state.msg_attr.why, "4.3.0", + "cannot access home directory %s: %m", mypwd->pw_dir); + mypwfree(mypwd); + return (defer_append(BOUNCE_FLAGS(state.request), + BOUNCE_ATTR(state.msg_attr))); + } + mypwfree(mypwd); + } if (deliver_dotforward(state, usr_attr, &status) == 0 && deliver_mailbox(state, usr_attr, &status) == 0) status = deliver_unknown(state, usr_attr); diff --git a/postfix/src/postconf/postconf.c b/postfix/src/postconf/postconf.c index 2ba32f106..6acedeff4 100644 --- a/postfix/src/postconf/postconf.c +++ b/postfix/src/postconf/postconf.c @@ -18,10 +18,10 @@ /* /* \fBpostconf\fR [\fB-btv\fR] [\fB-c \fIconfig_dir\fR] [\fItemplate_file\fR] /* DESCRIPTION -/* The \fBpostconf\fR(1) command displays the actual values -/* of configuration parameters, changes configuration parameter -/* values, or displays other configuration information about -/* the Postfix mail system. +/* The \fBpostconf\fR(1) command displays the values of +/* configuration parameters by default. It can also change +/* parameter values, or display other configuration information +/* about the Postfix mail system. /* /* Options: /* .IP \fB-a\fR @@ -77,8 +77,8 @@ /* With Postfix version 2.8 and later, the \fB-e\fR is no /* longer needed. /* .IP \fB-h\fR -/* Show parameter values only, not the "\fIname = \fR" label -/* that normally precedes the value. +/* Show parameter values only; do not prepend the "\fIname = +/* \fR" label that normally precedes the value. /* .IP \fB-l\fR /* List the names of all supported mailbox locking methods. /* Postfix supports the following methods: diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c index 98f2f5b83..12bca3025 100644 --- a/postfix/src/smtpd/smtpd_state.c +++ b/postfix/src/smtpd/smtpd_state.c @@ -84,6 +84,7 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream, state->service = mystrdup(service); state->buffer = vstring_alloc(100); state->addr_buf = vstring_alloc(100); + state->conn_count = state->conn_rate = 0; state->error_count = 0; state->error_mask = 0; state->notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks, diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index 422219055..757e56c69 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -28,6 +28,7 @@ || defined(FREEBSD8) \ || defined(BSDI2) || defined(BSDI3) || defined(BSDI4) \ || defined(OPENBSD2) || defined(OPENBSD3) || defined(OPENBSD4) \ + || defined(OPENBSD5) \ || defined(NETBSD1) || defined(NETBSD2) || defined(NETBSD3) \ || defined(NETBSD4) \ || defined(EKKOBSD1) @@ -187,6 +188,14 @@ # endif #endif +#ifndef NO_POSIX_GETPW_R +# if (defined(__FreeBSD_version) && __FreeBSD_version >= 510000) \ + || (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 300000000) \ + || (defined(OpenBSD) && OpenBSD >= 200811) /* OpenBSD 4.4 */ +# define HAVE_POSIX_GETPW_R +# endif +#endif + #endif /* @@ -235,6 +244,9 @@ # define EVENTS_STYLE EVENTS_STYLE_KQUEUE # define USE_SYSV_POLL #endif +#ifndef NO_POSIX_GETPW_R +# define HAVE_POSIX_GETPW_R +#endif #endif @@ -443,6 +455,10 @@ extern int opterr; #ifndef NO_DEVPOLL # define EVENTS_STYLE EVENTS_STYLE_DEVPOLL #endif +#ifndef NO_POSIX_GETPW_R +# define HAVE_POSIX_GETPW_R +# define GETPW_R_NEEDS_POSIX_PTHREAD_SEMANTICS +#endif /* * Allow build environment to override paths. @@ -772,6 +788,16 @@ extern int initgroups(const char *, int); # define EVENTS_STYLE EVENTS_STYLE_EPOLL /* introduced in 2.5 */ #endif #define USE_SYSV_POLL +#ifndef NO_POSIX_GETPW_R +# if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) \ + || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 1) \ + || (defined(_BSD_SOURCE) && _BSD_SOURCE >= 1) \ + || (defined(_SVID_SOURCE) && _SVID_SOURCE >= 1) \ + || (defined(_POSIX_SOURCE) && _POSIX_SOURCE >= 1) +# define HAVE_POSIX_GETPW_R +# endif +#endif + #endif #ifdef LINUX1 @@ -1358,6 +1384,10 @@ typedef int WAIT_STATUS_T; #define NORMAL_EXIT_STATUS(status) ((status) == 0) #endif +#ifdef NO_POSIX_GETPW_R +#undef HAVE_POSIX_GETPW_R +#endif + #ifndef OCTAL_TO_UNSIGNED #define OCTAL_TO_UNSIGNED(res, str) ((res) = strtoul((str), (char **) 0, 8)) #endif