diff options
Diffstat (limited to 'libmailutils')
-rw-r--r-- | libmailutils/base/Makefile.am | 2 | ||||
-rw-r--r-- | libmailutils/base/closefds.c | 129 | ||||
-rw-r--r-- | libmailutils/base/daemon.c | 220 | ||||
-rw-r--r-- | libmailutils/base/pidfile.c | 187 | ||||
-rw-r--r-- | libmailutils/diag/bt.c | 4 | ||||
-rw-r--r-- | libmailutils/server/acl.c | 7 | ||||
-rw-r--r-- | libmailutils/server/msrv.c | 4 | ||||
-rw-r--r-- | libmailutils/stream/prog_stream.c | 3 |
8 files changed, 385 insertions, 171 deletions
diff --git a/libmailutils/base/Makefile.am b/libmailutils/base/Makefile.am index 3d6820c77..d56938751 100644 --- a/libmailutils/base/Makefile.am +++ b/libmailutils/base/Makefile.am @@ -24,6 +24,7 @@ libbase_la_SOURCES = \ argcvjoin.c\ argcvrem.c\ assoc.c\ + closefds.c\ coord.c\ copyfile.c\ ctparse.c\ @@ -55,6 +56,7 @@ libbase_la_SOURCES = \ onexit.c\ opool.c\ permstr.c\ + pidfile.c\ registrar.c\ refcount.c\ renamefile.c\ diff --git a/libmailutils/base/closefds.c b/libmailutils/base/closefds.c new file mode 100644 index 000000000..49c024fb1 --- /dev/null +++ b/libmailutils/base/closefds.c @@ -0,0 +1,129 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ +#include <config.h> +#include <stdlib.h> +#include <unistd.h> +#include <mailutils/util.h> + +#if defined (HAVE_FUNC_CLOSEFROM) +# include <unistd.h> + +static int +close_fds_sys (int minfd) +{ + closefrom (minfd); + return 0; +} + +#elif defined (HAVE_FCNTL_CLOSEM) +# include <fcntl.h> + +static int +close_fds_sys (int minfd) +{ + fcntl (minfd, F_CLOSEM, 0); + return 0; +} + +#elif defined (HAVE_LIBPROC_H) && defined (HAVE_FUNC_PROC_PIDINFO) +#include <libproc.h> + +static int +close_fds_sys (int minfd) +{ + pid_t pid = getpid (); + struct proc_fdinfo *fdinfo; + int i, n, size; + + size = proc_pidinfo (pid, PROC_PIDLISTFDS, 0, NULL, 0); + if (size == 0) + return 0; + else if (size < 0) + return -1; + + fdinfo = calloc (size, sizeof (fdinfo[0])); + if (!fdinfo) + return -1; + + n = proc_pidinfo (pid, PROC_PIDLISTFDS, 0, fdinfo, size); + if (n <= 0) + { + free (fdinfo); + return -1; + } + + n /= PROC_PIDLISTFD_SIZE; + + for (i = minfd; i < n; i++) + { + close (fdinfo_buf[i].proc_fd); + } + + free (fdinfo); + return 0; +} + +#elif defined (HAVE_PROC_SELF_FD) +# include <sys/types.h> +# include <dirent.h> +# include <limits.h> + +static int +close_fds_sys (int minfd) +{ + DIR *dir; + struct dirent *ent; + + dir = opendir ("/proc/self/fd"); + if (!dir) + return -1; + while ((ent = readdir (dir)) != NULL) + { + long n; + char *p; + + if (ent->d_name[0] == '.') + continue; + + n = strtol (ent->d_name, &p, 10); + if (n >= minfd && n < INT_MAX && *p == 0) + close ((int) n); + } + closedir (dir); + return 0; +} + +#else +# define close_fds_sys(fd) (-1) +#endif + +static int +close_fds_bruteforce (int minfd) +{ + int i, n = mu_getmaxfd (); + + for (i = minfd; i < n; i++) + close (i); + + return 0; +} + +void +mu_close_fds (int minfd) +{ + if (close_fds_sys (minfd)) + close_fds_bruteforce (minfd); +} diff --git a/libmailutils/base/daemon.c b/libmailutils/base/daemon.c index a2a3252ca..d7ca0c6cd 100644 --- a/libmailutils/base/daemon.c +++ b/libmailutils/base/daemon.c @@ -1,187 +1,87 @@ /* GNU Mailutils -- a suite of utilities for electronic mail - Copyright (C) 2004-2020 Free Software Foundation, Inc. + Copyright (C) 2019-2020 Free Software Foundation, Inc. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. This library 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 - Lesser General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General - Public License along with this library. If not, see - <http://www.gnu.org/licenses/>. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> + You should have received a copy of the GNU Lesser General Public License + along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ +#include <config.h> +#include <confpaths.h> #include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <string.h> - +#include <unistd.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <mailutils/util.h> #include <mailutils/daemon.h> -#include <mailutils/errno.h> -#include <mailutils/error.h> -#include <mailutils/nls.h> - -static char *pidfile; -static pid_t current_pid; - -/* Return 0 if DIR is writable for EUID/EGID. - Otherwise, return error code. */ -static int -ewraccess (const char *dir) -{ - struct stat st; - if (stat (dir, &st)) - return errno; - if ((st.st_mode & S_IWOTH) - || (st.st_gid == getegid () && (st.st_mode & S_IWGRP)) - || (st.st_uid == geteuid () && (st.st_mode & S_IWUSR))) - return 0; - else - return EACCES; -} - -/* Return 0 if DIR is writable. If necessary and possible, raise to - EUID 0, in that case return prior EUID in the memory location pointed to - by PUID. */ -static int -access_dir (const char *dir, uid_t *puid) -{ - int ec = ewraccess (dir); - if (ec) - { - if (ec == EACCES && access (dir, W_OK) == 0) - { - uid_t uid = geteuid (); - /* See if we can become root */ - if (uid && getuid () == 0 && seteuid (0) == 0) - { - *puid = uid; - return 0; - } - } - } - return ec; -} int -mu_daemon_create_pidfile (const char *filename) +mu_daemon (void) { - char *p; int fd; - uid_t uid = 0; - int rc; - if (filename[0] != '/') - return EINVAL; + switch (fork ()) + { + case 0: + break; - if (pidfile) - free (pidfile); - pidfile = strdup (filename); - if (!pidfile) - return ENOMEM; + case -1: + return errno; - /* Determine the hosting directory name */ - p = strrchr (pidfile, '/'); - if (pidfile == p) - { - free (pidfile); - pidfile = NULL; - /* Sorry, pidfiles in root dir are not allowed */ - return EINVAL; - } - /* Check if we have write access to the directory */ - *p = 0; - rc = access_dir (pidfile, &uid); - if (rc) - { - /* Nope, clean up and return */ - free (pidfile); - pidfile = NULL; - return rc; + default: + _exit (0); } - /* Restore directory separator */ - *p = '/'; - - unlink (pidfile); - current_pid = getpid (); + if (setsid () == (pid_t) -1) + return errno; + + signal (SIGHUP, SIG_IGN); - if ((fd = open (pidfile, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644)) != -1) + switch (fork ()) { - FILE *fp = fdopen (fd, "w"); - if (!fp) - { - rc = errno; - free (pidfile); - close (fd); - } - else - { - fprintf (fp, "%lu", (unsigned long) current_pid); - fclose (fp); - atexit (mu_daemon_remove_pidfile); - } - } - else - { - rc = errno; - free (pidfile); - pidfile = NULL; - } + case 0: + break; - /* Restore previous EUID value. */ - if (uid) - seteuid (uid); - - return rc; -} + case -1: + return errno; -void -mu_daemon_remove_pidfile (void) -{ - if (getpid () == current_pid) - { - int rc; - uid_t uid = 0; + default: + _exit (0); + } - /* Determine the hosting directory name */ - char *p = strrchr (pidfile, '/'); - if (pidfile == p) - { - /* Should not happen */ - abort (); - } - /* Check if we have write access to the directory */ - *p = 0; - rc = access_dir (pidfile, &uid); - *p = '/'; - if (rc == 0) - { - if (unlink (pidfile) && errno != ENOENT) - rc = errno; - else - rc = 0; - } - - if (rc) - mu_error (_("cannot remove pidfile %s: %s"), - pidfile, mu_strerror (rc)); + chdir ("/"); + mu_close_fds (0); - free (pidfile); - pidfile = NULL; + fd = open (PATH_DEVNULL, O_RDWR); + if (fd == 0) + { + dup2 (fd, 1); + dup2 (fd, 2); + } + else if (fd > 0) + { + /* This means that mu_close_fds failed to close stdin. + Shouldn't happen, but just in case ... */ + dup2 (fd, 0); + dup2 (fd, 1); + dup2 (fd, 2); + close (fd); } + + return 0; } - + + + + + diff --git a/libmailutils/base/pidfile.c b/libmailutils/base/pidfile.c new file mode 100644 index 000000000..a2a3252ca --- /dev/null +++ b/libmailutils/base/pidfile.c @@ -0,0 +1,187 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2004-2020 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> + +#include <mailutils/daemon.h> +#include <mailutils/errno.h> +#include <mailutils/error.h> +#include <mailutils/nls.h> + +static char *pidfile; +static pid_t current_pid; + +/* Return 0 if DIR is writable for EUID/EGID. + Otherwise, return error code. */ +static int +ewraccess (const char *dir) +{ + struct stat st; + if (stat (dir, &st)) + return errno; + if ((st.st_mode & S_IWOTH) + || (st.st_gid == getegid () && (st.st_mode & S_IWGRP)) + || (st.st_uid == geteuid () && (st.st_mode & S_IWUSR))) + return 0; + else + return EACCES; +} + +/* Return 0 if DIR is writable. If necessary and possible, raise to + EUID 0, in that case return prior EUID in the memory location pointed to + by PUID. */ +static int +access_dir (const char *dir, uid_t *puid) +{ + int ec = ewraccess (dir); + if (ec) + { + if (ec == EACCES && access (dir, W_OK) == 0) + { + uid_t uid = geteuid (); + /* See if we can become root */ + if (uid && getuid () == 0 && seteuid (0) == 0) + { + *puid = uid; + return 0; + } + } + } + return ec; +} + +int +mu_daemon_create_pidfile (const char *filename) +{ + char *p; + int fd; + uid_t uid = 0; + int rc; + + if (filename[0] != '/') + return EINVAL; + + if (pidfile) + free (pidfile); + pidfile = strdup (filename); + if (!pidfile) + return ENOMEM; + + /* Determine the hosting directory name */ + p = strrchr (pidfile, '/'); + if (pidfile == p) + { + free (pidfile); + pidfile = NULL; + /* Sorry, pidfiles in root dir are not allowed */ + return EINVAL; + } + /* Check if we have write access to the directory */ + *p = 0; + rc = access_dir (pidfile, &uid); + if (rc) + { + /* Nope, clean up and return */ + free (pidfile); + pidfile = NULL; + return rc; + } + + /* Restore directory separator */ + *p = '/'; + + unlink (pidfile); + current_pid = getpid (); + + if ((fd = open (pidfile, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644)) != -1) + { + FILE *fp = fdopen (fd, "w"); + if (!fp) + { + rc = errno; + free (pidfile); + close (fd); + } + else + { + fprintf (fp, "%lu", (unsigned long) current_pid); + fclose (fp); + atexit (mu_daemon_remove_pidfile); + } + } + else + { + rc = errno; + free (pidfile); + pidfile = NULL; + } + + /* Restore previous EUID value. */ + if (uid) + seteuid (uid); + + return rc; +} + +void +mu_daemon_remove_pidfile (void) +{ + if (getpid () == current_pid) + { + int rc; + uid_t uid = 0; + + /* Determine the hosting directory name */ + char *p = strrchr (pidfile, '/'); + if (pidfile == p) + { + /* Should not happen */ + abort (); + } + /* Check if we have write access to the directory */ + *p = 0; + rc = access_dir (pidfile, &uid); + *p = '/'; + if (rc == 0) + { + if (unlink (pidfile) && errno != ENOENT) + rc = errno; + else + rc = 0; + } + + if (rc) + mu_error (_("cannot remove pidfile %s: %s"), + pidfile, mu_strerror (rc)); + + free (pidfile); + pidfile = NULL; + } +} + + + diff --git a/libmailutils/diag/bt.c b/libmailutils/diag/bt.c index c27634ef2..3b43dcd21 100644 --- a/libmailutils/diag/bt.c +++ b/libmailutils/diag/bt.c @@ -36,7 +36,6 @@ void mu_gdb_bt () { - int i; pid_t master_pid = getpid (); pid_t pid; static char buf[1024]; @@ -58,8 +57,7 @@ mu_gdb_bt () abort (); } - for (i = mu_getmaxfd (); i >= 0; i--) - close (i); + mu_close_fds (0); fd = open (fname, O_WRONLY|O_CREAT, 0600); if (fd == -1) diff --git a/libmailutils/server/acl.c b/libmailutils/server/acl.c index f209d6929..5f71d2bce 100644 --- a/libmailutils/server/acl.c +++ b/libmailutils/server/acl.c @@ -410,7 +410,6 @@ spawn_prog (const char *cmdline, int *pstatus, struct run_closure *rp) pid = fork (); if (pid == 0) { - int i; struct mu_wordsplit ws; if (mu_wordsplit (s, &ws, MU_WRDSF_DEFFLAGS)) @@ -419,9 +418,9 @@ spawn_prog (const char *cmdline, int *pstatus, struct run_closure *rp) mu_wordsplit_strerror (&ws)); _exit (127); } - - for (i = mu_getmaxfd (); i > 2; i--) - close (i); + + mu_close_fds (3); + execvp (ws.ws_wordv[0], ws.ws_wordv); _exit (127); } diff --git a/libmailutils/server/msrv.c b/libmailutils/server/msrv.c index 088f4d8b3..6203ae71c 100644 --- a/libmailutils/server/msrv.c +++ b/libmailutils/server/msrv.c @@ -522,9 +522,9 @@ mu_m_server_begin (mu_m_server_t msrv) { /* Become a daemon. Take care to close inherited fds and to hold first three one, in, out, err */ - if (daemon (0, 0) < 0) + if ((rc = mu_daemon ()) != 0) { - mu_error (_("failed to become a daemon: %s"), mu_strerror (errno)); + mu_error (_("failed to become a daemon: %s"), mu_strerror (rc)); exit (EXIT_FAILURE); } mu_onexit_reset (); diff --git a/libmailutils/stream/prog_stream.c b/libmailutils/stream/prog_stream.c index 08a19af2a..61ccfd39a 100644 --- a/libmailutils/stream/prog_stream.c +++ b/libmailutils/stream/prog_stream.c @@ -252,8 +252,7 @@ start_program_filter (int *p, struct _mu_prog_stream *fs, int flags) } /* Close unneded descripitors */ - for (i = mu_getmaxfd (); i > 2; i--) - close (i); + mu_close_fds (3); /*FIXME: Switch to other uid/gid if desired */ execvp (fs->progname, fs->argv); |