OpenDNSSEC-enforcer  1.3.14
privdrop.c
Go to the documentation of this file.
1 /*
2  * $Id: privdrop.c 6441 2012-06-19 14:04:57Z jerry $
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 
31 #define _GNU_SOURCE /* defines for setres(g|u)id */
32 
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <syslog.h>
38 #include <stdarg.h>
39 #include <errno.h>
40 #include <pwd.h>
41 #include <grp.h>
42 #include <ctype.h>
43 
44 #include "config.h"
45 #include "privdrop.h"
46 #include "daemon_util.h"
47 
48 
49 int
50 privdrop(const char *username, const char *groupname, const char *newroot)
51 {
52  int status;
53 
54  struct passwd *pwd;
55  struct group *grp;
56 
57  uid_t uid, olduid;
58  gid_t gid, oldgid;
59 
60  long ngroups_max;
61  gid_t *final_groups;
62  int final_group_len = -1;
63 
64  /* Save effective uid/gid */
65  uid = olduid = geteuid();
66  gid = oldgid = getegid();
67 
68  /* Check if we're going to drop uid */
69  if (username) {
70  /* Lookup the user id in /etc/passwd */
71  if ((pwd = getpwnam(username)) == NULL) {
72 #ifdef HAVE_SYSLOG_R
73  syslog_r(LOG_ERR, &sdata, "user '%s' does not exist. exiting...\n", username);
74 #else
75  syslog(LOG_ERR, "user '%s' does not exist. exiting...\n", username);
76 #endif
77  exit(1);
78  } else {
79  uid = pwd->pw_uid;
80  }
81  endpwent();
82  }
83 
84  /* Check if we're going to drop gid */
85  if (groupname) {
86  /* Lookup the group id in /etc/groups */
87  if ((grp = getgrnam(groupname)) == NULL) {
88 #ifdef HAVE_SYSLOG_R
89  syslog_r(LOG_ERR, &sdata, "group '%s' does not exist. exiting...\n", groupname);
90 #else
91  syslog(LOG_ERR, "group '%s' does not exist. exiting...\n", groupname);
92 #endif
93  exit(1);
94  } else {
95  gid = grp->gr_gid;
96  }
97  endgrent();
98  }
99 
100  /* Change root if requested */
101  if (newroot) {
102  if (chroot(newroot) != 0 || chdir("/") != 0) {
103 #ifdef HAVE_SYSLOG_R
104  syslog_r(LOG_ERR, &sdata, "chroot to '%s' failed. exiting...\n", newroot);
105 #else
106  syslog(LOG_ERR, "chroot to '%s' failed. exiting...\n", newroot);
107 #endif
108  exit(1);
109  }
110  }
111 
112  /* Do Additional groups first */
113  if (username != NULL && !olduid) {
114  if (initgroups(username, gid) < 0) {
115 #ifdef HAVE_SYSLOG_R
116  syslog_r(LOG_ERR, &sdata, "initgroups failed: %s: %.100s", username, strerror(errno));
117 #else
118  syslog(LOG_ERR, "initgroups failed: %s: %.100s", username, strerror(errno));
119 #endif
120  exit(1);
121  }
122 
123  ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
124  final_groups = (gid_t *)malloc(ngroups_max *sizeof(gid_t));
125  if (final_groups == NULL) {
126 #ifdef HAVE_SYSLOG_R
127  syslog_r(LOG_ERR, &sdata, "Malloc for group struct failed");
128 #else
129  syslog(LOG_ERR, "Malloc for group struct failed");
130 #endif
131  exit(1);
132  }
133 
134  final_group_len = getgroups(ngroups_max, final_groups);
135  /* If we are root then drop all groups other than the final one */
136  if (!olduid) setgroups(final_group_len, final_groups);
137 
138  free(final_groups);
139  }
140  else {
141  /* If we are root then drop all groups other than the final one */
142  if (!olduid) setgroups(1, &(gid));
143  }
144 
145  /* Drop gid? */
146  if (groupname) {
147 
148 #if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID)
149  status = setresgid(gid, gid, gid);
150 #elif defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID)
151  status = setregid(gid, gid);
152 #else
153  status = setegid(gid);
154  if (status != 0) {
155 #ifdef HAVE_SYSLOG_R
156  syslog_r(LOG_ERR, &sdata, "unable to drop group privileges: %s (%lu). exiting...\n",
157  groupname, (unsigned long) gid);
158 #else
159  syslog(LOG_ERR, "unable to drop group privileges: %s (%lu). exiting...\n",
160  groupname, (unsigned long) gid);
161 #endif
162  exit(1);
163  }
164  status = setgid(gid);
165 #endif
166 
167  if (status != 0) {
168 #ifdef HAVE_SYSLOG_R
169  syslog_r(LOG_ERR, &sdata, "unable to drop group privileges: %s (%lu). exiting...\n",
170  groupname, (unsigned long) gid);
171 #else
172  syslog(LOG_ERR, "unable to drop group privileges: %s (%lu). exiting...\n",
173  groupname, (unsigned long) gid);
174 #endif
175  exit(1);
176  return -1;
177  } else {
178 #ifdef HAVE_SYSLOG_R
179  syslog_r(LOG_ERR, &sdata, "group set to: %s (%lu)\n", groupname, (unsigned long) gid);
180 #else
181  syslog(LOG_ERR, "group set to: %s (%lu)\n", groupname, (unsigned long) gid);
182 #endif
183  }
184  }
185 
186  /* Drop uid? */
187  if (username) {
188  /* Set the user to drop to if specified; else just set the uid as the real one */
189 #if defined(HAVE_SETRESUID) && !defined(BROKEN_SETRESUID)
190  status = setresuid(uid, uid, uid);
191 #elif defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID)
192  status = setreuid(uid, uid);
193 #else
194 
195 # ifndef SETEUID_BREAKS_SETUID
196  status = seteuid(uid);
197  if (status != 0) {
198 #ifdef HAVE_SYSLOG_R
199  syslog_r(LOG_ERR, &sdata, "unable to drop user privileges (seteuid): %s (%lu). exiting...\n",
200  username, (unsigned long) uid);
201 #else
202  syslog(LOG_ERR, "unable to drop user privileges (seteuid): %s (%lu). exiting...\n",
203  username, (unsigned long) uid);
204 #endif
205  exit(1);
206  }
207 # endif /* SETEUID_BREAKS_SETUID */
208 
209  status = setuid(uid);
210 #endif
211 
212  if (status != 0) {
213 #ifdef HAVE_SYSLOG_R
214  syslog_r(LOG_ERR, &sdata, "unable to drop user privileges: %s (%lu). exiting...\n",
215  username, (unsigned long) uid);
216 #else
217  syslog(LOG_ERR, "unable to drop user privileges: %s (%lu). exiting...\n",
218  username, (unsigned long) uid);
219 #endif
220  exit(1);
221  return -1;
222  } else {
223 #ifdef HAVE_SYSLOG_R
224  syslog_r(LOG_ERR, &sdata, "user set to: %s (%lu)\n", username, (unsigned long) uid);
225 #else
226  syslog(LOG_ERR, "user set to: %s (%lu)\n", username, (unsigned long) uid);
227 #endif
228  }
229  }
230 
231  return 0;
232 }