OpenDNSSEC-enforcer  1.3.9
privdrop.c
Go to the documentation of this file.
1 /*
2  * $Id: privdrop.c 3114 2010-03-30 14:25:22Z sion $
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 
47 
48 int
49 privdrop(const char *username, const char *groupname, const char *newroot)
50 {
51  int status;
52 
53  struct passwd *pwd;
54  struct group *grp;
55 
56  uid_t uid, olduid;
57  gid_t gid, oldgid;
58 
59  long ngroups_max;
60  gid_t *final_groups;
61  int final_group_len = -1;
62 
63  /* Save effective uid/gid */
64  uid = olduid = geteuid();
65  gid = oldgid = getegid();
66 
67  /* Check if we're going to drop uid */
68  if (username) {
69  /* Lookup the user id in /etc/passwd */
70  if ((pwd = getpwnam(username)) == NULL) {
71  syslog(LOG_ERR, "user '%s' does not exist. exiting...\n", username);
72  exit(1);
73  } else {
74  uid = pwd->pw_uid;
75  }
76  endpwent();
77  }
78 
79  /* Check if we're going to drop gid */
80  if (groupname) {
81  /* Lookup the group id in /etc/groups */
82  if ((grp = getgrnam(groupname)) == NULL) {
83  syslog(LOG_ERR, "group '%s' does not exist. exiting...\n", groupname);
84  exit(1);
85  } else {
86  gid = grp->gr_gid;
87  }
88  endgrent();
89  }
90 
91  /* Change root if requested */
92  if (newroot) {
93  if (chroot(newroot) != 0 || chdir("/") != 0) {
94  syslog(LOG_ERR, "chroot to '%s' failed. exiting...\n", newroot);
95  exit(1);
96  }
97  }
98 
99  /* Do Additional groups first */
100  if (username != NULL && !olduid) {
101  if (initgroups(username, gid) < 0) {
102  syslog(LOG_ERR, "initgroups failed: %s: %.100s", username, strerror(errno));
103  exit(1);
104  }
105 
106  ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
107  final_groups = (gid_t *)malloc(ngroups_max *sizeof(gid_t));
108  if (final_groups == NULL) {
109  syslog(LOG_ERR, "Malloc for group struct failed");
110  exit(1);
111  }
112 
113  final_group_len = getgroups(ngroups_max, final_groups);
114  /* If we are root then drop all groups other than the final one */
115  if (!olduid) setgroups(final_group_len, final_groups);
116 
117  free(final_groups);
118  }
119  else {
120  /* If we are root then drop all groups other than the final one */
121  if (!olduid) setgroups(1, &(gid));
122  }
123 
124  /* Drop gid? */
125  if (groupname) {
126 
127 #if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID)
128  status = setresgid(gid, gid, gid);
129 #elif defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID)
130  status = setregid(gid, gid);
131 #else
132  status = setegid(gid);
133  if (status != 0) {
134  syslog(LOG_ERR, "unable to drop group privileges: %s (%lu). exiting...\n",
135  groupname, (unsigned long) gid);
136  exit(1);
137  }
138  status = setgid(gid);
139 #endif
140 
141  if (status != 0) {
142  syslog(LOG_ERR, "unable to drop group privileges: %s (%lu). exiting...\n",
143  groupname, (unsigned long) gid);
144  exit(1);
145  return -1;
146  } else {
147  syslog(LOG_ERR, "group set to: %s (%lu)\n", groupname, (unsigned long) gid);
148  }
149  }
150 
151  /* Drop uid? */
152  if (username) {
153  /* Set the user to drop to if specified; else just set the uid as the real one */
154 #if defined(HAVE_SETRESUID) && !defined(BROKEN_SETRESUID)
155  status = setresuid(uid, uid, uid);
156 #elif defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID)
157  status = setreuid(uid, uid);
158 #else
159 
160 # ifndef SETEUID_BREAKS_SETUID
161  status = seteuid(uid);
162  if (status != 0) {
163  syslog(LOG_ERR, "unable to drop user privileges (seteuid): %s (%lu). exiting...\n",
164  username, (unsigned long) uid);
165  exit(1);
166  }
167 # endif /* SETEUID_BREAKS_SETUID */
168 
169  status = setuid(uid);
170 #endif
171 
172  if (status != 0) {
173  syslog(LOG_ERR, "unable to drop user privileges: %s (%lu). exiting...\n",
174  username, (unsigned long) uid);
175  exit(1);
176  return -1;
177  } else {
178  syslog(LOG_ERR, "user set to: %s (%lu)\n", username, (unsigned long) uid);
179  }
180  }
181 
182  return 0;
183 }