test. A filter timeout was mis-reported as lost connection.
Found in code review. File: smtpd/smtpd_proxy.c.
+20060327
+
+ Cleanup: the SQL and LDAP clients now log a warning when
+ they skip an empty lookup result, so that humans don't have
+ to wonder why Postfix doesn't find all the database entries.
+ File: global/db_common.c.
+
+20060328
+
+ Feature: configurable chroot directive for the pipe(8)
+ delivery agent, by Przemyslaw Wegrzyn. Files:
+ global/pipe_command.c, pipe/pipe.c.
+
+ Bugfix: cut-and-paste error: lmtp_connection_cache_limit
+ was left with the name of smtp_connection_cache_limit.
+ Reported by Victor? File: src/global/mail_params.h.
+
+20060403
+
+ Cleanup: made fcntl/flock handling consistent with respect
+ to EINTR (reported by Carlo Contavalli). However, Postfix
+ is not meant to be signal safe. Only the master daemon
+ handles signals without terminating, and it uses only a
+ small subset of Postfix library routines. File: util/myflock.c.
+
+ Bugfix: the pipe-to-command error message was lost when the
+ command could not be executed. File: global/pipe_command.c.
+
Wish list:
Don't send xforward attributes to every site that announces
/some/where/dovecot.conf:
auth default {
- ..
+ mechanisms = plain login
+ passdb pam {
+ }
+ userdb passwd {
+ }
socket listen {
client {
path = /var/spool/postfix/private/auth
- mode = 0666
+ mode = 0660
+ user = postfix
+ group = postfix
}
}
}
reject_authenticated_sender_login_mismatch and
reject_unauthenticated_sender_login_mismatch, and revised the docs.
* Wietse made another iteration through the code to add plug-in support for
- multiple implementations.
- * The Dovecot SMTP server plug-in was originally implemented by Timo Sirainen
- of Procontrol, Finland.
+ multiple SASL implementations.
+ * The Dovecot SMTP server-only plug-in was originally implemented by Timo
+ Sirainen of Procontrol, Finland.
<li> <p> The "-DDEF_SASL_SERVER" stuff is not necessary; it just
makes Postfix configuration a little more convenient because you
-don't have to specify the SASL plug-in type in the Postfix main.cf
+don't have to specify the SASL plug-in type in the Postfix <a href="postconf.5.html">main.cf</a>
file. </p>
<li> <p> If you also want support for LDAP or TLS, you will have to merge
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtpd_sasl_auth_enable">smtpd_sasl_auth_enable</a> = yes
</pre>
</blockquote>
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
<a href="postconf.5.html#permit_mynetworks">permit_mynetworks</a> <a href="postconf.5.html#permit_sasl_authenticated">permit_sasl_authenticated</a> ...
</pre>
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtpd_sasl_authenticated_header">smtpd_sasl_authenticated_header</a> = yes
</pre>
</blockquote>
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#broken_sasl_auth_clients">broken_sasl_auth_clients</a> = yes
</pre>
</blockquote>
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a> = dovecot
<a href="postconf.5.html#smtpd_sasl_path">smtpd_sasl_path</a> = private/auth
</pre>
<pre>
/some/where/dovecot.conf:
auth default {
- ..
+ mechanisms = plain login
+ passdb pam {
+ }
+ userdb passwd {
+ }
socket listen {
client {
path = /var/spool/postfix/private/auth
- mode = 0666
+ mode = 0660
+ user = postfix
+ group = postfix
}
}
}
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
smtpd_sasl_application_name = smtpd
</pre>
</blockquote>
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtpd_sasl_local_domain">smtpd_sasl_local_domain</a> = $<a href="postconf.5.html#myhostname">myhostname</a>
</pre>
</blockquote>
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtp_sasl_auth_enable">smtp_sasl_auth_enable</a> = yes
<a href="postconf.5.html#smtp_sasl_password_maps">smtp_sasl_password_maps</a> = hash:/etc/postfix/sasl_passwd
<a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a> = cyrus
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtp_sender_dependent_authentication">smtp_sender_dependent_authentication</a> = yes
<a href="postconf.5.html#smtp_sasl_auth_enable">smtp_sasl_auth_enable</a> = yes
<a href="postconf.5.html#smtp_sasl_password_maps">smtp_sasl_password_maps</a> = hash:/etc/postfix/sasl_passwd
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtp_sasl_security_options">smtp_sasl_security_options</a> = noanonymous
</pre>
</blockquote>
<blockquote>
<pre>
-/etc/postfix/main.cf:
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
<a href="postconf.5.html#smtp_sasl_mechanism_filter">smtp_sasl_mechanism_filter</a> = !gssapi, !external, static:all
</pre>
</blockquote>
<a href="postconf.5.html#reject_unauthenticated_sender_login_mismatch">reject_unauthenticated_sender_login_mismatch</a>, and revised the docs.
<li> Wietse made another iteration through the code to add
-plug-in support for multiple implementations.
+plug-in support for multiple SASL implementations.
-<li> The Dovecot SMTP server plug-in was originally implemented by
+<li> The Dovecot SMTP server-only plug-in was originally implemented by
Timo Sirainen of Procontrol, Finland.
</ul>
file at the end of a service definition. The syntax is as
follows:
- <b>directory=</b><i>pathname</i> (optional, default: <b>$<a href="postconf.5.html#queue_directory">queue_directory</a></b>)
- Change to the named directory before executing the
- external command. Delivery is deferred in case of
+ <b>chroot=</b><i>pathname</i> (optional)
+ Change the process root directory and working
+ directory to the named directory. This happens
+ before switching to the privileges specified with
+ the <b>user</b> attribute, and before executing the
+ optional <b>directory=</b><i>pathname</i> directive. Delivery is
+ deferred in case of failure.
+
+ This feature is available as of Postfix 2.3.
+
+ <b>directory=</b><i>pathname</i> (optional)
+ Change to the named directory before executing the
+ external command. The directory must be accessible
+ for the user specified with the <b>user</b> attribute (see
+ below). The default working directory is
+ <b>$<a href="postconf.5.html#queue_directory">queue_directory</a></b>. Delivery is deferred in case of
failure.
This feature is available as of Postfix 2.2.
<b>user</b>=<i>username</i> (required)
<b>user</b>=<i>username</i>:<i>groupname</i>
- The external command is executed with the rights of
- the specified <i>username</i>. The software refuses to
- execute commands with root privileges, or with the
+ Execute the external command with the rights of the
+ specified <i>username</i>. The software refuses to exe-
+ cute commands with root privileges, or with the
privileges of the mail system owner. If <i>groupname</i>
is specified, the corresponding group ID is used
instead of the group ID of <i>username</i>.
<p> This feature is available in Postfix 2.3 and later. </p>
+</DD>
+
+<DT><b><a name="lmtp_connection_cache_time_limit">lmtp_connection_cache_time_limit</a>
+(default: 2s)</b></DT><DD>
+
+<p> The LMTP-specific version of the
+<a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a> configuration parameter.
+See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
</DD>
<DT><b><a name="lmtp_connection_reuse_time_limit">lmtp_connection_reuse_time_limit</a>
configuration parameter. See there for details.
.PP
This feature is available in Postfix 2.3 and later.
+.SH lmtp_connection_cache_time_limit (default: 2s)
+The LMTP-specific version of the
+smtp_connection_cache_time_limit configuration parameter.
+See there for details.
+.PP
+This feature is available in Postfix 2.3 and later.
.SH lmtp_connection_reuse_time_limit (default: 300s)
The LMTP-specific version of the smtp_connection_reuse_time_limit
configuration parameter. See there for details.
.fi
The external command attributes are given in the \fBmaster.cf\fR
file at the end of a service definition. The syntax is as follows:
-.IP "\fBdirectory=\fIpathname\fR (optional, default: \fB$queue_directory\fR)"
+.IP "\fBchroot=\fIpathname\fR (optional)"
+Change the process root directory and working directory to
+the named directory. This happens before switching to the
+privileges specified with the \fBuser\fR attribute, and
+before executing the optional \fBdirectory=\fIpathname\fR
+directive. Delivery is deferred in case of failure.
+.sp
+This feature is available as of Postfix 2.3.
+.IP "\fBdirectory=\fIpathname\fR (optional)"
Change to the named directory before executing the external command.
+The directory must be accessible for the user specified with the
+\fBuser\fR attribute (see below).
+The default working directory is \fB$queue_directory\fR.
Delivery is deferred in case of failure.
.sp
This feature is available as of Postfix 2.2.
be returned to the sender as undeliverable.
.IP "\fBuser\fR=\fIusername\fR (required)"
.IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR"
-The external command is executed with the rights of the
+Execute the external command with the rights of the
specified \fIusername\fR. The software refuses to execute
commands with root privileges, or with the privileges of the
mail system owner. If \fIgroupname\fR is specified, the
s;\blmtp_sasl_mechanism_filter\b;<a href="postconf.5.html#lmtp_sasl_mechanism_filter">$&</a>;g;
s;\blmtp_host_lookup\b;<a href="postconf.5.html#lmtp_host_lookup">$&</a>;g;
s;\blmtp_connection_cache_destinations\b;<a href="postconf.5.html#lmtp_connection_cache_destinations">$&</a>;g;
+ s;\blmtp_connection_cache_time_limit\b;<a href="postconf.5.html#lmtp_connection_cache_time_limit">$&</a>;g;
s;\blmtp_tls_per_site\b;<a href="postconf.5.html#lmtp_tls_per_site">$&</a>;g;
s;\blmtp_generic_maps\b;<a href="postconf.5.html#lmtp_generic_maps">$&</a>;g;
s;\blmtp_pix_workaround_threshold_time\b;<a href="postconf.5.html#lmtp_pix_workaround_threshold_time">$&</a>;g;
<pre>
/some/where/dovecot.conf:
auth default {
- ..
+ mechanisms = plain login
+ passdb pam {
+ }
+ userdb passwd {
+ }
socket listen {
client {
path = /var/spool/postfix/private/auth
- mode = 0666
+ mode = 0660
+ user = postfix
+ group = postfix
}
}
}
reject_unauthenticated_sender_login_mismatch, and revised the docs.
<li> Wietse made another iteration through the code to add
-plug-in support for multiple implementations.
+plug-in support for multiple SASL implementations.
-<li> The Dovecot SMTP server plug-in was originally implemented by
+<li> The Dovecot SMTP server-only plug-in was originally implemented by
Timo Sirainen of Procontrol, Finland.
</ul>
<p> This feature is available in Postfix 2.3 and later. </p>
+%PARAM lmtp_connection_cache_time_limit 2s
+
+<p> The LMTP-specific version of the
+smtp_connection_cache_time_limit configuration parameter.
+See there for details. </p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
%PARAM smtpd_delay_open_until_valid_rcpt yes
<p> Postpone the start of an SMTP mail transaction until a valid
own_inet_addr.o: own_inet_addr.c
own_inet_addr.o: own_inet_addr.h
pipe_command.o: ../../include/argv.h
+pipe_command.o: ../../include/chroot_uid.h
pipe_command.o: ../../include/clean_env.h
pipe_command.o: ../../include/exec_command.h
pipe_command.o: ../../include/iostuff.h
pipe_command.o: ../../include/msg.h
+pipe_command.o: ../../include/msg_vstream.h
pipe_command.o: ../../include/set_eugid.h
pipe_command.o: ../../include/set_ugid.h
pipe_command.o: ../../include/stringops.h
db_quote_callback_t quote_func)
{
char *myname = "db_common_expand";
- DB_COMMON_CTX *ctx = (DB_COMMON_CTX *)ctxArg;
+ DB_COMMON_CTX *ctx = (DB_COMMON_CTX *) ctxArg;
const char *vdomain = 0;
const char *kdomain = 0;
char *vuser = 0;
int i;
const char *cp;
- /* Skip NULL or empty values */
- if (value == 0 || *value == 0)
- return (0);
-
+ /* Skip NULL values, silently. */
+ if (value == 0)
+ return (0);
+
+ /* Don't silenty skip empty query string or empty lookup results. */
+ if (*value == 0) {
+ if (key)
+ msg_warn("table \"%s:%s\": empty lookup result for: \"%s\""
+ " -- ignored", ctx->dict->type, ctx->dict->name, key);
+ else
+ msg_warn("table \"%s:%s\": empty query string"
+ " -- ignored", ctx->dict->type, ctx->dict->name);
+ return (0);
+ }
if (key) {
- /* This is a result template and the input value is the result */
+ /* This is a result template and the input value is the result */
if (ctx->flags & (DB_COMMON_VALUE_DOMAIN | DB_COMMON_VALUE_USER))
if ((vdomain = strrchr(value, '@')) != 0)
++vdomain;
- if ((!vdomain || !*vdomain) && (ctx->flags&DB_COMMON_VALUE_DOMAIN) != 0
- || vdomain == value + 1 && (ctx->flags&DB_COMMON_VALUE_USER) != 0)
+ if ((!vdomain || !*vdomain) && (ctx->flags & DB_COMMON_VALUE_DOMAIN) != 0
+ || vdomain == value + 1 && (ctx->flags & DB_COMMON_VALUE_USER) != 0)
return (0);
/* The result format may use the local or domain part of the key */
++kdomain;
/*
- * The key should already be checked before the query. No harm if
- * the query did not get optimized out, so we just issue a warning.
+ * The key should already be checked before the query. No harm if the
+ * query did not get optimized out, so we just issue a warning.
*/
- if ((!kdomain || !*kdomain) && (ctx->flags&DB_COMMON_KEY_DOMAIN) != 0
- || kdomain == key + 1 && (ctx->flags & DB_COMMON_KEY_USER) != 0) {
+ if ((!kdomain || !*kdomain) && (ctx->flags & DB_COMMON_KEY_DOMAIN) != 0
+ || kdomain == key + 1 && (ctx->flags & DB_COMMON_KEY_USER) != 0) {
msg_warn("%s: %s: lookup key '%s' skipped after query", myname,
- ctx->dict->name, value);
+ ctx->dict->name, value);
return (0);
}
} else {
- /* This is a query template and the input value is the lookup key */
+ /* This is a query template and the input value is the lookup key */
if (ctx->flags & (DB_COMMON_KEY_DOMAIN | DB_COMMON_KEY_USER))
if ((vdomain = strrchr(value, '@')) != 0)
++vdomain;
- if ((!vdomain || !*vdomain) && (ctx->flags&DB_COMMON_KEY_DOMAIN) != 0
- || vdomain == value + 1 && (ctx->flags & DB_COMMON_KEY_USER) != 0)
+ if ((!vdomain || !*vdomain) && (ctx->flags & DB_COMMON_KEY_DOMAIN) != 0
+ || vdomain == value + 1 && (ctx->flags & DB_COMMON_KEY_USER) != 0)
return (0);
}
if (ctx->nparts > 0) {
- parts = argv_split(key ? kdomain : vdomain, ".");
+ parts = argv_split(key ? kdomain : vdomain, ".");
+
/*
- * Filter out input keys whose domains lack enough labels
- * to fill-in the query template. See below and also
- * db_common_parse() which initializes ctx->nparts.
+ * Filter out input keys whose domains lack enough labels to fill-in
+ * the query template. See below and also db_common_parse() which
+ * initializes ctx->nparts.
*/
if (parts->argc < ctx->nparts) {
argv_free(parts);
return (0);
}
+
/*
- * Skip domains with leading, consecutive or trailing '.'
- * separators among the required labels.
+ * Skip domains with leading, consecutive or trailing '.' separators
+ * among the required labels.
*/
for (i = 0; i < ctx->nparts; i++)
- if (*parts->argv[parts->argc-i-1] == 0) {
+ if (*parts->argv[parts->argc - i - 1] == 0) {
argv_free(parts);
return (0);
}
}
-
if (VSTRING_LEN(result) > 0)
- VSTRING_ADDCH(result, ',');
+ VSTRING_ADDCH(result, ',');
#define QUOTE_VAL(d, q, v, buf) do { \
if (q) \
} while (0)
/*
- * Replace all instances of %s with the address to look up. Replace
- * %u with the user portion, and %d with the domain portion. "%%"
- * expands to "%". lowercase -> addr, uppercase -> key
+ * Replace all instances of %s with the address to look up. Replace %u
+ * with the user portion, and %d with the domain portion. "%%" expands to
+ * "%". lowercase -> addr, uppercase -> key
*/
for (cp = format; *cp; cp++) {
if (*cp == '%') {
if (vuser == 0)
vuser = mystrndup(value, vdomain - value - 1);
QUOTE_VAL(ctx->dict, quote_func, vuser, result);
- }
- else
+ } else
QUOTE_VAL(ctx->dict, quote_func, value, result);
break;
break;
case 'S':
- if (key)
+ if (key)
QUOTE_VAL(ctx->dict, quote_func, key, result);
else
QUOTE_VAL(ctx->dict, quote_func, value, result);
if (kuser == 0)
kuser = mystrndup(key, kdomain - key - 1);
QUOTE_VAL(ctx->dict, quote_func, kuser, result);
- }
- else
+ } else
QUOTE_VAL(ctx->dict, quote_func, key, result);
} else {
if (vdomain) {
if (vuser == 0)
vuser = mystrndup(value, vdomain - value - 1);
QUOTE_VAL(ctx->dict, quote_func, vuser, result);
- }
- else
+ } else
QUOTE_VAL(ctx->dict, quote_func, value, result);
}
break;
QUOTE_VAL(ctx->dict, quote_func, vdomain, result);
break;
- case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- /*
- * Interpolate %[1-9] components into the query string.
- * By this point db_common_parse() has identified the
- * highest component index, and (see above) keys with
- * fewer components have been filtered out. The "parts"
- * ARGV is guaranteed to be initialized and hold enough
- * elements to satisfy the query template.
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+
+ /*
+ * Interpolate %[1-9] components into the query string. By
+ * this point db_common_parse() has identified the highest
+ * component index, and (see above) keys with fewer
+ * components have been filtered out. The "parts" ARGV is
+ * guaranteed to be initialized and hold enough elements to
+ * satisfy the query template.
*/
QUOTE_VAL(ctx->dict, quote_func,
- parts->argv[parts->argc-(*cp-'0')], result);
+ parts->argv[parts->argc - (*cp - '0')], result);
break;
default:
VSTRING_TERMINATE(result);
if (vuser)
- myfree(vuser);
+ myfree(vuser);
if (kuser)
- myfree(kuser);
+ myfree(kuser);
if (parts)
- argv_free(parts);
+ argv_free(parts);
return (1);
}
#define VAR_SMTP_CACHE_CONNT "smtp_connection_cache_time_limit"
#define DEF_SMTP_CACHE_CONNT "2s"
-#define VAR_LMTP_CACHE_CONNT "smtp_connection_cache_time_limit"
+#define VAR_LMTP_CACHE_CONNT "lmtp_connection_cache_time_limit"
#define DEF_LMTP_CACHE_CONNT "2s"
extern int var_smtp_cache_conn;
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20060325"
+#define MAIL_RELEASE_DATE "20060403"
#define MAIL_VERSION_NUMBER "2.3"
#ifdef SNAPSHOT
/* The command is specified as an argument vector. This vector is
/* passed without further inspection to the \fIexecvp\fR() routine.
/* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified.
+/* .IP "PIPE_CMD_CHROOT (char *)"
+/* Root and working directory for command execution. This takes
+/* effect before PIPE_CMD_CWD. A null pointer means don't
+/* change root and working directory anyway. Failure to change
+/* directory causes mail delivery to be deferred.
/* .IP "PIPE_CMD_CWD (char *)"
-/* Working directory for command execution. A null pointer means
+/* Working directory for command execution, after changing process
+/* privileges to PIPE_CMD_UID and PIPE_CMD_GID. A null pointer means
/* don't change directory anyway. Failure to change directory
/* causes mail delivery to be deferred.
/* .IP "PIPE_CMD_ENV (char **)"
#include <msg.h>
#include <vstream.h>
+#include <msg_vstream.h>
#include <vstring.h>
#include <stringops.h>
#include <iostuff.h>
#include <set_ugid.h>
#include <set_eugid.h>
#include <argv.h>
+#include <chroot_uid.h>
/* Global library. */
char **export; /* exportable environment */
char *shell; /* command shell */
char *cwd; /* preferred working directory */
+ char *chroot; /* root directory */
};
static int pipe_command_timeout; /* command has timed out */
args->export = 0;
args->shell = 0;
args->cwd = 0;
+ args->chroot = 0;
pipe_command_maxtime = var_command_maxtime;
case PIPE_CMD_CWD:
args->cwd = va_arg(ap, char *);
break;
+ case PIPE_CMD_CHROOT:
+ args->chroot = va_arg(ap, char *);
+ break;
default:
msg_panic("%s: unknown key: %d", myname, key);
}
static void pipe_child_cleanup(void)
{
- exit(EX_TEMPFAIL);
+
+ /*
+ * WARNING: don't place code here. This code may run as mail_owner, as
+ * root, or as the user/group specified with the "user" attribute. The
+ * only safe action is to terminate.
+ *
+ * Future proofing. If you need exit() here then you broke Postfix.
+ */
+ _exit(EX_TEMPFAIL);
}
/* pipe_command - execute command with extreme prejudice */
int pipe_command(VSTREAM *src, DSN_BUF *why,...)
{
- char *myname = "pipe_comand";
+ char *myname = "pipe_command";
va_list ap;
VSTREAM *cmd_in_stream;
VSTREAM *cmd_out_stream;
*/
case 0:
(void) msg_cleanup(pipe_child_cleanup);
+
+ /*
+ * In order to chroot it is necessary to switch euid back to root.
+ * Right after chroot we call set_ugid() so all privileges will be
+ * dropped again.
+ *
+ * XXX For consistency we use chroot_uid() to change root+current
+ * directory. However, we must not use chroot_uid() to change process
+ * privileges (assuming a version that accepts numeric privileges).
+ * That would create a maintenance problem, because we would have two
+ * different code paths to set the external command's privileges.
+ */
+ if (args.chroot) {
+ seteuid(0);
+ chroot_uid(args.chroot, (char *) 0);
+ }
+
+ /*
+ * XXX If we put code before the set_ugid() call, then the code that
+ * changes root directory must switch back to the mail_owner UID,
+ * otherwise we'd be running with root privileges.
+ */
set_ugid(args.uid, args.gid);
if (setsid() < 0)
msg_warn("setsid failed: %m");
/*
* Process plumbing. If possible, avoid running a shell.
*
- * From this point we would like to handle fatal errors ourselves
- * (ENOMEM would probably be one of the few soft error conditions).
- * For that we have to update exec_command() first so it returns an
- * error indication instead of terminating the process.
+ * As a safety for buggy libraries, we close the syslog socket.
+ * Otherwise we could leak a file descriptor that was created by a
+ * privileged process.
+ *
+ * XXX To avoid losing fatal error messages we open a VSTREAM and
+ * capture the output in the parent process.
*/
closelog();
+ msg_vstream_init(var_procname, VSTREAM_ERR);
if (args.argv) {
execvp(args.argv[0], args.argv);
msg_fatal("%s: execvp %s: %m", myname, args.argv[0]);
return (sp->dsn[0] == '4' ?
PIPE_STAT_DEFER : PIPE_STAT_BOUNCE);
}
- /* No "D.S.N text" or <sysexits.h> compatible status. Fake it. */
+
+ /*
+ * No "D.S.N text" or <sysexits.h> compatible status. Fake it.
+ */
else {
sp = sys_exits_detail(WEXITSTATUS(wait_status));
dsb_unix(why, sp->dsn,
#define PIPE_CMD_EXPORT 12 /* exportable environment */
#define PIPE_CMD_ORIG_RCPT 13 /* mail_copy() original recipient */
#define PIPE_CMD_CWD 14 /* working directory */
+#define PIPE_CMD_CHROOT 15 /* chroot() before exec() */
/*
* Command completion status.
/* .fi
/* The external command attributes are given in the \fBmaster.cf\fR
/* file at the end of a service definition. The syntax is as follows:
-/* .IP "\fBdirectory=\fIpathname\fR (optional, default: \fB$queue_directory\fR)"
+/* .IP "\fBchroot=\fIpathname\fR (optional)"
+/* Change the process root directory and working directory to
+/* the named directory. This happens before switching to the
+/* privileges specified with the \fBuser\fR attribute, and
+/* before executing the optional \fBdirectory=\fIpathname\fR
+/* directive. Delivery is deferred in case of failure.
+/* .sp
+/* This feature is available as of Postfix 2.3.
+/* .IP "\fBdirectory=\fIpathname\fR (optional)"
/* Change to the named directory before executing the external command.
+/* The directory must be accessible for the user specified with the
+/* \fBuser\fR attribute (see below).
+/* The default working directory is \fB$queue_directory\fR.
/* Delivery is deferred in case of failure.
/* .sp
/* This feature is available as of Postfix 2.2.
/* be returned to the sender as undeliverable.
/* .IP "\fBuser\fR=\fIusername\fR (required)"
/* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR"
-/* The external command is executed with the rights of the
+/* Execute the external command with the rights of the
/* specified \fIusername\fR. The software refuses to execute
/* commands with root privileges, or with the privileges of the
/* mail system owner. If \fIgroupname\fR is specified, the
gid_t gid; /* command privileges */
int flags; /* mail_copy() flags */
char *exec_dir; /* working directory */
+ char *chroot_dir; /* chroot directory */
VSTRING *eol; /* output record delimiter */
VSTRING *null_sender; /* null sender expansion */
off_t size_limit; /* max size in bytes we will accept */
attr->command = 0;
attr->flags = 0;
attr->exec_dir = 0;
+ attr->chroot_dir = 0;
attr->eol = vstring_strcpy(vstring_alloc(1), "\n");
attr->null_sender = vstring_strcpy(vstring_alloc(1), MAIL_ADDR_MAIL_DAEMON);
attr->size_limit = 0;
attr->exec_dir = mystrdup(*argv + sizeof("directory=") - 1);
}
+ /*
+ * chroot=string
+ */
+ else if (strncasecmp("chroot=", *argv, sizeof("chroot=") - 1) == 0) {
+ attr->chroot_dir = mystrdup(*argv + sizeof("chroot=") - 1);
+ }
+
/*
* eol=string
*/
PIPE_CMD_EOL, STR(attr.eol),
PIPE_CMD_EXPORT, export_env->argv,
PIPE_CMD_CWD, attr.exec_dir,
+ PIPE_CMD_CHROOT, attr.chroot_dir,
PIPE_CMD_ORIG_RCPT, rcpt_list->info[0].orig_addr,
PIPE_CMD_DELIVERED, rcpt_list->info[0].address,
PIPE_CMD_END);
-1, LOCK_SH | LOCK_NB, LOCK_EX | LOCK_NB, -1
};
- status = flock(fd, lock_ops[operation]);
+ while ((status = flock(fd, lock_ops[operation])) < 0
+ && errno == EINTR)
+ sleep(1);
break;
}
#endif
lock.l_type = lock_ops[operation & ~MYFLOCK_OP_NOWAIT];
request = (operation & MYFLOCK_OP_NOWAIT) ? F_SETLK : F_SETLKW;
while ((status = fcntl(fd, request, &lock)) < 0
- && request == F_SETLKW
- && (errno == EINTR || errno == ENOLCK || errno == EDEADLK))
+ && errno == EINTR)
sleep(1);
break;
}