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