OpenDNSSEC-enforcer
1.3.8
|
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 }