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