OpenDNSSEC-signer  1.4.8.2
privdrop.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009 Nominet UK. All rights reserved.
3  *
4  * Based heavily on uidswap.c from openssh-5.2p1
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
34 #define _GNU_SOURCE /* defines for setres{g|u}id */
35 
36 #include "config.h"
37 #include "shared/log.h"
38 #include "shared/privdrop.h"
39 #include "shared/status.h"
40 
41 #include <errno.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #include <ctype.h>
45 #include <stdarg.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <sys/types.h>
50 #include <syslog.h>
51 #include <unistd.h>
52 
53 #ifndef _SC_GETPW_R_SIZE_MAX
54 #define _SC_GETPW_R_SIZE_MAX 16384
55 #endif /* _SC_GETPW_R_SIZE_MAX */
56 
57 #ifndef _SC_GETGR_R_SIZE_MAX
58 #define _SC_GETGR_R_SIZE_MAX 16384
59 #endif /* _SC_GETGR_R_SIZE_MAX */
60 
61 static const char* privdrop_str = "privdrop";
62 
63 
68 uid_t
69 privuid(const char* username)
70 {
71  struct passwd pwd;
72  struct passwd* result;
73  long bufsize;
74  char* buf;
75  uid_t uid;
76  int s;
77 
78  uid = geteuid();
79 
80  if (username) {
81  bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
82  if (bufsize == -1) {
83  bufsize = 16384; /* should be more than enough */
84  }
85  buf = (char*) calloc(bufsize, sizeof(char));
86  if (!buf) {
87  ods_log_error("[%s] calloc failed: out of memory?", privdrop_str);
88  return -1;
89  }
90  /* Lookup the user id in /etc/passwd */
91  s = getpwnam_r(username, &pwd, buf, bufsize, &result); /* LEAK */
92  if (s) {
93  ods_log_error("[%s] unable to get user id for %s: %s",
94  privdrop_str, username, strerror(s));
95  }
96  if (result != NULL) {
97  uid = pwd.pw_uid;
98  }
99  free((void*) buf);
100  } else {
101  uid = -1;
102  }
103  return uid;
104 }
105 
106 
111 gid_t
112 privgid(const char *groupname)
113 {
114  struct group grp;
115  struct group* result;
116  long bufsize;
117  char* buf;
118  gid_t gid;
119  int s;
120 
121  gid = getegid();
122 
123  if (groupname) {
124  bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
125  if (bufsize == -1) {
126  bufsize = 16384; /* should be more than enough */
127  }
128  buf = (char*) calloc(bufsize, sizeof(char));
129  if (!buf) {
130  ods_log_error("[%s] calloc failed: out of memory?", privdrop_str);
131  return -1;
132  }
133  /* Lookup the group id in /etc/group */
134  s = getgrnam_r(groupname, &grp, buf, bufsize, &result); /* LEAK */
135  if (s) {
136  ods_log_error("[%s] unable to get group id for %s: %s",
137  privdrop_str, groupname, strerror(s));
138  }
139  if (result != NULL) {
140  gid = grp.gr_gid;
141  }
142  free((void*) buf);
143  } else {
144  gid = -1;
145  }
146  return gid;
147 }
148 
149 
155 privdrop(const char *username, const char *groupname, const char *newroot,
156  uid_t* puid, gid_t* pgid)
157 {
158  int status;
159  uid_t uid, olduid;
160  gid_t gid;
161  long ngroups_max;
162  gid_t *final_groups;
163  int final_group_len = -1;
164 
165  /* Save effective uid/gid */
166  uid = olduid = geteuid();
167  gid = getegid();
168 
169  /* Check if we're going to drop uid */
170  if (username) {
171  uid = privuid(username);
172  if (uid == (uid_t)-1) {
173  ods_log_error("[%s] user %s does not exist", privdrop_str,
174  username);
176  }
177  }
178 
179  /* Check if we're going to drop gid */
180  if (groupname) {
181  gid = privgid(groupname);
182  if (gid == (gid_t)-1) {
183  ods_log_error("[%s] group %s does not exist", privdrop_str,
184  groupname);
186  }
187  }
188 
189  /* Change root if requested */
190  if (newroot) {
191 #ifdef HAVE_CHROOT
192  status = chroot(newroot);
193  if (status != 0 || chdir("/") != 0) {
194  ods_log_error("[%s] chroot to %s failed: %.100s", privdrop_str,
195  newroot, strerror(errno));
196  return ODS_STATUS_CHROOT_ERR;
197  }
198 #else
199  ods_log_error("[%s] chroot to %s failed: !HAVE_CHROOT", privdrop_str,
200  newroot);
201  return ODS_STATUS_CHROOT_ERR;
202 #endif /* HAVE_CHROOT */
203  }
204 
205  /* Do additional groups first */
206  if (username != NULL && !olduid) {
207 #ifdef HAVE_INITGROUPS
208  if (initgroups(username, gid) < 0) {
209  ods_log_error("[%s] initgroups failed: %s: %.100s", privdrop_str,
210  username, strerror(errno));
212  }
213 #else
214  ods_log_error("initgroups failed: %s: !HAVE_INITGROUPS", username);
216 #endif /* HAVE_INITGROUPS */
217 
218  ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
219  final_groups = (gid_t *)malloc(ngroups_max *sizeof(gid_t));
220  if (!final_groups) {
221  return ODS_STATUS_MALLOC_ERR;
222  }
223 #if defined(HAVE_GETGROUPS) && defined(HAVE_SETGROUPS)
224  final_group_len = getgroups(ngroups_max, final_groups);
225  /* If we are root then drop all groups other than the final one */
226  if (!olduid) {
227  setgroups(final_group_len, final_groups);
228  }
229 #endif /* defined(HAVE_GETGROUPS) && defined(HAVE_SETGROUPS) */
230  free((void*)final_groups);
231  }
232  else {
233  /* If we are root then drop all groups other than the final one */
234 #if defined(HAVE_SETGROUPS)
235  if (!olduid) setgroups(1, &(gid));
236 #endif /* defined(HAVE_SETGROUPS) */
237  }
238 
239  /* Drop gid? */
240  if (groupname) {
241 
242 #if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID)
243  status = setresgid(gid, gid, gid);
244 #elif defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID)
245  status = setregid(gid, gid);
246 #else
247 
248 # ifndef SETEUID_BREAKS_SETUID
249  status = setegid(gid);
250  if (status != 0) {
251  ods_log_error("[%s] setegid() for %s (%lu) failed: %s",
252  privdrop_str, groupname, (unsigned long) gid, strerror(errno));
254  }
255 # endif /* SETEUID_BREAKS_SETUID */
256 
257  status = setgid(gid);
258 #endif
259 
260  if (status != 0) {
261  ods_log_error("[%s] setgid() for %s (%lu) failed: %s",
262  privdrop_str, groupname, (unsigned long) gid, strerror(errno));
264  } else {
265  ods_log_debug("[%s] group set to %s (%lu)", privdrop_str,
266  groupname, (unsigned long) gid);
267  }
268  }
269 
270  /* Drop uid? */
271  if (username) {
272  /* Set the user to drop to if specified; else just set the uid as the real one */
273 #if defined(HAVE_SETRESUID) && !defined(BROKEN_SETRESUID)
274  status = setresuid(uid, uid, uid);
275 #elif defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID)
276  status = setreuid(uid, uid);
277 #else
278 
279 # ifndef SETEUID_BREAKS_SETUID
280  status = seteuid(uid);
281  if (status != 0) {
282  ods_log_error("[%s] seteuid() for %s (%lu) failed: %s",
283  privdrop_str, username, (unsigned long) uid, strerror(errno));
285  }
286 # endif /* SETEUID_BREAKS_SETUID */
287 
288  status = setuid(uid);
289 #endif
290 
291  if (status != 0) {
292  ods_log_error("[%s] setuid() for %s (%lu) failed: %s",
293  privdrop_str, username, (unsigned long) uid, strerror(errno));
295  } else {
296  ods_log_debug("[%s] user set to %s (%lu)", privdrop_str,
297  username, (unsigned long) uid);
298  }
299  }
300 
301  *puid = uid;
302  *pgid = gid;
303  return ODS_STATUS_OK;
304 }
305 
306 
311 void
312 privclose(const char* username, const char* groupname)
313 {
314  if (username) {
315  endpwent();
316  }
317  if (groupname) {
318  endgrent();
319  }
320  return;
321 }
gid_t privgid(const char *groupname)
Definition: privdrop.c:112
void privclose(const char *username, const char *groupname)
Definition: privdrop.c:312
void ods_log_debug(const char *format,...)
Definition: log.c:270
enum ods_enum_status ods_status
Definition: status.h:90
void ods_log_error(const char *format,...)
Definition: log.c:334
uid_t privuid(const char *username)
Definition: privdrop.c:69
ods_status privdrop(const char *username, const char *groupname, const char *newroot, uid_t *puid, gid_t *pgid)
Definition: privdrop.c:155
#define _SC_GETGR_R_SIZE_MAX
Definition: privdrop.c:58
#define _SC_GETPW_R_SIZE_MAX
Definition: privdrop.c:54