Commit 04c2931b 04c2931bb963fefd4654ef2de2cf0667cf8a333d by Sergey Poznyakoff

Rewrite prog stream support.

Two interfaces are provided: mu_prog_stream_create offers full control
over the program execution environment (i.e. running privileges, cwd,
resource limits) via a set of hints.
A simpler interface, mu_command_stream_create, runs the command in the
current environment.

mu_filter_prog_stream_create is removed, because its functionality can
be achieved by a correspondingly crafted set of hints to
mu_prog_stream_create.

* include/mailutils/prog.h: New file.
* include/mailutils/mailutils.h: Include mailutils/prog.h
* include/mailutils/Makefile.am (pkginclude_HEADERS): Add prog.h
* include/mailutils/stream.h (mu_prog_stream_create)
(mu_filter_prog_stream_create): Remove prototypes.
* include/mailutils/sys/prog_stream.h (_mu_prog_stream): Change structure.
(_mu_prog_limit_codes, _mu_prog_limit_flags): New externs.
* include/mailutils/util.h (mu_set_user_privileges)
(mu_switch_to_privs): New prototypes.

* lib/userprivs.c: Move to libmailutils/base.
* lib/Makefile.am (libmuaux_a_SOURCES): Remove userprivs.c
* libmailutils/base/Makefile.am (libbase_la_SOURCES): Add userprivs.c
* libmailutils/base/userprivs.c (mu_set_user_privileges): New function.
(mu_switch_to_privs): Rewrite as another entry point to mu_set_user_privileges.

* libmailutils/stream/prog_stream.c (_mu_prog_limit_flags)
(_mu_prog_limit_codes): New global variables.
(start_program_filter): Use hints to control execution environment.
(_prog_stream_create): Save hints.
(mu_prog_stream_create): Change signature.
(mu_command_stream_create): New function (corresponds to the prior
mu_prog_stream_create).
(mu_filter_prog_stream_create): Remove function.
* comsat/action.c (action_exec): Use new mu_prog_stream_create calling
convention.
* examples/murun.c: Rewrite.

* mh/mhn.c (show_internal): Use new mu_prog_stream_create calling
convention.
* mh/tests/mhn.at: Reflect changes to mhn.

* imap4d/preauth.c: Use mu_command_stream_create.
* libmu_sieve/extensions/pipe.c
* mail/decode.c
* mail/pipe.c
* mail/send.c
* mh/mhl.c
* mu/shell.c

* mail/mail.h: Include mailutils/prog.h
* mh/mh.h

