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