* 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.
+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>
--- /dev/null
+/* 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
--- /dev/null
+/* 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
-/* 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.
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)
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);
#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
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);
--- /dev/null
+# 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])
+])
--- /dev/null
+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
--- /dev/null
+Files:
+tests/test-priv-set.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-priv-set
+check_PROGRAMS += test-priv-set
+
+License:
+GPL
Description:
-Determine (and maybe change) whether we can unlink directories.
+Determine whether we can unlink directories.
Files:
lib/unlinkdir.h
Depends-on:
stdbool
+priv-set
configure.ac:
gl_UNLINKDIR
Depends-on:
stdbool
+priv-set
configure.ac:
gl_WRITE_ANY_FILE
--- /dev/null
+/* 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;
+}