OpenDNSSEC-signer 1.2.1
|
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 }