From 4a9d024d163ecb817de31600402d3df08e2aed2b Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Tue, 9 Jun 2009 00:55:12 +0200 Subject: [PATCH] New module 'idpriv-drop'. --- ChangeLog | 8 ++++ lib/idpriv-drop.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/idpriv.h | 108 +++++++++++++++++++++++++++++++++++++++++++ m4/idpriv.m4 | 14 ++++++ modules/idpriv-drop | 27 +++++++++++ 5 files changed, 286 insertions(+) create mode 100644 lib/idpriv-drop.c create mode 100644 lib/idpriv.h create mode 100644 m4/idpriv.m4 create mode 100644 modules/idpriv-drop diff --git a/ChangeLog b/ChangeLog index 173db8fae..88cb906e8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2009-06-08 Bruno Haible + 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 + * modules/unistdio/u8-vasnprintf (Depends-on): Add memchr. * modules/unistdio/u8-u8-vasnprintf (Depends-on): Likewise. * modules/unistdio/u16-vasnprintf (Depends-on): Likewise. diff --git a/lib/idpriv-drop.c b/lib/idpriv-drop.c new file mode 100644 index 000000000..e1fa02f8b --- /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 . */ + +#include + +#include "idpriv.h" + +#include +#include +#include + +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 + + says about setreuid(): "The rule by which the saved uid id is modified + is complicated." Similarly, + 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//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'. + + 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; +} diff --git a/lib/idpriv.h b/lib/idpriv.h new file mode 100644 index 000000000..906627b59 --- /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 . */ + +#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 + */ + + +/* 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 */ diff --git a/m4/idpriv.m4 b/m4/idpriv.m4 new file mode 100644 index 000000000..2c53dbcbb --- /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 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]) +]) diff --git a/modules/idpriv-drop b/modules/idpriv-drop new file mode 100644 index 000000000..19472fadf --- /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 + -- 2.11.0