OpenDNSSEC-enforcer  1.3.8
/build/buildd/opendnssec-1.3.8/enforcer/common/privdrop.c
Go to the documentation of this file.
00001 /*
00002  * $Id: privdrop.c 3114 2010-03-30 14:25:22Z sion $
00003  *
00004  * Copyright (c) 2009 Nominet UK. All rights reserved.
00005  *
00006  * Based heavily on uidswap.c from openssh-5.2p1
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions
00010  * are met:
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00019  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00021  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00023  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
00025  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00026  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00027  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00028  *
00029  */
00030 
00031 #define _GNU_SOURCE /* defines for setres(g|u)id */
00032 
00033 #include <stdlib.h>
00034 #include <stdio.h>
00035 #include <unistd.h>
00036 #include <string.h>
00037 #include <syslog.h>
00038 #include <stdarg.h>
00039 #include <errno.h>
00040 #include <pwd.h>
00041 #include <grp.h>
00042 #include <ctype.h>
00043 
00044 #include "config.h"
00045 #include "privdrop.h"
00046 
00047 
00048 int
00049 privdrop(const char *username, const char *groupname, const char *newroot)
00050 {
00051     int status;
00052 
00053     struct passwd *pwd;
00054     struct group  *grp;
00055 
00056     uid_t uid, olduid;
00057     gid_t gid, oldgid;
00058 
00059     long ngroups_max;
00060     gid_t *final_groups;
00061     int final_group_len = -1;
00062 
00063     /* Save effective uid/gid */
00064     uid = olduid = geteuid();
00065     gid = oldgid = getegid();
00066 
00067     /* Check if we're going to drop uid */
00068     if (username) {
00069         /* Lookup the user id in /etc/passwd */
00070         if ((pwd = getpwnam(username)) == NULL) {
00071             syslog(LOG_ERR, "user '%s' does not exist. exiting...\n", username);
00072             exit(1);
00073         } else {
00074             uid = pwd->pw_uid;
00075         }
00076         endpwent();
00077     }
00078 
00079     /* Check if we're going to drop gid */
00080     if (groupname) {
00081         /* Lookup the group id in /etc/groups */
00082         if ((grp = getgrnam(groupname)) == NULL) {
00083             syslog(LOG_ERR, "group '%s' does not exist. exiting...\n", groupname);
00084             exit(1);
00085         } else {
00086             gid = grp->gr_gid;
00087         }
00088         endgrent();
00089     }
00090 
00091     /* Change root if requested */
00092     if (newroot) {
00093        if (chroot(newroot) != 0 || chdir("/") != 0) {
00094             syslog(LOG_ERR, "chroot to '%s' failed. exiting...\n", newroot);
00095             exit(1);
00096        }
00097     }
00098 
00099     /* Do Additional groups first */
00100     if (username != NULL && !olduid) {
00101         if (initgroups(username, gid) < 0) {
00102             syslog(LOG_ERR, "initgroups failed: %s: %.100s", username, strerror(errno));
00103             exit(1);
00104         }
00105 
00106         ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
00107         final_groups = (gid_t *)malloc(ngroups_max *sizeof(gid_t));
00108         if (final_groups == NULL) {
00109             syslog(LOG_ERR, "Malloc for group struct failed");
00110             exit(1);
00111         }
00112 
00113         final_group_len = getgroups(ngroups_max, final_groups);
00114         /* If we are root then drop all groups other than the final one */
00115         if (!olduid) setgroups(final_group_len, final_groups);
00116 
00117         free(final_groups);
00118     }
00119     else {
00120         /* If we are root then drop all groups other than the final one */
00121         if (!olduid) setgroups(1, &(gid));
00122     }
00123 
00124     /* Drop gid? */
00125     if (groupname) {
00126 
00127 #if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID)
00128         status = setresgid(gid, gid, gid);
00129 #elif defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID)
00130         status = setregid(gid, gid);
00131 #else
00132         status = setegid(gid);
00133         if (status != 0) {
00134            syslog(LOG_ERR, "unable to drop group privileges: %s (%lu). exiting...\n",
00135                groupname, (unsigned long) gid);
00136            exit(1);
00137         }
00138         status = setgid(gid);
00139 #endif
00140 
00141         if (status != 0) {
00142            syslog(LOG_ERR, "unable to drop group privileges: %s (%lu). exiting...\n",
00143                groupname, (unsigned long) gid);
00144            exit(1);
00145            return -1;
00146         } else {
00147             syslog(LOG_ERR, "group set to: %s (%lu)\n", groupname, (unsigned long) gid);
00148         }
00149     }
00150 
00151     /* Drop uid? */
00152     if (username) {
00153         /* Set the user to drop to if specified; else just set the uid as the real one */
00154 #if defined(HAVE_SETRESUID) && !defined(BROKEN_SETRESUID)
00155         status = setresuid(uid, uid, uid);
00156 #elif defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID)
00157         status = setreuid(uid, uid);
00158 #else
00159 
00160 # ifndef SETEUID_BREAKS_SETUID
00161         status = seteuid(uid);
00162         if (status != 0) {
00163            syslog(LOG_ERR, "unable to drop user privileges (seteuid): %s (%lu). exiting...\n",
00164                username, (unsigned long) uid);
00165            exit(1);
00166         }
00167 # endif  /* SETEUID_BREAKS_SETUID */
00168 
00169         status = setuid(uid);
00170 #endif
00171 
00172         if (status != 0) {
00173            syslog(LOG_ERR, "unable to drop user privileges: %s (%lu). exiting...\n",
00174                username, (unsigned long) uid);
00175            exit(1);
00176            return -1;
00177         } else {
00178             syslog(LOG_ERR, "user set to: %s (%lu)\n", username, (unsigned long) uid);
00179         }
00180     }
00181 
00182     return 0;
00183 }