/* @(#)setuid.c 1.23 15/09/15 Copyright 1998,1999,2004 Heiko Eissfeldt, Copyright 2004-2011 J. Schilling */ #include "config.h" #ifndef lint static UConst char sccsid[] = "@(#)setuid.c 1.23 15/09/15 Copyright 1998,1999,2004 Heiko Eissfeldt, Copyright 2004-2011 J. Schilling"; #endif /* * Security functions * * If these functions fail, it is because there was an installation error * or a programming error, and we can't be sure about what privileges * we do or do not have. This means we might not be able to recover * the privileges we need to fix anything that may be broken (e.g. the * CDDA state of some interface types), and we may in fact do something * quite dangerous (like write to the WAV file as root). * * In any case, it is unsafe to do anything but exit *now*. Ideally we'd * kill -9 our process group too, just to be sure. Root privileges are not * something you want floating around at random in user-level applications. * * If any signal handlers or child processes are introduced into this * program, it will be necessary to call dontneedroot() or neverneedroot() * on entry, respectively; otherwise, it will be possible to trick * the program into executing the signal handler or child process with * root privileges by sending signals at the right time. */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * A copy of the CDDL is also available via the Internet at * http://www.opensource.org/licenses/cddl1.txt * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include "config.h" #include #include #include #include #include #include "exitcodes.h" #include "setuid.h" /*#undef DEBUG*/ /*#define DEBUG*/ /* True at return from initsecurity */ static uid_t real_uid = (uid_t) (-1); static uid_t effective_uid = (uid_t) (-1); static gid_t real_gid = (gid_t) (-1); static gid_t effective_gid = (gid_t) (-1); /* * Run this at the beginning of the program to initialize this code and * to drop privileges before someone uses them to shoot us in the foot. * Do not pass(go), do not dollars += 200. */ void initsecurity() { int leffective_uid; #ifdef HAVE_ALARM alarm(0); /* can be inherited from parent process */ #endif real_uid = getuid(); leffective_uid = geteuid(); if ((int) real_uid != leffective_uid && leffective_uid != 0) { /* sanity check */ errmsgno(EX_BAD, _("Warning: setuid but not to root (uid=%ld, euid=%d)\n"), (long) real_uid, leffective_uid); fprintf(stderr, _("Dropping setuid privileges now.\n")); neverneedroot(); } else { effective_uid = leffective_uid; } real_gid = getgid(); effective_gid = getegid(); dontneedroot(); dontneedgroup(); } /* Temporarily gain root privileges. */ #if defined _POSIX_SAVED_IDS && defined(HAVE_SETEUID) && defined SCO /* SCO seems to lack the prototypes... */ int seteuid __PR((uid_t euid)); int setegid __PR((gid_t guid)); #endif void needroot(necessary) int necessary; { #ifdef DEBUG fprintf(stderr, "call to needroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n", effective_uid, real_uid, geteuid(), getuid(), getpid()); #endif if (effective_uid) { if (necessary) { errmsgno(EX_BAD, _("Fatal error: require root privilege but not setuid root.\n")); exit(PERM_ERROR); } else return; } if (real_uid == (uid_t) (-1)) { errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n")); exit(INTERNAL_ERROR); } if (geteuid() == 0) return; /* nothing to do */ #if defined _POSIX_SAVED_IDS && defined(HAVE_SETEUID) if (seteuid(effective_uid)) { errmsg(_("Error with seteuid in needroot().\n")); exit(PERM_ERROR); } #else #if defined(HAVE_SETREUID) if (setreuid(real_uid, effective_uid)) { errmsg(_("Error with setreuid in needroot().\n")); exit(PERM_ERROR); } #endif #endif if (geteuid() != 0 && necessary) { errmsgno(EX_BAD, _("Fatal error: did not get root privilege.\n")); exit(PERM_ERROR); } #ifdef DEBUG fprintf(stderr, "exit of needroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n", effective_uid, real_uid, geteuid(), getuid(), getpid()); #endif } /* * Temporarily drop root privileges. */ void dontneedroot() { #ifdef DEBUG fprintf(stderr, "call to dontneedroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n", effective_uid, real_uid, geteuid(), getuid(), getpid()); #endif if (real_uid == (uid_t) (-1)) { errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n")); exit(INTERNAL_ERROR); } if (effective_uid) return; if (geteuid() != 0) return; /* nothing to do */ #if defined _POSIX_SAVED_IDS && defined(HAVE_SETEUID) if (seteuid(real_uid)) { errmsg(_("Error with seteuid in dontneedroot().\n")); exit(PERM_ERROR); } #else #if defined(HAVE_SETREUID) if (setreuid(effective_uid, real_uid)) { errmsg(_("Error with setreuid in dontneedroot().\n")); exit(PERM_ERROR); } #endif #endif if (geteuid() != real_uid) { errmsgno(EX_BAD, _("Fatal error: did not drop root privilege.\n")); #ifdef DEBUG fprintf(stderr, "in to dontneedroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n", effective_uid, real_uid, geteuid(), getuid(), getpid()); #endif exit(PERM_ERROR); } } /* * Permanently drop root privileges. */ void neverneedroot() { #ifdef DEBUG fprintf(stderr, "call to neverneedroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n", effective_uid, real_uid, geteuid(), getuid(), getpid()); #endif if (real_uid == (uid_t) (-1)) { errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n")); exit(INTERNAL_ERROR); } if (geteuid() != effective_uid) { needroot(1); } if (geteuid() == effective_uid) { #if defined(HAVE_SETUID) if (setuid(real_uid)) { errmsg(_("Error with setuid in neverneedroot().\n")); exit(PERM_ERROR); } #endif } #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ defined(__DragonFly__) /* XXX this is a big hack and and not a permanent solution */ else { #if defined(HAVE_SETUID) if (setuid(real_uid)) { errmsg(_("Error with setuid in neverneedroot().\n")); exit(PERM_ERROR); } #endif } #endif if (geteuid() != real_uid || getuid() != real_uid) { errmsgno(EX_BAD, _("Fatal error: did not drop root privilege.\n")); #ifdef DEBUG fprintf(stderr, "in to neverneedroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n", effective_uid, real_uid, geteuid(), getuid(), getpid()); #endif exit(PERM_ERROR); } effective_uid = real_uid; #ifdef DEBUG fprintf(stderr, "exit of neverneedroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n", effective_uid, real_uid, geteuid(), getuid(), getpid()); #endif } /* Temporarily gain group privileges. */ void needgroup(necessary) int necessary; { #ifdef DEBUG fprintf(stderr, "call to needgroup (egid=%d, gid=%d), current=%d/%d, pid=%d\n", effective_gid, real_gid, getegid(), getgid(), getpid()); #endif if (real_gid == (gid_t) (-1)) { errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n")); exit(INTERNAL_ERROR); } if (getegid() == effective_gid) return; /* nothing to do */ #if defined _POSIX_SAVED_IDS && defined(HAVE_SETEGID) if (setegid(effective_gid)) { errmsg(_("Error with setegid in needgroup().\n")); exit(PERM_ERROR); } #else #if defined(HAVE_SETREGID) if (setregid(real_gid, effective_gid)) { errmsg(_("Error with setregid in needgroup().\n")); exit(PERM_ERROR); } #endif #endif if (necessary && getegid() != effective_gid) { errmsgno(EX_BAD, _("Fatal error: did not get group privilege.\n")); exit(PERM_ERROR); } } /* * Temporarily drop group privileges. */ void dontneedgroup() { #ifdef DEBUG fprintf(stderr, "call to dontneedgroup (egid=%d, gid=%d), current=%d/%d, pid=%d\n", effective_gid, real_gid, getegid(), getgid(), getpid()); #endif if (real_gid == (gid_t) (-1)) { errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n")); exit(INTERNAL_ERROR); } if (getegid() != effective_gid) return; /* nothing to do */ #if defined _POSIX_SAVED_IDS && defined(HAVE_SETEGID) if (setegid(real_gid)) { errmsg(_("Error with setegid in dontneedgroup().\n")); exit(PERM_ERROR); } #else #if defined(HAVE_SETREGID) if (setregid(effective_gid, real_gid)) { errmsg(_("Error with setregid in dontneedgroup().\n")); exit(PERM_ERROR); } #endif #endif if (getegid() != real_gid) { errmsgno(EX_BAD, _("Fatal error: did not drop group privilege.\n")); exit(PERM_ERROR); } #ifdef DEBUG fprintf(stderr, "exit if dontneedgroup (egid=%d, gid=%d), current=%d/%d, pid=%d\n", effective_gid, real_gid, getegid(), getgid(), getpid()); #endif } /* * Permanently drop group privileges. */ void neverneedgroup() { #ifdef DEBUG fprintf(stderr, "call to neverneedgroup (egid=%d, gid=%d), current=%d/%d, pid=%d\n", effective_gid, real_gid, getegid(), getgid(), getpid()); #endif if (real_gid == (gid_t) (-1)) { errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n")); exit(INTERNAL_ERROR); } if (getegid() != effective_gid) { needgroup(1); } if (getegid() == effective_gid) { #if defined(HAVE_SETGID) if (setgid(real_gid)) { errmsg(_("Error with setgid in neverneedgroup().\n")); exit(PERM_ERROR); } #endif } #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ defined(__DragonFly__) /* XXX this is a big hack and and not a permanent solution */ else { #if defined(HAVE_SETGID) if (setgid(real_gid)) { errmsg(_("Error with setgid in neverneedgroup().\n")); exit(PERM_ERROR); } #endif } #endif if (getegid() != real_gid || getgid() != real_gid) { errmsgno(EX_BAD, _("Fatal error: did not drop group privilege.\n")); #ifdef DEBUG fprintf(stderr, "in to neverneedgroup (_egid_=%d, gid=%d), current=%d/%d, pid=%d\n", effective_gid, real_gid, getegid(), getgid(), getpid()); #endif exit(PERM_ERROR); } effective_gid = real_gid; } #if defined(HPUX) int seteuid(uid) uid_t uid; { return (setresuid(-1, uid, -1)); } int setreuid(uid1, uid2) uid_t uid1; uid_t uid2; { return (setresuid(uid2, uid2, uid1 == uid2 ? uid2 : 0)); } int setregid(gid1, gid2) gid_t gid1; gid_t gid2; { return (setresgid(gid2, gid2, gid1 == gid2 ? gid2 : 0)); } #endif