OpenDNSSEC-signer  1.3.15
privdrop.c
Go to the documentation of this file.
1 /*
2  * $Id: privdrop.c 4340 2011-01-31 15:15: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, olduid;
78  int s;
79 
80  uid = olduid = 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 (result != NULL) {
95  uid = pwd.pw_uid;
96  }
97  free((void*) buf);
98  } else {
99  uid = -1;
100  }
101  return uid;
102 }
103 
104 
109 gid_t
110 privgid(const char *groupname)
111 {
112  struct group grp;
113  struct group* result;
114  long bufsize;
115  char* buf;
116  gid_t gid, oldgid;
117  int s;
118 
119  gid = oldgid = getegid();
120 
121  if (groupname) {
122  bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
123  if (bufsize == -1) {
124  bufsize = 16384; /* should be more than enough */
125  }
126  buf = (char*) calloc(bufsize, sizeof(char));
127  if (!buf) {
128  ods_log_error("[%s] calloc failed: out of memory?", privdrop_str);
129  return -1;
130  }
131  /* Lookup the group id in /etc/group */
132  s = getgrnam_r(groupname, &grp, buf, bufsize, &result); /* LEAK */
133  if (result != NULL) {
134  gid = grp.gr_gid;
135  }
136  free((void*) buf);
137  } else {
138  gid = -1;
139  }
140  return gid;
141 }
142 
143 
149 privdrop(const char *username, const char *groupname, const char *newroot,
150  uid_t* puid, gid_t* pgid)
151 {
152  int status;
153  uid_t uid, olduid;
154  gid_t gid, oldgid;
155  long ngroups_max;
156  gid_t *final_groups;
157  int final_group_len = -1;
158 
159  /* Save effective uid/gid */
160  uid = olduid = geteuid();
161  gid = oldgid = getegid();
162 
163  /* Check if we're going to drop uid */
164  if (username) {
165  uid = privuid(username);
166  if (uid == (uid_t)-1) {
167  ods_log_error("[%s] user %s does not exist", privdrop_str,
168  username);
170  }
171  }
172 
173  /* Check if we're going to drop gid */
174  if (groupname) {
175  gid = privgid(groupname);
176  if (gid == (gid_t)-1) {
177  ods_log_error("[%s] group %s does not exist", privdrop_str,
178  groupname);
180  }
181  }
182 
183  /* Change root if requested */
184  if (newroot) {
185 #ifdef HAVE_CHROOT
186  status = chroot(newroot);
187  if (status != 0 || chdir("/") != 0) {
188  ods_log_error("[%s] chroot to %s failed: %.100s", privdrop_str,
189  newroot, strerror(errno));
190  return ODS_STATUS_CHROOT_ERR;
191  }
192 #else
193  ods_log_error("[%s] chroot to %s failed: !HAVE_CHROOT", privdrop_str,
194  newroot);
195  return ODS_STATUS_CHROOT_ERR;
196 #endif /* HAVE_CHROOT */
197  }
198 
199  /* Do additional groups first */
200  if (username != NULL && !olduid) {
201 #ifdef HAVE_INITGROUPS
202  if (initgroups(username, gid) < 0) {
203  ods_log_error("[%s] initgroups failed: %s: %.100s", privdrop_str,
204  username, strerror(errno));
206  }
207 #else
208  ods_log_error("initgroups failed: %s: !HAVE_INITGROUPS", username);
210 #endif /* HAVE_INITGROUPS */
211 
212  ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
213  final_groups = (gid_t *)malloc(ngroups_max *sizeof(gid_t));
214  if (!final_groups) {
215  return ODS_STATUS_MALLOC_ERR;
216  }
217 #if defined(HAVE_GETGROUPS) && defined(HAVE_SETGROUPS)
218  final_group_len = getgroups(ngroups_max, final_groups);
219  /* If we are root then drop all groups other than the final one */
220  if (!olduid) {
221  setgroups(final_group_len, final_groups);
222  }
223 #endif /* defined(HAVE_GETGROUPS) && defined(HAVE_SETGROUPS) */
224  free((void*)final_groups);
225  }
226  else {
227  /* If we are root then drop all groups other than the final one */
228 #if defined(HAVE_SETGROUPS)
229  if (!olduid) setgroups(1, &(gid));
230 #endif /* defined(HAVE_SETGROUPS) */
231  }
232 
233  /* Drop gid? */
234  if (groupname) {
235 
236 #if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID)
237  status = setresgid(gid, gid, gid);
238 #elif defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID)
239  status = setregid(gid, gid);
240 #else
241 
242 # ifndef SETEUID_BREAKS_SETUID
243  status = setegid(gid);
244  if (status != 0) {
245  ods_log_error("[%s] setegid() for %s (%lu) failed: %s",
246  privdrop_str, groupname, (unsigned long) gid, strerror(errno));
248  }
249 # endif /* SETEUID_BREAKS_SETUID */
250 
251  status = setgid(gid);
252 #endif
253 
254  if (status != 0) {
255  ods_log_error("[%s] setgid() for %s (%lu) failed: %s",
256  privdrop_str, groupname, (unsigned long) gid, strerror(errno));
258  } else {
259  ods_log_debug("[%s] group set to %s (%lu)", privdrop_str,
260  groupname, (unsigned long) gid);
261  }
262  }
263 
264  /* Drop uid? */
265  if (username) {
266  /* Set the user to drop to if specified; else just set the uid as the real one */
267 #if defined(HAVE_SETRESUID) && !defined(BROKEN_SETRESUID)
268  status = setresuid(uid, uid, uid);
269 #elif defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID)
270  status = setreuid(uid, uid);
271 #else
272 
273 # ifndef SETEUID_BREAKS_SETUID
274  status = seteuid(uid);
275  if (status != 0) {
276  ods_log_error("[%s] seteuid() for %s (%lu) failed: %s",
277  privdrop_str, username, (unsigned long) uid, strerror(errno));
279  }
280 # endif /* SETEUID_BREAKS_SETUID */
281 
282  status = setuid(uid);
283 #endif
284 
285  if (status != 0) {
286  ods_log_error("[%s] setuid() for %s (%lu) failed: %s",
287  privdrop_str, username, (unsigned long) uid, strerror(errno));
289  } else {
290  ods_log_debug("[%s] user set to %s (%lu)", privdrop_str,
291  username, (unsigned long) uid);
292  }
293  }
294 
295  *puid = uid;
296  *pgid = gid;
297  return ODS_STATUS_OK;
298 }
299 
300 
305 void
306 privclose(const char* username, const char* groupname)
307 {
308  if (username) {
309  endpwent();
310  }
311  if (groupname) {
312  endgrent();
313  }
314  return;
315 }