diff options
author | Colin Watson <cjwatson@debian.org> | 2010-09-27 00:28:41 +0100 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2010-09-27 00:28:41 +0100 |
commit | 9eea6f5d590ca001ee29f4806a665b4f1dca54fa (patch) | |
tree | caae0e8141212f0d9e9a91754888fb837133e319 /lib/security.c | |
parent | 4274affde9622e429f2937c569cd76f38722cb0d (diff) |
Move security to libman.
* src/security.c: Move to ...
* lib/security.c: ... here.
* src/security.h: Move to ...
* lib/security.h: ... here.
* lib/Makefile.am (libman_la_SOURCES): Add security.c and
security.h.
* src/Makefile.am (lexgrog_SOURCES): Remove fake_security.c and
security.h.
(man_SOURCES, mandb_SOURCES): Remove security.c and security.h.
* lib/README: Add security.*.
* src/lexgrog_test.c: Call init_security, so that we can use generic
security functions rather than fake_security.
* src/fake_security.c: Remove.
* lib/security.c (do_system_drop_privs): Unroll calls to do_system
into calls to pipeline_start and pipeline_wait.
* src/whatis.c (use_grep): Likewise.
* src/util.c (do_system): Remove.
* include/manconfig.h.in (do_system): Remove.
* src/*.c: Update #include grouping for movements to libman.
Diffstat (limited to 'lib/security.c')
-rw-r--r-- | lib/security.c | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/lib/security.c b/lib/security.c new file mode 100644 index 00000000..4188c898 --- /dev/null +++ b/lib/security.c @@ -0,0 +1,262 @@ +/* + * security.c: Routines to aid secure uid operations + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 2 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Mon Aug 8 20:35:30 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> + +#include <sys/types.h> + +#if HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif + +#include "gettext.h" +#define _(String) gettext (String) + +#include "manconfig.h" + +#include "error.h" +#include "cleanup.h" +#include "pipeline.h" + +#include "security.h" + +#ifdef SECURE_MAN_UID + + /* + * This is the name of the user that the preformatted man pages belong to. + * If you are running man as a setuid program, you should make sure + * that all of the cat pages and the directories that + * they live in are writeable by this user. + */ + +# include <unistd.h> /* for _POSIX_SAVED_IDS */ +# if defined(_POSIX_SAVED_IDS) +# if defined(__ultrix__) + /* Ultrix pretends to have saved uids, but hasn't unless: */ +# if defined(POSIX) || defined(SYSTEM_FIVE) +# define POSIX_SAVED_IDS +# endif /* POSIX || SYSTEM_FIVE */ +# else /* !ultrix */ +# define POSIX_SAVED_IDS +# endif /* ultrix */ +# endif /* _POSIX_SAVED_IDS */ + +/* Sort out the function to use to set the euid. Used if we have suid */ + +# ifdef POSIX_SAVED_IDS +# if defined (HAVE_SETEUID) +# define SET_EUID(euid) seteuid(euid) +# elif defined (HAVE_SETREUID) +# define SET_EUID(euid) setreuid(-1, euid) +# elif defined (HAVE_SETRESUID) +# define SET_EUID(euid) setresuid(-1, euid, -1) +# endif /* HAVE_SETEUID */ + +/* Sort out the function to use to swap ruid with euid. Used if no suid. */ + +# else /* !POSIX_SAVED_IDS */ +# if defined (HAVE_SETREUID) +# define SWAP_UIDS(ida, idb) setreuid(idb, ida) +# elif defined (HAVE_SETRESUID) +# define SWAP_UIDS(ida, idb) setresuid(idb, ida, -1) +# warning Using setresuid() whithout _POSIX_SAVED_IDS! +# endif /* HAVE_SETREUID */ +# endif /* POSIX_SAVED_IDS */ + +# if defined (POSIX_SAVED_IDS) && !defined (SET_EUID) || \ + !defined (POSIX_SAVED_IDS) && !defined (SWAP_UIDS) +# error Cannot compile man as a setuid program: insufficient seteuid funcs. +# endif + +uid_t ruid; /* initial real user id */ +uid_t euid; /* initial effective user id */ +uid_t uid; /* current euid */ + +static struct passwd *man_owner; + +/* Keep a count of how many times we've dropped privileges, and only regain + * them if regain_effective_privs() is called an equal number of times. + */ +static int priv_drop_count = 0; + +static inline void gripe_set_euid (void) +{ + error (FATAL, errno, _("can't set effective uid")); +} + +void init_security (void) +{ + ruid = getuid (); + uid = euid = geteuid (); + debug ("ruid=%d, euid=%d\n", (int) ruid, (int) euid); + priv_drop_count = 0; + drop_effective_privs (); +} + +/* Return a pointer to the password entry structure for MAN_OWNER. This + * structure will be statically stored. + */ +struct passwd *get_man_owner (void) +{ + if (man_owner) + return man_owner; + + man_owner = getpwnam (MAN_OWNER); + if (!man_owner) + error (FAIL, 0, _("the setuid man user \"%s\" does not exist"), + MAN_OWNER); + assert (man_owner); + return man_owner; +} + +#endif /* SECURE_MAN_UID */ + +/* + * function to gain user privs by either (a) dropping effective privs + * completely (saved ids) or (b) reversing euid w/ uid. + * Ignore if superuser. + */ +void drop_effective_privs (void) +{ +#ifdef SECURE_MAN_UID + if (uid != ruid) { + debug ("drop_effective_privs()\n"); +# ifdef POSIX_SAVED_IDS + if (SET_EUID (ruid)) +# else + if (SWAP_UIDS (euid, ruid)) +# endif + gripe_set_euid (); + + uid = ruid; + } + + priv_drop_count++; + debug ("++priv_drop_count = %d\n", priv_drop_count); +#endif /* SECURE_MAN_UID */ +} + +/* + * function to (re)gain setuid privs by (a) setting euid from suid or (b) + * (re)reversing uid w/ euid. Ignore if superuser. + */ +void regain_effective_privs (void) +{ +#ifdef SECURE_MAN_UID + if (priv_drop_count) { + priv_drop_count--; + debug ("--priv_drop_count = %d\n", priv_drop_count); + if (priv_drop_count) + return; + } + + if (uid != euid) { + debug ("regain_effective_privs()\n"); +# ifdef POSIX_SAVED_IDS + if (SET_EUID (euid)) +# else + if (SWAP_UIDS (ruid, euid)) +# endif + gripe_set_euid (); + + uid = euid; + } +#endif /* SECURE_MAN_UID */ +} + +/* + * If we want to execute a system command with no effective priveledges + * we have to either + * (a) Use saved id's (if available) to completely drop effective + * priveledges and re-engage them after the call. + * (b) fork() and then drop effective privs in the child. Do the + * system() command from the child and wait for it to die. + * (b) does not need saved ids as, once dropped, the effective privs are + * not required in the child again. (a) does not require a fork() as the + * system()'d processes will not have suid=MAN_OWNER and will be unable + * to gain any man derived priveledges. + */ +int do_system_drop_privs (pipeline *p) +{ +#ifdef SECURE_MAN_UID + +# ifdef POSIX_SAVED_IDS + if (uid == ruid) { + pipeline_start (p); + return pipeline_wait (p); + } else { + int status; + drop_effective_privs (); + pipeline_start (p); + status = pipeline_wait (p); + regain_effective_privs (); + return status; + } + +# else /* !POSIX_SAVED_IDS */ + + pid_t child; + int status; + + fflush (NULL); + child = fork (); + + if (child < 0) { + error (0, errno, _("can't fork")); + status = 0; + } else if (child == 0) { + pop_all_cleanups (); + if (SWAP_UIDS (ruid, ruid)) + gripe_set_euid (); + pipeline_start (p); + exit (pipeline_wait (p)); + } else { + pid_t res; + int save = errno; + do { /* cope with non-restarting system calls */ + res = waitpid (child, &status, 0); + } while (res == -1 && errno == EINTR); + if (res == -1) + status = -1; + else + errno = save; + } + + return status; +# endif /* all ways to do a sys command after dropping privs */ + +#else /* !SECURE_MAN_UID */ + pipeline_start (p); + return pipeline_wait (p); +#endif /* SECURE_MAN_UID */ +} |