00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00036 #define _GNU_SOURCE
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
00058
00059 #ifndef _SC_GETGR_R_SIZE_MAX
00060 #define _SC_GETGR_R_SIZE_MAX 16384
00061 #endif
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;
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
00093 s = getpwnam_r(username, &pwd, buf, bufsize, &result);
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;
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
00132 s = getgrnam_r(groupname, &grp, buf, bufsize, &result);
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
00160 uid = olduid = geteuid();
00161 gid = oldgid = getegid();
00162
00163
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
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
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
00197 }
00198
00199
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
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
00220 if (!olduid) {
00221 setgroups(final_group_len, final_groups);
00222 }
00223 #endif
00224 free((void*)final_groups);
00225 }
00226 else {
00227
00228 #if defined(HAVE_SETGROUPS)
00229 if (!olduid) setgroups(1, &(gid));
00230 #endif
00231 }
00232
00233
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
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
00265 if (username) {
00266
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
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 }