restrict-access.c revision 0c450f355c18905dd18d1559bf815d6b6332cd26
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bosestatic gid_t primary_gid = (gid_t)-1, privileged_gid = (gid_t)-1;
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bosevoid restrict_access_set_env(const char *user, uid_t uid,
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose env_put(t_strconcat("RESTRICT_USER=", user, NULL));
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose env_put(t_strconcat("RESTRICT_CHROOT=", chroot_dir, NULL));
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose env_put(t_strdup_printf("RESTRICT_SETUID=%s", dec2str(uid)));
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose env_put(t_strdup_printf("RESTRICT_SETGID=%s", dec2str(gid)));
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose env_put(t_strdup_printf("RESTRICT_SETGID_PRIV=%s",
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose if (extra_groups != NULL && *extra_groups != '\0') {
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bosestatic void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid)
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose if (primary_gid == getgid() && primary_gid == getegid()) {
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* everything is already set */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose "gid=%s, egid=%s: %m",
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov dec2str(primary_gid), dec2str(geteuid()),
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* privileged_gid is hopefully in saved ID. if not,
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose there's nothing we can do about it. */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose if (setresgid(primary_gid, primary_gid, privileged_gid) != 0) {
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose i_fatal("setresgid(%s,%s,%s) failed with euid=%s: %m",
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* real: primary_gid
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose effective: privileged_gid
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose saved: privileged_gid */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose if (setregid(primary_gid, privileged_gid) != 0) {
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose i_fatal("setregid(%s,%s) failed with euid=%s: %m",
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov dec2str(primary_gid), dec2str(privileged_gid),
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* effective: privileged_gid -> primary_gid */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bosestatic gid_t *get_groups_list(unsigned int *gid_count_r)
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* @UNSAFE */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bosestatic bool drop_restricted_groups(gid_t *gid_list, unsigned int *gid_count,
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose const char *env;
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov unsigned int i, used;
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose first_valid = env == NULL ? 0 : (gid_t)strtoul(env, NULL, 10);
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose last_valid = env == NULL ? (gid_t)-1 : (gid_t)strtoul(env, NULL, 10);
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov for (i = 0, used = 0; i < *gid_count; i++) {
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose (last_valid == (gid_t)-1 || gid_list[i] <= last_valid)) {
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose i_fatal("unknown group name in extra_groups: %s", name);
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashovstatic void fix_groups_list(const char *extra_groups, gid_t egid,
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose unsigned int gid_count;
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose if (!drop_restricted_groups(gid_list, &gid_count,
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* nothing dropped, no extra groups to grant. */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* nothing to do */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* Some OSes don't like an empty groups list,
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose so use the effective GID as the only one. */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose gid_list[0] = egid != (gid_t)-1 ? egid : getegid();
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* @UNSAFE: add extra groups to gids list */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose gid_list2 = t_new(gid_t, gid_count + str_array_length(tmp));
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose memcpy(gid_list2, gid_list, gid_count * sizeof(gid_t));
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose i_fatal("setgroups(%s) failed: Too many extra groups",
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose const char *env;
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov bool is_root, have_root_group, preserve_groups = FALSE;
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* set the primary/privileged group */
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov primary_gid = env == NULL || *env == '\0' ? (gid_t)-1 :
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose privileged_gid = env == NULL || *env == '\0' ? (gid_t)-1 :
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose if (primary_gid != (gid_t)-1 || privileged_gid != (gid_t)-1) {
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose restrict_init_groups(primary_gid, privileged_gid);
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov /* set system user's groups */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* add extra groups. if we set system user's groups, drop the
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose restricted groups at the same time. */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose fix_groups_list(env, primary_gid, preserve_groups,
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* chrooting */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* kludge: localtime() must be called before chroot(),
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose or the timezone isn't known */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* uid last */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose uid = env == NULL || *env == '\0' ? 0 : (uid_t)strtoul(env, NULL, 10);
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov i_fatal("setuid(%s) failed with euid=%s: %m",
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* verify that we actually dropped the privileges */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose if (setuid(0) == 0) {
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov i_fatal("Running as root isn't permitted");
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose else if (primary_gid == 0 || privileged_gid == 0)
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) {
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose i_fatal("We couldn't drop root group privileges "
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose "(wanted=%s, gid=%s, egid=%s)",
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* clear the environment, so we don't fail if we get back here */
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose /* if we're dropping privileges before executing and
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose a privileged group is set, the groups must be fixed
caee9828ee30609e9f433957dbb3d0163390a207Sumit Bose after exec */