changeset 11613:1d540dc13d74

New module 'idpriv-drop'.
author Bruno Haible <bruno@clisp.org>
date Tue, 09 Jun 2009 00:55:12 +0200
parents 991284aa5fe0
children b3e5d914be7d
files ChangeLog lib/idpriv-drop.c lib/idpriv.h m4/idpriv.m4 modules/idpriv-drop
diffstat 5 files changed, 286 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2009-06-08  Bruno Haible  <bruno@clisp.org>
+
+	New module 'idpriv-drop'.
+	* lib/idpriv.h: New file.
+	* lib-idpriv-drop.c: New file.
+	* m4/idpriv.m4: New file.
+	* modules/idpriv-drop: New file.
+
 2009-06-08  Bruno Haible  <bruno@clisp.org>
 
 	* modules/unistdio/u8-vasnprintf (Depends-on): Add memchr.
new file mode 100644
--- /dev/null
+++ b/lib/idpriv-drop.c
@@ -0,0 +1,129 @@
+/* Dropping uid/gid privileges of the current process permanently.
+   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/>.  */
+
+#include <config.h>
+
+#include "idpriv.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+int
+idpriv_drop (void)
+{
+#if HAVE_GETUID
+  int uid = getuid ();
+#endif
+#if HAVE_GETGID
+  int gid = getgid ();
+#endif
+
+  /* Drop the gid privilege first, because in some cases the gid privilege
+     cannot be dropped after the uid privilege has been dropped.  */
+
+  /* This is for executables that have the setgid bit set.  */
+#if HAVE_SETRESGID /* glibc, FreeBSD, OpenBSD, HP-UX */
+  /* This code is needed: In particular, on HP-UX 11.11, setregid (gid, gid)
+     may leave the saved gid as 0.  See also the comment below regarding
+     setresuid.  */
+  if (setresgid (gid, gid, gid) < 0)
+    return -1;
+#elif HAVE_SETREGID /* MacOS X, NetBSD, AIX, IRIX, Solaris, OSF/1, Cygwin */
+  if (setregid (gid, gid) < 0)
+    return -1;
+#elif HAVE_SETEGID /* Solaris 2.4 */
+  if (setegid (gid) < 0)
+    return -1;
+#endif
+
+  /* This is for executables that have the setuid bit set.  */
+#if HAVE_SETRESUID /* glibc, FreeBSD, OpenBSD, HP-UX */
+  /* On systems which have setresuid(), we use it instead of setreuid(),
+     because
+       <http://www.usenix.org/events/sec02/full_papers/chen/chen.pdf>
+     says about setreuid(): "The rule by which the saved uid id is modified
+     is complicated." Similarly, <http://unixpapa.com/incnote/setuid.html>
+     says about setreuid(): "What exactly happens to the saved UID when this
+     is used seems to vary a lot."  */
+  if (setresuid (uid, uid, uid) < 0)
+    return -1;
+#elif HAVE_SETREUID /* MacOS X, NetBSD, AIX, IRIX, Solaris, OSF/1, Cygwin */
+  if (setreuid (uid, uid) < 0)
+    return -1;
+#elif HAVE_SETEUID /* Solaris 2.4 */
+  if (seteuid (uid) < 0)
+    return -1;
+#endif
+
+  /* Verify that the privileges have really been dropped.
+     This verification is here for security reasons.  Doesn't matter if it
+     takes a couple of system calls.
+     On Solaris (which has saved uids and gids but no getresuid, getresgid
+     functions), we could read /proc/<pid>/cred and verify the saved uid and
+     gid found there. But it's not clear to me when to interpret the file as a
+     'prcred_t' and when as a 'prcred32_t'.
+     <http://www.usenix.org/events/sec02/full_papers/chen/chen.pdf>
+     section 8.1.3 also recommends to use a setreuid call as a probe, but
+     this call would unexpectedly succeed (and the verification thus fail)
+     on Linux if the process has the CAP_SETUID capability.
+     When the verification fails, it indicates that we need to use different
+     API in the code above. Therefore 'abort ()', not 'return -1'.  */
+#if HAVE_GETRESUID /* glibc, FreeBSD, OpenBSD, HP-UX */
+  {
+    uid_t real;
+    uid_t effective;
+    uid_t saved;
+    if (getresuid (&real, &effective, &saved) < 0
+	|| real != uid
+	|| effective != uid
+	|| saved != uid)
+      abort ();
+  }
+#else
+# if HAVE_GETEUID
+  if (geteuid () != uid)
+    abort ();
+# endif
+# if HAVE_GETUID
+  if (getuid () != uid)
+    abort ();
+# endif
+#endif
+#if HAVE_GETRESGID /* glibc, FreeBSD, OpenBSD, HP-UX */
+  {
+    gid_t real;
+    gid_t effective;
+    gid_t saved;
+    if (getresgid (&real, &effective, &saved) < 0
+	|| real != gid
+	|| effective != gid
+	|| saved != gid)
+      abort ();
+  }
+#else
+# if HAVE_GETEGID
+  if (getegid () != gid)
+    abort ();
+# endif
+# if HAVE_GETGID
+  if (getgid () != gid)
+    abort ();
+# endif
+#endif
+
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/lib/idpriv.h
@@ -0,0 +1,108 @@
+/* Dropping uid/gid privileges of the current process.
+   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/>.  */
+
+#ifndef _IDPRIV_H
+#define _IDPRIV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This module allows programs which are installed with setuid or setgid bit
+   (and which therefore initially run with an effective user id or group id
+   different from the one of the current user) to drop their uid or gid
+   privilege, either permanently or temporarily.
+
+   It is absolutely necessary to minimize the amount of code that is running
+   with escalated privileges (e.g. with effective uid = root). The reason is
+   that any bug or exploit in a part of a program that is running with
+   escalated privileges is a security vulnerability that - upon discovery -
+   puts the users in danger and requires immediate fixing. Then consider that
+   there's a bug every 10 or 20 lines of code on average...
+
+   For programs that temporarily drop privileges but have the ability to
+   restore them later, there are additionally the dangers that
+     - Any bug in the non-privileged part of the program may be used to
+       create invalid data structures that will trigger security
+       vulnerabilities in the privileged part of the program.
+     - Code execution exploits in the non-privileged part of the program may
+       be used to invoke the function that restores high privileges and then
+       execute additional arbitrary code.
+
+   1) The usual, and reasonably safe, way to minimize the amount of code
+      running with privileges is to create a separate executable, with setuid
+      or setgid bit, that contains only code for the tasks that require
+      privileges (and,of course, strict checking of the arguments, so that the
+      program cannot be abused). The main program is installed without setuid
+      or setgid bit.
+
+   2) A less safe way is to do some privileged tasks at the beginning of the
+      program's run, and drop privileges permanently as soon as possible.
+
+      Note: There may still be security issues if the privileged task puts
+      sensitive data into the process memory or opens communication channels
+      to restricted facilities.
+
+   3) The most unsafe way is to drop privileges temporarily for most of the
+      main program but to re-enable them for the duration of privileged tasks.
+
+      As explained above, this approach has uncontrollable dangers for
+      security.
+
+      This approach is normally not usable in multithreaded programs, because
+      you cannot know what kind of system calls the other threads could be
+      doing during the time the privileges are enabled.
+
+   With approach 1, you don't need gnulib modules.
+   With approach 2, you need the gnulib module 'idpriv-drop'.
+   With approach 3, you need the gnulib module 'idpriv-droptemp'. But really,
+   you should better stay away from this approach.
+ */
+
+/* For more in-depth discussion of these topics, see the paper
+   Hao Chen, David Wagner, Drew Dean: Setuid Demystified
+   <http://www.usenix.org/events/sec02/full_papers/chen/chen.pdf>  */
+
+
+/* For approach 2.  */
+
+/* Drop the uid and gid privileges of the current process.
+   Return 0 if successful, or -1 with errno set upon failure. The recommended
+   handling of failure is to terminate the process.  */
+extern int idpriv_drop (void);
+
+
+/* For approach 3.  */
+
+/* Drop the uid and gid privileges of the current process in a way that allows
+   them to be restored later.
+   Return 0 if successful, or -1 with errno set upon failure. The recommended
+   handling of failure is to terminate the process.  */
+extern int idpriv_temp_drop (void);
+
+/* Restore the uid and gid privileges of the current process.
+   Return 0 if successful, or -1 with errno set upon failure. The recommended
+   handling of failure is to not perform the actions that require the escalated
+   privileges.  */
+extern int idpriv_temp_restore (void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _IDPRIV_H */
new file mode 100644
--- /dev/null
+++ b/m4/idpriv.m4
@@ -0,0 +1,14 @@
+# idpriv.m4 serial 1
+dnl Copyright (C) 2009 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_IDPRIV],
+[
+  dnl Persuade glibc <unistd.h> to declare {get,set}res{uid,gid}.
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS_ONCE([getuid geteuid getresuid getgid getegid getresgid])
+  AC_CHECK_FUNCS_ONCE([setresuid setreuid seteuid setresgid setregid setegid])
+])
new file mode 100644
--- /dev/null
+++ b/modules/idpriv-drop
@@ -0,0 +1,27 @@
+Description:
+Drop uid/gid privileges of the current process.
+
+Files:
+lib/idpriv.h
+lib/idpriv-drop.c
+m4/idpriv.m4
+
+Depends-on:
+unistd
+extensions
+
+configure.ac:
+gl_IDPRIV
+
+Makefile.am:
+lib_SOURCES += idpriv-drop.c
+
+Include:
+"idpriv.h"
+
+License:
+GPL
+
+Maintainer:
+Bruno Haible
+