[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: dropping setuid/setgid privileges
From: |
Bruno Haible |
Subject: |
Re: dropping setuid/setgid privileges |
Date: |
Sun, 7 Jun 2009 23:35:00 +0200 |
User-agent: |
KMail/1.9.9 |
James Youngman wrote:
> The locate program in findutils drops privileges, that fairly similar
> functionality might make a good gnulib module. Certainly it's the
> kind of thing one would hope to wirte and debug once, and reuse.
Sam Steingold asked for it as well recently [1], and Ben provided a
reference to a paper [2] showing that due to different semantics of
setreuid etc. across platforms, writing portable code in this area is
non-trivial.
Here is a proposed module for dropping privileges permanently.
A module for dropping them temporarily will come later.
[1] http://lists.gnu.org/archive/html/bug-gnulib/2008-11/msg00114.html
[2] http://www.usenix.org/events/sec02/full_papers/chen/chen.pdf
================================ lib/idpriv.h ================================
/* 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
#include <sys/types.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 is 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.
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.
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 */
============================== lib/idpriv-drop.c ==============================
/* 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 <unistd.h>
int
idpriv_drop (void)
{
/* Drop the gid privilege first, because in some cases dropping the gid
privilege cannot be dropped after the uid privilege has been dropped. */
#if HAVE_GETUID
int uid = getuid ();
#endif
#if HAVE_GETGID
int gid = getgid ();
#endif
/* 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 uid 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.
<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. */
#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 (getuid () != uid)
abort ();
#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 (getgid () != gid)
abort ();
#endif
return 0;
}
============================= modules/idpriv-drop =============================
Description:
Drop uid/gid privileges of the current process.
Files:
lib/idpriv.h
lib/idpriv-drop.c
Depends-on:
unistd
extensions
configure.ac:
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])
Makefile.am:
lib_SOURCES += idpriv-drop.c
Include:
"idpriv.h"
License:
GPL
Maintainer:
Bruno Haible
===============================================================================
- Re: [PATCH] chroot specify user/group feature, James Youngman, 2009/06/04
- Re: dropping setuid/setgid privileges,
Bruno Haible <=
- Re: dropping setuid/setgid privileges, James Youngman, 2009/06/07
- Re: dropping setuid/setgid privileges, Bruno Haible, 2009/06/07
- Re: dropping setuid/setgid privileges, Bruno Haible, 2009/06/08
- Re: dropping setuid/setgid privileges, Sergey Poznyakoff, 2009/06/10
- Re: dropping setuid/setgid privileges, Bruno Haible, 2009/06/11
- Re: dropping setuid/setgid privileges, Sergey Poznyakoff, 2009/06/11
- Re: dropping setuid/setgid privileges, Bruno Haible, 2009/06/11
- Re: dropping setuid/setgid privileges, James Youngman, 2009/06/11
- Re: dropping setuid/setgid privileges, Bruno Haible, 2009/06/11
- Re: dropping setuid/setgid privileges, James Youngman, 2009/06/12