restrict-access.c revision d7b81a1e17f5b115cb1b36bf6c6f64295e357dc2
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic gid_t process_privileged_gid = (gid_t)-1;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenvoid restrict_access_init(struct restrict_access_settings *set)
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen const char *ret;
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen ret = t_strdup_printf("%s(%s)", dec2str(uid), pw->pw_name);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = t_strdup_printf("%s(%s)", dec2str(gid), group->gr_name);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (primary_gid == getgid() && primary_gid == getegid()) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* everything is already set */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "euid=%s, gid=%s, egid=%s: %m "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "(This binary should probably be called with "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "process group set to %s instead of %s)",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen get_gid_str(primary_gid), get_uid_str(geteuid()),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen get_gid_str(getgid()), get_gid_str(getegid()),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen get_gid_str(primary_gid), get_uid_str(geteuid()));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (getegid() != 0 && primary_gid == getgid() &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* privileged_gid is hopefully in saved ID. if not,
ca843e046e98b12f4730f4b87ee2e1a659c26e78Timo Sirainen there's nothing we can do about it. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (setresgid(primary_gid, primary_gid, privileged_gid) != 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal("setresgid(%s,%s,%s) failed with euid=%s: %m",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen get_gid_str(primary_gid), get_gid_str(primary_gid),
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen get_gid_str(privileged_gid), get_uid_str(geteuid()));
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen if (geteuid() == 0) {
009217abb57a24a4076092e8e4e165545747839eStephan Bosch /* real, effective, saved -> privileged_gid */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* real, effective -> primary_gid
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen saved -> keep */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (setregid(primary_gid, primary_gid) != 0) {
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch i_fatal("setregid(%s,%s) failed with euid=%s: %m",
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch get_gid_str(primary_gid), get_gid_str(privileged_gid),
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Boschgid_t *restrict_get_groups_list(unsigned int *gid_count_r)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* @UNSAFE */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((ret = getgroups(gid_count, gid_list)) < 0)
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainenstatic void drop_restricted_groups(const struct restrict_access_settings *set,
8ce3071e80b9973230048ecadfcb073fb82cc69fTimo Sirainen /* @UNSAFE */
8ce3071e80b9973230048ecadfcb073fb82cc69fTimo Sirainen unsigned int i, used;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal("unknown group name in extra_groups: %s", name);
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainenstatic void fix_groups_list(const struct restrict_access_settings *set,
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen bool preserve_existing, bool *have_root_group)
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen unsigned int i, gid_count;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* if we're using a privileged GID, we can temporarily drop our
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen effective GID. we still want to be able to use its privileges,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen so add it to supplementary groups. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen add_primary_gid = process_privileged_gid != (gid_t)-1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen gid_list = restrict_get_groups_list(&gid_count);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen drop_restricted_groups(set, gid_list, &gid_count,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* see if the list already contains the primary GID */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = 0; i < gid_count; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* Some OSes don't like an empty groups list,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen so use the primary GID as the only one. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* @UNSAFE: add extra groups and/or primary GID to gids list */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen gid_list2 = t_new(gid_t, gid_count + str_array_length(tmp) + 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memcpy(gid_list2, gid_list, gid_count * sizeof(gid_t));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal("setgroups(%s) failed: Too many extra groups",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid restrict_access(const struct restrict_access_settings *set,
e911b23f3e05308df9b98b1a3fdaf72e4302d8fdTimo Sirainen bool is_root, have_root_group, preserve_groups = FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* set the primary/privileged group */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* set system user's groups */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (set->system_groups_user != NULL && is_root) {
77f1da4b5e2b800197d8db548235497d5e9d6a4fTimo Sirainen /* add extra groups. if we set system user's groups, drop the
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen restricted groups at the same time. */
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen /* chrooting */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* kludge: localtime() must be called before chroot(),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen or the timezone isn't known */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal("chroot(%s) failed: %m", set->chroot_dir);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* uid last */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "(This binary should probably be called with "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "process user set to %s instead of %s)",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen get_uid_str(set->uid), get_uid_str(geteuid()),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen get_uid_str(set->uid), get_uid_str(geteuid()));
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen /* verify that we actually dropped the privileges */
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen if ((set->uid != (uid_t)-1 && set->uid != 0) || disallow_root) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (setuid(0) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal("This process must not be run as root");
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen else if (process_primary_gid == 0 || process_privileged_gid == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal("We couldn't drop root group privileges "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "(wanted=%s, gid=%s, egid=%s)",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen get_gid_str(getgid()), get_gid_str(getegid()));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenvoid restrict_access_set_env(const struct restrict_access_settings *set)
e7dd5065d21c569e5e6ddd713bd345dadd1cf51dTimo Sirainen if (set->chroot_dir != NULL && *set->chroot_dir != '\0')
c51644e9e04effbbc9c415cadcfbcb4d9465855cTimo Sirainen env_put(t_strconcat("RESTRICT_CHROOT=", set->chroot_dir, NULL));
f0d93763f210ecdb85a115fdd0210a16cfc5ff5cTimo Sirainen env_put(t_strdup_printf("RESTRICT_SETGID_PRIV=%s",
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen if (set->extra_groups != NULL && *set->extra_groups != '\0') {
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen env_put(t_strdup_printf("RESTRICT_GID_FIRST=%s",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen env_put(t_strdup_printf("RESTRICT_GID_LAST=%s",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic const char *null_if_empty(const char *str)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return str == NULL || *str == '\0' ? NULL : str;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenvoid restrict_access_by_env(const char *home, bool disallow_root)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if ((value = getenv("RESTRICT_SETUID")) != NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((value = getenv("RESTRICT_SETGID")) != NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((value = getenv("RESTRICT_SETGID_PRIV")) != NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen set.privileged_gid = (gid_t)strtol(value, NULL, 10);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((value = getenv("RESTRICT_GID_FIRST")) != NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen set.first_valid_gid = (gid_t)strtol(value, NULL, 10);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((value = getenv("RESTRICT_GID_LAST")) != NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen set.last_valid_gid = (gid_t)strtol(value, NULL, 10);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen set.extra_groups = null_if_empty(getenv("RESTRICT_SETEXTRAGROUPS"));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen set.system_groups_user = null_if_empty(getenv("RESTRICT_USER"));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen set.chroot_dir = null_if_empty(getenv("RESTRICT_CHROOT"));
42fcc708268a89aa9640693e71d13a2bb76e19c8Timo Sirainen /* clear the environment, so we don't fail if we get back here */
42fcc708268a89aa9640693e71d13a2bb76e19c8Timo Sirainen /* if we're dropping privileges before executing and
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen a privileged group is set, the groups must be fixed
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen after exec */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenconst char *restrict_access_get_current_chroot(void)