Mercurial > hg > octave-lyh > gnulib-hg
changeset 11538:af683213eeb0
priv-set: new module and accompanying tests; adapt write-any-file
* lib/priv-set.c: New file.
* lib/priv-set.h: New file.
* lib/unlinkdir.c: Make cannot_unlink_dir thread-safe.
* lib/write-any-file.c: Simplify by using priv-set module.
* m4/priv-set.m4: New file.
* modules/priv-set: New file.
* modules/unlinkdir: Add dependency on priv-set module.
* modules/write-any-file: Likewise.
These changes address a problem that originally arose with GNU tar:
http://thread.gmane.org/gmane.comp.gnu.tar.bugs/3242
Tests for module 'priv-set'.
* modules/priv-set-tests: New file.
author | David Bartley <dtbartle@csclub.uwaterloo.ca> |
---|---|
date | Wed, 29 Apr 2009 03:52:26 -0400 |
parents | 0ec84326efef |
children | 6194ff98b9d0 |
files | ChangeLog lib/priv-set.c lib/priv-set.h lib/unlinkdir.c lib/write-any-file.c m4/priv-set.m4 modules/priv-set modules/priv-set-tests modules/unlinkdir modules/write-any-file tests/test-priv-set.c |
diffstat | 11 files changed, 375 insertions(+), 37 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2009-04-27 David Bartley <dtbartle@csclub.uwaterloo.ca> + + priv-set: new module and tests; adapt write-any-file + * lib/priv-set.c: New file. + * lib/priv-set.h: New file. + * lib/unlinkdir.c: Make cannot_unlink_dir thread-safe. + * lib/write-any-file.c: Simplify by using priv-set module. + * m4/priv-set.m4: New file. + * modules/priv-set: New file. + * modules/unlinkdir: Add dependency on priv-set module. + * modules/write-any-file: Likewise. + + Tests for module 'priv-set'. + * modules/priv-set-tests: New file. + * tests/test-priv-set.c: New file. + 2009-05-03 Jim Meyering <meyering@redhat.com> Bruno Haible <bruno@clisp.org>
new file mode 100644 --- /dev/null +++ b/lib/priv-set.c @@ -0,0 +1,143 @@ +/* Query, remove, or restore a Solaris privilege. + + Copyright (C) 2009 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + + Written by David Bartley. */ + +#include <config.h> +#include "priv-set.h" + +#if HAVE_GETPPRIV + +# include <errno.h> +# include <stdbool.h> +# include <priv.h> + +/* Holds a (cached) copy of the effective set. */ +static priv_set_t *eff_set; + +/* Holds a set of privileges that we have removed. */ +static priv_set_t *rem_set; + +static bool initialized; + +static int +priv_set_initialize (void) +{ + if (! initialized) + { + eff_set = priv_allocset (); + if (!eff_set) + { + return -1; + } + rem_set = priv_allocset (); + if (!rem_set) + { + priv_freeset (eff_set); + return -1; + } + if (getppriv (PRIV_EFFECTIVE, eff_set) != 0) + { + priv_freeset (eff_set); + priv_freeset (rem_set); + return -1; + } + priv_emptyset (rem_set); + initialized = true; + } + + return 0; +} + + +/* Check if priv is in the effective set. + Returns 1 if priv is a member and 0 if not. + Returns -1 on error with errno set appropriately. */ +int +priv_set_ismember (const char *priv) +{ + if (! initialized && priv_set_initialize () != 0) + return -1; + + return priv_ismember (eff_set, priv); +} + + +/* Try to remove priv from the effective set. + Returns 0 if priv was removed from or was not present in the effective set. + Returns -1 on error with errno set appropriately. */ +int +priv_set_remove (const char *priv) +{ + if (! initialized && priv_set_initialize () != 0) + return -1; + + if (priv_ismember (eff_set, priv)) + { + /* priv_addset/priv_delset can only fail if priv is invalid, which is + checked above by the priv_ismember call. */ + priv_delset (eff_set, priv); + if (setppriv (PRIV_SET, PRIV_EFFECTIVE, eff_set) != 0) + { + priv_addset (eff_set, priv); + return -1; + } + priv_addset (rem_set, priv); + } + else + { + errno = EINVAL; + return -1; + } + + return 0; +} + + +/* Try to restore priv to the effective set. + Returns 0 if priv was re-added to the effective set (after being prviously + removed by a call to priv_set_remove) or if priv was already in the + effective set. + Returns -1 on error with errno set appropriately. */ +int +priv_set_restore (const char *priv) +{ + if (! initialized && priv_set_initialize () != 0) + return -1; + + if (priv_ismember (rem_set, priv)) + { + /* priv_addset/priv_delset can only fail if priv is invalid, which is + checked above by the priv_ismember call. */ + priv_addset (eff_set, priv); + if (setppriv (PRIV_SET, PRIV_EFFECTIVE, eff_set) != 0) + { + priv_delset (eff_set, priv); + return -1; + } + priv_delset (rem_set, priv); + } + else + { + errno = EINVAL; + return -1; + } + + return 0; +} + +#endif
new file mode 100644 --- /dev/null +++ b/lib/priv-set.h @@ -0,0 +1,50 @@ +/* Query, remove, or restore a Solaris privilege. + + Copyright (C) 2009 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + + Written by David Bartley. */ + +#if HAVE_GETPPRIV + +# include <priv.h> + +int priv_set_ismember (const char *priv); +int priv_set_remove (const char *priv); +int priv_set_restore (const char *priv); + +static inline int priv_set_remove_linkdir (void) +{ + return priv_set_remove (PRIV_SYS_LINKDIR); +} + +static inline int priv_set_restore_linkdir (void) +{ + return priv_set_restore (PRIV_SYS_LINKDIR); +} + +#else + +static inline int priv_set_remove_linkdir (void) +{ + return -1; +} + +static inline int priv_set_restore_linkdir (void) +{ + return -1; +} + +#endif
--- a/lib/unlinkdir.c +++ b/lib/unlinkdir.c @@ -1,4 +1,4 @@ -/* unlinkdir.c - determine (and maybe change) whether we can unlink directories +/* unlinkdir.c - determine whether we can unlink directories Copyright (C) 2005-2006, 2009 Free Software Foundation, Inc. @@ -15,26 +15,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* Written by Paul Eggert and Jim Meyering. */ +/* Written by Paul Eggert, Jim Meyering, and David Bartley. */ #include <config.h> #include "unlinkdir.h" - -#if HAVE_PRIV_H -# include <priv.h> -#endif +#include "priv-set.h" #include <unistd.h> #if ! UNLINK_CANNOT_UNLINK_DIR /* Return true if we cannot unlink directories, false if we might be - able to unlink directories. If possible, tell the kernel we don't - want to be able to unlink directories, so that we can return true. - - Note: this function may modify the process privilege set, to remove - the PRIV_SYS_LINKDIR privilege, so is neither thread-safe, nor - appropriate for use in a library. */ + able to unlink directories. */ bool cannot_unlink_dir (void) @@ -44,20 +36,11 @@ if (! initialized) { -# if defined PRIV_EFFECTIVE && defined PRIV_SYS_LINKDIR +# if defined PRIV_SYS_LINKDIR /* We might be able to unlink directories if we cannot determine our privileges, or if we have the - PRIV_SYS_LINKDIR privilege and cannot delete it. */ - priv_set_t *pset = priv_allocset (); - if (pset) - { - cannot = - (getppriv (PRIV_EFFECTIVE, pset) == 0 - && (! priv_ismember (pset, PRIV_SYS_LINKDIR) - || (priv_delset (pset, PRIV_SYS_LINKDIR) == 0 - && setppriv (PRIV_SET, PRIV_EFFECTIVE, pset) == 0))); - priv_freeset (pset); - } + PRIV_SYS_LINKDIR privilege. */ + cannot = (priv_set_ismember (PRIV_SYS_LINKDIR) == 0); # else /* In traditional Unix, only root can unlink directories. */ cannot = (geteuid () != 0);
--- a/lib/write-any-file.c +++ b/lib/write-any-file.c @@ -20,10 +20,8 @@ #include <config.h> #include "write-any-file.h" +#include "priv-set.h" -#if HAVE_PRIV_H -# include <priv.h> -#endif #include <unistd.h> /* Return true if we know that we can write any file, including @@ -38,15 +36,8 @@ if (! initialized) { bool can = false; -#if defined PRIV_EFFECTIVE && defined PRIV_FILE_DAC_WRITE - priv_set_t *pset = priv_allocset (); - if (pset) - { - can = - (getppriv (PRIV_EFFECTIVE, pset) == 0 - && priv_ismember (pset, PRIV_FILE_DAC_WRITE)); - priv_freeset (pset); - } +#if defined PRIV_FILE_DAC_WRITE + can = (priv_set_ismember (PRIV_FILE_DAC_WRITE) == 1); #else /* In traditional Unix, only root can unlink directories. */ can = (geteuid () == 0);
new file mode 100644 --- /dev/null +++ b/m4/priv-set.m4 @@ -0,0 +1,15 @@ +# serial 6 + +# Copyright (C) 2009 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Written by David Bartley. + +AC_DEFUN([gl_PRIV_SET], +[ + AC_REQUIRE([AC_C_INLINE]) + AC_CHECK_FUNCS([getppriv]) +])
new file mode 100644 --- /dev/null +++ b/modules/priv-set @@ -0,0 +1,26 @@ +Description: +Query, remove or restore a Solaris privilege + +Files: +lib/priv-set.h +lib/priv-set.c +m4/priv-set.m4 + +Depends-on: +errno +stdbool + +configure.ac: +gl_PRIV_SET + +Makefile.am: +lib_SOURCES += priv-set.c + +Include: +"priv-set.h" + +License: +GPL + +Maintainer: +David Bartley
new file mode 100644 --- /dev/null +++ b/modules/priv-set-tests @@ -0,0 +1,13 @@ +Files: +tests/test-priv-set.c + +Depends-on: + +configure.ac: + +Makefile.am: +TESTS += test-priv-set +check_PROGRAMS += test-priv-set + +License: +GPL
--- a/modules/unlinkdir +++ b/modules/unlinkdir @@ -1,5 +1,5 @@ Description: -Determine (and maybe change) whether we can unlink directories. +Determine whether we can unlink directories. Files: lib/unlinkdir.h @@ -8,6 +8,7 @@ Depends-on: stdbool +priv-set configure.ac: gl_UNLINKDIR
--- a/modules/write-any-file +++ b/modules/write-any-file @@ -8,6 +8,7 @@ Depends-on: stdbool +priv-set configure.ac: gl_WRITE_ANY_FILE
new file mode 100644 --- /dev/null +++ b/tests/test-priv-set.c @@ -0,0 +1,99 @@ +/* Test the priv-set module. + Copyright (C) 2009 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by David Bartley <dtbartle@csclub.uwaterloo.ca>, 2007. */ + +#include <config.h> +#include "priv-set.h" + +#if HAVE_GETPPRIV +# include <priv.h> +#endif +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +int +main (void) +{ +#if HAVE_GETPPRIV + priv_set_t *set; + + ASSERT (set = priv_allocset ()); + ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); + ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 1); + + /* Do a series of removes and restores making sure that the results are + consistent with our ismember function and solaris' priv_ismember. */ + ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 1); + ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); + ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 1); + ASSERT (priv_set_restore (PRIV_PROC_EXEC) == -1); + ASSERT (errno == EINVAL); + ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 1); + ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); + ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 1); + ASSERT (priv_set_remove (PRIV_PROC_EXEC) == 0); + ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 0); + ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); + ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 0); + ASSERT (priv_set_remove (PRIV_PROC_EXEC) == -1); + ASSERT (errno == EINVAL); + ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 0); + ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); + ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 0); + ASSERT (priv_set_restore (PRIV_PROC_EXEC) == 0); + ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 1); + ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); + ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 1); + ASSERT (priv_set_restore (PRIV_PROC_EXEC) == -1); + ASSERT (errno == EINVAL); + ASSERT (priv_set_ismember (PRIV_PROC_EXEC) == 1); + ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); + ASSERT (priv_ismember (set, PRIV_PROC_EXEC) == 1); + + /* Test the priv_set_linkdir wrappers. */ + ASSERT (getppriv (PRIV_EFFECTIVE, set) == 0); + if (priv_ismember (set, PRIV_SYS_LINKDIR)) + { + ASSERT (priv_set_restore_linkdir () == -1); + ASSERT (errno == EINVAL); + ASSERT (priv_set_remove_linkdir () == 0); + ASSERT (priv_set_remove_linkdir () == -1); + ASSERT (errno == EINVAL); + ASSERT (priv_set_restore_linkdir () == 0); + } +#else + ASSERT (priv_set_restore_linkdir () == -1); + ASSERT (priv_set_remove_linkdir () == -1); +#endif + + return 0; +}