static gid_t process_privileged_gid = (gid_t)-1;
static bool process_using_priv_gid = FALSE;
-void restrict_access_set_env(const char *user, uid_t uid,
- gid_t gid, gid_t privileged_gid,
- const char *chroot_dir,
- gid_t first_valid_gid, gid_t last_valid_gid,
- const char *extra_groups)
+void restrict_access_init(struct restrict_access_settings *set)
{
- if (user != NULL && *user != '\0')
- env_put(t_strconcat("RESTRICT_USER=", user, NULL));
- if (chroot_dir != NULL && *chroot_dir != '\0')
- env_put(t_strconcat("RESTRICT_CHROOT=", chroot_dir, NULL));
-
- env_put(t_strdup_printf("RESTRICT_SETUID=%s", dec2str(uid)));
- env_put(t_strdup_printf("RESTRICT_SETGID=%s", dec2str(gid)));
- if (privileged_gid != (gid_t)-1) {
- env_put(t_strdup_printf("RESTRICT_SETGID_PRIV=%s",
- dec2str(privileged_gid)));
- }
- if (extra_groups != NULL && *extra_groups != '\0') {
- env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=",
- extra_groups, NULL));
- }
+ memset(set, 0, sizeof(*set));
- if (first_valid_gid != 0) {
- env_put(t_strdup_printf("RESTRICT_GID_FIRST=%s",
- dec2str(first_valid_gid)));
- }
- if (last_valid_gid != 0) {
- env_put(t_strdup_printf("RESTRICT_GID_LAST=%s",
- dec2str(last_valid_gid)));
- }
+ set->uid = (uid_t)-1;
+ set->gid = (gid_t)-1;
+ set->privileged_gid = (gid_t)-1;
+ set->first_valid_gid = 0;
+ set->last_valid_gid = (gid_t)-1;
}
static const char *get_uid_str(uid_t uid)
return gid_list;
}
-static void drop_restricted_groups(gid_t *gid_list, unsigned int *gid_count,
+static void drop_restricted_groups(const struct restrict_access_settings *set,
+ gid_t *gid_list, unsigned int *gid_count,
bool *have_root_group)
{
/* @UNSAFE */
- gid_t first_valid, last_valid;
- const char *env;
unsigned int i, used;
- env = getenv("RESTRICT_GID_FIRST");
- first_valid = env == NULL ? 0 : (gid_t)strtoul(env, NULL, 10);
- env = getenv("RESTRICT_GID_LAST");
- last_valid = env == NULL ? (gid_t)-1 : (gid_t)strtoul(env, NULL, 10);
-
for (i = 0, used = 0; i < *gid_count; i++) {
- if (gid_list[i] >= first_valid &&
- (last_valid == (gid_t)-1 || gid_list[i] <= last_valid)) {
+ if (gid_list[i] >= set->first_valid_gid &&
+ (set->last_valid_gid == (gid_t)-1 ||
+ gid_list[i] <= set->last_valid_gid)) {
if (gid_list[i] == 0)
*have_root_group = TRUE;
gid_list[used++] = gid_list[i];
return group->gr_gid;
}
-static void fix_groups_list(const char *extra_groups,
+static void fix_groups_list(const struct restrict_access_settings *set,
bool preserve_existing, bool *have_root_group)
{
gid_t gid, *gid_list, *gid_list2;
so add it to supplementary groups. */
add_primary_gid = process_privileged_gid != (gid_t)-1;
- tmp = extra_groups == NULL ? &empty :
- t_strsplit_spaces(extra_groups, ", ");
+ tmp = set->extra_groups == NULL ? &empty :
+ t_strsplit_spaces(set->extra_groups, ", ");
if (preserve_existing) {
gid_list = get_groups_list(&gid_count);
- drop_restricted_groups(gid_list, &gid_count,
+ drop_restricted_groups(set, gid_list, &gid_count,
have_root_group);
/* see if the list already contains the primary GID */
for (i = 0; i < gid_count; i++) {
if (setgroups(gid_count, gid_list) < 0) {
if (errno == EINVAL) {
i_fatal("setgroups(%s) failed: Too many extra groups",
- extra_groups == NULL ? "" : extra_groups);
+ set->extra_groups == NULL ? "" :
+ set->extra_groups);
} else {
i_fatal("setgroups() failed: %m");
}
}
}
-void restrict_access_by_env(bool disallow_root)
+void restrict_access(const struct restrict_access_settings *set,
+ const char *home)
{
- const char *env;
- uid_t uid;
bool is_root, have_root_group, preserve_groups = FALSE;
bool allow_root_gid;
is_root = geteuid() == 0;
/* set the primary/privileged group */
- env = getenv("RESTRICT_SETGID");
- process_primary_gid = env == NULL || *env == '\0' ? (gid_t)-1 :
- (gid_t)strtoul(env, NULL, 10);
- env = getenv("RESTRICT_SETGID_PRIV");
- process_privileged_gid = env == NULL || *env == '\0' ? (gid_t)-1 :
- (gid_t)strtoul(env, NULL, 10);
+ process_primary_gid = set->gid;
+ process_privileged_gid = set->privileged_gid;
have_root_group = process_primary_gid == 0;
if (process_primary_gid != (gid_t)-1 ||
}
/* set system user's groups */
- env = getenv("RESTRICT_USER");
- if (env != NULL && *env != '\0' && is_root) {
- if (initgroups(env, process_primary_gid) < 0) {
+ if (set->system_groups_user != NULL && is_root) {
+ if (initgroups(set->system_groups_user,
+ process_primary_gid) < 0) {
i_fatal("initgroups(%s, %s) failed: %m",
- env, get_gid_str(process_primary_gid));
+ set->system_groups_user,
+ get_gid_str(process_primary_gid));
}
preserve_groups = TRUE;
}
/* add extra groups. if we set system user's groups, drop the
restricted groups at the same time. */
- env = getenv("RESTRICT_SETEXTRAGROUPS");
if (is_root) T_BEGIN {
- fix_groups_list(env, preserve_groups, &have_root_group);
+ fix_groups_list(set, preserve_groups,
+ &have_root_group);
} T_END;
/* chrooting */
- env = getenv("RESTRICT_CHROOT");
- if (env != NULL && *env != '\0') {
+ if (set->chroot_dir != NULL) {
/* kludge: localtime() must be called before chroot(),
or the timezone isn't known */
- const char *home = getenv("HOME");
time_t t = 0;
(void)localtime(&t);
- if (chroot(env) != 0)
- i_fatal("chroot(%s) failed: %m", env);
+ if (chroot(set->chroot_dir) != 0)
+ i_fatal("chroot(%s) failed: %m", set->chroot_dir);
if (home != NULL) {
if (chdir(home) < 0) {
}
/* uid last */
- env = getenv("RESTRICT_SETUID");
- uid = env == NULL || *env == '\0' ? 0 : (uid_t)strtoul(env, NULL, 10);
- if (uid != 0) {
- if (setuid(uid) != 0) {
+ if (set->uid != (uid_t)-1) {
+ if (setuid(set->uid) != 0) {
i_fatal("setuid(%s) failed with euid=%s: %m",
- get_uid_str(uid), get_uid_str(geteuid()));
+ get_uid_str(set->uid), get_uid_str(geteuid()));
}
}
/* verify that we actually dropped the privileges */
- if (uid != 0 || disallow_root) {
+ if (set->uid != (uid_t)-1) {
if (setuid(0) == 0) {
- if (uid == 0)
- i_fatal("Running as root isn't permitted");
+ if (set->uid == 0)
+ i_fatal("This process must not be run as root");
i_fatal("We couldn't drop root privileges");
}
}
- env = getenv("RESTRICT_GID_FIRST");
- if (env != NULL && atoi(env) != 0)
+ if (set->first_valid_gid != 0)
allow_root_gid = FALSE;
else if (process_primary_gid == 0 || process_privileged_gid == 0)
allow_root_gid = TRUE;
else
allow_root_gid = FALSE;
- if (!allow_root_gid && uid != 0) {
+ if (!allow_root_gid && set->uid != 0) {
if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) {
if (process_primary_gid == 0)
i_fatal("GID 0 isn't permitted");
get_gid_str(getgid()), get_gid_str(getegid()));
}
}
+}
+
+void restrict_access_set_env(const struct restrict_access_settings *set)
+{
+ if (set->system_groups_user != NULL &&
+ *set->system_groups_user != '\0') {
+ env_put(t_strconcat("RESTRICT_USER=",
+ set->system_groups_user, NULL));
+ }
+ if (set->chroot_dir != NULL && *set->chroot_dir != '\0')
+ env_put(t_strconcat("RESTRICT_CHROOT=", set->chroot_dir, NULL));
+
+ if (set->uid != (uid_t)-1) {
+ env_put(t_strdup_printf("RESTRICT_SETUID=%s",
+ dec2str(set->uid)));
+ }
+ if (set->gid != (gid_t)-1) {
+ env_put(t_strdup_printf("RESTRICT_SETGID=%s",
+ dec2str(set->gid)));
+ }
+ if (set->privileged_gid != (gid_t)-1) {
+ env_put(t_strdup_printf("RESTRICT_SETGID_PRIV=%s",
+ dec2str(set->privileged_gid)));
+ }
+ if (set->extra_groups != NULL && *set->extra_groups != '\0') {
+ env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=",
+ set->extra_groups, NULL));
+ }
+
+ if (set->first_valid_gid != 0) {
+ env_put(t_strdup_printf("RESTRICT_GID_FIRST=%s",
+ dec2str(set->first_valid_gid)));
+ }
+ if (set->last_valid_gid != 0) {
+ env_put(t_strdup_printf("RESTRICT_GID_LAST=%s",
+ dec2str(set->last_valid_gid)));
+ }
+}
+
+static const char *null_if_empty(const char *str)
+{
+ return str == NULL || *str == '\0' ? NULL : str;
+}
+
+void restrict_access_by_env(const char *home, bool disallow_root)
+{
+ struct restrict_access_settings set;
+ const char *value;
+
+ restrict_access_init(&set);
+
+ if ((value = getenv("RESTRICT_SETUID")) != NULL)
+ set.uid = (uid_t)strtol(value, NULL, 10);
+ if ((value = getenv("RESTRICT_SETGID")) != NULL)
+ set.gid = (gid_t)strtol(value, NULL, 10);
+ if ((value = getenv("RESTRICT_SETGID_PRIV")) != NULL)
+ set.privileged_gid = (gid_t)strtol(value, NULL, 10);
+ if ((value = getenv("RESTRICT_GID_FIRST")) != NULL)
+ set.first_valid_gid = (gid_t)strtol(value, NULL, 10);
+ if ((value = getenv("RESTRICT_GID_LAST")) != NULL)
+ set.last_valid_gid = (gid_t)strtol(value, NULL, 10);
+
+ if (disallow_root) {
+ if (set.uid == (uid_t)-1 || set.uid == 0)
+ i_fatal("This process must not be run as root");
+ }
+
+ set.extra_groups = null_if_empty(getenv("RESTRICT_SETEXTRAGROUPS"));
+ set.system_groups_user = null_if_empty(getenv("RESTRICT_USER"));
+ set.chroot_dir = null_if_empty(getenv("RESTRICT_CHROOT"));
+
+ restrict_access(&set, home);
/* clear the environment, so we don't fail if we get back here */
- env_put("RESTRICT_USER=");
- env_put("RESTRICT_CHROOT=");
env_put("RESTRICT_SETUID=");
if (process_privileged_gid == (gid_t)-1) {
/* if we're dropping privileges before executing and
env_put("RESTRICT_SETGID=");
env_put("RESTRICT_SETGID_PRIV=");
}
- env_put("RESTRICT_SETEXTRAGROUPS=");
env_put("RESTRICT_GID_FIRST=");
env_put("RESTRICT_GID_LAST=");
+ env_put("RESTRICT_SETEXTRAGROUPS=");
+ env_put("RESTRICT_USER=");
+ env_put("RESTRICT_CHROOT=");
}
void restrict_access_allow_coredumps(bool allow ATTR_UNUSED)
#ifndef RESTRICT_ACCESS_H
#define RESTRICT_ACCESS_H
-/* set environment variables so they can be read with
- restrict_access_by_env(). If privileged_gid != (gid_t)-1,
- the privileged GID can be temporarily enabled/disabled. */
-void restrict_access_set_env(const char *user, uid_t uid,
- gid_t gid, gid_t privileged_gid,
- const char *chroot_dir,
- gid_t first_valid_gid, gid_t last_valid_gid,
- const char *extra_groups);
-
-/* chroot, setuid() and setgid() based on environment variables.
+struct restrict_access_settings {
+ /* UID to use, or (uid_t)-1 if you don't want to change it */
+ uid_t uid;
+ /* Effective GID to use, or (gid_t)-1 if you don't want to change it */
+ gid_t gid;
+ /* If not (gid_t)-1, the privileged GID can be temporarily
+ enabled/disabled. */
+ gid_t privileged_gid;
+
+ /* Add access to these space or comma -separated extra groups */
+ const char *extra_groups;
+ /* Add access to groups this system user belongs to */
+ const char *system_groups_user;
+
+ /* All specified GIDs must be in this range. If extra_groups or system
+ group user contains other GIDs, they're silently dropped. */
+ gid_t first_valid_gid, last_valid_gid;
+
+ /* Chroot directory */
+ const char *chroot_dir;
+};
+
+/* Initialize settings with values that don't change anything. */
+void restrict_access_init(struct restrict_access_settings *set);
+/* Restrict access as specified by the settings. If home is not NULL,
+ it's chdir()ed after chrooting, otherwise it chdirs to / (the chroot). */
+void restrict_access(const struct restrict_access_settings *set,
+ const char *home);
+/* Set environment variables so they can be read with
+ restrict_access_by_env(). */
+void restrict_access_set_env(const struct restrict_access_settings *set);
+/* Read restrictions from environment and call restrict_access().
If disallow_roots is TRUE, we'll kill ourself if we didn't have the
- environment settings and we have root uid or gid. */
-void restrict_access_by_env(bool disallow_root);
+ environment settings. */
+void restrict_access_by_env(const char *home, bool disallow_root);
/* Try to set up the process in a way that core dumps are still allowed
after calling restrict_access_by_env(). */