From 242a53171ea50e8353d4d270c402941ad1ec5df8 Mon Sep 17 00:00:00 2001 From: David Bartley Date: Wed, 29 Apr 2009 03:52:26 -0400 Subject: [PATCH] New module 'priv-set' and accompanying tests. --- ChangeLog | 16 +++++ lib/priv-set.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/priv-set.h | 50 +++++++++++++++++ lib/unlinkdir.c | 31 ++-------- lib/write-any-file.c | 15 +---- m4/priv-set.m4 | 15 +++++ modules/priv-set | 26 +++++++++ modules/priv-set-tests | 13 ++++ modules/unlinkdir | 3 +- modules/write-any-file | 1 + tests/test-priv-set.c | 98 +++++++++++++++++++++++++++++++++ 11 files changed, 374 insertions(+), 37 deletions(-) create mode 100644 lib/priv-set.c create mode 100644 lib/priv-set.h create mode 100644 m4/priv-set.m4 create mode 100644 modules/priv-set create mode 100644 modules/priv-set-tests create mode 100644 tests/test-priv-set.c diff --git a/ChangeLog b/ChangeLog index 6389ebe..2e9433a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2009-04-27 David Bartley + + New module 'priv-set'. + * 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 modules 'priv-set'. + * modules/priv-set-tests: New file. + * tests/test-priv-set.c: New file. + 2009-04-27 Bruno Haible New module 'libunistring'. diff --git a/lib/priv-set.c b/lib/priv-set.c new file mode 100644 index 0000000..0051ce6 --- /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 . + + Written by David Bartley. */ + +#include +#include "priv-set.h" + +#if HAVE_GETPPRIV + +# include +# include +# include + +/* 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 diff --git a/lib/priv-set.h b/lib/priv-set.h new file mode 100644 index 0000000..c461c42 --- /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 . + + Written by David Bartley. */ + +#if HAVE_GETPPRIV + +# include + +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 diff --git a/lib/unlinkdir.c b/lib/unlinkdir.c index 9c3ff00..a9713c7 100644 --- 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 . */ -/* Written by Paul Eggert and Jim Meyering. */ +/* Written by Paul Eggert, Jim Meyering, and David Bartley. */ #include #include "unlinkdir.h" - -#if HAVE_PRIV_H -# include -#endif +#include "priv-set.h" #include #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 @@ cannot_unlink_dir (void) 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); diff --git a/lib/write-any-file.c b/lib/write-any-file.c index 8492ac8..26db765 100644 --- a/lib/write-any-file.c +++ b/lib/write-any-file.c @@ -20,10 +20,8 @@ #include #include "write-any-file.h" +#include "priv-set.h" -#if HAVE_PRIV_H -# include -#endif #include /* Return true if we know that we can write any file, including @@ -38,15 +36,8 @@ can_write_any_file (void) 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); diff --git a/m4/priv-set.m4 b/m4/priv-set.m4 new file mode 100644 index 0000000..057c5ac --- /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]) +]) diff --git a/modules/priv-set b/modules/priv-set new file mode 100644 index 0000000..77e0164 --- /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 diff --git a/modules/priv-set-tests b/modules/priv-set-tests new file mode 100644 index 0000000..cf87bc6 --- /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 diff --git a/modules/unlinkdir b/modules/unlinkdir index e8c68a6..f3b7fd7 100644 --- 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 @@ m4/unlinkdir.m4 Depends-on: stdbool +priv-set configure.ac: gl_UNLINKDIR diff --git a/modules/write-any-file b/modules/write-any-file index 3136510..07eac74 100644 --- a/modules/write-any-file +++ b/modules/write-any-file @@ -8,6 +8,7 @@ m4/write-any-file.m4 Depends-on: stdbool +priv-set configure.ac: gl_WRITE_ANY_FILE diff --git a/tests/test-priv-set.c b/tests/test-priv-set.c new file mode 100644 index 0000000..90c96bc --- /dev/null +++ b/tests/test-priv-set.c @@ -0,0 +1,98 @@ +/* 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 . */ + +/* Written by David Bartley , 2007. */ + +#include +#include "priv-set.h" + +#if HAVE_GETPPRIV +# include +#endif +#include +#include +#include +#include +#include + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +int main () +{ +#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; +} -- 1.5.6.5