From: Wietse Venema Date: Tue, 1 Jan 2002 05:00:00 +0000 (-0500) Subject: snapshot-20020101 X-Git-Tag: v1.1.0~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=596f99980116620f06bbe514223e03adbc1cc8e7;p=thirdparty%2Fpostfix.git snapshot-20020101 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 9cf5f1d44..c2254003e 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -5875,7 +5875,19 @@ Apologies for any names omitted. 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: diff --git a/postfix/INSTALL.sh b/postfix/INSTALL.sh index 0470d728c..5db0ac701 100644 --- a/postfix/INSTALL.sh +++ b/postfix/INSTALL.sh @@ -3,44 +3,59 @@ # 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 <&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 @@ -372,20 +401,25 @@ grep "^pickup[ ]*fifo[ ]*n[ ]*n" \ ed $CONFIG_DIRECTORY/master.cf </dev/null && { - echo changing master.cf, making the cleanup service public - ed $CONFIG_DIRECTORY/master.cf </dev/null && { + echo changing master.cf, making the $name service public + ed $CONFIG_DIRECTORY/master.cf <SYNOPSIS - postdrop [option ...] + postdrop [-rv] [-c config_dir] DESCRIPTION The postdrop command creates a file in the maildrop direc- @@ -14,25 +14,46 @@ POSTDROP(1) POSTDROP(1) Options: + -c The main.cf configuration file is in the named + directory instead of the default configuration + directory. See also the MAIL_CONFIG environment + setting below. + + -r 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. + -v Enable verbose logging for debugging purposes. Mul- tiple -v options make the software increasingly verbose. SECURITY - The command is designed to run with set-gid privileges, - and with group write permission to the maildrop queue - directory. + The command is designed to run with set-group ID privi- + leges, so that it can write to the maildrop queue direc- + tory and so that it can connect to Postfix daemon pro- + cesses. DIAGNOSTICS - Fatal errors: malformed input, I/O error, out of memory. - Problems are logged to syslogd(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 syslogd(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. ENVIRONMENT - The program deletes most environment information, because - the C library can't be trusted. + MAIL_CONFIG + Directory with the main.cf 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 main.cf file, in the + alternate_config_directory configuration parameter + value. + + Only the super-user is allowed to specify arbitrary + directory names. FILES /var/spool/postfix, mail queue diff --git a/postfix/html/postqueue.1.html b/postfix/html/postqueue.1.html index 0387d7662..9df608b87 100644 --- a/postfix/html/postqueue.1.html +++ b/postfix/html/postqueue.1.html @@ -6,9 +6,9 @@ POSTQUEUE(1) POSTQUEUE(1) postqueue - Postfix queue control SYNOPSIS - postqueue -f - postqueue -p - postqueue -s site + postqueue [-c config_dir] -f + postqueue [-c config_dir] -p + postqueue [-c config_dir] -s site DESCRIPTION The postqueue program implements the Postfix user inter- @@ -18,43 +18,59 @@ POSTQUEUE(1) POSTQUEUE(1) The following options are recognized: - -f Flush the queue: attempt to deliver all queued + -c The main.cf configuration file is in the named + directory instead of the default configuration + directory. See also the MAIL_CONFIG environment + setting below. + + -f Flush the queue: attempt to deliver all queued mail. - This option implements the traditional sendmail -q - command, by contacting the Postfix qmgr(8) daemon. + This option implements the traditional sendmail -q + command, by contacting the Postfix qmgr(8) daemon. -p Produce a traditional sendmail-style queue listing. - This option implements the traditional mailq com- + This option implements the traditional mailq com- mand, by contacting the Postfix showq(8) daemon. -s site - Schedule immediate delivery of all mail that is - queued for the named site. The site must be eligi- + Schedule immediate delivery of all mail that is + queued for the named site. The site must be eligi- ble for the "fast flush" service. See flush(8) for more information about the "fast flush" service. - This option implements the traditional sendmail - -qRsite command, by connecting to the SMTP server - at $myhostname. + This option implements the traditional sendmail + -qRsite command, by contacting the Postfix flush(8) + daemon. -v Enable verbose logging for debugging purposes. Mul- - tiple -v options make the software increasingly + tiple -v options make the software increasingly verbose. SECURITY - 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. DIAGNOSTICS - Problems are logged to syslogd(8) and to the standard + Problems are logged to syslogd(8) and to the standard error stream. ENVIRONMENT - The program deletes most environment information, because - the C library can't be trusted. + MAIL_CONFIG + Directory with the main.cf 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 main.cf file, in the + alternate_config_directory configuration parameter + value. + + Only the super-user is allowed to specify arbitrary + directory names. FILES /var/spool/postfix, mail queue diff --git a/postfix/man/man1/postdrop.1 b/postfix/man/man1/postdrop.1 index 3b980708a..5e456e9d5 100644 --- a/postfix/man/man1/postdrop.1 +++ b/postfix/man/man1/postdrop.1 @@ -8,7 +8,7 @@ Postfix mail posting utility .SH SYNOPSIS .na .nf -\fBpostdrop\fR [\fIoption ...\fR] +\fBpostdrop\fR [\fI-rv\fR] [\fB-c \fIconfig_dir\fR] .SH DESCRIPTION .ad .fi @@ -16,6 +16,14 @@ 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. @@ -24,8 +32,9 @@ 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 @@ -38,8 +47,16 @@ INT, QUIT or TERM signal, the queue file is deleted. .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 diff --git a/postfix/man/man1/postqueue.1 b/postfix/man/man1/postqueue.1 index c7fea395e..b1fa423b6 100644 --- a/postfix/man/man1/postqueue.1 +++ b/postfix/man/man1/postqueue.1 @@ -8,11 +8,11 @@ Postfix queue control .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 @@ -21,6 +21,10 @@ 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. @@ -38,7 +42,7 @@ See \fBflush\fR(8) for more information about the "fast flush" 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. @@ -47,8 +51,8 @@ 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 @@ -59,8 +63,17 @@ stream. .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 diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 33f7f3722..73b3d1236 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -513,6 +513,7 @@ mail_conf.o: ../../include/dict.h 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 diff --git a/postfix/src/global/flush_clnt.c b/postfix/src/global/flush_clnt.c index 56852b1b0..8f35b6c5b 100644 --- a/postfix/src/global/flush_clnt.c +++ b/postfix/src/global/flush_clnt.c @@ -100,7 +100,7 @@ int flush_purge(void) 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); @@ -126,7 +126,7 @@ int flush_refresh(void) 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); @@ -152,7 +152,7 @@ int flush_send(const char *site) 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); @@ -179,7 +179,7 @@ int flush_add(const char *site, const char *queue_id) 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, diff --git a/postfix/src/global/mail_conf.c b/postfix/src/global/mail_conf.c index 2af13f29c..7e473477b 100644 --- a/postfix/src/global/mail_conf.c +++ b/postfix/src/global/mail_conf.c @@ -73,6 +73,7 @@ /* System library. */ #include +#include #include /* Utility library. */ @@ -84,12 +85,55 @@ #include #include #include +#include /* 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) @@ -114,9 +158,19 @@ void mail_conf_suck(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); diff --git a/postfix/src/global/mail_params.c b/postfix/src/global/mail_params.c index 019a0fe89..0200dca37 100644 --- a/postfix/src/global/mail_params.c +++ b/postfix/src/global/mail_params.c @@ -65,6 +65,7 @@ /* 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; @@ -190,6 +191,7 @@ char *var_verp_delims; 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; @@ -318,6 +320,7 @@ void mail_params_init() 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[] = { diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 3d9f5e89e..60b6a2d90 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -202,6 +202,10 @@ extern time_t var_starttime; #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 . diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index ced149a49..8e4410526 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -15,7 +15,7 @@ * 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 diff --git a/postfix/src/postconf/postconf.c b/postfix/src/postconf/postconf.c index c5f6a37a3..5df775206 100644 --- a/postfix/src/postconf/postconf.c +++ b/postfix/src/postconf/postconf.c @@ -280,7 +280,6 @@ static void edit_parameters(int argc, char **argv) VSTRING *buf = vstring_alloc(100); VSTRING *key = vstring_alloc(10); char *cp; - char *ep; char *edit_key; char *edit_val; HTABLE *table; @@ -292,18 +291,7 @@ static void edit_parameters(int argc, char **argv) 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. @@ -312,17 +300,12 @@ static void edit_parameters(int argc, char **argv) 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; @@ -366,7 +349,8 @@ static void edit_parameters(int argc, char **argv) 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); diff --git a/postfix/src/postdrop/postdrop.c b/postfix/src/postdrop/postdrop.c index 1dc606f40..0176bee50 100644 --- a/postfix/src/postdrop/postdrop.c +++ b/postfix/src/postdrop/postdrop.c @@ -4,20 +4,29 @@ /* 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. @@ -26,8 +35,16 @@ /* 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 @@ -186,6 +203,27 @@ int main(int argc, char **argv) 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 @@ -229,19 +267,6 @@ int main(int argc, char **argv) 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 diff --git a/postfix/src/postqueue/postqueue.c b/postfix/src/postqueue/postqueue.c index 85796b003..69cae635c 100644 --- a/postfix/src/postqueue/postqueue.c +++ b/postfix/src/postqueue/postqueue.c @@ -4,17 +4,21 @@ /* 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. /* @@ -32,23 +36,32 @@ /* 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 @@ -89,7 +102,6 @@ #include #include #include -#include #include #include @@ -104,6 +116,7 @@ #include #include #include +#include /* Global library. */ @@ -114,6 +127,7 @@ #include #include #include +#include #include /* Application-specific. */ @@ -126,12 +140,6 @@ #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). */ @@ -184,8 +192,8 @@ static void show_queue(void) * 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"); } } @@ -198,107 +206,39 @@ static void flush_queue(void) * 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 */ @@ -310,9 +250,9 @@ int main(int argc, char **argv) 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. @@ -327,7 +267,7 @@ 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"); /* * Initialize. Set up logging, read the global configuration file and @@ -341,47 +281,76 @@ int main(int argc, char **argv) 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. */ @@ -402,7 +371,7 @@ int main(int argc, char **argv) exit(0); break; case PQ_MODE_DEFAULT: - fatal_error(EX_USAGE, "usage: %s -[fpsv]", argv[0]); - break; + usage(); + /* NOTREACHED */ } } diff --git a/postfix/src/sendmail/sendmail.c b/postfix/src/sendmail/sendmail.c index ba9367d12..54cb2443f 100644 --- a/postfix/src/sendmail/sendmail.c +++ b/postfix/src/sendmail/sendmail.c @@ -324,13 +324,6 @@ #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. */ @@ -393,7 +386,7 @@ static void enqueue(const int flags, const char *sender, const char *full_name, 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); } @@ -402,11 +395,11 @@ static void enqueue(const int flags, const char *sender, const char *full_name, * 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; @@ -438,9 +431,9 @@ static void enqueue(const int flags, const char *sender, const char *full_name, 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); @@ -481,8 +474,9 @@ static void enqueue(const int flags, const char *sender, const char *full_name, 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); } /* @@ -503,13 +497,13 @@ static void enqueue(const int flags, const char *sender, const char *full_name, * 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 @@ -519,35 +513,6 @@ static void enqueue(const int flags, const char *sender, const char *full_name, 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) @@ -580,7 +545,7 @@ 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 @@ -628,7 +593,7 @@ int main(int argc, char **argv) */ 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 @@ -638,7 +603,6 @@ int main(int argc, char **argv) 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 @@ -692,7 +656,7 @@ int main(int argc, char **argv) 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; @@ -701,14 +665,14 @@ int main(int argc, char **argv) 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"); @@ -742,7 +706,7 @@ int main(int argc, char **argv) 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); @@ -772,7 +736,8 @@ int main(int argc, char **argv) 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': @@ -782,7 +747,7 @@ int main(int argc, char **argv) msg_verbose++; break; case '?': - fatal_error(EX_USAGE, "usage: %s [options]", argv[0]); + msg_fatal_status(EX_USAGE, "usage: %s [options]", argv[0]); } } diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index fccaead71..1eb00cbe3 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -19,8 +19,8 @@ SRCS = alldig.c argv.c argv_split.c attr_print0.c attr_print64.c \ 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 \ @@ -47,8 +47,8 @@ OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \ 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 \ @@ -1038,10 +1038,6 @@ skipblanks.o: sys_defs.h 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 @@ -1054,6 +1050,12 @@ spawn_command.o: clean_env.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 diff --git a/postfix/src/util/dict.c b/postfix/src/util/dict.c index 80184516d..5fd308879 100644 --- a/postfix/src/util/dict.c +++ b/postfix/src/util/dict.c @@ -182,6 +182,7 @@ #include "vstring.h" #include "readlline.h" #include "mac_parse.h" +#include "stringops.h" #include "dict.h" #include "dict_ht.h" @@ -372,40 +373,18 @@ void dict_load_file(const char *dict_name, const char *path) 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); diff --git a/postfix/src/util/iostuff.h b/postfix/src/util/iostuff.h index ac9681b1d..7da0f297d 100644 --- a/postfix/src/util/iostuff.h +++ b/postfix/src/util/iostuff.h @@ -29,9 +29,6 @@ extern int timed_write(int, void *, unsigned, int, void *); 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 diff --git a/postfix/src/util/msg.c b/postfix/src/util/msg.c index 08379a90b..683359b2b 100644 --- a/postfix/src/util/msg.c +++ b/postfix/src/util/msg.c @@ -20,6 +20,10 @@ /* 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; /* @@ -48,11 +52,14 @@ /* 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. @@ -163,6 +170,23 @@ NORETURN msg_fatal(const char *fmt,...) 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,...) diff --git a/postfix/src/util/msg.h b/postfix/src/util/msg.h index fae8a2327..42198a2a7 100644 --- a/postfix/src/util/msg.h +++ b/postfix/src/util/msg.h @@ -22,6 +22,7 @@ extern void PRINTFLIKE(1, 2) msg_info(const char *,...); 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); diff --git a/postfix/src/util/sock_empty_wait.c b/postfix/src/util/sock_empty_wait.c deleted file mode 100644 index 8cc681992..000000000 --- a/postfix/src/util/sock_empty_wait.c +++ /dev/null @@ -1,207 +0,0 @@ -/*++ -/* NAME -/* sock_empty_wait 3 -/* SUMMARY -/* wait until socket send buffer is near empty -/* SYNOPSIS -/* #include -/* -/* 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 -#include -#include -#include -#include - -/* Utility library. */ - -#include -#include - -/* 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); -} diff --git a/postfix/src/util/split_nameval.c b/postfix/src/util/split_nameval.c new file mode 100644 index 000000000..f3e519bad --- /dev/null +++ b/postfix/src/util/split_nameval.c @@ -0,0 +1,94 @@ +/*++ +/* NAME +/* split_nameval 3 +/* SUMMARY +/* name-value splitter +/* SYNOPSIS +/* #include +/* +/* 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 +#include + +/* Utility library. */ + +#include +#include + +/* 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); +} diff --git a/postfix/src/util/stringops.h b/postfix/src/util/stringops.h index ca6838058..0cd80c797 100644 --- a/postfix/src/util/stringops.h +++ b/postfix/src/util/stringops.h @@ -31,6 +31,7 @@ extern char *basename(const char *); #endif extern VSTRING *unescape(VSTRING *, const char *); extern int alldig(const char *); +extern const char *split_nameval(char *, char **, char **); /* LICENSE /* .ad