restrict-access.c revision 8f061b0f360e0e94a2753b26360b07b38de70be9
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
002f84aea86371aa079b867c0ec39396b97109d3Lukas Slebodnikstatic gid_t process_primary_gid = (gid_t)-1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic gid_t process_privileged_gid = (gid_t)-1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic bool process_using_priv_gid = FALSE;
87d3b47abba6a40fcf809c85a2b138bc1013d9c5Jakub Hrozekvoid restrict_access_init(struct restrict_access_settings *set)
d3da1c165cdb4c1ec126a8f4b6b544ca415b9d20Pavel Březina const char *ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = t_strdup_printf("%s(%s)", dec2str(uid), pw->pw_name);
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek ret = t_strdup_printf("%s(%s)", dec2str(gid), group->gr_name);
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozekstatic void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid)
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek if (primary_gid == getgid() && primary_gid == getegid()) {
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek /* everything is already set */
48130eef6c5c64a07094b9e8582ba358b2048f24Jakub Hrozek "gid=%s, egid=%s: %m",
48130eef6c5c64a07094b9e8582ba358b2048f24Jakub Hrozek get_gid_str(primary_gid), get_uid_str(geteuid()),
48130eef6c5c64a07094b9e8582ba358b2048f24Jakub Hrozek get_gid_str(getgid()), get_gid_str(getegid()));
faa16fc9f0c9a02b26497e7cf148a92586144c08David Disseldorp if (getegid() != 0 && primary_gid == getgid() &&
faa16fc9f0c9a02b26497e7cf148a92586144c08David Disseldorp /* privileged_gid is hopefully in saved ID. if not,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher there's nothing we can do about it. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (setresgid(primary_gid, primary_gid, privileged_gid) != 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_fatal("setresgid(%s,%s,%s) failed with euid=%s: %m",
d921c1eba437662437847279f251a0a5d8f70127Maxim if (geteuid() == 0) {
d921c1eba437662437847279f251a0a5d8f70127Maxim /* real, effective, saved -> privileged_gid */
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer /* real, effective -> primary_gid
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer saved -> keep */
327127bb7fcc07f882209f029e14026de1b23c94Maxim get_gid_str(primary_gid), get_gid_str(privileged_gid),
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic gid_t *get_groups_list(unsigned int *gid_count_r)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if ((ret = getgroups(gid_count, gid_list)) < 0)
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzkestatic void drop_restricted_groups(const struct restrict_access_settings *set,
772464c842968d6e544118ae1aa7c49a7cda2ad6Stephen Gallagher unsigned int i, used;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_fatal("unknown group name in extra_groups: %s", name);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic void fix_groups_list(const struct restrict_access_settings *set,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher bool preserve_existing, bool *have_root_group)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int i, gid_count;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* if we're using a privileged GID, we can temporarily drop our
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher effective GID. we still want to be able to use its privileges,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher so add it to supplementary groups. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher add_primary_gid = process_privileged_gid != (gid_t)-1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher tmp = set->extra_groups == NULL ? &empty :
9917c138d9a270deb5820915384fbde751190c2aLukas Slebodnik drop_restricted_groups(set, gid_list, &gid_count,
c3889e5a101a075defe533d81f5296d5e680f639Lukas Slebodnik /* see if the list already contains the primary GID */
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina for (i = 0; i < gid_count; i++) {
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher /* Some OSes don't like an empty groups list,
9dbdf62243f01f6aee41c2b5f2976c56da47f25dLukas Slebodnik so use the primary GID as the only one. */
9dbdf62243f01f6aee41c2b5f2976c56da47f25dLukas Slebodnik /* @UNSAFE: add extra groups and/or primary GID to gids list */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher gid_list2 = t_new(gid_t, gid_count + str_array_length(tmp) + 1);
539b1be3507abdf8ac235b06eeed5011b0b5cde2Ondrej Kos memcpy(gid_list2, gid_list, gid_count * sizeof(gid_t));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_fatal("setgroups(%s) failed: Too many extra groups",
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozekvoid restrict_access(const struct restrict_access_settings *set,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher bool is_root, have_root_group, preserve_groups = FALSE;
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek /* set the primary/privileged group */
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek /* set system user's groups */
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek if (set->system_groups_user != NULL && is_root) {
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek /* add extra groups. if we set system user's groups, drop the
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek restricted groups at the same time. */
f5b6f977d4144c28e9c66f3f1c9d634d595d1117Marko Myllynen /* chrooting */
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik /* kludge: localtime() must be called before chroot(),
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik or the timezone isn't known */
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik i_fatal("chroot(%s) failed: %m", set->chroot_dir);
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik /* uid last */
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik i_fatal("setuid(%s) failed with euid=%s: %m",
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik get_uid_str(set->uid), get_uid_str(geteuid()));
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik /* verify that we actually dropped the privileges */
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik if ((set->uid != (uid_t)-1 && set->uid != 0) || disallow_root) {
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik if (setuid(0) == 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_fatal("This process must not be run as root");
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik i_fatal("We couldn't drop root privileges");
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik else if (process_primary_gid == 0 || process_privileged_gid == 0)
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) {
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik i_fatal("We couldn't drop root group privileges "
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik "(wanted=%s, gid=%s, egid=%s)",
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik get_gid_str(getgid()), get_gid_str(getegid()));
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnikvoid restrict_access_set_env(const struct restrict_access_settings *set)
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik if (set->chroot_dir != NULL && *set->chroot_dir != '\0')
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov env_put(t_strconcat("RESTRICT_CHROOT=", set->chroot_dir, NULL));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher env_put(t_strdup_printf("RESTRICT_SETUID=%s",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher env_put(t_strdup_printf("RESTRICT_SETGID=%s",
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek env_put(t_strdup_printf("RESTRICT_SETGID_PRIV=%s",
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek if (set->extra_groups != NULL && *set->extra_groups != '\0') {
77c0d1f6074059dafd2293f9c42ea0f9d60f8aadJakub Hrozek env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=",
e07a94a66985b674c5df11ca466792902164c4e2George McCollister env_put(t_strdup_printf("RESTRICT_GID_FIRST=%s",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic const char *null_if_empty(const char *str)
336879aabae137f9a81304f147fb0d43001654b0Simo Sorcevoid restrict_access_by_env(const char *home, bool disallow_root)
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce const char *value;
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik if ((value = getenv("RESTRICT_SETGID")) != NULL)
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik if ((value = getenv("RESTRICT_SETGID_PRIV")) != NULL)
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik set.privileged_gid = (gid_t)strtol(value, NULL, 10);
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik if ((value = getenv("RESTRICT_GID_FIRST")) != NULL)
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik set.first_valid_gid = (gid_t)strtol(value, NULL, 10);
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik if ((value = getenv("RESTRICT_GID_LAST")) != NULL)
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik set.last_valid_gid = (gid_t)strtol(value, NULL, 10);
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik set.extra_groups = null_if_empty(getenv("RESTRICT_SETEXTRAGROUPS"));
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik set.system_groups_user = null_if_empty(getenv("RESTRICT_USER"));
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik set.chroot_dir = null_if_empty(getenv("RESTRICT_CHROOT"));
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik /* clear the environment, so we don't fail if we get back here */
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik /* if we're dropping privileges before executing and
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik a privileged group is set, the groups must be fixed
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik after exec */
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnikvoid restrict_access_allow_coredumps(bool allow ATTR_UNUSED)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (void)prctl(PR_SET_DUMPABLE, allow, 0, 0, 0);