1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2 
3 #define _GNU_SOURCE /* setresgid() */
4 #include <stdio.h> /* for AIX */
5 #include <sys/types.h>
6 #include <unistd.h>
7 
8 #include "lib.h"
9 #include "str.h"
10 #include "restrict-access.h"
11 #include "env-util.h"
12 #include "ipwd.h"
13 
14 #include <time.h>
15 #ifdef HAVE_PR_SET_DUMPABLE
16 #  include <sys/prctl.h>
17 #endif
18 
19 static gid_t process_primary_gid = (gid_t)-1;
20 static gid_t process_privileged_gid = (gid_t)-1;
21 static bool process_using_priv_gid = FALSE;
22 static char *chroot_dir = NULL;
23 
restrict_access_init(struct restrict_access_settings * set)24 void restrict_access_init(struct restrict_access_settings *set)
25 {
26 	i_zero(set);
27 
28 	set->uid = (uid_t)-1;
29 	set->gid = (gid_t)-1;
30 	set->privileged_gid = (gid_t)-1;
31 }
32 
get_uid_str(uid_t uid)33 static const char *get_uid_str(uid_t uid)
34 {
35 	struct passwd pw;
36 	const char *ret;
37 	int old_errno = errno;
38 
39 	if (i_getpwuid(uid, &pw) <= 0)
40 		ret = dec2str(uid);
41 	else
42 		ret = t_strdup_printf("%s(%s)", dec2str(uid), pw.pw_name);
43 	errno = old_errno;
44 	return ret;
45 }
46 
get_gid_str(gid_t gid)47 static const char *get_gid_str(gid_t gid)
48 {
49 	struct group group;
50 	const char *ret;
51 	int old_errno = errno;
52 
53 	if (i_getgrgid(gid, &group) <= 0)
54 		ret = dec2str(gid);
55 	else
56 		ret = t_strdup_printf("%s(%s)", dec2str(gid), group.gr_name);
57 	errno = old_errno;
58 	return ret;
59 }
60 
restrict_init_groups(gid_t primary_gid,gid_t privileged_gid,const char * gid_source)61 static void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid,
62 				 const char *gid_source)
63 {
64 	string_t *str;
65 
66 	if (privileged_gid == (gid_t)-1) {
67 		if (primary_gid == getgid() && primary_gid == getegid()) {
68 			/* everything is already set */
69 			return;
70 		}
71 
72 		if (setgid(primary_gid) == 0)
73 			return;
74 
75 		str = t_str_new(128);
76 		str_printfa(str, "setgid(%s", get_gid_str(primary_gid));
77 		if (gid_source != NULL)
78 			str_printfa(str, " from %s", gid_source);
79 		str_printfa(str, ") failed with euid=%s, gid=%s, egid=%s: %m "
80 			    "(This binary should probably be called with "
81 			    "process group set to %s instead of %s)",
82 			    get_uid_str(geteuid()),
83 			    get_gid_str(getgid()), get_gid_str(getegid()),
84 			    get_gid_str(primary_gid), get_gid_str(getegid()));
85 		i_fatal("%s", str_c(str));
86 	}
87 
88 	if (getegid() != 0 && primary_gid == getgid() &&
89 	    primary_gid == getegid()) {
90 		/* privileged_gid is hopefully in saved ID. if not,
91 		   there's nothing we can do about it. */
92 		return;
93 	}
94 
95 #ifdef HAVE_SETRESGID
96 	if (setresgid(primary_gid, primary_gid, privileged_gid) != 0) {
97 		i_fatal("setresgid(%s,%s,%s) failed with euid=%s: %m",
98 			get_gid_str(primary_gid), get_gid_str(primary_gid),
99 			get_gid_str(privileged_gid), get_uid_str(geteuid()));
100 	}
101 #else
102 	if (geteuid() == 0) {
103 		/* real, effective, saved -> privileged_gid */
104 		if (setgid(privileged_gid) < 0) {
105 			i_fatal("setgid(%s) failed: %m",
106 				get_gid_str(privileged_gid));
107 		}
108 	}
109 	/* real, effective -> primary_gid
110 	   saved -> keep */
111 	if (setregid(primary_gid, primary_gid) != 0) {
112 		i_fatal("setregid(%s,%s) failed with euid=%s: %m",
113 			get_gid_str(primary_gid), get_gid_str(privileged_gid),
114 			get_uid_str(geteuid()));
115 	}
116 #endif
117 }
118 
restrict_get_groups_list(unsigned int * gid_count_r)119 gid_t *restrict_get_groups_list(unsigned int *gid_count_r)
120 {
121 	gid_t *gid_list;
122 	int ret, gid_count;
123 
124 	if ((gid_count = getgroups(0, NULL)) < 0)
125 		i_fatal("getgroups() failed: %m");
126 
127 	/* @UNSAFE */
128 	gid_list = t_new(gid_t, gid_count+1); /* +1 in case gid_count=0 */
129 	if ((ret = getgroups(gid_count, gid_list)) < 0)
130 		i_fatal("getgroups() failed: %m");
131 
132 	*gid_count_r = ret;
133 	return gid_list;
134 }
135 
drop_restricted_groups(const struct restrict_access_settings * set,gid_t * gid_list,unsigned int * gid_count,bool * have_root_group)136 static void drop_restricted_groups(const struct restrict_access_settings *set,
137 				   gid_t *gid_list, unsigned int *gid_count,
138 				   bool *have_root_group)
139 {
140 	/* @UNSAFE */
141 	unsigned int i, used;
142 
143 	for (i = 0, used = 0; i < *gid_count; i++) {
144 		if (gid_list[i] >= set->first_valid_gid &&
145 		    (set->last_valid_gid == 0 ||
146 		     gid_list[i] <= set->last_valid_gid)) {
147 			if (gid_list[i] == 0)
148 				*have_root_group = TRUE;
149 			gid_list[used++] = gid_list[i];
150 		}
151 	}
152 	*gid_count = used;
153 }
154 
get_group_id(const char * name)155 static gid_t get_group_id(const char *name)
156 {
157 	struct group group;
158 	gid_t gid;
159 
160 	if (str_to_gid(name, &gid) == 0)
161 		return gid;
162 
163 	switch (i_getgrnam(name, &group)) {
164 	case -1:
165 		i_fatal("getgrnam(%s) failed: %m", name);
166 	case 0:
167 		i_fatal("unknown group name in extra_groups: %s", name);
168 	default:
169 		return group.gr_gid;
170 	}
171 }
172 
fix_groups_list(const struct restrict_access_settings * set,bool preserve_existing,bool * have_root_group)173 static void fix_groups_list(const struct restrict_access_settings *set,
174 			    bool preserve_existing, bool *have_root_group)
175 {
176 	gid_t gid, *gid_list, *gid_list2;
177 	const char *const *tmp, *empty = NULL;
178 	unsigned int i, gid_count;
179 	bool add_primary_gid;
180 
181 	/* if we're using a privileged GID, we can temporarily drop our
182 	   effective GID. we still want to be able to use its privileges,
183 	   so add it to supplementary groups. */
184 	add_primary_gid = process_privileged_gid != (gid_t)-1;
185 
186 	tmp = set->extra_groups == NULL ? &empty :
187 		t_strsplit_spaces(set->extra_groups, ", ");
188 
189 	if (preserve_existing) {
190 		gid_list = restrict_get_groups_list(&gid_count);
191 		drop_restricted_groups(set, gid_list, &gid_count,
192 				       have_root_group);
193 		/* see if the list already contains the primary GID */
194 		for (i = 0; i < gid_count; i++) {
195 			if (gid_list[i] == process_primary_gid) {
196 				add_primary_gid = FALSE;
197 				break;
198 			}
199 		}
200 	} else {
201 		gid_list = NULL;
202 		gid_count = 0;
203 	}
204 	if (gid_count == 0) {
205 		/* Some OSes don't like an empty groups list,
206 		   so use the primary GID as the only one. */
207 		gid_list = t_new(gid_t, 2);
208 		gid_list[0] = process_primary_gid;
209 		gid_count = 1;
210 		add_primary_gid = FALSE;
211 	}
212 
213 	if (*tmp != NULL || add_primary_gid) {
214 		/* @UNSAFE: add extra groups and/or primary GID to gids list */
215 		gid_list2 = t_new(gid_t, gid_count + str_array_length(tmp) + 1);
216 		memcpy(gid_list2, gid_list, gid_count * sizeof(gid_t));
217 		for (; *tmp != NULL; tmp++) {
218 			gid = get_group_id(*tmp);
219 			if (gid != process_primary_gid)
220 				gid_list2[gid_count++] = gid;
221 		}
222 		if (add_primary_gid)
223 			gid_list2[gid_count++] = process_primary_gid;
224 		gid_list = gid_list2;
225 	}
226 
227 	if (setgroups(gid_count, gid_list) < 0) {
228 		if (errno == EINVAL) {
229 			i_fatal("setgroups(%s) failed: Too many extra groups",
230 				set->extra_groups == NULL ? "" :
231 				set->extra_groups);
232 		} else {
233 			i_fatal("setgroups() failed: %m");
234 		}
235 	}
236 }
237 
238 static const char *
get_setuid_error_str(const struct restrict_access_settings * set,uid_t target_uid)239 get_setuid_error_str(const struct restrict_access_settings *set, uid_t target_uid)
240 {
241 	string_t *str = t_str_new(128);
242 
243 	str_printfa(str, "setuid(%s", get_uid_str(target_uid));
244 	if (set->uid_source != NULL)
245 		str_printfa(str, " from %s", set->uid_source);
246 	str_printfa(str, ") failed with euid=%s: %m ",
247 		    get_uid_str(geteuid()));
248 	if (errno == EAGAIN) {
249 		str_append(str, "(ulimit -u reached)");
250 	} else {
251 		str_printfa(str, "(This binary should probably be called with "
252 			    "process user set to %s instead of %s)",
253 			    get_uid_str(target_uid), get_uid_str(geteuid()));
254 	}
255 	return str_c(str);
256 }
257 
restrict_access(const struct restrict_access_settings * set,enum restrict_access_flags flags,const char * home)258 void restrict_access(const struct restrict_access_settings *set,
259 		     enum restrict_access_flags flags, const char *home)
260 {
261 	bool is_root, have_root_group, preserve_groups = FALSE;
262 	bool allow_root_gid;
263 	bool allow_root = (flags & RESTRICT_ACCESS_FLAG_ALLOW_ROOT) != 0;
264 	uid_t target_uid = set->uid;
265 
266 	is_root = geteuid() == 0;
267 
268 	if (!is_root &&
269 	    !set->allow_setuid_root &&
270 	    getuid() == 0) {
271 		/* recover current effective UID */
272 		if (target_uid == (uid_t)-1)
273 			target_uid = geteuid();
274 		else
275 			i_assert(target_uid > 0);
276 		/* try to elevate to root */
277 		if (seteuid(0) < 0)
278 			i_fatal("seteuid(0) failed: %m");
279 		is_root = TRUE;
280 	}
281 
282 	/* set the primary/privileged group */
283 	process_primary_gid = set->gid;
284 	process_privileged_gid = set->privileged_gid;
285 	if (process_privileged_gid == process_primary_gid) {
286 		/* a pointless configuration, ignore it */
287 		process_privileged_gid = (gid_t)-1;
288 	}
289 
290 	have_root_group = process_primary_gid == 0;
291 	if (process_primary_gid != (gid_t)-1 ||
292 	    process_privileged_gid != (gid_t)-1) {
293 		if (process_primary_gid == (gid_t)-1)
294 			process_primary_gid = getegid();
295 		restrict_init_groups(process_primary_gid,
296 				     process_privileged_gid, set->gid_source);
297 	} else {
298 		if (process_primary_gid == (gid_t)-1)
299 			process_primary_gid = getegid();
300 	}
301 
302 	/* set system user's groups */
303 	if (set->system_groups_user != NULL && is_root) {
304 		if (initgroups(set->system_groups_user,
305 			       process_primary_gid) < 0) {
306 			i_fatal("initgroups(%s, %s) failed: %m",
307 				set->system_groups_user,
308 				get_gid_str(process_primary_gid));
309 		}
310 		preserve_groups = TRUE;
311 	}
312 
313 	/* add extra groups. if we set system user's groups, drop the
314 	   restricted groups at the same time. */
315 	if (is_root) T_BEGIN {
316 		fix_groups_list(set, preserve_groups,
317 				&have_root_group);
318 	} T_END;
319 
320 	/* chrooting */
321 	if (set->chroot_dir != NULL) {
322 		/* kludge: localtime() must be called before chroot(),
323 		   or the timezone isn't known */
324 		time_t t = 0;
325 		(void)localtime(&t);
326 
327 		if (chroot(set->chroot_dir) != 0)
328 			i_fatal("chroot(%s) failed: %m", set->chroot_dir);
329 		/* makes static analyzers happy, and is more secure */
330 		if (chdir("/") != 0)
331 			i_fatal("chdir(/) failed: %m");
332 
333 		chroot_dir = i_strdup(set->chroot_dir);
334 
335 		if (home != NULL) {
336 			if (chdir(home) < 0) {
337 				i_error("chdir(%s) failed: %m", home);
338 			}
339 		}
340 	}
341 
342 	/* uid last */
343 	if (target_uid != (uid_t)-1) {
344 		if (setuid(target_uid) != 0)
345 			i_fatal("%s", get_setuid_error_str(set, target_uid));
346 	}
347 
348 	/* verify that we actually dropped the privileges */
349 	if ((target_uid != (uid_t)-1 && target_uid != 0) || !allow_root) {
350 		if (setuid(0) == 0) {
351 			if (!allow_root &&
352 			    (target_uid == 0 || target_uid == (uid_t)-1))
353 				i_fatal("This process must not be run as root");
354 
355 			i_fatal("We couldn't drop root privileges");
356 		}
357 	}
358 
359 	if (set->first_valid_gid != 0)
360 		allow_root_gid = FALSE;
361 	else if (process_primary_gid == 0 || process_privileged_gid == 0)
362 		allow_root_gid = TRUE;
363 	else
364 		allow_root_gid = FALSE;
365 
366 	if (!allow_root_gid && target_uid != 0 &&
367 	    (target_uid != (uid_t)-1 || !is_root)) {
368 		if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) {
369 			if (process_primary_gid == 0)
370 				i_fatal("GID 0 isn't permitted");
371 			i_fatal("We couldn't drop root group privileges "
372 				"(wanted=%s, gid=%s, egid=%s)",
373 				get_gid_str(process_primary_gid),
374 				get_gid_str(getgid()), get_gid_str(getegid()));
375 		}
376 	}
377 }
378 
restrict_access_set_env(const struct restrict_access_settings * set)379 void restrict_access_set_env(const struct restrict_access_settings *set)
380 {
381 	if (set->system_groups_user != NULL &&
382 	    *set->system_groups_user != '\0')
383 		env_put("RESTRICT_USER", set->system_groups_user);
384 	if (set->chroot_dir != NULL && *set->chroot_dir != '\0')
385 		env_put("RESTRICT_CHROOT", set->chroot_dir);
386 
387 	if (set->uid != (uid_t)-1)
388 		env_put("RESTRICT_SETUID", dec2str(set->uid));
389 	if (set->gid != (gid_t)-1)
390 		env_put("RESTRICT_SETGID", dec2str(set->gid));
391 	if (set->privileged_gid != (gid_t)-1)
392 		env_put("RESTRICT_SETGID_PRIV", dec2str(set->privileged_gid));
393 	if (set->extra_groups != NULL && *set->extra_groups != '\0')
394 		env_put("RESTRICT_SETEXTRAGROUPS", set->extra_groups);
395 
396 	if (set->first_valid_gid != 0)
397 		env_put("RESTRICT_GID_FIRST", dec2str(set->first_valid_gid));
398 	if (set->last_valid_gid != 0)
399 		env_put("RESTRICT_GID_LAST", dec2str(set->last_valid_gid));
400 }
401 
null_if_empty(const char * str)402 static const char *null_if_empty(const char *str)
403 {
404 	return str == NULL || *str == '\0' ? NULL : str;
405 }
406 
restrict_access_get_env(struct restrict_access_settings * set_r)407 void restrict_access_get_env(struct restrict_access_settings *set_r)
408 {
409 	const char *value;
410 
411 	restrict_access_init(set_r);
412 	if ((value = getenv("RESTRICT_SETUID")) != NULL) {
413 		if (str_to_uid(value, &set_r->uid) < 0)
414 			i_fatal("Invalid uid: %s", value);
415 	}
416 	if ((value = getenv("RESTRICT_SETGID")) != NULL) {
417 		if (str_to_gid(value, &set_r->gid) < 0)
418 			i_fatal("Invalid gid: %s", value);
419 	}
420 	if ((value = getenv("RESTRICT_SETGID_PRIV")) != NULL) {
421 		if (str_to_gid(value, &set_r->privileged_gid) < 0)
422 			i_fatal("Invalid privileged_gid: %s", value);
423 	}
424 	if ((value = getenv("RESTRICT_GID_FIRST")) != NULL) {
425 		if (str_to_gid(value, &set_r->first_valid_gid) < 0)
426 			i_fatal("Invalid first_valid_gid: %s", value);
427 	}
428 	if ((value = getenv("RESTRICT_GID_LAST")) != NULL) {
429 		if (str_to_gid(value, &set_r->last_valid_gid) < 0)
430 			i_fatal("Invalid last_value_gid: %s", value);
431 	}
432 
433 	set_r->extra_groups = null_if_empty(getenv("RESTRICT_SETEXTRAGROUPS"));
434 	set_r->system_groups_user = null_if_empty(getenv("RESTRICT_USER"));
435 	set_r->chroot_dir = null_if_empty(getenv("RESTRICT_CHROOT"));
436 }
437 
restrict_access_by_env(enum restrict_access_flags flags,const char * home)438 void restrict_access_by_env(enum restrict_access_flags flags, const char *home)
439 {
440 	struct restrict_access_settings set;
441 
442 	restrict_access_get_env(&set);
443 	restrict_access(&set, flags, home);
444 
445 	/* clear the environment, so we don't fail if we get back here */
446 	env_remove("RESTRICT_SETUID");
447 	if (process_privileged_gid == (gid_t)-1) {
448 		/* if we're dropping privileges before executing and
449 		   a privileged group is set, the groups must be fixed
450 		   after exec */
451 		env_remove("RESTRICT_SETGID");
452 		env_remove("RESTRICT_SETGID_PRIV");
453 	}
454 	env_remove("RESTRICT_GID_FIRST");
455 	env_remove("RESTRICT_GID_LAST");
456 	if (getuid() != 0)
457 		env_remove("RESTRICT_SETEXTRAGROUPS");
458 	else {
459 		/* Preserve RESTRICT_SETEXTRAGROUPS, so if we're again dropping
460 		   more privileges we'll still preserve the extra groups. This
461 		   mainly means preserving service { extra_groups } for lmtp
462 		   and doveadm accesses. */
463 	}
464 	env_remove("RESTRICT_USER");
465 	env_remove("RESTRICT_CHROOT");
466 }
467 
restrict_access_get_current_chroot(void)468 const char *restrict_access_get_current_chroot(void)
469 {
470 	return chroot_dir;
471 }
472 
restrict_access_set_dumpable(bool allow ATTR_UNUSED)473 void restrict_access_set_dumpable(bool allow ATTR_UNUSED)
474 {
475 #ifdef HAVE_PR_SET_DUMPABLE
476 	if (prctl(PR_SET_DUMPABLE, allow ? 1 : 0, 0, 0, 0) < 0)
477 		i_error("prctl(PR_SET_DUMPABLE) failed: %m");
478 #endif
479 }
480 
restrict_access_get_dumpable(void)481 bool restrict_access_get_dumpable(void)
482 {
483 #ifdef HAVE_PR_SET_DUMPABLE
484 	bool allow = FALSE;
485 	if (prctl(PR_GET_DUMPABLE, &allow, 0, 0, 0) < 0)
486 		i_error("prctl(PR_GET_DUMPABLE) failed: %m");
487 	return allow;
488 #endif
489 	return TRUE;
490 }
491 
restrict_access_allow_coredumps(bool allow)492 void restrict_access_allow_coredumps(bool allow)
493 {
494 	if (getenv("PR_SET_DUMPABLE") != NULL) {
495 		restrict_access_set_dumpable(allow);
496 	}
497 }
498 
restrict_access_use_priv_gid(void)499 int restrict_access_use_priv_gid(void)
500 {
501 	i_assert(!process_using_priv_gid);
502 
503 	if (process_privileged_gid == (gid_t)-1)
504 		return 0;
505 	if (setegid(process_privileged_gid) < 0) {
506 		i_error("setegid(privileged) failed: %m");
507 		return -1;
508 	}
509 	process_using_priv_gid = TRUE;
510 	return 0;
511 }
512 
restrict_access_drop_priv_gid(void)513 void restrict_access_drop_priv_gid(void)
514 {
515 	if (!process_using_priv_gid)
516 		return;
517 
518 	if (setegid(process_primary_gid) < 0)
519 		i_fatal("setegid(primary) failed: %m");
520 	process_using_priv_gid = FALSE;
521 }
522 
restrict_access_have_priv_gid(void)523 bool restrict_access_have_priv_gid(void)
524 {
525 	return process_privileged_gid != (gid_t)-1;
526 }
527 
restrict_access_deinit(void)528 void restrict_access_deinit(void)
529 {
530 	i_free(chroot_dir);
531 }
532