Line data Source code
1 : /* Copyright (c) 2003, Roger Dingledine
2 : * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 : * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 : /* See LICENSE for licensing information */
5 :
6 : /**
7 : * \file setuid.c
8 : * \brief Change the user ID after Tor has started (Unix only)
9 : **/
10 :
11 : #include "orconfig.h"
12 : #include "lib/process/setuid.h"
13 :
14 : #if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_CAP_SET_PROC)
15 : #define HAVE_LINUX_CAPABILITIES
16 : #endif
17 :
18 : #include "lib/container/smartlist.h"
19 : #include "lib/fs/userdb.h"
20 : #include "lib/log/log.h"
21 : #include "lib/log/util_bug.h"
22 : #include "lib/malloc/malloc.h"
23 :
24 : #ifdef HAVE_SYS_TYPES_H
25 : #include <sys/types.h>
26 : #endif
27 : #ifdef HAVE_UNISTD_H
28 : #include <unistd.h>
29 : #endif
30 : #ifdef HAVE_GRP_H
31 : #include <grp.h>
32 : #endif
33 : #ifdef HAVE_PWD_H
34 : #include <pwd.h>
35 : #endif
36 : #ifdef HAVE_SYS_CAPABILITY_H
37 : #include <sys/capability.h>
38 : #endif
39 : #ifdef HAVE_SYS_PRCTL_H
40 : #include <sys/prctl.h>
41 : #endif
42 :
43 : #include <errno.h>
44 : #include <string.h>
45 :
46 : #ifndef _WIN32
47 : /** Log details of current user and group credentials. Return 0 on
48 : * success. Logs and return -1 on failure.
49 : */
50 : static int
51 0 : log_credential_status(void)
52 : {
53 : /** Log level to use when describing non-error UID/GID status. */
54 : #define CREDENTIAL_LOG_LEVEL LOG_INFO
55 : /* Real, effective and saved UIDs */
56 0 : uid_t ruid, euid, suid;
57 : /* Read, effective and saved GIDs */
58 0 : gid_t rgid, egid, sgid;
59 : /* Supplementary groups */
60 0 : gid_t *sup_gids = NULL;
61 0 : int sup_gids_size;
62 : /* Number of supplementary groups */
63 0 : int ngids;
64 :
65 : /* log UIDs */
66 : #ifdef HAVE_GETRESUID
67 0 : if (getresuid(&ruid, &euid, &suid) != 0) {
68 0 : log_warn(LD_GENERAL, "Error getting changed UIDs: %s", strerror(errno));
69 0 : return -1;
70 : } else {
71 0 : log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
72 : "UID is %u (real), %u (effective), %u (saved)",
73 : (unsigned)ruid, (unsigned)euid, (unsigned)suid);
74 : }
75 : #else /* !defined(HAVE_GETRESUID) */
76 : /* getresuid is not present on MacOS X, so we can't get the saved (E)UID */
77 : ruid = getuid();
78 : euid = geteuid();
79 : (void)suid;
80 :
81 : log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
82 : "UID is %u (real), %u (effective), unknown (saved)",
83 : (unsigned)ruid, (unsigned)euid);
84 : #endif /* defined(HAVE_GETRESUID) */
85 :
86 : /* log GIDs */
87 : #ifdef HAVE_GETRESGID
88 0 : if (getresgid(&rgid, &egid, &sgid) != 0) {
89 0 : log_warn(LD_GENERAL, "Error getting changed GIDs: %s", strerror(errno));
90 0 : return -1;
91 : } else {
92 0 : log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
93 : "GID is %u (real), %u (effective), %u (saved)",
94 : (unsigned)rgid, (unsigned)egid, (unsigned)sgid);
95 : }
96 : #else /* !defined(HAVE_GETRESGID) */
97 : /* getresgid is not present on MacOS X, so we can't get the saved (E)GID */
98 : rgid = getgid();
99 : egid = getegid();
100 : (void)sgid;
101 : log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
102 : "GID is %u (real), %u (effective), unknown (saved)",
103 : (unsigned)rgid, (unsigned)egid);
104 : #endif /* defined(HAVE_GETRESGID) */
105 :
106 : /* log supplementary groups */
107 0 : sup_gids_size = 64;
108 0 : sup_gids = tor_calloc(64, sizeof(gid_t));
109 0 : while ((ngids = getgroups(sup_gids_size, sup_gids)) < 0 &&
110 0 : errno == EINVAL &&
111 : sup_gids_size < NGROUPS_MAX) {
112 0 : sup_gids_size *= 2;
113 0 : sup_gids = tor_reallocarray(sup_gids, sizeof(gid_t), sup_gids_size);
114 : }
115 :
116 0 : if (ngids < 0) {
117 0 : log_warn(LD_GENERAL, "Error getting supplementary GIDs: %s",
118 : strerror(errno));
119 0 : tor_free(sup_gids);
120 0 : return -1;
121 : } else {
122 0 : int i, retval = 0;
123 0 : char *s = NULL;
124 0 : smartlist_t *elts = smartlist_new();
125 :
126 0 : for (i = 0; i<ngids; i++) {
127 0 : smartlist_add_asprintf(elts, "%u", (unsigned)sup_gids[i]);
128 : }
129 :
130 0 : s = smartlist_join_strings(elts, " ", 0, NULL);
131 :
132 0 : log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Supplementary groups are: %s",s);
133 :
134 0 : tor_free(s);
135 0 : SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
136 0 : smartlist_free(elts);
137 0 : tor_free(sup_gids);
138 :
139 0 : return retval;
140 : }
141 :
142 : return 0;
143 : }
144 : #endif /* !defined(_WIN32) */
145 :
146 : /** Return true iff we were compiled with capability support, and capabilities
147 : * seem to work. **/
148 : int
149 6 : have_capability_support(void)
150 : {
151 : #ifdef HAVE_LINUX_CAPABILITIES
152 6 : cap_t caps = cap_get_proc();
153 6 : if (caps == NULL)
154 : return 0;
155 6 : cap_free(caps);
156 6 : return 1;
157 : #else /* !defined(HAVE_LINUX_CAPABILITIES) */
158 : return 0;
159 : #endif /* defined(HAVE_LINUX_CAPABILITIES) */
160 : }
161 :
162 : #ifdef HAVE_LINUX_CAPABILITIES
163 : /** Helper. Drop all capabilities but a small set, and set PR_KEEPCAPS as
164 : * appropriate.
165 : *
166 : * If pre_setuid, retain only CAP_NET_BIND_SERVICE, CAP_SETUID, and
167 : * CAP_SETGID, and use PR_KEEPCAPS to ensure that capabilities persist across
168 : * setuid().
169 : *
170 : * If not pre_setuid, retain only CAP_NET_BIND_SERVICE, and disable
171 : * PR_KEEPCAPS.
172 : *
173 : * Return 0 on success, and -1 on failure.
174 : */
175 : static int
176 0 : drop_capabilities(int pre_setuid)
177 : {
178 : /* We keep these three capabilities, and these only, as we setuid.
179 : * After we setuid, we drop all but the first. */
180 0 : const cap_value_t caplist[] = {
181 : CAP_NET_BIND_SERVICE, CAP_SETUID, CAP_SETGID
182 : };
183 0 : const char *where = pre_setuid ? "pre-setuid" : "post-setuid";
184 0 : const int n_effective = pre_setuid ? 3 : 1;
185 0 : const int n_permitted = pre_setuid ? 3 : 1;
186 0 : const int n_inheritable = 1;
187 0 : const int keepcaps = pre_setuid ? 1 : 0;
188 :
189 : /* Sets whether we keep capabilities across a setuid. */
190 0 : if (prctl(PR_SET_KEEPCAPS, keepcaps) < 0) {
191 0 : log_warn(LD_CONFIG, "Unable to call prctl() %s: %s",
192 : where, strerror(errno));
193 0 : return -1;
194 : }
195 :
196 0 : cap_t caps = cap_get_proc();
197 0 : if (!caps) {
198 0 : log_warn(LD_CONFIG, "Unable to call cap_get_proc() %s: %s",
199 : where, strerror(errno));
200 0 : return -1;
201 : }
202 0 : cap_clear(caps);
203 :
204 0 : cap_set_flag(caps, CAP_EFFECTIVE, n_effective, caplist, CAP_SET);
205 0 : cap_set_flag(caps, CAP_PERMITTED, n_permitted, caplist, CAP_SET);
206 0 : cap_set_flag(caps, CAP_INHERITABLE, n_inheritable, caplist, CAP_SET);
207 :
208 0 : int r = cap_set_proc(caps);
209 0 : cap_free(caps);
210 0 : if (r < 0) {
211 0 : log_warn(LD_CONFIG, "No permission to set capabilities %s: %s",
212 : where, strerror(errno));
213 0 : return -1;
214 : }
215 :
216 : return 0;
217 : }
218 : #endif /* defined(HAVE_LINUX_CAPABILITIES) */
219 :
220 : /** Call setuid and setgid to run as <b>user</b> and switch to their
221 : * primary group. Return 0 on success. On failure, log and return -1.
222 : *
223 : * If SWITCH_ID_KEEP_BINDLOW is set in 'flags', try to use the capability
224 : * system to retain the abilitity to bind low ports.
225 : *
226 : * If SWITCH_ID_WARN_IF_NO_CAPS is set in flags, also warn if we have
227 : * don't have capability support.
228 : */
229 : int
230 0 : switch_id(const char *user, const unsigned flags)
231 : {
232 : #ifndef _WIN32
233 0 : const struct passwd *pw = NULL;
234 0 : uid_t old_uid;
235 0 : gid_t old_gid;
236 0 : static int have_already_switched_id = 0;
237 0 : const int keep_bindlow = !!(flags & SWITCH_ID_KEEP_BINDLOW);
238 0 : const int warn_if_no_caps = !!(flags & SWITCH_ID_WARN_IF_NO_CAPS);
239 :
240 0 : tor_assert(user);
241 :
242 0 : if (have_already_switched_id)
243 : return 0;
244 :
245 : /* Log the initial credential state */
246 0 : if (log_credential_status())
247 : return -1;
248 :
249 0 : log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Changing user and groups");
250 :
251 : /* Get old UID/GID to check if we changed correctly */
252 0 : old_uid = getuid();
253 0 : old_gid = getgid();
254 :
255 : /* Lookup the user and group information, if we have a problem, bail out. */
256 0 : pw = tor_getpwnam(user);
257 0 : if (pw == NULL) {
258 0 : log_warn(LD_CONFIG, "Error setting configured user: %s not found", user);
259 0 : return -1;
260 : }
261 :
262 : #ifdef HAVE_LINUX_CAPABILITIES
263 0 : (void) warn_if_no_caps;
264 0 : if (keep_bindlow) {
265 0 : if (drop_capabilities(1))
266 : return -1;
267 : }
268 : #else /* !defined(HAVE_LINUX_CAPABILITIES) */
269 : (void) keep_bindlow;
270 : if (warn_if_no_caps) {
271 : log_warn(LD_CONFIG, "KeepBindCapabilities set, but no capability support "
272 : "on this system.");
273 : }
274 : #endif /* defined(HAVE_LINUX_CAPABILITIES) */
275 :
276 : /* Properly switch egid,gid,euid,uid here or bail out */
277 0 : if (setgroups(1, &pw->pw_gid)) {
278 0 : log_warn(LD_GENERAL, "Error setting groups to gid %d: \"%s\".",
279 : (int)pw->pw_gid, strerror(errno));
280 0 : if (old_uid == pw->pw_uid) {
281 0 : log_warn(LD_GENERAL, "Tor is already running as %s. You do not need "
282 : "the \"User\" option if you are already running as the user "
283 : "you want to be. (If you did not set the User option in your "
284 : "torrc, check whether it was specified on the command line "
285 : "by a startup script.)", user);
286 : } else {
287 0 : log_warn(LD_GENERAL, "If you set the \"User\" option, you must start Tor"
288 : " as root.");
289 : }
290 0 : return -1;
291 : }
292 :
293 0 : if (setegid(pw->pw_gid)) {
294 0 : log_warn(LD_GENERAL, "Error setting egid to %d: %s",
295 : (int)pw->pw_gid, strerror(errno));
296 0 : return -1;
297 : }
298 :
299 0 : if (setgid(pw->pw_gid)) {
300 0 : log_warn(LD_GENERAL, "Error setting gid to %d: %s",
301 : (int)pw->pw_gid, strerror(errno));
302 0 : return -1;
303 : }
304 :
305 0 : if (setuid(pw->pw_uid)) {
306 0 : log_warn(LD_GENERAL, "Error setting configured uid to %s (%d): %s",
307 : user, (int)pw->pw_uid, strerror(errno));
308 0 : return -1;
309 : }
310 :
311 0 : if (seteuid(pw->pw_uid)) {
312 0 : log_warn(LD_GENERAL, "Error setting configured euid to %s (%d): %s",
313 : user, (int)pw->pw_uid, strerror(errno));
314 0 : return -1;
315 : }
316 :
317 : /* This is how OpenBSD rolls:
318 : if (setgroups(1, &pw->pw_gid) || setegid(pw->pw_gid) ||
319 : setgid(pw->pw_gid) || setuid(pw->pw_uid) || seteuid(pw->pw_uid)) {
320 : setgid(pw->pw_gid) || seteuid(pw->pw_uid) || setuid(pw->pw_uid)) {
321 : log_warn(LD_GENERAL, "Error setting configured UID/GID: %s",
322 : strerror(errno));
323 : return -1;
324 : }
325 : */
326 :
327 : /* We've properly switched egid, gid, euid, uid, and supplementary groups if
328 : * we're here. */
329 : #ifdef HAVE_LINUX_CAPABILITIES
330 0 : if (keep_bindlow) {
331 0 : if (drop_capabilities(0))
332 : return -1;
333 : }
334 : #endif /* defined(HAVE_LINUX_CAPABILITIES) */
335 :
336 : #if !defined(CYGWIN) && !defined(__CYGWIN__)
337 : /* If we tried to drop privilege to a group/user other than root, attempt to
338 : * restore root (E)(U|G)ID, and abort if the operation succeeds */
339 :
340 : /* Only check for privilege dropping if we were asked to be non-root */
341 0 : if (pw->pw_uid) {
342 : /* Try changing GID/EGID */
343 0 : if (pw->pw_gid != old_gid &&
344 0 : (setgid(old_gid) != -1 || setegid(old_gid) != -1)) {
345 0 : log_warn(LD_GENERAL, "Was able to restore group credentials even after "
346 : "switching GID: this means that the setgid code didn't work.");
347 0 : return -1;
348 : }
349 :
350 : /* Try changing UID/EUID */
351 0 : if (pw->pw_uid != old_uid &&
352 0 : (setuid(old_uid) != -1 || seteuid(old_uid) != -1)) {
353 0 : log_warn(LD_GENERAL, "Was able to restore user credentials even after "
354 : "switching UID: this means that the setuid code didn't work.");
355 0 : return -1;
356 : }
357 : }
358 : #endif /* !defined(CYGWIN) && !defined(__CYGWIN__) */
359 :
360 : /* Check what really happened */
361 0 : if (log_credential_status()) {
362 : return -1;
363 : }
364 :
365 0 : have_already_switched_id = 1; /* mark success so we never try again */
366 :
367 : #if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && \
368 : defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
369 0 : if (pw->pw_uid) {
370 : /* Re-enable core dumps if we're not running as root. */
371 0 : log_info(LD_CONFIG, "Re-enabling coredumps");
372 0 : if (prctl(PR_SET_DUMPABLE, 1)) {
373 0 : log_warn(LD_CONFIG, "Unable to re-enable coredumps: %s",strerror(errno));
374 : }
375 : }
376 : #endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && ... */
377 : return 0;
378 :
379 : #else /* defined(_WIN32) */
380 : (void)user;
381 : (void)flags;
382 :
383 : log_warn(LD_CONFIG, "Switching users is unsupported on your OS.");
384 : return -1;
385 : #endif /* !defined(_WIN32) */
386 : }
|