* po/POTFILES.in: Add libmailutils/base/userprivs.c.
1 parent ac08a857
......@@ -18,6 +18,7 @@
#include "comsat.h"
#include <mailutils/io.h>
#include <mailutils/argcv.h>
#include <mailutils/prog.h>
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
#include <obstack.h>
......@@ -225,7 +226,6 @@ action_exec (mu_stream_t tty, int argc, char **argv)
{
mu_stream_t pstream;
struct stat stb;
char *command;
int status;
if (argc == 0)
......@@ -249,25 +249,19 @@ action_exec (mu_stream_t tty, int argc, char **argv)
if (stb.st_mode & (S_ISUID|S_ISGID))
{
mu_diag_output (MU_DIAG_ERROR, _("will not execute set[ug]id programs"));
mu_diag_output (MU_DIAG_ERROR,
_("will not execute set[ug]id programs"));
return;
}
/* FIXME: Redirect stderr to out */
/* FIXME: need this:
status = mu_prog_stream_create_argv (&pstream, argv[0], argv,
status = mu_prog_stream_create (&pstream,
argv[0], argc, argv,
MU_PROG_HINT_ERRTOOUT,
NULL,
MU_STREAM_READ);
*/
status = mu_argcv_join (argc, argv, " ", mu_argcv_escape_no, &command);
if (status)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_argcv_join", NULL, status);
return;
}
status = mu_prog_stream_create (&pstream, command, MU_STREAM_READ);
if (status)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create_argv", argv[0],
mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create", argv[0],
status);
return;
}
......
......@@ -24,20 +24,7 @@
#include <ctype.h>
#include <string.h>
#include <mailutils/mailutils.h>
#include <mailutils/argcv.h>
static void
read_and_print (mu_stream_t in, mu_stream_t out)
{
size_t size;
char buffer[128];
while (mu_stream_readline (in, buffer, sizeof (buffer), &size) == 0
&& size > 0)
{
mu_stream_write (out, buffer, size, NULL);
}
}
#include <mailutils/sys/prog_stream.h>
int
main (int argc, char *argv[])
......@@ -45,34 +32,154 @@ main (int argc, char *argv[])
int rc;
mu_stream_t stream, out;
int read_stdin = 0;
int i = 1;
char *cmdline;
int i;
int flags = MU_STREAM_READ;
struct mu_prog_hints hints;
int hint_flags = 0;
char *progname = NULL;
gid_t gid[20];
size_t gn = 0;
if (argc > 1 && strcmp (argv[i], "--stdin") == 0)
for (i = 1; i < argc; i++)
{
if (strcmp (argv[i], "--stdin") == 0)
{
read_stdin = 1;
flags |= MU_STREAM_WRITE;
}
else if (strcmp (argv[i], "--chdir") == 0)
{
hints.mu_prog_workdir = argv[i+1];
hint_flags |= MU_PROG_HINT_WORKDIR;
i++;
}
else if (strncmp (argv[i], "--limit", 7) == 0
&& mu_isdigit (argv[i][7]))
{
int n;
if (i + 1 == argc)
{
fprintf (stderr, "%s requires argument\n", argv[i]);
exit (1);
}
n = argv[i][7] - '0';
if (!(_mu_prog_limit_flags & MU_PROG_HINT_LIMIT(n)))
{
fprintf (stderr, "%s is not supported\n", argv[i]+2);
continue;
}
hint_flags |= MU_PROG_HINT_LIMIT(n);
hints.mu_prog_limit[n] = strtoul (argv[i+1], NULL, 10);
i++;
}
else if (strcmp (argv[i], "--prio") == 0)
{
if (i + 1 == argc)
{
fprintf (stderr, "%s requires argument\n", argv[i]);
exit (1);
}
hint_flags |= MU_PROG_HINT_PRIO;
hints.mu_prog_prio = strtoul (argv[i+1], NULL, 10);
i++;
}
else if (strcmp (argv[i], "--exec") == 0)
{
if (i + 1 == argc)
{
fprintf (stderr, "%s requires argument\n", argv[i]);
exit (1);
}
progname = argv[++i];
}
else if (strcmp (argv[i], "--errignore") == 0)
hint_flags |= MU_PROG_HINT_IGNOREFAIL;
else if (strcmp (argv[i], "--uid") == 0)
{
if (i + 1 == argc)
{
fprintf (stderr, "%s requires argument\n", argv[i]);
exit (1);
}
hint_flags |= MU_PROG_HINT_UID;
hints.mu_prog_uid = strtoul (argv[i+1], NULL, 10);
i++;
}
else if (strcmp (argv[i], "--gid") == 0)
{
mu_list_t list;
mu_iterator_t itr;
if (i + 1 == argc)
{
fprintf (stderr, "%s requires argument\n", argv[i]);
exit (1);
}
mu_list_create (&list);
mu_list_set_destroy_item (list, mu_list_free_item);
rc = mu_string_split (argv[++i], ",", list);
if (mu_list_get_iterator (list, &itr) == 0)
{
char *p;
for (mu_iterator_first (itr);
!mu_iterator_is_done (itr); mu_iterator_next (itr))
{
if (gn >= MU_ARRAY_SIZE (gid))
{
fprintf (stderr, "too many gids\n");
exit (1);
}
gid[gn++] = strtoul (p, NULL, 10);
}
mu_iterator_destroy (&itr);
}
else
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_list_get_iterator", NULL,
rc);
exit (1);
}
mu_list_destroy (&list);
hint_flags |= MU_PROG_HINT_GID;
}
else if (strcmp (argv[i], "--") == 0)
{
i++;
break;
}
else
break;
}
if (i == argc)
{
fprintf (stderr, "Usage: %s [--stdin] progname [args]\n", argv[0]);
fprintf (stderr,
"Usage: %s [--stdin] [--chdir dir] [--limit{0-9} lim] [--prio N]\n"
" [--exec progname] progname [args]\n", argv[0]);
exit (1);
}
MU_ASSERT (mu_argcv_string (argc - i, &argv[i], &cmdline));
argc -= i;
argv += i;
if (!progname)
progname = argv[0];
if (read_stdin)
{
mu_stream_t in;
MU_ASSERT (mu_stdio_stream_create (&in, MU_STDIN_FD, 0));
rc = mu_filter_prog_stream_create (&stream, cmdline, in);
/* Make sure closing/destroying stream will close/destroy in */
mu_stream_unref (in);
MU_ASSERT (mu_stdio_stream_create (&hints.mu_prog_input,
MU_STDIN_FD, 0));
hint_flags |= MU_PROG_HINT_INPUT;
}
else
rc = mu_prog_stream_create (&stream, cmdline, flags);
rc = mu_prog_stream_create (&stream, progname, argc, argv,
hint_flags, &hints, flags);
if (hint_flags & MU_PROG_HINT_INPUT)
/* Make sure closing/destroying stream will close/destroy input */
mu_stream_unref (hints.mu_prog_input);
if (rc)
{
fprintf (stderr, "%s: cannot create program filter stream: %s\n",
......@@ -82,7 +189,7 @@ main (int argc, char *argv[])
MU_ASSERT (mu_stdio_stream_create (&out, MU_STDOUT_FD, 0));
read_and_print (stream, out);
mu_stream_copy (out, stream, 0, NULL);
mu_stream_close (stream);
mu_stream_destroy (&stream);
mu_stream_close (out);
......
......@@ -448,7 +448,7 @@ do_preauth_program (struct sockaddr *pcs, struct sockaddr *sa)
return NULL;
}
rc = mu_prog_stream_create (&str, ws.ws_wordv[0], MU_STREAM_READ);
rc = mu_command_stream_create (&str, ws.ws_wordv[0], MU_STREAM_READ);
mu_wordsplit_free (&ws);
if (rc)
{
......
......@@ -77,6 +77,7 @@ pkginclude_HEADERS = \
pam.h\
parse822.h\
pop3.h\
prog.h\
progmailer.h\
property.h\
python.h\
......
......@@ -66,5 +66,6 @@
#include <mailutils/wordsplit.h>
#include <mailutils/log.h>
#include <mailutils/stdstream.h>
#include <mailutils/prog.h>
/* EOF */
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2009 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Mailutils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#ifndef _MAILUTILS_PROG_H
#define _MAILUTILS_PROG_H
#include <sys/time.h>
#include <sys/resource.h>
#include <mailutils/types.h>
#define MU_PROG_LIMIT_AS 0
#define MU_PROG_LIMIT_CPU 1
#define MU_PROG_LIMIT_DATA 2
#define MU_PROG_LIMIT_FSIZE 3
#define MU_PROG_LIMIT_NPROC 4
#define MU_PROG_LIMIT_CORE 5
#define MU_PROG_LIMIT_MEMLOCK 6
#define MU_PROG_LIMIT_NOFILE 7
#define MU_PROG_LIMIT_RSS 8
#define MU_PROG_LIMIT_STACK 9
#define _MU_PROG_LIMIT_MAX 10
#define MU_PROG_HINT_WORKDIR 0x0001 /* Workdir is set */
#define MU_PROG_HINT_PRIO 0x0002 /* Prio is set */
#define MU_PROG_HINT_INPUT 0x0004 /* Input stream is set */
#define MU_PROG_HINT_UID 0x0008 /* Uid is set */
#define MU_PROG_HINT_GID 0x0010 /* Supplementary gids are set */
#define MU_PROG_HINT_ERRTOOUT 0x0020 /* Redirect stderr to stdout */
#define MU_PROG_HINT_ERRTOSTREAM 0x0040 /* Redirect stderr to errstream */
#define MU_PROG_HINT_IGNOREFAIL 0x0080 /* Ignore hint setup failures */
#define _MU_PROG_HINT_MASK 0x00ff
#define MU_PROG_HINT_LIMIT(n) (0x100 << (n)) /* MU_PROG_LIMIT_n is set */
struct mu_prog_hints
{
char *mu_prog_workdir; /* Working directory */
uid_t mu_prog_uid; /* Run as this user */
gid_t *mu_prog_gidv; /* Array of supplementary gids */
size_t mu_prog_gidc; /* Number of elements in gidv */
rlim_t mu_prog_limit[_MU_PROG_LIMIT_MAX]; /* Limits */
int mu_prog_prio; /* Scheduling priority */
mu_stream_t mu_prog_input; /* Input stream */
mu_stream_t mu_prog_error; /* Error stream */
};
int mu_prog_stream_create (mu_stream_t *pstream,
const char *progname,
size_t argc, char **argv,
int hflags,
struct mu_prog_hints *hints,
int flags);
int mu_command_stream_create (mu_stream_t *pstream, const char *command,
int flags);
#endif
......@@ -250,9 +250,6 @@ int mu_fd_stream_create (mu_stream_t *pstream, char *filename, int fd,
#define MU_STDERR_FD 2
int mu_stdio_stream_create (mu_stream_t *pstream, int fd, int flags);
int mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags);
int mu_filter_prog_stream_create (mu_stream_t *pstream, const char *progname,
mu_stream_t input);
int mu_memory_stream_create (mu_stream_t *pstream, int flags);
int mu_static_memory_stream_create (mu_stream_t *pstream, const void *mem,
size_t size);
......
......@@ -18,18 +18,25 @@
#define _MAILUTILS_SYS_PROG_STREAM_H
#include <mailutils/sys/stream.h>
#include <mailutils/prog.h>
struct _mu_prog_stream
{
struct _mu_stream stream;
struct _mu_stream stream; /* Base stream */
char *progname; /* Program name */
size_t argc; /* Number of arguments */
char **argv; /* Program arguments */
int hint_flags; /* Hint flags */
struct mu_prog_hints hints; /* Invocation hints */
pid_t pid;
int status;
pid_t writer_pid;
int argc;
char **argv;
mu_stream_t in, out;
mu_stream_t input;
mu_stream_t in, out;
};
extern int _mu_prog_limit_flags;
extern int _mu_prog_limit_codes[_MU_PROG_LIMIT_MAX];
#endif
......
......@@ -175,8 +175,10 @@ int mu_getpass (mu_stream_t in, mu_stream_t out, const char *prompt,
/* ----------------------- */
/* Get the host name, doing a gethostbyname() if possible. */
int mu_get_host_name (char **host);
int mu_spawnvp(const char *prog, char *av[], int *stat);
int mu_spawnvp (const char *prog, char *av[], int *stat);
int mu_scheme_autodetect_p (mu_url_t);
int mu_set_user_privileges (uid_t uid, gid_t *gidv, size_t gidc);
int mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups);
struct timeval;
int mu_fd_wait (int fd, int *pflags, struct timeval *tvp);
......
......@@ -27,7 +27,6 @@ libmuaux_a_SOURCES += \
signal.c\
strexit.c\
tcpwrap.c\
userprivs.c\
mu_umaxtostr.c\
mu_umaxtostr.h
......
......@@ -63,6 +63,7 @@ libbase_la_SOURCES = \
tempfile.c\
ticket.c\
tilde.c\
userprivs.c\
usremail.c\
version.c\
wicket.c
......
......@@ -29,75 +29,90 @@
#include <mailutils/nls.h>
#include <mailutils/list.h>
#include <mailutils/iterator.h>
#include <xalloc.h>
/* Switch to the given UID/GID */
int
mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
mu_set_user_privileges (uid_t uid, gid_t *gidv, size_t gidc)
{
int rc = 0;
gid_t *emptygidset;
size_t size = 1, j = 1;
mu_iterator_t itr;
gid_t gid;
if (getuid ())
return EACCES;
if (uid == 0)
return 0;
/* Create a list of supplementary groups */
mu_list_count (retain_groups, &size);
size++;
emptygidset = xmalloc (size * sizeof emptygidset[0]);
emptygidset[0] = gid ? gid : getegid ();
if (mu_list_get_iterator (retain_groups, &itr) == 0)
{
for (mu_iterator_first (itr);
!mu_iterator_is_done (itr); mu_iterator_next (itr))
mu_iterator_current (itr,
(void **)(emptygidset + j++));
mu_iterator_destroy (&itr);
}
/* Reset group permissions */
if (geteuid () == 0 && setgroups (j, emptygidset))
if (gidv && gidc)
{
if (geteuid () == 0 && setgroups (gidc, gidv))
{
mu_error(_("setgroups(1, %lu) failed: %s"),
(unsigned long) emptygidset[0], mu_strerror (errno));
rc = 1;
(unsigned long) gidv[0], mu_strerror (errno));
return errno;
}
gid = gidv[0];
}
else
{
struct passwd *pwd = getpwuid (uid);
if (pwd)
gid = pwd->pw_gid;
else
gid = getegid ();
}
free (emptygidset);
/* Switch to the user's gid. On some OSes the effective gid must
be reset first */
#if defined(HAVE_SETEGID)
if ((rc = setegid (gid)) < 0)
if (setegid (gid) < 0)
{
rc = errno;
mu_error (_("setegid(%lu) failed: %s"),
(unsigned long) gid, mu_strerror (errno));
(unsigned long) gid, mu_strerror (rc));
}
#elif defined(HAVE_SETREGID)
if ((rc = setregid (gid, gid)) < 0)
if (setregid (gid, gid) < 0)
{
rc = errno;
mu_error (_("setregid(%lu,%lu) failed: %s"),
(unsigned long) gid, (unsigned long) gid,
mu_strerror (errno));
mu_strerror (rc));
}
#elif defined(HAVE_SETRESGID)
if ((rc = setresgid (gid, gid, gid)) < 0)
if (setresgid (gid, gid, gid) < 0)
{
rc = errno;
mu_error (_("setresgid(%lu,%lu,%lu) failed: %s"),
(unsigned long) gid,
(unsigned long) gid,
(unsigned long) gid,
mu_strerror (errno));
mu_strerror (rc));
}
#endif
if (rc == 0 && gid != 0)
{
if ((rc = setgid (gid)) < 0 && getegid () != gid)
if (setgid (gid) < 0)
{
rc = errno;
mu_error (_("setgid(%lu) failed: %s"),
(unsigned long) gid, mu_strerror (errno));
(unsigned long) gid, mu_strerror (rc));
}
else if (getegid () != gid)
{
rc = MU_ERR_FAILURE;
mu_error (_("setgid(%lu) failed: %s"),
(unsigned long) gid, mu_strerror (rc));
}
if (rc == 0 && getegid () != gid)
{
mu_error (_("Cannot set effective gid to %lu"),
(unsigned long) gid);
rc = 1;
rc = MU_ERR_FAILURE;
}
}
......@@ -114,24 +129,24 @@ mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
{
if (setreuid (uid, -1) < 0)
{
rc = errno;
mu_error (_("setreuid(%lu,-1) failed: %s"),
(unsigned long) uid,
mu_strerror (errno));
rc = 1;
mu_strerror (rc));
}
if (setuid (uid) < 0)
{
rc = errno;
mu_error (_("second setuid(%lu) failed: %s"),
(unsigned long) uid, mu_strerror (errno));
rc = 1;
(unsigned long) uid, mu_strerror (rc));
}
} else
#endif
{
rc = errno;
mu_error (_("setuid(%lu) failed: %s"),
(unsigned long) uid,
mu_strerror (errno));
rc = 1;
mu_strerror (rc));
}
}
......@@ -139,15 +154,46 @@ mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
if (uid != 0 && setuid (0) == 0)
{
mu_error (_("seteuid(0) succeeded when it should not"));
rc = 1;
rc = MU_ERR_FAILURE;
}
else if (uid != euid && setuid (euid) == 0)
{
mu_error (_("Cannot drop non-root setuid privileges"));
rc = 1;
rc = MU_ERR_FAILURE;
}
}
return rc;
}
int
mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
{
int rc = 0;
gid_t *emptygidset;
size_t size = 1, j = 1;
mu_iterator_t itr;
if (uid == 0)
return 0;
/* Create a list of supplementary groups */
mu_list_count (retain_groups, &size);
size++;
emptygidset = malloc (size * sizeof emptygidset[0]);
if (!emptygidset)
return ENOMEM;
emptygidset[0] = gid ? gid : getegid ();
if (mu_list_get_iterator (retain_groups, &itr) == 0)
{
for (mu_iterator_first (itr);
!mu_iterator_is_done (itr); mu_iterator_next (itr))
mu_iterator_current (itr,
(void **)(emptygidset + j++));
mu_iterator_destroy (&itr);
}
rc = mu_set_user_privileges (uid, emptygidset, j);
free (emptygidset);
return rc;
}
......
......@@ -71,9 +71,105 @@ _prog_stream_unregister (struct _mu_prog_stream *stream)
#define REDIRECT_STDIN_P(f) ((f) & MU_STREAM_WRITE)
#define REDIRECT_STDOUT_P(f) ((f) & MU_STREAM_READ)
#ifdef RLIMIT_AS
# define _MU_RLIMIT_AS_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_AS)
#else
# define _MU_RLIMIT_AS_FLAG 0
# define RLIMIT_AS 0
#endif
#ifdef RLIMIT_CPU
# define _MU_RLIMIT_CPU_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_CPU)
#else
# define _MU_RLIMIT_CPU_FLAG 0
# define RLIMIT_CPU 0
#endif
#ifdef RLIMIT_DATA
# define _MU_RLIMIT_DATA_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_DATA)
#else
# define _MU_RLIMIT_DATA_FLAG 0
# define RLIMIT_DATA 0
#endif
#ifdef RLIMIT_FSIZE
# define _MU_RLIMIT_FSIZE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_FSIZE)
#else
# define _MU_RLIMIT_FSIZE_FLAG 0
# define RLIMIT_FSIZE 0
#endif
#ifdef RLIMIT_NPROC
# define _MU_RLIMIT_NPROC_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_NPROC)
#else
# define _MU_RLIMIT_NPROC_FLAG 0
# define RLIMIT_NPROC 0
#endif
#ifdef RLIMIT_CORE
# define _MU_RLIMIT_CORE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_CORE)
#else
# define _MU_RLIMIT_CORE_FLAG 0
# define RLIMIT_CORE 0
#endif
#ifdef RLIMIT_MEMLOCK
# define _MU_RLIMIT_MEMLOCK_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_MEMLOCK)
#else
# define _MU_RLIMIT_MEMLOCK_FLAG 0
# define RLIMIT_MEMLOCK 0
#endif
#ifdef RLIMIT_NOFILE
# define _MU_RLIMIT_NOFILE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_NOFILE)
#else
# define _MU_RLIMIT_NOFILE_FLAG 0
# define RLIMIT_NOFILE 0
#endif
#ifdef RLIMIT_RSS
# define _MU_RLIMIT_RSS_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_RSS)
#else
# define _MU_RLIMIT_RSS_FLAG 0
# define RLIMIT_RSS 0
#endif
#ifdef RLIMIT_STACK
# define _MU_RLIMIT_STACK MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_STACK)
#else
# define _MU_RLIMIT_STACK 0
# define RLIMIT_STACK 0
#endif
#define _MU_PROG_AVAILABLE_LIMITS \
(_MU_RLIMIT_AS_FLAG | \
_MU_RLIMIT_CPU_FLAG | \
_MU_RLIMIT_DATA_FLAG | \
_MU_RLIMIT_FSIZE_FLAG | \
_MU_RLIMIT_NPROC_FLAG | \
_MU_RLIMIT_CORE_FLAG | \
_MU_RLIMIT_MEMLOCK_FLAG | \
_MU_RLIMIT_NOFILE_FLAG | \
_MU_RLIMIT_RSS_FLAG | \
_MU_RLIMIT_STACK)
int _mu_prog_limit_flags = _MU_PROG_AVAILABLE_LIMITS;
int _mu_prog_limit_codes[_MU_PROG_LIMIT_MAX] = {
RLIMIT_AS,
RLIMIT_CPU,
RLIMIT_DATA,
RLIMIT_FSIZE,
RLIMIT_NPROC,
RLIMIT_CORE,
RLIMIT_MEMLOCK,
RLIMIT_NOFILE,
RLIMIT_RSS,
RLIMIT_STACK
};
static int
start_program_filter (pid_t *pid, int *p, int argc, char **argv,
char *errfile, int flags)
start_program_filter (int *p, struct _mu_prog_stream *fs, int flags)
{
int rightp[2], leftp[2];
int i;
......@@ -84,7 +180,7 @@ start_program_filter (pid_t *pid, int *p, int argc, char **argv,
if (REDIRECT_STDOUT_P (flags))
pipe (rightp);
switch (*pid = fork ())
switch (fs->pid = fork ())
{
/* The child branch. */
case 0:
......@@ -112,27 +208,68 @@ start_program_filter (pid_t *pid, int *p, int argc, char **argv,
close (leftp[1]);
}
/* Error output */
if (errfile)
if (fs->hint_flags & MU_PROG_HINT_ERRTOOUT)
dup2 (1, 2);
if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
{
i = open (errfile, O_CREAT|O_WRONLY|O_APPEND, 0644);
if (i > 0 && i != 2)
if (chdir (fs->hints.mu_prog_workdir))
{
dup2 (i, 2);
close (i);
mu_error (_("cannot change to %s: %s"),
fs->hints.mu_prog_workdir, mu_strerror (errno));
if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
_exit (127);
}
}
if (fs->hint_flags & MU_PROG_HINT_UID)
{
if (mu_set_user_privileges (fs->hints.mu_prog_uid,
fs->hints.mu_prog_gidv,
fs->hints.mu_prog_gidc)
&& !(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
_exit (127);
}
for (i = 0; i < _MU_PROG_LIMIT_MAX; i++)
{
if (MU_PROG_HINT_LIMIT(i) & fs->hint_flags)
{
struct rlimit rlim;
rlim.rlim_cur = rlim.rlim_max = fs->hints.mu_prog_limit[i];
if (setrlimit (_mu_prog_limit_codes[i], &rlim))
{
mu_error (_("error setting limit %d to %lu: %s"),
i, (unsigned long) rlim.rlim_cur,
mu_strerror (errno));
if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
_exit (127);
}
}
}
if (MU_PROG_HINT_PRIO & fs->hint_flags)
{
if (setpriority (PRIO_PROCESS, 0, fs->hints.mu_prog_prio))
{
mu_error (_("error setting priority: %s"),
mu_strerror (errno));
if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
_exit (127);
}
}
/* Close unneded descripitors */
for (i = getmaxfd (); i > 2; i--)
close (i);
/*FIXME: Switch to other uid/gid if desired */
execvp (argv[0], argv);
execvp (fs->progname, fs->argv);
/* Report error via syslog */
syslog (LOG_ERR|LOG_USER, "can't run %s (ruid=%d, euid=%d): %m",
argv[0], getuid (), geteuid ());
exit (127);
fs->progname, getuid (), geteuid ());
_exit (127);
/********************/
/* Parent branches: */
......@@ -187,10 +324,16 @@ _prog_wait (pid_t pid, int *pstatus)
static void
_prog_done (mu_stream_t stream)
{
struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
int status;
struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
mu_argcv_free (fs->argc, fs->argv);
free (fs->progname);
if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
free (fs->hints.mu_prog_workdir);
if (fs->hint_flags & MU_PROG_HINT_INPUT)
mu_stream_unref (fs->hints.mu_prog_input);
if (fs->in)
mu_stream_destroy (&fs->in);
if (fs->out)
......@@ -245,8 +388,6 @@ static int
feed_input (struct _mu_prog_stream *fs)
{
pid_t pid;
size_t size;
char buffer[128];
int rc = 0;
pid = fork ();
......@@ -261,10 +402,7 @@ feed_input (struct _mu_prog_stream *fs)
case 0:
/* Child */
while (mu_stream_read (fs->input, buffer, sizeof (buffer),
&size) == 0
&& size > 0)
mu_stream_write (fs->out, buffer, size, NULL);
mu_stream_copy (fs->out, fs->hints.mu_prog_input, 0, NULL);
mu_stream_close (fs->out);
exit (0);
......@@ -295,7 +433,7 @@ _prog_open (mu_stream_t stream)
mu_stream_get_flags (stream, &flags);
seekable_flag = (flags & MU_STREAM_SEEK);
rc = start_program_filter (&fs->pid, pfd, fs->argc, fs->argv, NULL, flags);
rc = start_program_filter (pfd, fs, flags);
if (rc)
return rc;
......@@ -322,7 +460,7 @@ _prog_open (mu_stream_t stream)
}
_prog_stream_register (fs);
if (fs->input)
if (fs->hint_flags & MU_PROG_HINT_INPUT)
return feed_input (fs);
return 0;
}
......@@ -408,30 +546,25 @@ _prog_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr)
return 0;
}
struct _mu_prog_stream *
_prog_stream_create (const char *progname, int flags)
/* NOTE: Steals argv */
static struct _mu_prog_stream *
_prog_stream_create (const char *progname, size_t argc, char **argv,
int hint_flags, struct mu_prog_hints *hints, int flags)
{
struct _mu_prog_stream *fs;
struct mu_wordsplit ws;
fs = (struct _mu_prog_stream *) _mu_stream_create (sizeof (*fs), flags);
if (!fs)
return NULL;
ws.ws_comment = "#";
if (mu_wordsplit (progname, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
fs->progname = strdup (progname);
if (!fs->progname)
{
mu_error (_("cannot split line `%s': %s"), progname,
mu_wordsplit_strerror (&ws));
free (fs);
return NULL;
}
fs->argc = ws.ws_wordc;
fs->argv = ws.ws_wordv;
ws.ws_wordc = 0;
ws.ws_wordv = NULL;
mu_wordsplit_free (&ws);
fs->argc = argc;
fs->argv = argv;
fs->stream.read = _prog_read;
fs->stream.write = _prog_write;
fs->stream.open = _prog_open;
......@@ -440,14 +573,67 @@ _prog_stream_create (const char *progname, int flags)
fs->stream.flush = _prog_flush;
fs->stream.done = _prog_done;
if (!hints)
fs->hint_flags = 0;
else
{
fs->hint_flags = (hint_flags & _MU_PROG_HINT_MASK) |
(hint_flags & _MU_PROG_AVAILABLE_LIMITS);
if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
{
fs->hints.mu_prog_workdir = strdup (hints->mu_prog_workdir);
if (!fs->hints.mu_prog_workdir)
{
free (fs);
return NULL;
}
}
memcpy (fs->hints.mu_prog_limit, hints->mu_prog_limit,
sizeof (fs->hints.mu_prog_limit));
fs->hints.mu_prog_prio = hints->mu_prog_prio;
if (fs->hint_flags & MU_PROG_HINT_INPUT)
{
fs->hints.mu_prog_input = hints->mu_prog_input;
mu_stream_ref (fs->hints.mu_prog_input);
}
if (fs->hint_flags & MU_PROG_HINT_UID)
{
fs->hints.mu_prog_uid = hints->mu_prog_uid;
if (fs->hint_flags & MU_PROG_HINT_GID)
{
fs->hints.mu_prog_gidv = calloc (hints->mu_prog_gidc,
sizeof (fs->hints.mu_prog_gidv[0]));
if (!fs->hints.mu_prog_gidv)
{
mu_stream_unref ((mu_stream_t) fs);
return NULL;
}
memcpy (fs->hints.mu_prog_gidv, hints->mu_prog_gidv,
hints->mu_prog_gidc * fs->hints.mu_prog_gidv[0]);
fs->hints.mu_prog_gidc = hints->mu_prog_gidc;
}
else
{
fs->hints.mu_prog_gidc = 0;
fs->hints.mu_prog_gidv = NULL;
}
}
}
return fs;
}
int
mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags)
mu_prog_stream_create (mu_stream_t *pstream,
const char *progname, size_t argc, char **argv,
int hint_flags,
struct mu_prog_hints *hints,
int flags)
{
int rc;
mu_stream_t stream;
char **xargv;
size_t i;
if (pstream == NULL)
return MU_ERR_OUT_PTR_NULL;
......@@ -455,8 +641,26 @@ mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags)
if (progname == NULL)
return EINVAL;
if ((stream = (mu_stream_t) _prog_stream_create (progname, flags)) == NULL)
xargv = calloc (argc + 1, sizeof (xargv[0]));
if (!xargv)
return ENOMEM;
for (i = 0; i < argc; i++)
{
xargv[i] = strdup (argv[i]);
if (!xargv[i])
{
mu_argcv_free (i, argv);
return ENOMEM;
}
}
stream = (mu_stream_t) _prog_stream_create (progname, argc, xargv,
hint_flags, hints, flags);
if (!stream)
{
mu_argcv_free (argc, xargv);
return ENOMEM;
}
rc = mu_stream_open (stream);
if (rc)
......@@ -467,30 +671,38 @@ mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags)
}
int
mu_filter_prog_stream_create (mu_stream_t *pstream, const char *progname,
mu_stream_t input)
mu_command_stream_create (mu_stream_t *pstream, const char *command,
int flags)
{
int rc;
mu_stream_t stream;
struct _mu_prog_stream *fs;
struct mu_wordsplit ws;
if (pstream == NULL)
return MU_ERR_OUT_PTR_NULL;
if (progname == NULL)
if (command == NULL)
return EINVAL;
fs = _prog_stream_create (progname, MU_STREAM_RDWR);
if (!fs)
return ENOMEM;
mu_stream_ref (input);
fs->input = input;
stream = (mu_stream_t) fs;
rc = mu_stream_open (stream);
if (rc)
mu_stream_destroy (&stream);
else
ws.ws_comment = "#";
if (mu_wordsplit (command, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
{
mu_error (_("cannot split line `%s': %s"), command,
mu_wordsplit_strerror (&ws));
return errno;
}
rc = mu_prog_stream_create (&stream,
ws.ws_wordv[0],
ws.ws_wordc, ws.ws_wordv,
0, NULL, flags);
if (rc == 0)
{
ws.ws_wordc = 0;
ws.ws_wordv = NULL;
*pstream = stream;
}
mu_wordsplit_free (&ws);
return rc;
}
......
......@@ -39,6 +39,7 @@
#include <signal.h>
#include <regex.h>
#include <mailutils/sieve.h>
#include <mailutils/prog.h>
int
sieve_action_pipe (mu_sieve_machine_t mach, mu_list_t args, mu_list_t tags)
......@@ -86,7 +87,7 @@ sieve_action_pipe (mu_sieve_machine_t mach, mu_list_t args, mu_list_t tags)
rc = mu_message_get_streamref (msg, &mstr);
ONERR (rc, _("cannot get message stream"), NULL);
rc = mu_prog_stream_create (&pstr, cmd, MU_STREAM_WRITE);
rc = mu_command_stream_create (&pstr, cmd, MU_STREAM_WRITE);
ONERR (rc, _("cannot create command stream"), cmd);
if (mu_sieve_tag_lookup (tags, "envelope", &val))
......
......@@ -409,10 +409,11 @@ run_metamail (const char *mailcap_cmd, mu_message_t mesg)
break;
}
status = mu_prog_stream_create (&pstr, mailcap_cmd, MU_STREAM_WRITE);
status = mu_command_stream_create (&pstr, mailcap_cmd,
MU_STREAM_WRITE);
if (status)
{
mu_error ("mu_prog_stream_create: %s", mu_strerror (status));
mu_error ("mu_command_stream_create: %s", mu_strerror (status));
break;
}
......
......@@ -82,6 +82,7 @@
#include <mailutils/cstr.h>
#include <mailutils/io.h>
#include <mailutils/stdstream.h>
#include <mailutils/prog.h>
#ifdef __cplusplus
extern "C" {
......
......@@ -38,7 +38,7 @@ mail_pipe (int argc, char **argv)
if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &list))
return 1;
rc = mu_prog_stream_create (&outstr, cmd, MU_STREAM_WRITE);
rc = mu_command_stream_create (&outstr, cmd, MU_STREAM_WRITE);
if (rc)
{
mu_error (_("cannot open `%s': %s"), cmd, mu_strerror (rc));
......
......@@ -702,7 +702,7 @@ msg_to_pipe (const char *cmd, mu_message_t msg)
mu_stream_t progstream, msgstream;
int status, rc;
status = mu_prog_stream_create (&progstream, cmd, MU_STREAM_WRITE);
status = mu_command_stream_create (&progstream, cmd, MU_STREAM_WRITE);
if (status)
{
util_error (_("Cannot pipe to %s: %s"), cmd, mu_strerror (status));
......
......@@ -56,6 +56,7 @@
#include <mailutils/mime.h>
#include <mailutils/io.h>
#include <mailutils/property.h>
#include <mailutils/prog.h>
#include <mailutils/mh.h>
#include <mu_umaxtostr.h>
......
......@@ -149,7 +149,7 @@ open_output ()
moreproc = NULL;
if (moreproc)
rc = mu_prog_stream_create (&output, moreproc, MU_STREAM_WRITE);
rc = mu_command_stream_create (&output, moreproc, MU_STREAM_WRITE);
else
rc = mu_stdio_stream_create (&output, MU_STDOUT_FD, MU_STREAM_WRITE);
......
......@@ -1403,7 +1403,7 @@ show_internal (mu_message_t msg, msg_part_t part, char *encoding,
int
mhn_exec (mu_stream_t *str, const char *cmd, int flags)
{
int rc = mu_prog_stream_create (str, cmd, MU_STREAM_WRITE);
int rc = mu_command_stream_create (str, cmd, MU_STREAM_WRITE);
if (rc)
{
mu_error (_("cannot create proc stream (command %s): %s"),
......@@ -1773,16 +1773,30 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding,
break;
case store_to_command:
/* FIXME: Change to homedir, reflect this in the message below.
Chdir should better be implemented within mu_prog_stream_create
Example message:
storing msg 4 part 1 using command (cd /home/gray; less)
*/
printf (_("storing msg %s part %s using command %s\n"),
prefix, partstr, name);
rc = mu_prog_stream_create (&out, name, MU_STREAM_WRITE);
{
struct mu_prog_hints hints;
struct mu_wordsplit ws;
hints.mu_prog_workdir = mu_get_homedir ();
ws.ws_comment = "#";
if (mu_wordsplit (name, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
{
mu_error (_("cannot split line `%s': %s"), name,
mu_wordsplit_strerror (&ws));
break;
}
printf (_("storing msg %s part %s using command (cd %s; %s)\n"),
prefix, partstr, hints.mu_prog_workdir, name);
rc = mu_prog_stream_create (&out,
ws.ws_wordv[0],
ws.ws_wordc, ws.ws_wordv,
MU_PROG_HINT_WORKDIR,
&hints, MU_STREAM_WRITE);
mu_wordsplit_free (&ws);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create", NULL, rc);
}
break;
case store_to_stdout:
......
......@@ -247,7 +247,7 @@ mkdir Mail/inbox
cp $abs_top_srcdir/testsuite/mh/mbox1/4 Mail/inbox
echo "Current-Folder: inbox" > Mail/context
echo "mhn-store-text: | $abs_top_srcdir/mh/tests/mhed -" >> $MH
mhn +inbox -store -part 1 4 | sed "s|$abs_top_srcdir/mh/tests/||;s| *$||" || exit $?
mhn +inbox -store -part 1 4 | sed "s|$abs_top_srcdir/mh/tests/||;s|(cd \(.*\)\;|(cd home\;|;s| *$||" || exit $?
],
[0],
[-- Editor invocation: -
......@@ -263,7 +263,7 @@ But, now that I'm perfectly sure I have none,
Why, I do it again and again.'
-- Input file end
storing msg 4 part 1 using command mhed -
storing msg 4 part 1 using command (cd home; mhed -)
])
dnl -------------------------------------------------------------------
......
......@@ -240,7 +240,7 @@ mutool_open_pager ()
if (mutool_shell_interactive && (pager = getenv ("PAGER")) != NULL)
{
mu_stream_t stream;
int rc = mu_prog_stream_create (&stream, pager, MU_STREAM_WRITE);
int rc = mu_command_stream_create (&stream, pager, MU_STREAM_WRITE);
if (rc == 0)
return stream;
mu_error (_("cannot start pager: %s"), mu_strerror (rc));
......
......@@ -118,6 +118,7 @@ mail/unalias.c
mail/util.c
mail/z.c
libmailutils/base/userprivs.c
libmailutils/cfg/driver.c
libmailutils/cfg/format.c
libmailutils/cfg/lexer.l
......