Cleanup: the code for "mailq", "sendmail -q", and for
"sendmail -qRsite" was moved from the sendmail command to
a new set-gid postqueue command. The pickup and qmgr FIFOs
- are no longer world writable.
+ are no longer world writable. Files: sendmail/sendmail.c,
+ postqueue/postqueue.c.
+
+20020101
+
+ Security: new alternate_config_directory parameter that
+ lists directories that a set-gid command will accept as
+ its configuration directory. The list must be specified in
+ the default main.cf file. File: global/mail_conf.c.
+
+ Cleanup: "sendmail -qRsite" is no longer implemented by
+ connecting to the SMTP port. It is now implemented by
+ talking to the fast flush service. File: postqueue/postqueue.c.
Open problems:
# Sample Postfix installation script. Run this from the top-level
# Postfix source directory.
-PATH=/bin:/usr/bin:/usr/sbin:/usr/etc:/sbin:/etc
+PATH=/bin:/usr/bin:/usr/sbin:/usr/etc:/sbin:/etc:/usr/contrib/bin:/usr/gnu/bin:/usr/ucb:/usr/bsd
umask 022
-test -t 0 &&
-cat <<EOF
+test -t 0 && cat <<EOF
Warning: this script replaces existing sendmail or Postfix programs.
Make backups if you want to be able to recover.
-In addition to doing a fresh install, this script can change an
-existing installation from using a world-writable maildrop to a
-group-writable one. It cannot be used to change Postfix queue
-file/directory ownership.
-
Before installing files, this script prompts you for some definitions.
Most definitions will be remembered, so you have to specify them
only once. All definitions have a reasonable default value.
+EOF
- install_root - prefix for installed file names (for package building)
+install_root_text="the prefix for installed file names. This is
+useful only if you are building ready-to-install packages for other
+machines."
- tempdir - where to write scratch files while installing Postfix
+tempdir_text="directory for scratch files while installing Postfix.
+You must must have write permission in this directory."
- config_directory - directory with Postfix configuration files.
- daemon_directory - directory with Postfix daemon programs.
- command_directory - directory with Postfix administrative commands.
- queue_directory - directory with Postfix queues.
+config_directory_text="the directory with Postfix configuration
+files. For security reasons this directory must be owned by the
+super-user."
- sendmail_path - full pathname of the Postfix sendmail command.
- newaliases_path - full pathname of the Postfix newaliases command.
- mailq_path - full pathname of the Postfix mailq command.
+daemon_directory_text="the directory with Postfix daemon programs.
+This directory should not be in the command search path of any
+users."
- mail_owner - Postfix queue account (with unique user/group id numbers).
+command_directory_text="the directory with Postfix administrative
+commands. This directory should be in the command search path of
+adminstrative users."
- setgid - group for submission (with a unique group id number).
+queue_directory_text="the directory with Postfix queues."
- manpages - "no" or path to man tree. Example: /usr/local/man.
+sendmail_path_text="the full pathname of the Postfix sendmail
+command. This is the Sendmail-compatible mail posting interface."
-EOF
+newaliases_path_text="the full pathname of the Postfix newaliases
+command. This is the Sendmail-compatible command to build alias
+databases."
+
+mailq_path_text="the full pathname of the Postfix mailq command.
+This is the Sendmail-compatible mail queue listing command."
+
+mail_owner_text="the owner of the Postfix queue. Specify a user
+account with numerical user ID and group ID values that are not
+used by any other user accounts."
+
+setgid_text="the group for mail submission and queue management
+commands. Specify a group name with a numerical group ID that is
+not shared with other accounts, not even with the Postfix account."
+
+manpages_text="where to install the Postfix on-line manual pages."
# By now, shells must have functions. Ultrix users must use sh5 or lose.
# The following shell functions replace files/symlinks while minimizing
do
while :
do
+ echo
+ eval echo Please specify \$${name}_text | fmt
eval echo \$n "$name: [\$$name]\ \$c"
read ans
case $ans in
do
while :
do
+ echo
+ eval echo Please specify \$${name}_text | fmt
eval echo \$n "$name: [\$$name]\ \$c"
read ans
case $ans in
# Sanity checks
+case $manpages in
+ no) echo Error: manpages no longer accepts "no" values. 1>&2
+ echo Error: re-run this script in interactive mode. 1>&2; exit 1;;
+esac
+
+case $setgid in
+ no) echo Error: setgid no longer accepts "no" values. 1>&2
+ echo Error: re-run this script in interactive mode. 1>&2; exit 1;;
+esac
+
for path in $daemon_directory $command_directory \
$queue_directory $sendmail_path $newaliases_path $mailq_path $manpages
do
ed $CONFIG_DIRECTORY/master.cf <<EOF
/^pickup[ ]*fifo[ ]*n[ ]*n/
s/\(n[ ]*\)n/\1-/
+p
w
q
EOF
}
-grep "^cleanup[ ]*unix[ ]*-" \
- $CONFIG_DIRECTORY/master.cf >/dev/null && {
- echo changing master.cf, making the cleanup service public
- ed $CONFIG_DIRECTORY/master.cf <<EOF
-/^cleanup[ ]*unix[ ]*-/
+for name in cleanup flush
+do
+ grep "^$name[ ]*unix[ ]*-" \
+ $CONFIG_DIRECTORY/master.cf >/dev/null && {
+ echo changing master.cf, making the $name service public
+ ed $CONFIG_DIRECTORY/master.cf <<EOF
+/^$name[ ]*unix[ ]*-/
s/-/n/
+p
w
q
EOF
-}
+ }
+done
found=`bin/postconf -c $CONFIG_DIRECTORY -h hash_queue_names`
missing=
-Incompatible changes with snapshot-20011230
+Incompatible changes with snapshot-200201XX
===========================================
-This release modifies the existing master.cf file, making the local
-pickup unprivileged, and making the cleanup service "public" (for
-future performance improvements of local mail submission).
+If you run multiple Postfix instances then you have to specify
+their configuration directories in the default main.cf file as
+"alternate_config_directory = /dir1 /dir2 ...". Otherwise, some
+Postfix sendmail commands will no longer work (namely, the ones
+that are now implemented by set-group ID client programs).
+
+This release modifies the existing master.cf file. The local pickup
+service is now unprivileged, and the cleanup and flush service are
+now "public".
Should you have to back out to a previous release, then you have
to edit the master.cf file, making the pickup service "privileged",
-and making the cleanup service "private".
+and making the cleanup and flush service "private".
-Major changes with snapshot-20011230
+Major changes with snapshot-200201XX
====================================
Simplification of the local Postfix security model.
-- The world-writable maildrop directory is gone. Postfix now uses
- the set-gid postdrop command for local mail submissions. The
- local mail pickup daemon is now an unprivileged process.
+- The world-writable maildrop directory is gone. Postfix now always
+ uses the set-gid postdrop command for local mail submissions.
+ The local mail pickup daemon is now an unprivileged process.
-- The world-writable pickup and queue manager FIFOs are gone.
- Postfix now uses the new set-gid postqueue command for all the
- queue operations that were implemented by the Postfix sendmail
+- The world-accessible pickup and queue manager server FIFOs are
+ gone. Postfix now uses a new set-gid postqueue command for all
+ the queue operations that were implemented by the Postfix sendmail
command.
Incompatible changes with snapshot-20011226
rewrite unix - - n - - trivial-rewrite
bounce unix - - n - 0 bounce
defer unix - - n - 0 bounce
-flush unix - - n 1000? 0 flush
+flush unix n - n 1000? 0 flush
smtp unix - - n - - smtp
showq unix n - n - - showq
error unix - - n - - error
# to the Postfix queue directory. This facility is used by the master
# daemon to lock out other master daemon instances.
#
+# Note: this is a read-only variable.
+#
process_id_directory = pid
# The program_directory parameter specifies the location of Postfix
postdrop - Postfix mail posting utility
<b>SYNOPSIS</b>
- <b>postdrop</b> [<i>option</i> <i>...</i>]
+ <b>postdrop</b> [<i>-rv</i>] [<b>-c</b> <i>config_dir</i>]
<b>DESCRIPTION</b>
The <b>postdrop</b> command creates a file in the <b>maildrop</b> direc-
Options:
+ <b>-c</b> The <b>main.cf</b> configuration file is in the named
+ directory instead of the default configuration
+ directory. See also the MAIL_CONFIG environment
+ setting below.
+
+ <b>-r</b> Use a Postfix-internal protocol for reading the
+ message from standard input, and for reporting sta-
+ tus information on standard output. This is cur-
+ rently the only supported method.
+
<b>-v</b> Enable verbose logging for debugging purposes. Mul-
tiple <b>-v</b> options make the software increasingly
verbose.
<b>SECURITY</b>
- The command is designed to run with set-gid privileges,
- and with group write permission to the <b>maildrop</b> queue
- directory.
+ The command is designed to run with set-group ID privi-
+ leges, so that it can write to the <b>maildrop</b> queue direc-
+ tory and so that it can connect to Postfix daemon pro-
+ cesses.
<b>DIAGNOSTICS</b>
- Fatal errors: malformed input, I/O error, out of memory.
- Problems are logged to <b>syslogd</b>(8) and to the standard
- error stream. When the input is incomplete, or when the
- process receives a HUP, INT, QUIT or TERM signal, the
+ Fatal errors: malformed input, I/O error, out of memory.
+ Problems are logged to <b>syslogd</b>(8) and to the standard
+ error stream. When the input is incomplete, or when the
+ process receives a HUP, INT, QUIT or TERM signal, the
queue file is deleted.
<b>ENVIRONMENT</b>
- The program deletes most environment information, because
- the C library can't be trusted.
+ MAIL_CONFIG
+ Directory with the <b>main.cf</b> file. In order to avoid
+ exploitation of set-group ID privileges, it is not
+ possible to specify arbitrary directory names.
+
+ A non-standard directory is allowed only if the
+ name is listed in the standard <b>main.cf</b> file, in the
+ <b>alternate</b><i>_</i><b>config</b><i>_</i><b>directory</b> configuration parameter
+ value.
+
+ Only the super-user is allowed to specify arbitrary
+ directory names.
<b>FILES</b>
/var/spool/postfix, mail queue
postqueue - Postfix queue control
<b>SYNOPSIS</b>
- <b>postqueue</b> <b>-f</b>
- <b>postqueue</b> <b>-p</b>
- <b>postqueue</b> <b>-s</b> <i>site</i>
+ <b>postqueue</b> [<b>-c</b> <i>config_dir</i>] <b>-f</b>
+ <b>postqueue</b> [<b>-c</b> <i>config_dir</i>] <b>-p</b>
+ <b>postqueue</b> [<b>-c</b> <i>config_dir</i>] <b>-s</b> <i>site</i>
<b>DESCRIPTION</b>
The <b>postqueue</b> program implements the Postfix user inter-
The following options are recognized:
- <b>-f</b> Flush the queue: attempt to deliver all queued
+ <b>-c</b> The <b>main.cf</b> configuration file is in the named
+ directory instead of the default configuration
+ directory. See also the MAIL_CONFIG environment
+ setting below.
+
+ <b>-f</b> Flush the queue: attempt to deliver all queued
mail.
- This option implements the traditional <b>sendmail</b> <b>-q</b>
- command, by contacting the Postfix <a href="qmgr.8.html"><b>qmgr</b>(8)</a> daemon.
+ This option implements the traditional <b>sendmail</b> <b>-q</b>
+ command, by contacting the Postfix <a href="qmgr.8.html"><b>qmgr</b>(8)</a> daemon.
<b>-p</b> Produce a traditional sendmail-style queue listing.
- This option implements the traditional <b>mailq</b> com-
+ This option implements the traditional <b>mailq</b> com-
mand, by contacting the Postfix <a href="showq.8.html"><b>showq</b>(8)</a> daemon.
<b>-s</b> <i>site</i>
- Schedule immediate delivery of all mail that is
- queued for the named <i>site</i>. The site must be eligi-
+ Schedule immediate delivery of all mail that is
+ queued for the named <i>site</i>. The site must be eligi-
ble for the "fast flush" service. See <a href="flushd.8.html"><b>flush</b>(8)</a> for
more information about the "fast flush" service.
- This option implements the traditional <b>sendmail</b>
- <b>-qR</b><i>site</i> command, by connecting to the SMTP server
- at <b>$myhostname</b>.
+ This option implements the traditional <b>sendmail</b>
+ <b>-qR</b><i>site</i> command, by contacting the Postfix <a href="flushd.8.html"><b>flush</b>(8)</a>
+ daemon.
<b>-v</b> Enable verbose logging for debugging purposes. Mul-
- tiple <b>-v</b> options make the software increasingly
+ tiple <b>-v</b> options make the software increasingly
verbose.
<b>SECURITY</b>
- By design, this program is set-user (or group) id, so that
- it can connect to public, but protected, Postfix daemon
- processes.
+ This program is designed to run with set-group ID privi-
+ leges, so that it can connect to Postfix daemon processes.
<b>DIAGNOSTICS</b>
- Problems are logged to <b>syslogd</b>(8) and to the standard
+ Problems are logged to <b>syslogd</b>(8) and to the standard
error stream.
<b>ENVIRONMENT</b>
- The program deletes most environment information, because
- the C library can't be trusted.
+ MAIL_CONFIG
+ Directory with the <b>main.cf</b> file.
+
+ In order to avoid exploitation of set-group ID
+ privileges, it is not possible to specify arbitrary
+ directory names.
+
+ A non-standard directory is allowed only if the
+ name is listed in the standard <b>main.cf</b> file, in the
+ <b>alternate</b><i>_</i><b>config</b><i>_</i><b>directory</b> configuration parameter
+ value.
+
+ Only the super-user is allowed to specify arbitrary
+ directory names.
<b>FILES</b>
/var/spool/postfix, mail queue
.SH SYNOPSIS
.na
.nf
-\fBpostdrop\fR [\fIoption ...\fR]
+\fBpostdrop\fR [\fI-rv\fR] [\fB-c \fIconfig_dir\fR]
.SH DESCRIPTION
.ad
.fi
directory and copies its standard input to the file.
Options:
+.IP \fB-c \fIconfig_dir\fR
+The \fBmain.cf\fR configuration file is in the named directory
+instead of the default configuration directory. See also the
+MAIL_CONFIG environment setting below.
+.IP \fB-r\fR
+Use a Postfix-internal protocol for reading the message from
+standard input, and for reporting status information on standard
+output. This is currently the only supported method.
.IP \fB-v\fR
Enable verbose logging for debugging purposes. Multiple \fB-v\fR
options make the software increasingly verbose.
.nf
.ad
.fi
-The command is designed to run with set-gid privileges, and with
-group write permission to the \fBmaildrop\fR queue directory.
+The command is designed to run with set-group ID privileges, so
+that it can write to the \fBmaildrop\fR queue directory and so that
+it can connect to Postfix daemon processes.
.SH DIAGNOSTICS
.ad
.fi
.nf
.ad
.fi
-The program deletes most environment information, because the C
-library can't be trusted.
+.IP MAIL_CONFIG
+Directory with the \fBmain.cf\fR file. In order to avoid exploitation
+of set-group ID privileges, it is not possible to specify arbitrary
+directory names.
+
+A non-standard directory is allowed only if the name is listed in the
+standard \fBmain.cf\fR file, in the \fBalternate_config_directory\fR
+configuration parameter value.
+
+Only the super-user is allowed to specify arbitrary directory names.
.SH FILES
.na
.nf
.SH SYNOPSIS
.na
.nf
-\fBpostqueue\fR \fB-f\fR
+\fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-f\fR
.br
-\fBpostqueue\fR \fB-p\fR
+\fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-p\fR
.br
-\fBpostqueue\fR \fB-s \fIsite\fR
+\fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR
.SH DESCRIPTION
.ad
.fi
traditionally available via the \fBsendmail\fR(1) command.
The following options are recognized:
+.IP \fB-c \fIconfig_dir\fR
+The \fBmain.cf\fR configuration file is in the named directory
+instead of the default configuration directory. See also the
+MAIL_CONFIG environment setting below.
.IP \fB-f\fR
Flush the queue: attempt to deliver all queued mail.
service.
This option implements the traditional \fBsendmail -qR\fIsite\fR
-command, by connecting to the SMTP server at \fB$myhostname\fR.
+command, by contacting the Postfix \fBflush\fR(8) daemon.
.IP \fB-v\fR
Enable verbose logging for debugging purposes. Multiple \fB-v\fR
options make the software increasingly verbose.
.nf
.ad
.fi
-By design, this program is set-user (or group) id, so that it
-can connect to public, but protected, Postfix daemon processes.
+This program is designed to run with set-group ID privileges, so
+that it can connect to Postfix daemon processes.
.SH DIAGNOSTICS
.ad
.fi
.nf
.ad
.fi
-The program deletes most environment information, because the C
-library can't be trusted.
+.IP MAIL_CONFIG
+Directory with the \fBmain.cf\fR file.
+
+In order to avoid exploitation of set-group ID privileges, it is not
+possible to specify arbitrary directory names.
+
+A non-standard directory is allowed only if the name is listed in the
+standard \fBmain.cf\fR file, in the \fBalternate_config_directory\fR
+configuration parameter value.
+
+Only the super-user is allowed to specify arbitrary directory names.
.SH FILES
.na
.nf
mail_conf.o: ../../include/argv.h
mail_conf.o: ../../include/safe.h
mail_conf.o: ../../include/stringops.h
+mail_conf.o: ../../include/safe_open.h
mail_conf.o: mail_params.h
mail_conf.o: mail_conf.h
mail_conf_bool.o: mail_conf_bool.c
if (*var_fflush_domains == 0)
status = FLUSH_STAT_DENY;
else
- status = mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
+ status = mail_command_client(MAIL_CLASS_PUBLIC, MAIL_SERVICE_FLUSH,
ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_PURGE,
ATTR_TYPE_END);
if (*var_fflush_domains == 0)
status = FLUSH_STAT_DENY;
else
- status = mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
+ status = mail_command_client(MAIL_CLASS_PUBLIC, MAIL_SERVICE_FLUSH,
ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_REFRESH,
ATTR_TYPE_END);
if (*var_fflush_domains == 0)
status = FLUSH_STAT_DENY;
else
- status = mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
+ status = mail_command_client(MAIL_CLASS_PUBLIC, MAIL_SERVICE_FLUSH,
ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_SEND,
ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
ATTR_TYPE_END);
if (*var_fflush_domains == 0)
status = FLUSH_STAT_DENY;
else
- status = mail_command_client(MAIL_CLASS_PRIVATE, MAIL_SERVICE_FLUSH,
+ status = mail_command_client(MAIL_CLASS_PUBLIC, MAIL_SERVICE_FLUSH,
ATTR_TYPE_STR, MAIL_ATTR_REQ, FLUSH_REQ_ADD,
ATTR_TYPE_STR, MAIL_ATTR_SITE, site,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
/* System library. */
#include <sys_defs.h>
+#include <unistd.h>
#include <stdlib.h>
/* Utility library. */
#include <dict.h>
#include <safe.h>
#include <stringops.h>
+#include <readlline.h>
/* Global library. */
#include "mail_params.h"
#include "mail_conf.h"
+/* mail_conf_checkdir - authorize non-default directory */
+
+static void mail_conf_checkdir(const char *config_dir)
+{
+ VSTRING *buf;
+ VSTREAM *fp;
+ char *path;
+ char *name;
+ char *value;
+ char *cp;
+ int found = 0;
+
+ /*
+ * If running set-[ug]id, require that a non-default configuration
+ * directory name is blessed as a bona fide configuration directory in
+ * the default main.cf file.
+ */
+ path = concatenate(DEF_CONFIG_DIR, "/", "main.cf", (char *) 0);
+ if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0)
+ msg_fatal("open file %s: %m", path);
+
+ buf = vstring_alloc(1);
+ while (found == 0 && readlline(buf, fp, (int *) 0)) {
+ if (split_nameval(vstring_str(buf), &name, &value) == 0
+ && strcmp(name, VAR_CONFIG_DIRS) == 0) {
+ while (found == 0 && (cp = mystrtok(&value, "\t\r\n")) != 0)
+ if (strcmp(cp, config_dir) == 0)
+ found = 1;
+ }
+ }
+ if (vstream_fclose(fp))
+ msg_fatal("read file %s: %m", path);
+ vstring_free(buf);
+
+ if (found == 0) {
+ msg_error("untrusted configuration directory name: %s", config_dir);
+ msg_fatal("specify \"%s = %s\" in %s",
+ VAR_CONFIG_DIRS, config_dir, path);
+ }
+ myfree(path);
+}
+
/* mail_conf_read - read global configuration file */
void mail_conf_read(void)
dict_unknown_allowed = 1;
if (var_config_dir)
myfree(var_config_dir);
- var_config_dir = mystrdup((config_dir = safe_getenv(CONF_ENV_PATH)) != 0 ?
- config_dir : DEF_CONFIG_DIR); /* XXX */
+ if ((config_dir = getenv(CONF_ENV_PATH)) == 0)
+ config_dir = DEF_CONFIG_DIR;
+ var_config_dir = mystrdup(config_dir);
set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir);
+
+ /*
+ * If the configuration directory name comes from a different trust
+ * domain, require that it is listed in the default main.cf file.
+ */
+ if (strcmp(var_config_dir, DEF_CONFIG_DIR) != 0 /* non-default */
+ && safe_getenv(CONF_ENV_PATH) == 0 /* non-default */
+ && geteuid() != 0) /* untrusted */
+ mail_conf_checkdir(var_config_dir);
path = concatenate(var_config_dir, "/", "main.cf", (char *) 0);
dict_load_file(CONFIG_DICT, path);
myfree(path);
/* char *var_verp_delims;
/* char *var_verp_filter;
/* char *var_par_dom_match;
+/* char *var_config_dirs;
/*
/* char *var_import_environ;
/* char *var_export_environ;
char *var_verp_filter;
int var_in_flow_delay;
char *var_par_dom_match;
+char *var_config_dirs;
char *var_import_environ;
char *var_export_environ;
VAR_VERP_DELIMS, DEF_VERP_DELIMS, &var_verp_delims, 2, 2,
VAR_VERP_FILTER, DEF_VERP_FILTER, &var_verp_filter, 1, 0,
VAR_PAR_DOM_MATCH, DEF_PAR_DOM_MATCH, &var_par_dom_match, 0, 0,
+ VAR_CONFIG_DIRS, DEF_CONFIG_DIRS, &var_config_dirs, 0, 0,
0,
};
static CONFIG_STR_FN_TABLE function_str_defaults_2[] = {
#endif
extern char *var_config_dir;
+#define VAR_CONFIG_DIRS "alternate_config_directory"
+#define DEF_CONFIG_DIRS ""
+extern char *var_config_dirs;
+
/*
* Preferred type of indexed files. The DEF_DB_TYPE macro value is system
* dependent. It is defined in <sys_defs.h>.
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20011230"
+#define DEF_MAIL_VERSION "Snapshot-20020101"
extern char *var_mail_version;
/* LICENSE
VSTRING *buf = vstring_alloc(100);
VSTRING *key = vstring_alloc(10);
char *cp;
- char *ep;
char *edit_key;
char *edit_val;
HTABLE *table;
HTABLE_INFO **ht_info;
HTABLE_INFO **ht;
int interesting;
-
- /*
- * Ugly macros to make complex expressions less unreadable.
- */
-#define SKIP(start, var, cond) \
- for (var = start; *var && (cond); var++);
-
-#define TRIM(s) { \
- char *p; \
- for (p = (s) + strlen(s); p > (s) && ISSPACE(p[-1]); p--); \
- *p = 0; \
- }
+ const char *err;
/*
* Store command-line parameters for quick lookup.
while ((cp = *argv++) != 0) {
if (strchr(cp, '\n') != 0)
msg_fatal("edit accepts no multi-line input");
- SKIP(cp, edit_key, ISSPACE(*edit_key)); /* find key begin */
- if (*edit_key == '#')
+ while (ISSPACE(*cp))
+ cp++;
+ if (*cp == '#')
msg_fatal("edit accepts no comment input");
- SKIP(edit_key, ep, !ISSPACE(*ep) && *ep != '='); /* key end */
- SKIP(ep, cp, ISSPACE(*cp)); /* skip blanks before '=' */
- if (*cp != '=') /* need '=' */
- msg_fatal("missing '=' after attribute name: \"%s\"", edit_key);
- *ep = 0; /* terminate key */
- cp++; /* skip over '=' */
- SKIP(cp, edit_val, ISSPACE(*edit_val)); /* skip leading blanks */
- TRIM(edit_val); /* trim trailing blanks */
+ if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0)
+ msg_fatal("%s: \"%s\"", err, cp);
cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue));
cvalue->value = edit_val;
cvalue->found = 0;
interesting = 0;
while (vstring_get(buf, src) != VSTREAM_EOF) {
- SKIP(STR(buf), cp, ISSPACE(*cp) /* including newline */ );
+ for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++)
+ /* void */ ;
/* Copy comment, all-whitespace, or empty line. */
if (*cp == '#' || *cp == 0) {
vstream_fputs(STR(buf), dst);
/* SUMMARY
/* Postfix mail posting utility
/* SYNOPSIS
-/* \fBpostdrop\fR [\fIoption ...\fR]
+/* \fBpostdrop\fR [\fI-rv\fR] [\fB-c \fIconfig_dir\fR]
/* DESCRIPTION
/* The \fBpostdrop\fR command creates a file in the \fBmaildrop\fR
/* directory and copies its standard input to the file.
/*
/* Options:
+/* .IP \fB-c \fIconfig_dir\fR
+/* The \fBmain.cf\fR configuration file is in the named directory
+/* instead of the default configuration directory. See also the
+/* MAIL_CONFIG environment setting below.
+/* .IP \fB-r\fR
+/* Use a Postfix-internal protocol for reading the message from
+/* standard input, and for reporting status information on standard
+/* output. This is currently the only supported method.
/* .IP \fB-v\fR
/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
/* options make the software increasingly verbose.
/* SECURITY
/* .ad
/* .fi
-/* The command is designed to run with set-gid privileges, and with
-/* group write permission to the \fBmaildrop\fR queue directory.
+/* The command is designed to run with set-group ID privileges, so
+/* that it can write to the \fBmaildrop\fR queue directory and so that
+/* it can connect to Postfix daemon processes.
/* DIAGNOSTICS
/* Fatal errors: malformed input, I/O error, out of memory. Problems
/* are logged to \fBsyslogd\fR(8) and to the standard error stream.
/* ENVIRONMENT
/* .ad
/* .fi
-/* The program deletes most environment information, because the C
-/* library can't be trusted.
+/* .IP MAIL_CONFIG
+/* Directory with the \fBmain.cf\fR file. In order to avoid exploitation
+/* of set-group ID privileges, it is not possible to specify arbitrary
+/* directory names.
+/*
+/* A non-standard directory is allowed only if the name is listed in the
+/* standard \fBmain.cf\fR file, in the \fBalternate_config_directory\fR
+/* configuration parameter value.
+/*
+/* Only the super-user is allowed to specify arbitrary directory names.
/* FILES
/* /var/spool/postfix, mail queue
/* /etc/postfix, configuration files
msg_syslog_init(mail_task(argv[0]), LOG_PID, LOG_FACILITY);
set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0]));
+ /*
+ * Parse JCL. This program is set-gid and must sanitize all command-line
+ * arguments. The configuration directory argument is validated by the
+ * mail configuration read routine.
+ */
+ while ((c = GETOPT(argc, argv, "c:rv")) > 0) {
+ switch (c) {
+ case 'c':
+ if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
+ msg_fatal("out of memory");
+ break;
+ case 'r': /* forward compatibility */
+ break;
+ case 'v':
+ msg_verbose++;
+ break;
+ default:
+ msg_fatal("usage: %s [-c config_dir] [-v]", argv[0]);
+ }
+ }
+
/*
* Read the global configuration file and extract configuration
* information. Some claim that the user should supply the working
signal(SIGTERM, postdrop_sig);
msg_cleanup(postdrop_cleanup);
- /*
- * Parse JCL.
- */
- while ((c = GETOPT(argc, argv, "v")) > 0) {
- switch (c) {
- case 'v':
- msg_verbose++;
- break;
- default:
- msg_fatal("usage: %s [-v]", argv[0]);
- }
- }
-
/*
* Create queue file. mail_stream_file() never fails. Send the queue ID
* to the caller. Stash away a copy of the queue file name so we can
/* SUMMARY
/* Postfix queue control
/* SYNOPSIS
-/* \fBpostqueue\fR \fB-f\fR
+/* \fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-f\fR
/* .br
-/* \fBpostqueue\fR \fB-p\fR
+/* \fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-p\fR
/* .br
-/* \fBpostqueue\fR \fB-s \fIsite\fR
+/* \fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR
/* DESCRIPTION
/* The \fBpostqueue\fR program implements the Postfix user interface
/* for queue management. It implements all the operations that are
/* traditionally available via the \fBsendmail\fR(1) command.
/*
/* The following options are recognized:
+/* .IP \fB-c \fIconfig_dir\fR
+/* The \fBmain.cf\fR configuration file is in the named directory
+/* instead of the default configuration directory. See also the
+/* MAIL_CONFIG environment setting below.
/* .IP \fB-f\fR
/* Flush the queue: attempt to deliver all queued mail.
/*
/* service.
/*
/* This option implements the traditional \fBsendmail -qR\fIsite\fR
-/* command, by connecting to the SMTP server at \fB$myhostname\fR.
+/* command, by contacting the Postfix \fBflush\fR(8) daemon.
/* .IP \fB-v\fR
/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
/* options make the software increasingly verbose.
/* SECURITY
/* .ad
/* .fi
-/* By design, this program is set-user (or group) id, so that it
-/* can connect to public, but protected, Postfix daemon processes.
+/* This program is designed to run with set-group ID privileges, so
+/* that it can connect to Postfix daemon processes.
/* DIAGNOSTICS
/* Problems are logged to \fBsyslogd\fR(8) and to the standard error
/* stream.
/* ENVIRONMENT
/* .ad
/* .fi
-/* The program deletes most environment information, because the C
-/* library can't be trusted.
+/* .IP MAIL_CONFIG
+/* Directory with the \fBmain.cf\fR file.
+/*
+/* In order to avoid exploitation of set-group ID privileges, it is not
+/* possible to specify arbitrary directory names.
+/*
+/* A non-standard directory is allowed only if the name is listed in the
+/* standard \fBmain.cf\fR file, in the \fBalternate_config_directory\fR
+/* configuration parameter value.
+/*
+/* Only the super-user is allowed to specify arbitrary directory names.
/* FILES
/* /var/spool/postfix, mail queue
/* /etc/postfix, configuration files
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
-#include <errno.h>
#include <signal.h>
#include <sysexits.h>
#include <argv.h>
#include <safe.h>
#include <connect.h>
+#include <valid_hostname.h>
/* Global library. */
#include <debug_process.h>
#include <mail_run.h>
#include <mail_flush.h>
+#include <flush_clnt.h>
#include <smtp_stream.h>
/* Application-specific. */
#define PQ_MODE_FLUSH_QUEUE 2 /* flush queue */
#define PQ_MODE_FLUSH_SITE 3 /* flush site */
- /*
- * Error handlers.
- */
-static void postqueue_cleanup(void);
-static NORETURN PRINTFLIKE(2, 3) fatal_error(int, const char *,...);
-
/*
* Silly little macros (SLMs).
*/
* showq daemon.
*/
else {
- fatal_error(EX_UNAVAILABLE,
- "Queue report unavailable - mail system is down");
+ msg_fatal_status(EX_UNAVAILABLE,
+ "Queue report unavailable - mail system is down");
}
}
* Trigger the flush queue service.
*/
if (mail_flush_deferred() < 0)
- fatal_error(EX_UNAVAILABLE,
- "Cannot flush mail queue - mail system is down");
-}
-
-/* chat - send command and examine reply */
-
-static void chat(VSTREAM * fp, VSTRING * buf, const char *fmt,...)
-{
- va_list ap;
-
- smtp_get(buf, fp, var_line_limit);
- if (STR(buf)[0] != '2')
- fatal_error(EX_UNAVAILABLE, "server rejected ETRN request: %s",
- STR(buf));
-
- if (msg_verbose)
- msg_info("<<< %s", STR(buf));
-
- if (msg_verbose) {
- va_start(ap, fmt);
- vstring_vsprintf(buf, fmt, ap);
- va_end(ap);
- msg_info(">>> %s", STR(buf));
- }
- va_start(ap, fmt);
- smtp_vprintf(fp, fmt, ap);
- va_end(ap);
+ msg_fatal_status(EX_UNAVAILABLE,
+ "Cannot flush mail queue - mail system is down");
}
/* flush_site - flush mail for site */
static void flush_site(const char *site)
{
- VSTRING *buf = vstring_alloc(10);
- VSTREAM *fp;
- int sock;
int status;
- /*
- * Make connection to the local SMTP server. Translate "connection
- * refused" into something less misleading.
- */
- vstring_sprintf(buf, "%s:smtp", var_myhostname);
- if ((sock = inet_connect(STR(buf), BLOCKING, 10)) < 0) {
- if (errno == ECONNREFUSED)
- fatal_error(EX_UNAVAILABLE, "mail service at %s is down",
- var_myhostname);
- fatal_error(EX_UNAVAILABLE, "connect to mail service at %s: %m",
- var_myhostname);
- }
- fp = vstream_fdopen(sock, O_RDWR);
-
- /*
- * Prepare for trouble.
- */
- vstream_control(fp, VSTREAM_CTL_EXCEPT, VSTREAM_CTL_END);
- status = vstream_setjmp(fp);
- if (status != 0) {
- switch (status) {
- case SMTP_ERR_EOF:
- fatal_error(EX_UNAVAILABLE, "server at %s aborted connection",
- var_myhostname);
- case SMTP_ERR_TIME:
- fatal_error(EX_IOERR,
- "timeout while talking to server at %s", var_myhostname);
- }
+ switch (status = flush_send(site)) {
+ case FLUSH_STAT_OK:
+ exit(0);
+ case FLUSH_STAT_BAD:
+ msg_fatal_status(EX_USAGE, "Invalid request: \"%s\"", site);
+ case FLUSH_STAT_FAIL:
+ msg_fatal_status(EX_UNAVAILABLE,
+ "Cannot flush mail queue - mail system is down");
+ case FLUSH_STAT_DENY:
+ msg_fatal_status(EX_UNAVAILABLE,
+ "Flush service is not configured for destination \"%s\"",
+ site);
+ default:
+ msg_fatal_status(EX_SOFTWARE,
+ "Unknown flush server reply status %d", status);
}
- smtp_timeout_setup(fp, 60);
-
- /*
- * Chat with the SMTP server.
- */
- chat(fp, buf, "helo %s", var_myhostname);
- chat(fp, buf, "etrn %s", site);
- chat(fp, buf, "quit");
-
- vstream_fclose(fp);
- vstring_free(buf);
-}
-
-static int fatal_status;
-
-/* postqueue_cleanup - callback for the runtime error handler */
-
-static NORETURN postqueue_cleanup(void)
-{
- exit(fatal_status > 0 ? fatal_status : 1);
}
-/* fatal_error - give up and notify parent */
+/* usage - scream and die */
-static void fatal_error(int status, const char *fmt,...)
+static NORETURN usage(void)
{
- VSTRING *text = vstring_alloc(10);
- va_list ap;
-
- fatal_status = status;
- va_start(ap, fmt);
- vstring_vsprintf(text, fmt, ap);
- va_end(ap);
- msg_fatal("%s", vstring_str(text));
+ msg_fatal_status(EX_USAGE, "usage: specify one of -f, -p, or -s");
}
/* main - the main program */
int c;
int fd;
int mode = PQ_MODE_DEFAULT;
- int debug_me = 0;
char *site_to_flush = 0;
ARGV *import_env;
+ char *last;
/*
* Be consistent with file permissions.
for (fd = 0; fd < 3; fd++)
if (fstat(fd, &st) == -1
&& (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
- fatal_error(EX_UNAVAILABLE, "open /dev/null: %m");
+ msg_fatal_status(EX_UNAVAILABLE, "open /dev/null: %m");
/*
* Initialize. Set up logging, read the global configuration file and
set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0]));
/*
- * Further initialization...
+ * Parse JCL. This program is set-gid and must sanitize all command-line
+ * parameters. The configuration directory argument is validated by the
+ * mail configuration read routine.
*/
- mail_conf_read();
-
- /*
- * Strip the environment so we don't have to trust the C library.
- */
- import_env = argv_split(var_import_environ, ", \t\r\n");
- clean_env(import_env->argv);
- argv_free(import_env);
-
- if (chdir(var_queue_dir))
- fatal_error(EX_UNAVAILABLE, "chdir %s: %m", var_queue_dir);
-
- signal(SIGPIPE, SIG_IGN);
- msg_cleanup(postqueue_cleanup);
-
- /*
- * Parse JCL.
- */
- while ((c = GETOPT(argc, argv, "fps:v")) > 0) {
+ while ((c = GETOPT(argc, argv, "c:fps:v")) > 0) {
switch (c) {
+ case 'c': /* non-default configuration */
+ if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
+ msg_fatal_status(EX_UNAVAILABLE, "out of memory");
+ break;
case 'f': /* flush queue */
+ if (mode != PQ_MODE_DEFAULT)
+ usage();
mode = PQ_MODE_FLUSH_QUEUE;
break;
case 'p': /* traditional mailq */
+ if (mode != PQ_MODE_DEFAULT)
+ usage();
mode = PQ_MODE_MAILQ_LIST;
break;
break;
case 's': /* flush site */
+ if (mode != PQ_MODE_DEFAULT)
+ usage();
mode = PQ_MODE_FLUSH_SITE;
- site_to_flush = optarg;
+ if (*optarg == '[' && *(last = optarg + strlen(optarg) - 1) == ']') {
+
+ *last = 0;
+ if (valid_hostaddr(optarg + 1, DONT_GRIPE))
+ site_to_flush = optarg;
+ else
+ site_to_flush = 0;
+ *last = ']';
+ } else {
+ if (valid_hostname(optarg, DONT_GRIPE)
+ || valid_hostaddr(optarg, DONT_GRIPE))
+ site_to_flush = optarg;
+ else
+ site_to_flush = 0;
+ }
+ if (site_to_flush == 0)
+ msg_fatal_status(EX_USAGE,
+ "Cannot flush mail queue - invalid destination: \"%.100s%s\"",
+ optarg, strlen(optarg) > 100 ? "..." : "");
break;
case 'v':
msg_verbose++;
break;
default:
- fatal_error(EX_USAGE, "usage: %s -[fpsv]", argv[0]);
+ usage();
}
}
+ /*
+ * Further initialization...
+ */
+ mail_conf_read();
+
+ /*
+ * Strip the environment so we don't have to trust the C library.
+ */
+ import_env = argv_split(var_import_environ, ", \t\r\n");
+ clean_env(import_env->argv);
+ argv_free(import_env);
+
+ if (chdir(var_queue_dir))
+ msg_fatal_status(EX_UNAVAILABLE, "chdir %s: %m", var_queue_dir);
+
+ signal(SIGPIPE, SIG_IGN);
+
/*
* Start processing.
*/
exit(0);
break;
case PQ_MODE_DEFAULT:
- fatal_error(EX_USAGE, "usage: %s -[fpsv]", argv[0]);
- break;
+ usage();
+ /* NOTREACHED */
}
}
#define SM_MODE_USER 5 /* user (stand-alone) mode */
#define SM_MODE_FLUSHQ 6 /* user (stand-alone) mode */
- /*
- * Queue file name. Global, so that the cleanup routine can find it when
- * called by the run-time error handler.
- */
-static void sendmail_cleanup(void);
-static NORETURN PRINTFLIKE(2, 3) fatal_error(int, const char *,...);
-
/*
* Flag parade.
*/
msg_warn("-f option specified malformed sender: %s", sender);
} else {
if ((sender = username()) == 0)
- fatal_error(EX_OSERR, "unable to find out your login name");
+ msg_fatal_status(EX_OSERR, "unable to find out your login name");
saved_sender = mystrdup(sender);
}
* the content. XXX Make postdrop a manifest constant.
*/
errno = 0;
- postdrop_command = concatenate(var_command_dir, "/postdrop",
+ postdrop_command = concatenate(var_command_dir, "/postdrop -r",
msg_verbose ? " -v" : (char *) 0, (char *) 0);
if ((handle = mail_stream_command(postdrop_command)) == 0)
- fatal_error(EX_UNAVAILABLE, "%s(%ld): unable to execute %s: %m",
- saved_sender, (long) uid, postdrop_command);
+ msg_fatal_status(EX_UNAVAILABLE, "%s(%ld): unable to execute %s: %m",
+ saved_sender, (long) uid, postdrop_command);
myfree(postdrop_command);
dst = handle->stream;
if (tp->type == TOK822_ADDR) {
tok822_internalize(buf, tp->head, TOK822_STR_DEFL);
if (REC_PUT_BUF(dst, REC_TYPE_RCPT, buf) < 0)
- fatal_error(EX_TEMPFAIL,
+ msg_fatal_status(EX_TEMPFAIL,
"%s(%ld): error writing queue file: %m",
- saved_sender, (long) uid);
+ saved_sender, (long) uid);
}
}
tok822_free_tree(tree);
if ((flags & SM_FLAG_AEOF) && VSTRING_LEN(buf) == 1 && *STR(buf) == '.')
break;
if (REC_PUT_BUF(dst, type, buf) < 0)
- fatal_error(EX_TEMPFAIL, "%s(%ld): error writing queue file: %m",
- saved_sender, (long) uid);
+ msg_fatal_status(EX_TEMPFAIL,
+ "%s(%ld): error writing queue file: %m",
+ saved_sender, (long) uid);
}
/*
* handler from removing the file.
*/
if (vstream_ferror(VSTREAM_IN))
- fatal_error(EX_DATAERR, "%s(%ld): error reading input: %m",
- saved_sender, (long) uid);
+ msg_fatal_status(EX_DATAERR, "%s(%ld): error reading input: %m",
+ saved_sender, (long) uid);
if ((status = mail_stream_finish(handle, (VSTRING *) 0)) != 0)
- fatal_error((status & CLEANUP_STAT_BAD) ? EX_SOFTWARE :
- (status & CLEANUP_STAT_WRITE) ? EX_TEMPFAIL :
- EX_UNAVAILABLE, "%s(%ld): %s", saved_sender,
- (long) uid, cleanup_strerror(status));
+ msg_fatal_status((status & CLEANUP_STAT_BAD) ? EX_SOFTWARE :
+ (status & CLEANUP_STAT_WRITE) ? EX_TEMPFAIL :
+ EX_UNAVAILABLE, "%s(%ld): %s", saved_sender,
+ (long) uid, cleanup_strerror(status));
/*
* Cleanup. Not really necessary as we're about to exit, but good for
myfree(saved_sender);
}
-static int fatal_status;
-
-/* sendmail_cleanup - callback for the runtime error handler */
-
-static NORETURN sendmail_cleanup(void)
-{
-
- /*
- * We're possibly running from a signal handler, so we should not be
- * doing complicated things such as memory of buffer management, but if
- * for some reason we can't cleanup it is even worse to just die quietly.
- */
- exit(fatal_status > 0 ? fatal_status : 1);
-}
-
-/* fatal_error - give up and notify parent */
-
-static void fatal_error(int status, const char *fmt,...)
-{
- VSTRING *text = vstring_alloc(10);
- va_list ap;
-
- fatal_status = status;
- va_start(ap, fmt);
- vstring_vsprintf(text, fmt, ap);
- va_end(ap);
- msg_fatal("%s", vstring_str(text));
-}
-
/* main - the main program */
int main(int argc, char **argv)
for (fd = 0; fd < 3; fd++)
if (fstat(fd, &st) == -1
&& (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
- fatal_error(EX_UNAVAILABLE, "open /dev/null: %m");
+ msg_fatal_status(EX_UNAVAILABLE, "open /dev/null: %m");
/*
* The CDE desktop calendar manager leaks a parent file descriptor into
*/
mail_conf_read();
if (chdir(var_queue_dir))
- fatal_error(EX_UNAVAILABLE, "chdir %s: %m", var_queue_dir);
+ msg_fatal_status(EX_UNAVAILABLE, "chdir %s: %m", var_queue_dir);
/*
* Stop run-away process accidents by limiting the queue file size. This
set_file_limit((off_t) var_message_limit);
signal(SIGPIPE, SIG_IGN);
- msg_cleanup(sendmail_cleanup);
/*
* Optionally start the debugger on ourself. This must be done after
msg_info("-%c option ignored", c);
break;
case 'n':
- fatal_error(EX_USAGE, "-%c option not supported", c);
+ msg_fatal_status(EX_USAGE, "-%c option not supported", c);
case 'F': /* full name */
full_name = optarg;
break;
break;
case 'V': /* VERP */
if (verp_delims_verify(optarg) != 0)
- fatal_error(EX_USAGE, "-V requires two characters from %s",
- var_verp_filter);
+ msg_fatal_status(EX_USAGE, "-V requires two characters from %s",
+ var_verp_filter);
verp_delims = optarg;
break;
case 'b':
switch (*optarg) {
default:
- fatal_error(EX_USAGE, "unsupported: -%c%c", c, *optarg);
+ msg_fatal_status(EX_USAGE, "unsupported: -%c%c", c, *optarg);
case 'd': /* daemon mode */
if (mode == SM_MODE_FLUSHQ)
msg_warn("ignoring -q option in daemon mode");
break;
case 'A':
if (optarg[1] == 0)
- fatal_error(EX_USAGE, "-oA requires pathname");
+ msg_fatal_status(EX_USAGE, "-oA requires pathname");
myfree(var_alias_db_map);
var_alias_db_map = mystrdup(optarg + 1);
set_mail_conf_str(VAR_ALIAS_DB_MAP, var_alias_db_map);
if (*site_to_flush == 0)
msg_fatal("specify: -qRsitename");
} else {
- fatal_error(EX_USAGE, "-q%c is not implemented", optarg[0]);
+ msg_fatal_status(EX_USAGE, "-q%c is not implemented",
+ optarg[0]);
}
break;
case 't':
msg_verbose++;
break;
case '?':
- fatal_error(EX_USAGE, "usage: %s [options]", argv[0]);
+ msg_fatal_status(EX_USAGE, "usage: %s [options]", argv[0]);
}
}
read_wait.c readable.c readlline.c ring.c safe_getenv.c \
safe_open.c sane_accept.c sane_link.c sane_rename.c \
sane_socketpair.c sane_time.c scan_dir.c set_eugid.c set_ugid.c \
- sigdelay.c skipblanks.c sock_empty_wait.c spawn_command.c \
- split_at.c stat_as.c stream_connect.c stream_listen.c \
+ sigdelay.c skipblanks.c spawn_command.c split_at.c \
+ split_nameval.c stat_as.c stream_connect.c stream_listen.c \
stream_trigger.c sys_compat.c timed_connect.c timed_read.c \
timed_wait.c timed_write.c translit.c trimblanks.c unescape.c \
unix_connect.c unix_listen.c unix_trigger.c unsafe.c username.c \
read_wait.o readable.o readlline.o ring.o safe_getenv.o \
safe_open.o sane_accept.o sane_link.o sane_rename.o \
sane_socketpair.o sane_time.o scan_dir.o set_eugid.o set_ugid.o \
- sigdelay.o skipblanks.o sock_empty_wait.o spawn_command.o \
- split_at.o stat_as.o stream_connect.o stream_listen.o \
+ sigdelay.o skipblanks.o spawn_command.o split_at.o \
+ split_nameval.o stat_as.o stream_connect.o stream_listen.o \
stream_trigger.o sys_compat.o timed_connect.o timed_read.o \
timed_wait.o timed_write.o translit.o trimblanks.o unescape.o \
unix_connect.o unix_listen.o unix_trigger.o unsafe.o username.o \
skipblanks.o: stringops.h
skipblanks.o: vstring.h
skipblanks.o: vbuf.h
-sock_empty_wait.o: sock_empty_wait.c
-sock_empty_wait.o: sys_defs.h
-sock_empty_wait.o: msg.h
-sock_empty_wait.o: iostuff.h
spawn_command.o: spawn_command.c
spawn_command.o: sys_defs.h
spawn_command.o: msg.h
split_at.o: split_at.c
split_at.o: sys_defs.h
split_at.o: split_at.h
+split_nameval.o: split_nameval.c
+split_nameval.o: sys_defs.h
+split_nameval.o: msg.h
+split_nameval.o: stringops.h
+split_nameval.o: vstring.h
+split_nameval.o: vbuf.h
stat_as.o: stat_as.c
stat_as.o: sys_defs.h
stat_as.o: msg.h
#include "vstring.h"
#include "readlline.h"
#include "mac_parse.h"
+#include "stringops.h"
#include "dict.h"
#include "dict_ht.h"
void dict_load_fp(const char *dict_name, VSTREAM *fp)
{
VSTRING *buf;
- char *start;
char *member;
char *val;
- char *cp;
- char *ep;
int lineno;
-
- /*
- * Ugly macros to make complex expressions less unreadable.
- */
-#define SKIP(start, var, cond) \
- for (var = start; *var && (cond); var++);
-
-#define TRIM(s) { \
- char *p; \
- for (p = (s) + strlen(s); p > (s) && ISSPACE(p[-1]); p--); \
- *p = 0; \
- }
+ const char *err;
buf = vstring_alloc(100);
lineno = 0;
while (readlline(buf, fp, &lineno)) {
- start = STR(buf);
- SKIP(start, member, ISSPACE(*member)); /* find member begin */
- SKIP(member, ep, !ISSPACE(*ep) && *ep != '='); /* find member end */
- SKIP(ep, cp, ISSPACE(*cp)); /* skip blanks before '=' */
- if (*cp != '=') /* need '=' */
- msg_fatal("%s, line %d: missing '=' after attribute name: \"%s\"",
- VSTREAM_PATH(fp), lineno, member);
- *ep = 0; /* terminate member name */
- cp++; /* skip over '=' */
- SKIP(cp, val, ISSPACE(*val)); /* skip leading blanks */
- TRIM(val); /* trim trailing blanks */
+ if ((err = split_nameval(STR(buf), &member, &val)) != 0)
+ msg_fatal("%s, line %d: %s: \"%s\"",
+ VSTREAM_PATH(fp), lineno, err, STR(buf));
dict_update(dict_name, member, val);
}
vstring_free(buf);
extern void doze(unsigned);
extern void rand_sleep(unsigned, unsigned);
extern int duplex_pipe(int *);
-extern int sock_empty_wait(int, int);
-extern int sock_maximize_send_lowat(int);
-extern void sock_set_send_lowat(int, int);
#define BLOCKING 0
#define NON_BLOCKING 1
/* NORETURN msg_fatal(format, ...)
/* const char *format;
/*
+/* NORETURN msg_fatal_status(status, format, ...)
+/* int status;
+/* const char *format;
+/*
/* NORETURN msg_panic(format, ...)
/* const char *format;
/*
/* msg_fatal() reports an unrecoverable error and terminates the program
/* with a non-zero exit status.
/*
+/* msg_fatal_status() reports an unrecoverable error and terminates the
+/* program with the specified exit status.
+/*
/* msg_panic() reports an internal inconsistency, terminates the
/* program immediately (i.e. without calling the optional user-specified
/* cleanup routine), and forces a core dump when possible.
/*
-/* msg_cleanup() specifies a function that msg_fatal() should
+/* msg_cleanup() specifies a function that msg_fatal[_status]() should
/* invoke before terminating the program, and returns the
/* current function pointer. Specify a null argument to disable
/* this feature.
exit(1);
}
+/* msg_fatal_status - report error and terminate gracefully */
+
+NORETURN msg_fatal_status(int status, const char *fmt,...)
+{
+ va_list ap;
+
+ if (msg_exiting++ == 0) {
+ va_start(ap, fmt);
+ msg_vprintf(MSG_FATAL, fmt, ap);
+ va_end(ap);
+ if (msg_cleanup_fn)
+ msg_cleanup_fn();
+ }
+ sleep(1);
+ exit(status);
+}
+
/* msg_panic - report error and dump core */
NORETURN msg_panic(const char *fmt,...)
extern void PRINTFLIKE(1, 2) msg_warn(const char *,...);
extern void PRINTFLIKE(1, 2) msg_error(const char *,...);
extern NORETURN PRINTFLIKE(1, 2) msg_fatal(const char *,...);
+extern NORETURN PRINTFLIKE(2, 3) msg_fatal_status(int, const char *,...);
extern NORETURN PRINTFLIKE(1, 2) msg_panic(const char *,...);
extern int msg_error_limit(int);
+++ /dev/null
-/*++
-/* NAME
-/* sock_empty_wait 3
-/* SUMMARY
-/* wait until socket send buffer is near empty
-/* SYNOPSIS
-/* #include <iostuff.h>
-/*
-/* int sock_empty_wait(fd, timeout)
-/* int fd;
-/* int timeout;
-/* AUXILIARY ROUTINES
-/* int sock_maximize_send_lowat(fd)
-/* int fd;
-/*
-/* void sock_set_send_lowat(fd, send_lowat)
-/* int fd;
-/* int send_lowat;
-/* DESCRIPTION
-/* sock_empty_wait() blocks the process until the specified socket's
-/* send buffer is near empty, in the hope that the contents of the
-/* next write() operation will not be merged with preceding data.
-/*
-/* sock_maximize_send_lowat() maximizes the socket send buffer
-/* low-water mark, which controls how much free buffer space must
-/* be available before the socket is considered writable.
-/* The result value is the old low-water mark value.
-/*
-/* sock_set_send_lowat() sets the socket send buffer low-water mark
-/* to the specified value.
-/*
-/* Arguments:
-/* .IP fd
-/* File descriptor in the range 0..FD_SETSIZE.
-/* .IP timeout
-/* If positive, deadline in seconds. A zero value effects a poll.
-/* A negative value means wait until something happens.
-/* DIAGNOSTICS
-/* sock_maximize_send_lowat() returns -1 if the kernel does not
-/* support access to or control over the send buffer low-water mark.
-/* Otherwise, all system call errors are fatal.
-/*
-/* A zero result means success. When the specified deadline is
-/* exceeded, sock_empty_wait() returns -1 and sets errno to ETIMEDOUT.
-/* BUGS
-/* Linux and Solaris do not provide the necessary support. Until
-/* they become better citizens they are punished by having to sleep
-/* for 10 seconds, and even then there is no reasonable way to find
-/* out if all the written data was acknowledged by the remote TCP.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <sys/socket.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <iostuff.h>
-
-/* sock_maximize_send_lowat - maximize the send buffer low-water mark */
-
-int sock_maximize_send_lowat(int fd)
-{
- char *myname = "sock_maximize_send_lowat";
- int send_buffer_size;
- int saved_low_water_mark;
- int want_low_water_mark;
- int got_low_water_mark;
- SOCKOPT_SIZE optlen;
-
- /*
- * Get the send buffer size. If this succeeds then we know the file
- * handle is a socket.
- */
- optlen = sizeof(send_buffer_size);
- if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF,
- (char *) &send_buffer_size, &optlen) < 0)
- msg_fatal("%s: getsockopt SO_SNDBUF: %m", myname);
-
- /*
- * Save the send buffer low-water mark. XXX Solaris 8 does not support
- * this operation.
- */
- optlen = sizeof(saved_low_water_mark);
- if (getsockopt(fd, SOL_SOCKET, SO_SNDLOWAT,
- (char *) &saved_low_water_mark, &optlen) < 0) {
- if (msg_verbose)
- msg_info("%s: getsockopt SO_SNDLOWAT: %m", myname);
- return (-1);
- }
-
- /*
- * Max out the send buffer low-water mark. XXX Linux 2.4.14 does not
- * support this operation.
- */
- want_low_water_mark = send_buffer_size;
- if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT,
- (char *) &want_low_water_mark,
- sizeof(want_low_water_mark)) < 0) {
- if (msg_verbose)
- msg_info("%s: setsockopt SO_SNDLOWAT: %m", myname);
- return (-1);
- }
-
- /*
- * Make sure the kernel did not cheat.
- */
- optlen = sizeof(got_low_water_mark);
- if (getsockopt(fd, SOL_SOCKET, SO_SNDLOWAT,
- (char *) &got_low_water_mark, &optlen) < 0)
- msg_fatal("%s: getsockopt SO_SNDLOWAT: %m", myname);
- if (got_low_water_mark == 1)
- return (-1);
-
- /*
- * Make debugging a bit easier.
- */
- if (msg_verbose) {
- msg_info("%s: send buffer %d, low-water mark was %d, wanted %d, got %d",
- myname, send_buffer_size, saved_low_water_mark,
- want_low_water_mark, got_low_water_mark);
-
- }
- return (saved_low_water_mark);
-}
-
-/* sock_set_send_lowat - restore socket send buffer low-water mark */
-
-void sock_set_send_lowat(int fd, int want_low_water_mark)
-{
- char *myname = "sock_set_send_lowat";
- int got_low_water_mark;
- SOCKOPT_SIZE optlen;
-
- /*
- * Set the send buffer low-water mark.
- */
- if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT,
- (char *) &want_low_water_mark,
- sizeof(want_low_water_mark)) < 0)
- msg_fatal("%s: setsockopt SO_SNDLOWAT: %m", myname);
-
- /*
- * Make debugging a bit easier.
- */
- if (msg_verbose) {
- optlen = sizeof(got_low_water_mark);
- if (getsockopt(fd, SOL_SOCKET, SO_SNDLOWAT,
- (char *) &got_low_water_mark, &optlen) < 0)
- msg_fatal("%s: getsockopt SO_SNDLOWAT: %m", myname);
- msg_info("%s: low-water mark wanted %d, got %d",
- myname, want_low_water_mark, got_low_water_mark);
- }
-}
-
-/* sock_empty_wait - wait until socket send buffer is near empty */
-
-int sock_empty_wait(int fd, int timeout)
-{
- int saved_errno;
- int saved_low_water_mark;
- int result;
-
- /*
- * Max out the send buffer low-water mark.
- */
- saved_low_water_mark = sock_maximize_send_lowat(fd);
-
- /*
- * Wait until the socket is considered writable.
- */
- result = write_wait(fd, timeout);
-
- /*
- * Work around systems that have no functional SO_SNDLOWAT control.
- */
- if (result == 0 && saved_low_water_mark <= 0)
- sleep(10);
-
- /*
- * Restore the send buffer low-water mark.
- */
- if (saved_low_water_mark > 0) {
- saved_errno = errno;
- sock_set_send_lowat(fd, saved_low_water_mark);
- errno = saved_errno;
- }
-
- /*
- * Done.
- */
- return (result);
-}
--- /dev/null
+/*++
+/* NAME
+/* split_nameval 3
+/* SUMMARY
+/* name-value splitter
+/* SYNOPSIS
+/* #include <split_nameval.h>
+/*
+/* const char *split_nameval(buf, name, value)
+/* char *buf;
+/* char **name;
+/* char **value;
+/* DESCRIPTION
+/* split_nameval() takes a logical line from readlline() and expects
+/* text of the form "name = value" or "name =". The buffer
+/* argument is broken up into name and value substrings.
+/*
+/* Arguments:
+/* .IP buf
+/* Result from readlline() or equivalent. The buffer is modified.
+/* .IP name
+/* Upon successful completion, this is set to the name
+/* substring.
+/* .IP value
+/* Upon successful completion, this is set to the value
+/* substring.
+/* FEATURES
+/* SEE ALSO
+/* dict(3) mid-level dictionary routines
+/* BUGS
+/* DIAGNOSTICS
+/* Fatal errors: out of memory.
+/*
+/* The result is a null pointer in case of success, a string
+/* describing the error otherwise: missing '=' after attribute
+/* name; missing attribute name.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System libraries. */
+
+#include "sys_defs.h"
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+
+/* split_nameval - split text into name and value */
+
+const char *split_nameval(char *buf, char **name, char **value)
+{
+ char *np; /* name substring */
+ char *vp; /* value substring */
+ char *cp;
+ char *ep;
+
+ /*
+ * Ugly macros to make complex expressions less unreadable.
+ */
+#define SKIP(start, var, cond) \
+ for (var = start; *var && (cond); var++);
+
+#define TRIM(s) { \
+ char *p; \
+ for (p = (s) + strlen(s); p > (s) && ISSPACE(p[-1]); p--); \
+ *p = 0; \
+ }
+
+ SKIP(buf, np, ISSPACE(*np)); /* find name begin */
+ if (*np == 0)
+ return ("missing attribute name");
+ SKIP(np, ep, !ISSPACE(*ep) && *ep != '='); /* find name end */
+ SKIP(ep, cp, ISSPACE(*cp)); /* skip blanks before '=' */
+ if (*cp != '=') /* need '=' */
+ return ("missing '=' after attribute name");
+ *ep = 0; /* terminate name */
+ cp++; /* skip over '=' */
+ SKIP(cp, vp, ISSPACE(*vp)); /* skip leading blanks */
+ TRIM(vp); /* trim trailing blanks */
+ *name = np;
+ *value = vp;
+ return (0);
+}
#endif
extern VSTRING *unescape(VSTRING *, const char *);
extern int alldig(const char *);
+extern const char *split_nameval(char *, char **, char **);
/* LICENSE
/* .ad