summaryrefslogtreecommitdiffstats
path: root/gl/lib/idpriv-drop.c
diff options
context:
space:
mode:
Diffstat (limited to 'gl/lib/idpriv-drop.c')
-rw-r--r--gl/lib/idpriv-drop.c131
1 files changed, 131 insertions, 0 deletions
diff --git a/gl/lib/idpriv-drop.c b/gl/lib/idpriv-drop.c
new file mode 100644
index 0000000..b059944
--- /dev/null
+++ b/gl/lib/idpriv-drop.c
@@ -0,0 +1,131 @@
+/* Dropping uid/gid privileges of the current process permanently.
+ Copyright (C) 2009-2023 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 <https://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 /* Mac OS 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
+ Hao Chen, David Wagner, Drew Dean: Setuid Demystified
+ <https://www.usenix.org/legacy/publications/library/proceedings/sec02/full_papers/chen/chen.pdf>
+ says about setreuid(): "The rule by which the saved uid id is modified
+ is complicated." Similarly, <https://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 /* Mac OS 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'.
+ Hao Chen, David Wagner, Drew Dean: Setuid Demystified
+ <https://www.usenix.org/legacy/publications/library/proceedings/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;
+}