restrict-access.c revision de0034cc6bb52585bc82289801435418a7ee7298
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainenstatic gid_t process_privileged_gid = (gid_t)-1;
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainenvoid restrict_access_init(struct restrict_access_settings *set)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen const char *ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen ret = t_strdup_printf("%s(%s)", dec2str(uid), pw.pw_name);
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen const char *ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen ret = t_strdup_printf("%s(%s)", dec2str(gid), group.gr_name);
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainenstatic void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (primary_gid == getgid() && primary_gid == getegid()) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* everything is already set */
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen str_printfa(str, "setgid(%s", get_gid_str(primary_gid));
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen str_printfa(str, ") failed with euid=%s, gid=%s, egid=%s: %m "
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen "(This binary should probably be called with "
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen "process group set to %s instead of %s)",
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen get_gid_str(getgid()), get_gid_str(getegid()),
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen get_gid_str(primary_gid), get_gid_str(getegid()));
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen if (getegid() != 0 && primary_gid == getgid() &&
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* privileged_gid is hopefully in saved ID. if not,
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen there's nothing we can do about it. */
5702c81e2d788449c3bc207eb9c19e539458ad9eTimo Sirainen if (setresgid(primary_gid, primary_gid, privileged_gid) != 0) {
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen i_fatal("setresgid(%s,%s,%s) failed with euid=%s: %m",
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen get_gid_str(primary_gid), get_gid_str(primary_gid),
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen get_gid_str(privileged_gid), get_uid_str(geteuid()));
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (geteuid() == 0) {
5702c81e2d788449c3bc207eb9c19e539458ad9eTimo Sirainen /* real, effective, saved -> privileged_gid */
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen /* real, effective -> primary_gid
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen saved -> keep */
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen if (setregid(primary_gid, primary_gid) != 0) {
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen i_fatal("setregid(%s,%s) failed with euid=%s: %m",
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen get_gid_str(primary_gid), get_gid_str(privileged_gid),
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainengid_t *restrict_get_groups_list(unsigned int *gid_count_r)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* @UNSAFE */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen gid_list = t_new(gid_t, gid_count+1); /* +1 in case gid_count=0 */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if ((ret = getgroups(gid_count, gid_list)) < 0)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstatic void drop_restricted_groups(const struct restrict_access_settings *set,
8d56f3334e22619abf56833d290bb1f49ac6722cTimo Sirainen /* @UNSAFE */
8d56f3334e22619abf56833d290bb1f49ac6722cTimo Sirainen unsigned int i, used;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_fatal("unknown group name in extra_groups: %s", name);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstatic void fix_groups_list(const struct restrict_access_settings *set,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen bool preserve_existing, bool *have_root_group)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen unsigned int i, gid_count;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* if we're using a privileged GID, we can temporarily drop our
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen effective GID. we still want to be able to use its privileges,
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen so add it to supplementary groups. */
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen add_primary_gid = process_privileged_gid != (gid_t)-1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen gid_list = restrict_get_groups_list(&gid_count);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen drop_restricted_groups(set, gid_list, &gid_count,
5297aa3ceddf3a4ecc09f49c832bc424eff8f906Timo Sirainen /* see if the list already contains the primary GID */
5702c81e2d788449c3bc207eb9c19e539458ad9eTimo Sirainen for (i = 0; i < gid_count; i++) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* Some OSes don't like an empty groups list,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen so use the primary GID as the only one. */
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen /* @UNSAFE: add extra groups and/or primary GID to gids list */
5702c81e2d788449c3bc207eb9c19e539458ad9eTimo Sirainen gid_list2 = t_new(gid_t, gid_count + str_array_length(tmp) + 1);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen memcpy(gid_list2, gid_list, gid_count * sizeof(gid_t));
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen i_fatal("setgroups(%s) failed: Too many extra groups",
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstatic const char *
f2b95f63ebdf77dba4dac938cf8c65c839f1067dTimo Sirainenget_setuid_error_str(const struct restrict_access_settings *set, uid_t target_uid)
5702c81e2d788449c3bc207eb9c19e539458ad9eTimo Sirainen str_printfa(str, "setuid(%s", get_uid_str(target_uid));
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen str_printfa(str, " from %s", set->uid_source);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen str_printfa(str, ") failed with euid=%s: %m ",
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen str_printfa(str, "(This binary should probably be called with "
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen "process user set to %s instead of %s)",
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen get_uid_str(target_uid), get_uid_str(geteuid()));
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainenvoid restrict_access(const struct restrict_access_settings *set,
8451cf67733f6633510f6619301474be349c5035Timo Sirainen bool is_root, have_root_group, preserve_groups = FALSE;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen /* recover current effective UID */
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen /* try to elevate to root */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* set the primary/privileged group */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (process_privileged_gid == process_primary_gid) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* a pointless configuration, ignore it */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* set system user's groups */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if (set->system_groups_user != NULL && is_root) {
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen /* add extra groups. if we set system user's groups, drop the
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen restricted groups at the same time. */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* chrooting */
1fb8ce8b21d0616796ced699b1573b5dd0b61793Timo Sirainen /* kludge: localtime() must be called before chroot(),
1fb8ce8b21d0616796ced699b1573b5dd0b61793Timo Sirainen or the timezone isn't known */
9ec30d84a736a2d0726b600213dcf630ff28bdebTimo Sirainen i_fatal("chroot(%s) failed: %m", set->chroot_dir);
9ec30d84a736a2d0726b600213dcf630ff28bdebTimo Sirainen /* makes static analyzers happy, and is more secure */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen /* uid last */
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen i_fatal("%s", get_setuid_error_str(set, target_uid));
5702c81e2d788449c3bc207eb9c19e539458ad9eTimo Sirainen /* verify that we actually dropped the privileges */
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen if ((target_uid != (uid_t)-1 && target_uid != 0) || disallow_root) {
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if (setuid(0) == 0) {
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen i_fatal("This process must not be run as root");
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen else if (process_primary_gid == 0 || process_privileged_gid == 0)
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_fatal("We couldn't drop root group privileges "
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen "(wanted=%s, gid=%s, egid=%s)",
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen get_gid_str(getgid()), get_gid_str(getegid()));
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainenvoid restrict_access_set_env(const struct restrict_access_settings *set)
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen if (set->chroot_dir != NULL && *set->chroot_dir != '\0')
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen env_put(t_strconcat("RESTRICT_CHROOT=", set->chroot_dir, NULL));
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen env_put(t_strdup_printf("RESTRICT_SETGID_PRIV=%s",
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (set->extra_groups != NULL && *set->extra_groups != '\0') {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=",
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen env_put(t_strdup_printf("RESTRICT_GID_FIRST=%s",
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen env_put(t_strdup_printf("RESTRICT_GID_LAST=%s",
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstatic const char *null_if_empty(const char *str)
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen return str == NULL || *str == '\0' ? NULL : str;
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainenvoid restrict_access_get_env(struct restrict_access_settings *set_r)
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen if ((value = getenv("RESTRICT_SETUID")) != NULL) {
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen if ((value = getenv("RESTRICT_SETGID")) != NULL) {
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen if ((value = getenv("RESTRICT_SETGID_PRIV")) != NULL) {
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen if (str_to_gid(value, &set_r->privileged_gid) < 0)
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen if ((value = getenv("RESTRICT_GID_FIRST")) != NULL) {
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen if (str_to_gid(value, &set_r->first_valid_gid) < 0)
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen i_fatal("Invalid first_valid_gid: %s", value);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen if ((value = getenv("RESTRICT_GID_LAST")) != NULL) {
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen if (str_to_gid(value, &set_r->last_valid_gid) < 0)
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen set_r->extra_groups = null_if_empty(getenv("RESTRICT_SETEXTRAGROUPS"));
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen set_r->system_groups_user = null_if_empty(getenv("RESTRICT_USER"));
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen set_r->chroot_dir = null_if_empty(getenv("RESTRICT_CHROOT"));
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainenvoid restrict_access_by_env(const char *home, bool disallow_root)
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen /* clear the environment, so we don't fail if we get back here */
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen /* if we're dropping privileges before executing and
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen a privileged group is set, the groups must be fixed
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen after exec */
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen /* Preserve RESTRICT_SETEXTRAGROUPS, so if we're again dropping
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen more privileges we'll still preserve the extra groups. This
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen mainly means preserving service { extra_groups } for lmtp
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen and doveadm accesses. */
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainenconst char *restrict_access_get_current_chroot(void)
#ifdef HAVE_PR_SET_DUMPABLE
bool restrict_access_get_dumpable(void)
#ifdef HAVE_PR_SET_DUMPABLE
return allow;
return TRUE;
int restrict_access_use_priv_gid(void)
void restrict_access_drop_priv_gid(void)
if (!process_using_priv_gid)
bool restrict_access_have_priv_gid(void)