OpenDNSSEC-signer 1.2.1

/build/buildd-opendnssec_1.2.1.dfsg-1-mips-p9AT07/opendnssec-1.2.1.dfsg/signer/src/util/privdrop.c

Go to the documentation of this file.
00001 /*
00002  * $Id: privdrop.c 4294 2011-01-13 19:58:29Z jakob $
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 
00036 #define _GNU_SOURCE /* defines for setres{g|u}id */
00037 
00038 #include <stdlib.h>
00039 #include <stdio.h>
00040 #include <unistd.h>
00041 #include <string.h>
00042 #include <syslog.h>
00043 #include <stdarg.h>
00044 #include <errno.h>
00045 #include <pwd.h>
00046 #include <grp.h>
00047 #include <ctype.h>
00048 #include <sys/types.h>
00049 
00050 #include "config.h"
00051 #include "util/log.h"
00052 #include "util/privdrop.h"
00053 #include "util/se_malloc.h"
00054 
00055 
00056 #ifndef _SC_GETPW_R_SIZE_MAX
00057 #define _SC_GETPW_R_SIZE_MAX 16384
00058 #endif /* _SC_GETPW_R_SIZE_MAX */
00059 
00060 #ifndef _SC_GETGR_R_SIZE_MAX
00061 #define _SC_GETGR_R_SIZE_MAX 16384
00062 #endif /* _SC_GETGR_R_SIZE_MAX */
00063 
00064 
00069 uid_t
00070 privuid(const char* username)
00071 {
00072     struct passwd pwd;
00073     struct passwd* result;
00074     long bufsize;
00075     char* buf;
00076     uid_t uid, olduid;
00077     int s;
00078 
00079     uid = olduid = geteuid();
00080 
00081     if (username) {
00082         bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
00083         if (bufsize == -1) {
00084             bufsize = 16384; /* should be more than enough */
00085         }
00086         buf = (char*) se_calloc(bufsize, sizeof(char));
00087         /* Lookup the user id in /etc/passwd */
00088         s = getpwnam_r(username, &pwd, buf, bufsize, &result);
00089         if (result == NULL) {
00090             se_free((void*) buf);
00091             return -1;
00092         } else {
00093             uid = pwd.pw_uid;
00094             se_free((void*) buf);
00095         }
00096         endpwent();
00097     } else {
00098         uid = -1;
00099     }
00100     return uid;
00101 }
00102 
00103 
00108 gid_t
00109 privgid(const char *groupname)
00110 {
00111     struct group grp;
00112     struct group* result;
00113     long bufsize;
00114     char* buf;
00115     gid_t gid, oldgid;
00116     int s;
00117 
00118     gid = oldgid = getegid();
00119 
00120     if (groupname) {
00121         bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
00122         if (bufsize == -1) {
00123             bufsize = 16384; /* should be more than enough */
00124         }
00125         buf = (char*) se_calloc(bufsize, sizeof(char));
00126         /* Lookup the group id in /etc/group */
00127         s = getgrnam_r(groupname, &grp, buf, bufsize, &result);
00128         if (result == NULL) {
00129             se_free((void*) buf);
00130             return -1;
00131         } else {
00132             gid = grp.gr_gid;
00133             se_free((void*) buf);
00134         }
00135         endgrent();
00136     } else {
00137         gid = -1;
00138     }
00139     return gid;
00140 }
00141 
00142 
00147 int
00148 privdrop(const char *username, const char *groupname, const char *newroot)
00149 {
00150     int status;
00151     uid_t uid, olduid;
00152     gid_t gid, oldgid;
00153     long ngroups_max;
00154     gid_t *final_groups;
00155     int final_group_len = -1;
00156 
00157     /* Save effective uid/gid */
00158     uid = olduid = geteuid();
00159     gid = oldgid = getegid();
00160 
00161     /* Check if we're going to drop uid */
00162     if (username) {
00163         uid = privuid(username);
00164         if (uid == (uid_t)-1) {
00165             se_log_error("user %s does not exist", username);
00166             return -1;
00167         }
00168     }
00169 
00170     /* Check if we're going to drop gid */
00171     if (groupname) {
00172         gid = privgid(groupname);
00173         if (gid == (gid_t)-1) {
00174             se_log_error("group %s does not exist", groupname);
00175             return -1;
00176         }
00177     }
00178 
00179     /* Change root if requested */
00180     if (newroot) {
00181 #ifdef HAVE_CHROOT
00182        status = chroot(newroot);
00183        if (status != 0 || chdir("/") != 0) {
00184            se_log_error("chroot to %s failed: %.100s", newroot, strerror(errno));
00185            return -1;
00186        }
00187 #else
00188        se_log_error("chroot to %s failed: !HAVE_CHROOT", newroot);
00189        return -1;
00190 #endif /* HAVE_CHROOT */
00191 
00192     }
00193 
00194     /* Do additional groups first */
00195     if (username != NULL && !olduid) {
00196 #ifdef HAVE_INITGROUPS
00197         if (initgroups(username, gid) < 0) {
00198             se_log_error("initgroups failed: %s: %.100s", username,
00199                 strerror(errno));
00200             return -1;
00201         }
00202 #else
00203         se_log_error("initgroups failed: %s: !HAVE_INITGROUPS", username);
00204         return -1;
00205 #endif /* HAVE_INITGROUPS */
00206 
00207         ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
00208         final_groups = (gid_t *)se_malloc(ngroups_max *sizeof(gid_t));
00209 #if defined(HAVE_GETGROUPS) && defined(HAVE_SETGROUPS)
00210         final_group_len = getgroups(ngroups_max, final_groups);
00211         /* If we are root then drop all groups other than the final one */
00212         if (!olduid) {
00213             setgroups(final_group_len, final_groups);
00214         }
00215 #endif /* defined(HAVE_GETGROUPS) && defined(HAVE_SETGROUPS) */
00216         se_free((void*)final_groups);
00217     }
00218     else {
00219         /* If we are root then drop all groups other than the final one */
00220 #if defined(HAVE_SETGROUPS)
00221         if (!olduid) setgroups(1, &(gid));
00222 #endif /* defined(HAVE_SETGROUPS) */
00223     }
00224 
00225     /* Drop gid? */
00226     if (groupname) {
00227 
00228 #if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID)
00229         status = setresgid(gid, gid, gid);
00230 #elif defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID)
00231         status = setregid(gid, gid);
00232 #else
00233 
00234 # ifndef SETEUID_BREAKS_SETUID
00235         status = setegid(gid);
00236         if (status != 0) {
00237            se_log_error("setegid() for %s (%lu) failed: %s",
00238                groupname, (unsigned long) gid, strerror(errno));
00239            return -1;
00240         }
00241 # endif  /* SETEUID_BREAKS_SETUID */
00242 
00243         status = setgid(gid);
00244 #endif
00245 
00246         if (status != 0) {
00247            se_log_error("setgid() for %s (%lu) failed: %s",
00248                groupname, (unsigned long) gid, strerror(errno));
00249            return -1;
00250         } else {
00251             se_log_debug("group set to %s (%lu)", groupname, (unsigned long) gid);
00252         }
00253     }
00254 
00255     /* Drop uid? */
00256     if (username) {
00257         /* Set the user to drop to if specified; else just set the uid as the real one */
00258 #if defined(HAVE_SETRESUID) && !defined(BROKEN_SETRESUID)
00259         status = setresuid(uid, uid, uid);
00260 #elif defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID)
00261         status = setreuid(uid, uid);
00262 #else
00263 
00264 # ifndef SETEUID_BREAKS_SETUID
00265         status = seteuid(uid);
00266         if (status != 0) {
00267            se_log_error("seteuid() for %s (%lu) failed: %s",
00268                username, (unsigned long) uid, strerror(errno));
00269            return -1;
00270         }
00271 # endif  /* SETEUID_BREAKS_SETUID */
00272 
00273         status = setuid(uid);
00274 #endif
00275 
00276         if (status != 0) {
00277            se_log_error("setuid() for %s (%lu) failed: %s",
00278                username, (unsigned long) uid, strerror(errno));
00279            return -1;
00280         } else {
00281             se_log_debug("user set to %s (%lu)", username, (unsigned long) uid);
00282         }
00283     }
00284 
00285     return 0;
00286 }