/* Arguments:
/* .IP ldapsource
/* The prefix which will be used to obtain configuration parameters
-/* for this search. If it's 'ldapone', the configuration variables below
+/* for this search. If it's 'ldapone', the configuration variables below
/* would look like 'ldapone_server_host', 'ldapone_search_base', and so
/* on in main.cf.
/* .IP dummy
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10532, USA
-/*
+/*
/* John Hensley
/* Merit Network, Inc.
/* hensley@merit.edu
* themselves, including their configuration file parameters.
*/
-/*
+/*
* structure containing all the configuration parameters for a given
* LDAP source, plus its connection handle
*/
typedef struct {
- DICT dict; /* generic member */
- char *ldapsource;
- char *server_host;
+ DICT dict; /* generic member */
+ char *ldapsource;
+ char *server_host;
int server_port;
- char *search_base;
- char *query_filter;
- char *result_attribute;
+ char *search_base;
+ char *query_filter;
+ char *result_attribute;
int bind;
- char *bind_dn;
- char *bind_pw;
+ char *bind_dn;
+ char *bind_pw;
int timeout;
- LDAP *ld;
+ LDAP *ld;
} DICT_LDAP;
/*
char *myname = "dict_ldap_lookup";
DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
static VSTRING *result;
- int LDAP_UNBIND = 0;
- LDAPMessage *res = 0;
- LDAPMessage *entry = 0;
+ LDAPMessage *res = 0;
+ LDAPMessage *entry = 0;
struct timeval tv;
VSTRING *filter_buf = 0;
char **attr_values;
- long i = 0, j = 0;
- int rc = 0;
+ long i = 0;
+ int rc = 0;
void (*saved_alarm) (int);
dict_errno = 0;
* Initialize.
*/
if (result == 0)
- result = vstring_alloc(2);
+ result = vstring_alloc(2);
- vstring_strcpy(result,"");
+ vstring_strcpy(result, "");
if (msg_verbose)
- msg_info("%s: In dict_ldap_lookup", myname);
+ msg_info("%s: In dict_ldap_lookup", myname);
if (dict_ldap->ld == 0) {
- msg_warn("%s: no existing connection for ldapsource %s, reopening",
- myname, dict_ldap->ldapsource);
- if (msg_verbose)
- msg_info("%s: connecting to server %s", myname,
- dict_ldap->server_host);
-
- if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR)
- msg_fatal("%s: signal: %m", myname);
-
- alarm(dict_ldap->timeout);
- if (setjmp(env) == 0)
- dict_ldap->ld = ldap_open(dict_ldap->server_host,
- (int) dict_ldap->server_port);
- alarm(0);
-
- if (signal(SIGALRM, saved_alarm) == SIG_ERR)
- msg_fatal("%s: signal: %m", myname);
-
- if (msg_verbose)
- msg_info("%s: after ldap_open", myname);
-
- if (dict_ldap->ld == 0) {
- msg_fatal("%s: Unable to contact LDAP server %s",
- myname, dict_ldap->server_host);
- } else {
- /*
- * If this server requires us to bind, do so.
- */
- if (dict_ldap->bind) {
- if (msg_verbose)
- msg_info("%s: about to bind: server %s, base %s", myname,
- dict_ldap->server_host, dict_ldap->search_base);
-
- rc = ldap_bind_s(dict_ldap->ld, dict_ldap->search_base, NULL,
- LDAP_AUTH_SIMPLE);
- if (rc != LDAP_SUCCESS) {
- msg_fatal("%s: Unable to bind with search base %s at server %s (%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc));
- } else {
- if (msg_verbose)
- msg_info("%s: Successful bind to server %s with search base %s(%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc));
- }
- }
- if (msg_verbose)
- msg_info("%s: cached connection handle for LDAP source %s",
- myname, dict_ldap->ldapsource);
- }
+ msg_warn("%s: no existing connection for ldapsource %s, reopening",
+ myname, dict_ldap->ldapsource);
+ if (msg_verbose)
+ msg_info("%s: connecting to server %s", myname,
+ dict_ldap->server_host);
+
+ if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR)
+ msg_fatal("%s: signal: %m", myname);
+
+ alarm(dict_ldap->timeout);
+ if (setjmp(env) == 0)
+ dict_ldap->ld = ldap_open(dict_ldap->server_host,
+ (int) dict_ldap->server_port);
+ alarm(0);
+
+ if (signal(SIGALRM, saved_alarm) == SIG_ERR)
+ msg_fatal("%s: signal: %m", myname);
+
+ if (msg_verbose)
+ msg_info("%s: after ldap_open", myname);
+
+ if (dict_ldap->ld == 0) {
+ msg_fatal("%s: Unable to contact LDAP server %s",
+ myname, dict_ldap->server_host);
+ } else {
+
+ /*
+ * If this server requires us to bind, do so.
+ */
+ if (dict_ldap->bind) {
+ if (msg_verbose)
+ msg_info("%s: about to bind: server %s, base %s", myname,
+ dict_ldap->server_host, dict_ldap->search_base);
+
+ rc = ldap_bind_s(dict_ldap->ld, dict_ldap->search_base, NULL,
+ LDAP_AUTH_SIMPLE);
+ if (rc != LDAP_SUCCESS) {
+ msg_fatal("%s: Unable to bind with search base %s at server %s (%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc));
+ } else {
+ if (msg_verbose)
+ msg_info("%s: Successful bind to server %s with search base %s(%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc));
+ }
+ }
+ if (msg_verbose)
+ msg_info("%s: cached connection handle for LDAP source %s",
+ myname, dict_ldap->ldapsource);
+ }
}
/*
vstring_sprintf(filter_buf, dict_ldap->query_filter, name);
if (msg_verbose)
- msg_info("%s: searching with filter %s", myname,
- vstring_str(filter_buf));
-
- if ((rc=ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
- LDAP_SCOPE_SUBTREE,
- vstring_str(filter_buf),
- 0, 0, &tv, &res)) != LDAP_SUCCESS) {
-
- msg_info("%s: right after search", myname);
-
- msg_warn("%s: Unable to search base %s at server %s (%d -- %s): ",
- myname, dict_ldap->search_base, dict_ldap->server_host, rc,
- ldap_err2string(rc));
- LDAP_UNBIND = 1;
+ msg_info("%s: searching with filter %s", myname,
+ vstring_str(filter_buf));
+
+ if ((rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
+ LDAP_SCOPE_SUBTREE,
+ vstring_str(filter_buf),
+ 0, 0, &tv, &res)) != LDAP_SUCCESS) {
+
+ ldap_unbind(dict_ldap->ld);
+ dict_ldap->ld = 0;
+ if (msg_verbose)
+ msg_info("%s: freed connection handle for LDAP source %s", myname, dict_ldap->ldapsource);
+ msg_fatal("%s: Unable to search base %s at server %s (%d -- %s): ",
+ myname, dict_ldap->search_base, dict_ldap->server_host, rc,
+ ldap_err2string(rc));
} else {
- /*
- * Extract the requested result_attribute.
- */
- if (msg_verbose)
- msg_info("%s: search completed", myname);
-
- if ((entry = ldap_first_entry(dict_ldap->ld, res)) != 0) {
- attr_values = ldap_get_values(dict_ldap->ld, entry,
- dict_ldap->result_attribute);
- /*
- * Append each returned address to the result list.
- */
- while (attr_values[i]) {
- if (VSTRING_LEN(result) > 0)
- vstring_strcat(result, ",");
- vstring_strcat(result, attr_values[i]);
- i++;
- }
- ldap_value_free(attr_values);
- if (msg_verbose)
- msg_info("%s: search returned: %s", myname, vstring_str(result));
- } else {
- if (msg_verbose)
- msg_info("%s: search returned nothing", myname);
- }
+
+ /*
+ * Extract the requested result_attribute.
+ */
+ if (msg_verbose)
+ msg_info("%s: search found %d", myname,
+ ldap_count_entries(dict_ldap->ld, res));
+
+ for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL; entry = ldap_next_entry(dict_ldap->ld, entry)) {
+ attr_values = ldap_get_values(dict_ldap->ld, entry,
+ dict_ldap->result_attribute);
+ if (attr_values == NULL) {
+ msg_warn("%s: entry doesn't have any values for %s", myname, dict_ldap->result_attribute);
+ continue;
+ }
+
+ /*
+ * Append each returned address to the result list.
+ */
+ for (i = 0; attr_values[i] != NULL; i++) {
+ if (VSTRING_LEN(result) > 0)
+ vstring_strcat(result, ",");
+ vstring_strcat(result, attr_values[i]);
+ }
+ ldap_value_free(attr_values);
+ }
+ if (msg_verbose)
+ msg_info("%s: search returned: %s", myname, vstring_str(result));
}
/*
* perform the query.
*/
if (res != 0)
- ldap_msgfree(res);
+ ldap_msgfree(res);
else
- dict_errno = 1;
- if (LDAP_UNBIND == 1) {
- /*
- * there was an LDAP problem; free the handle
- */
- ldap_unbind(dict_ldap->ld);
- dict_ldap->ld = 0;
- if (msg_verbose)
- msg_info("%s: freed connection handle for LDAP source %s",
- myname, dict_ldap->ldapsource);
- }
+ dict_errno = 1;
if (filter_buf != 0)
- vstring_free(filter_buf);
- return (entry != 0 ? vstring_str(result) : 0);
+ vstring_free(filter_buf);
+ return (VSTRING_LEN(result) > 0 ? vstring_str(result) : 0);
}
/* dict_ldap_update - add or update database entry */
static void dict_ldap_update(DICT *dict, const char *unused_name,
- const char *unused_value)
+ const char *unused_value)
{
msg_fatal("dict_ldap_update: operation not implemented");
}
DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
if (dict_ldap->ld)
- ldap_unbind(dict_ldap->ld);
+ ldap_unbind(dict_ldap->ld);
myfree(dict_ldap->ldapsource);
myfree(dict_ldap->server_host);
myfree(dict_ldap->result_attribute);
myfree(dict_ldap->bind_dn);
myfree(dict_ldap->bind_pw);
- myfree((char *)dict_ldap);
+ myfree((char *) dict_ldap);
}
/* dict_ldap_open - create association with data base */
char *myname = "dict_ldap_open";
DICT_LDAP *dict_ldap;
VSTRING *config_param;
- int rc = 0;
+ int rc = 0;
void (*saved_alarm) (int);
dict_ldap = (DICT_LDAP *) mymalloc(sizeof(*dict_ldap));
dict_ldap->dict.flags = dict_flags | DICT_FLAG_FIXED;
if (msg_verbose)
- msg_info("%s: using LDAP source %s", myname, ldapsource);
+ msg_info("%s: using LDAP source %s", myname, ldapsource);
dict_ldap->ldapsource = mystrdup(ldapsource);
config_param = vstring_alloc(15);
vstring_sprintf(config_param, "%s_server_host", ldapsource);
- dict_ldap->server_host =
- mystrdup((char *)get_config_str(vstring_str(config_param),
- "localhost",0,0));
+ dict_ldap->server_host =
+ mystrdup((char *) get_config_str(vstring_str(config_param),
+ "localhost", 0, 0));
if (msg_verbose)
- msg_info("%s: %s is %s", myname, vstring_str(config_param),
- dict_ldap->server_host);
+ msg_info("%s: %s is %s", myname, vstring_str(config_param),
+ dict_ldap->server_host);
- /* get configured value of "ldapsource_server_port"; default to
- /* LDAP_PORT (389) */
+ /*
+ * get configured value of "ldapsource_server_port"; default to LDAP_PORT
+ * (389)
+ */
vstring_sprintf(config_param, "%s_server_port", ldapsource);
- dict_ldap->server_port =
- get_config_int(vstring_str(config_param),LDAP_PORT,0,0);
+ dict_ldap->server_port =
+ get_config_int(vstring_str(config_param), LDAP_PORT, 0, 0);
if (msg_verbose)
- msg_info("%s: %s is %d", myname, vstring_str(config_param),
- dict_ldap->server_port);
+ msg_info("%s: %s is %d", myname, vstring_str(config_param),
+ dict_ldap->server_port);
vstring_sprintf(config_param, "%s_search_base", ldapsource);
- dict_ldap->search_base =
- mystrdup((char *)get_config_str(vstring_str(config_param),"",0,0));
+ dict_ldap->search_base =
+ mystrdup((char *) get_config_str(vstring_str(config_param), "", 0, 0));
if (msg_verbose)
- msg_info("%s: %s is %s", myname, vstring_str(config_param),
- dict_ldap->search_base);
+ msg_info("%s: %s is %s", myname, vstring_str(config_param),
+ dict_ldap->search_base);
/* get configured value of "ldapsource_timeout"; default to 10 */
vstring_sprintf(config_param, "%s_timeout", ldapsource);
- dict_ldap->timeout = get_config_int(config_param,10,0,0);
+ dict_ldap->timeout = get_config_int(config_param, 10, 0, 0);
if (msg_verbose)
- msg_info("%s: %s is %d", myname, vstring_str(config_param),
- dict_ldap->timeout);
+ msg_info("%s: %s is %d", myname, vstring_str(config_param),
+ dict_ldap->timeout);
vstring_sprintf(config_param, "%s_query_filter", ldapsource);
- dict_ldap->query_filter =
- mystrdup((char *)get_config_str(vstring_str(config_param),
- "(mailacceptinggeneralid=%s)",0,0));
+ dict_ldap->query_filter =
+ mystrdup((char *) get_config_str(vstring_str(config_param),
+ "(mailacceptinggeneralid=%s)", 0, 0));
if (msg_verbose)
- msg_info("%s: %s is %s", myname, vstring_str(config_param),
- dict_ldap->query_filter);
+ msg_info("%s: %s is %s", myname, vstring_str(config_param),
+ dict_ldap->query_filter);
vstring_sprintf(config_param, "%s_result_attribute", ldapsource);
- dict_ldap->result_attribute =
- mystrdup((char *)get_config_str(vstring_str(config_param),
- "maildrop",0,0));
+ dict_ldap->result_attribute =
+ mystrdup((char *) get_config_str(vstring_str(config_param),
+ "maildrop", 0, 0));
if (msg_verbose)
- msg_info("%s: %s is %s", myname, vstring_str(config_param),
- dict_ldap->result_attribute);
+ msg_info("%s: %s is %s", myname, vstring_str(config_param),
+ dict_ldap->result_attribute);
/* get configured value of "ldapsource_bind"; default to true */
vstring_sprintf(config_param, "%s_bind", ldapsource);
dict_ldap->bind = get_config_bool(vstring_str(config_param), 1);
if (msg_verbose)
- msg_info("%s: %s is %d", myname, vstring_str(config_param),
- dict_ldap->bind);
+ msg_info("%s: %s is %d", myname, vstring_str(config_param),
+ dict_ldap->bind);
/* get configured value of "ldapsource_bind_dn"; default to "" */
vstring_sprintf(config_param, "%s_bind_dn", ldapsource);
- dict_ldap->bind_dn =
- mystrdup((char *)get_config_str(vstring_str(config_param),"",0,0));
+ dict_ldap->bind_dn =
+ mystrdup((char *) get_config_str(vstring_str(config_param), "", 0, 0));
if (msg_verbose)
- msg_info("%s: %s is %s", myname, vstring_str(config_param),
- dict_ldap->bind_dn);
+ msg_info("%s: %s is %s", myname, vstring_str(config_param),
+ dict_ldap->bind_dn);
/* get configured value of "ldapsource_bind_pw"; default to "" */
vstring_sprintf(config_param, "%s_bind_pw", ldapsource);
- dict_ldap->bind_pw =
- mystrdup((char *)get_config_str(vstring_str(config_param),"",0,0));
+ dict_ldap->bind_pw =
+ mystrdup((char *) get_config_str(vstring_str(config_param), "", 0, 0));
if (msg_verbose)
- msg_info("%s: %s is %s", myname, vstring_str(config_param),
- dict_ldap->bind_pw);
+ msg_info("%s: %s is %s", myname, vstring_str(config_param),
+ dict_ldap->bind_pw);
- /*
+ /*
* establish the connection to the LDAP server
*/
if (msg_verbose)
- msg_info("%s: connecting to server %s", myname,
- dict_ldap->server_host);
+ msg_info("%s: connecting to server %s", myname,
+ dict_ldap->server_host);
if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR)
- msg_fatal("%s: signal: %m", myname);
+ msg_fatal("%s: signal: %m", myname);
alarm(dict_ldap->timeout);
if (setjmp(env) == 0)
- dict_ldap->ld = ldap_open(dict_ldap->server_host,
- (int) dict_ldap->server_port);
+ dict_ldap->ld = ldap_open(dict_ldap->server_host,
+ (int) dict_ldap->server_port);
alarm(0);
if (signal(SIGALRM, saved_alarm) == SIG_ERR)
- msg_fatal("%s: signal: %m", myname);
+ msg_fatal("%s: signal: %m", myname);
if (msg_verbose)
- msg_info("%s: after ldap_open", myname);
+ msg_info("%s: after ldap_open", myname);
if (dict_ldap->ld == 0) {
- msg_fatal("%s: Unable to contact LDAP server %s",
- myname, dict_ldap->server_host);
+ msg_fatal("%s: Unable to contact LDAP server %s",
+ myname, dict_ldap->server_host);
} else {
- /*
- * If this server requires us to bind, do so.
- */
- if (dict_ldap->bind) {
- if (msg_verbose)
- msg_info("%s: about to bind: server %s, base %s", myname,
- dict_ldap->server_host, dict_ldap->search_base);
-
- rc = ldap_bind_s(dict_ldap->ld, dict_ldap->search_base, NULL,
- LDAP_AUTH_SIMPLE);
- if (rc != LDAP_SUCCESS) {
- msg_fatal("%s: Unable to bind with search base %s at server %s (%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc));
- } else {
- if (msg_verbose)
- msg_info("%s: Successful bind to server %s with search base %s(%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc));
- }
- }
- if (msg_verbose)
- msg_info("%s: cached connection handle for LDAP source %s",
- myname, dict_ldap->ldapsource);
+
+ /*
+ * If this server requires us to bind, do so.
+ */
+ if (dict_ldap->bind) {
+ if (msg_verbose)
+ msg_info("%s: about to bind: server %s, base %s", myname,
+ dict_ldap->server_host, dict_ldap->search_base);
+
+ rc = ldap_bind_s(dict_ldap->ld, dict_ldap->search_base, NULL,
+ LDAP_AUTH_SIMPLE);
+ if (rc != LDAP_SUCCESS) {
+ msg_fatal("%s: Unable to bind with search base %s at server %s (%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc));
+ } else {
+ if (msg_verbose)
+ msg_info("%s: Successful bind to server %s with search base %s(%d -- %s): ", myname, dict_ldap->search_base, dict_ldap->server_host, rc, ldap_err2string(rc));
+ }
+ }
+ if (msg_verbose)
+ msg_info("%s: cached connection handle for LDAP source %s",
+ myname, dict_ldap->ldapsource);
}
return (&dict_ldap->dict);
/*
/* int vstream_pclose(stream)
/* VSTREAM *stream;
+/*
+/* VSTREAM *vstream_popen_vargs(key, value, ...)
+/* int key;
/* DESCRIPTION
/* vstream_popen() opens a one-way or two-way stream to the specified
/* \fIcommand\fR, which is executed by a child process. The \fIflags\fR
/* standard output are redirected to the stream, which is based on a
/* socketpair.
/*
+/* vstream_popen_vargs() offers the user more control over the
+/* child process and over how it is managed. The key argument
+/* specifies what value will follow. pipe_command() takes a list
+/* of (key, value) arguments, terminated by VSTREAM_POPEN_END. The
+/* following is a listing of key codes together with the expected
+/* value type.
+/* .RS
+/* .IP "VSTREAM_POPEN_COMMAND (char *)"
+/* Specifies the command to execute as a string. The string is
+/* passed to the shell when it contains shell meta characters
+/* or when it appears to be a shell built-in command, otherwise
+/* the command is executed without invoking a shell.
+/* One of VSTREAM_POPEN_COMMAND or VSTREAM_POPEN_ARGV must be specified.
+/* .IP "VSTREAM_POPEN_ARGV (char **)"
+/* The command is specified as an argument vector. This vector is
+/* passed without further inspection to the \fIexecvp\fR() routine.
+/* One of VSTREAM_POPEN_COMMAND or VSTREAM_POPEN_ARGV must be specified.
+/* See also the VSTREAM_POPEN_SHELL attribute below.
+/* .IP "VSTREAM_POPEN_ENV (char **)"
+/* Additional environment information, in the form of a null-terminated
+/* list of name, value, name, value, ... elements. By default only the
+/* command search path is initialized to _PATH_DEFPATH.
+/* .IP "VSTREAM_POPEN_UID (int)"
+/* The user ID to execute the command as. The user ID must be non-zero.
+/* .IP "VSTREAM_POPEN_GID (int)"
+/* The group ID to execute the command as. The group ID must be non-zero.
+/* .IP "VSTREAM_POPEN_SHELL (char *)"
+/* The shell to use when executing the command specified with
+/* VSTREAM_POPEN_COMMAND. This shell is invoked regardless of the
+/* command content.
+/* .IP "VSTREAM_POPEN_WAITPID_FN ((*)(pid_t, WAIT_STATUS_T *, int))"
+/* waitpid()-like function to reap the child exit status when
+/* vstream_pclose() is called.
+/* .RE
+/* .PP
/* vstream_pclose() closes the named stream and returns the child
/* exit status. It is an error to specify a stream that was not
/* returned by vstream_popen() or that is no longer open.
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <stdlib.h>
#include <errno.h>
+#ifdef USE_PATHS_H
+#include <paths.h>
+#endif
+#include <syslog.h>
/* Utility library. */
#include <msg.h>
-#include <binhash.h>
#include <exec_command.h>
#include <vstream.h>
+#include <argv.h>
+#include <set_ugid.h>
+#include <clean_env.h>
/* Application-specific. */
-static BINHASH *vstream_popen_table = 0;
+typedef struct VSTREAM_POPEN_ARGS {
+ char **argv;
+ char *command;
+ uid_t uid;
+ gid_t gid;
+ int privileged;
+ char **env;
+ char *shell;
+ VSTREAM_WAITPID_FN waitpid_fn;
+} VSTREAM_POPEN_ARGS;
-/* vstream_popen - open stream to child process */
+/* vstream_parse_args - get arguments from variadic list */
-VSTREAM *vstream_popen(const char *command, int flags)
+static VSTREAM *vstream_parse_args(VSTREAM_POPEN_ARGS *args, va_list ap)
+{
+ char *myname = "vstream_parse_args";
+ int key;
+
+ /*
+ * First, set the default values (on all non-zero entries)
+ */
+ args->argv = 0;
+ args->command = 0;
+ args->uid = 0;
+ args->gid = 0;
+ args->privileged = 0;
+ args->env = 0;
+ args->shell = 0;
+ args->waitpid_fn = 0;
+
+ /*
+ * Then, override the defaults with user-supplied inputs.
+ */
+ while ((key = va_arg(ap, int)) != VSTREAM_POPEN_END) {
+ switch (key) {
+ case VSTREAM_POPEN_ARGV:
+ if (args->command != 0)
+ msg_panic("%s: got VSTREAM_POPEN_ARGV and VSTREAM_POPEN_COMMAND", myname);
+ args->argv = va_arg(ap, char **);
+ break;
+ case VSTREAM_POPEN_COMMAND:
+ if (args->argv != 0)
+ msg_panic("%s: got VSTREAM_POPEN_ARGV and VSTREAM_POPEN_COMMAND", myname);
+ args->command = va_arg(ap, char *);
+ break;
+ case VSTREAM_POPEN_UID:
+ args->privileged = 1;
+ args->uid = va_arg(ap, int);
+ break;
+ case VSTREAM_POPEN_GID:
+ args->privileged = 1;
+ args->gid = va_arg(ap, int);
+ break;
+ case VSTREAM_POPEN_ENV:
+ args->env = va_arg(ap, char **);
+ break;
+ case VSTREAM_POPEN_SHELL:
+ args->shell = va_arg(ap, char *);
+ break;
+ case VSTREAM_POPEN_WAITPID_FN:
+ args->waitpid_fn = va_arg(ap, VSTREAM_WAITPID_FN);
+ break;
+ default:
+ msg_panic("%s: unknown key: %d", myname, key);
+ }
+ }
+
+ if (args->command == 0 && args->argv == 0)
+ msg_panic("%s: missing VSTREAM_POPEN_ARGV or VSTREAM_POPEN_COMMAND", myname);
+ if (args->privileged != 0 && args->uid == 0)
+ msg_panic("%s: privileged uid", myname);
+ if (args->privileged != 0 && args->gid == 0)
+ msg_panic("%s: privileged gid", myname);
+}
+
+/* vstream_popen_vargs - open stream to child process */
+
+VSTREAM *vstream_popen_vargs(int flags,...)
{
+ char *myname = "vstream_popen_vargs";
+ VSTREAM_POPEN_ARGS args;
+ va_list ap;
VSTREAM *stream;
int sockfd[2];
- pid_t pid;
+ int pid;
int fd;
+ ARGV *argv;
+ char **cpp;
+
+ va_start(ap, flags);
+ vstream_parse_args(&args, ap);
+ va_end(ap);
+
+ if (args.command == 0)
+ args.command = args.argv[0];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0)
return (0);
msg_fatal("dup2: %m");
if (sockfd[0] >= 2 && close(sockfd[0]))
msg_warn("close: %m");
- exec_command(command);
+
+ /*
+ * Don't try to become someone else unless the user specified it.
+ */
+ if (args.privileged)
+ set_ugid(args.uid, args.gid);
+
+ /*
+ * Environment plumbing. Always reset the command search path. XXX
+ * That should probably be done by clean_env().
+ */
+ clean_env();
+ if (setenv("PATH", _PATH_DEFPATH, 1))
+ msg_fatal("%s: setenv: %m", myname);
+ if (args.env)
+ for (cpp = args.env; *cpp; cpp += 2)
+ if (setenv(cpp[0], cpp[1], 1))
+ msg_fatal("setenv: %m");
+
+ /*
+ * Process plumbing. If possible, avoid running a shell.
+ */
+ closelog();
+ if (args.argv) {
+ execvp(args.argv[0], args.argv);
+ msg_fatal("%s: execvp %s: %m", myname, args.argv[0]);
+ } else if (args.shell && *args.shell) {
+ argv = argv_split(args.shell, " \t\r\n");
+ argv_add(argv, args.command, (char *) 0);
+ argv_terminate(argv);
+ execvp(argv->argv[0], argv->argv);
+ msg_fatal("%s: execvp %s: %m", myname, argv->argv[0]);
+ } else {
+ exec_command(args.command);
+ }
/* NOTREACHED */
default: /* parent */
if (close(sockfd[0]))
msg_warn("close: %m");
stream = vstream_fdopen(sockfd[1], flags);
- if (vstream_popen_table == 0)
- vstream_popen_table = binhash_create(10);
- binhash_enter(vstream_popen_table, (char *) &stream,
- sizeof(stream), (char *) pid);
+ stream->waitpid_fn = args.waitpid_fn;
+ stream->pid = pid;
return (stream);
}
}
+/* vstream_popen - retro-compatible wrapper for new interface */
+
+VSTREAM *vstream_popen(const char *command, int flags)
+{
+ return (vstream_popen_vargs(flags,
+ VSTREAM_POPEN_COMMAND, command,
+ VSTREAM_POPEN_END));
+}
+
/* vstream_pclose - close stream to child process */
int vstream_pclose(VSTREAM *stream)
{
- char *myname = "vstream_pclose";
- BINHASH_INFO *info;
- int pid;
+ pid_t saved_pid = stream->pid;
+ VSTREAM_WAITPID_FN saved_waitpid_fn = stream->waitpid_fn;
+ pid_t pid;
WAIT_STATUS_T wait_status;
/*
- * Sanity check.
+ * Close the pipe. Don't trigger an alarm in vstream_fclose().
*/
- if (vstream_popen_table == 0
- || (info = binhash_locate(vstream_popen_table, (char *) &stream,
- sizeof(stream))) == 0)
- msg_panic("%s: spurious stream %p", myname, (char *) stream);
+ if (saved_pid == 0)
+ msg_panic("vstream_pclose: stream has no process");
+ stream->pid = 0;
+ vstream_fclose(stream);
/*
- * Close the stream and reap the child exit status. Ignore errors while
- * flushing the stream. The child might already have terminated.
+ * Reap the child exit status.
*/
- (void) vstream_fclose(stream);
do {
- pid = waitpid((pid_t) info->value, &wait_status, 0);
+ if (saved_waitpid_fn != 0)
+ pid = saved_waitpid_fn(saved_pid, &wait_status, 0);
+ else
+ pid = waitpid(saved_pid, &wait_status, 0);
} while (pid == -1 && errno == EINTR);
- binhash_delete(vstream_popen_table, (char *) &stream, sizeof(stream),
- (void (*) (char *)) 0);
-
return (pid == -1 ? -1 :
WIFSIGNALED(wait_status) ? WTERMSIG(wait_status) :
WEXITSTATUS(wait_status));