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