From: Timo Sirainen Date: Wed, 8 Apr 2009 00:48:53 +0000 (-0400) Subject: Cleaned up restrict_access*() API. X-Git-Tag: 2.0.alpha1~1008 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a85d9c3e0b7cb1744a5c8f71501b3039d678c47a;p=thirdparty%2Fdovecot%2Fcore.git Cleaned up restrict_access*() API. --HG-- branch : HEAD --- diff --git a/src/auth/main.c b/src/auth/main.c index 16574063df..ff63731772 100644 --- a/src/auth/main.c +++ b/src/auth/main.c @@ -236,7 +236,7 @@ static void drop_privileges(void) add_extra_listeners(); /* Password lookups etc. may require roots, allow it. */ - restrict_access_by_env(FALSE); + restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); } diff --git a/src/deliver/auth-client.c b/src/deliver/auth-client.c index c799ff9ea8..c52e8e2b3d 100644 --- a/src/deliver/auth-client.c +++ b/src/deliver/auth-client.c @@ -141,7 +141,7 @@ int auth_client_lookup_and_restrict(const char *auth_socket, bool debug, case 1: if (set_env(&reply, *user, euid) == 0) { *user = p_strdup(pool, reply.user); - restrict_access_by_env(TRUE); + restrict_access_by_env(getenv("HOME"), TRUE); ret = EX_OK; } break; diff --git a/src/dict/main.c b/src/dict/main.c index 03151006f3..7273af9d90 100644 --- a/src/dict/main.c +++ b/src/dict/main.c @@ -57,7 +57,7 @@ static void drop_privileges(void) sql_drivers_init(); sql_drivers_register_all(); - restrict_access_by_env(FALSE); + restrict_access_by_env(NULL, FALSE); } static void main_init(void) diff --git a/src/imap/main.c b/src/imap/main.c index db5f8db1a5..212afc0e2d 100644 --- a/src/imap/main.c +++ b/src/imap/main.c @@ -159,7 +159,7 @@ static void main_preinit(const struct imap_settings **set_r, module_dir_load((*set_r)->mail_plugin_dir, (*set_r)->mail_plugins, TRUE, version); - restrict_access_by_env(!IS_STANDALONE()); + restrict_access_by_env(getenv("HOME"), !IS_STANDALONE()); restrict_access_allow_coredumps(TRUE); } diff --git a/src/lib/restrict-access.c b/src/lib/restrict-access.c index 8317126b53..34bfad8b48 100644 --- a/src/lib/restrict-access.c +++ b/src/lib/restrict-access.c @@ -21,36 +21,15 @@ static gid_t process_primary_gid = (gid_t)-1; 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) @@ -148,22 +127,17 @@ static gid_t *get_groups_list(unsigned int *gid_count_r) 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]; @@ -185,7 +159,7 @@ static gid_t get_group_id(const char *name) 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; @@ -198,12 +172,12 @@ static void fix_groups_list(const char *extra_groups, 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++) { @@ -242,29 +216,25 @@ static void fix_groups_list(const char *extra_groups, 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 || @@ -279,33 +249,32 @@ void restrict_access_by_env(bool disallow_root) } /* 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) { @@ -320,33 +289,30 @@ void restrict_access_by_env(bool disallow_root) } /* 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"); @@ -356,10 +322,80 @@ void restrict_access_by_env(bool disallow_root) 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 @@ -368,9 +404,11 @@ void restrict_access_by_env(bool disallow_root) 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) diff --git a/src/lib/restrict-access.h b/src/lib/restrict-access.h index db2b527b0e..0f8210fdcd 100644 --- a/src/lib/restrict-access.h +++ b/src/lib/restrict-access.h @@ -1,19 +1,41 @@ #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(). */ diff --git a/src/login-common/main.c b/src/login-common/main.c index 9373c22d2c..4bb3d954dc 100644 --- a/src/login-common/main.c +++ b/src/login-common/main.c @@ -299,7 +299,7 @@ static void drop_privileges(unsigned int *max_fds_r) /* Refuse to run as root - we should never need it and it's dangerous with SSL. */ - restrict_access_by_env(TRUE); + restrict_access_by_env(NULL, TRUE); /* make sure we can't fork() */ restrict_process_size((unsigned int)-1, 1); diff --git a/src/master/auth-process.c b/src/master/auth-process.c index 50dacf9fa1..d2ca6f21a6 100644 --- a/src/master/auth-process.c +++ b/src/master/auth-process.c @@ -417,11 +417,17 @@ static int connect_auth_socket(struct auth_process_group *group, static void auth_set_environment(const struct master_settings *master_set, const struct master_auth_settings *set) { + struct restrict_access_settings rset; + master_settings_export_to_env(master_set); /* setup access environment */ - restrict_access_set_env(set->user, set->uid, set->gid, - (gid_t)-1, set->chroot, 0, 0, NULL); + restrict_access_init(&rset); + rset.system_groups_user = set->user; + rset.uid = set->uid; + rset.gid = set->gid; + rset.chroot_dir = set->chroot; + restrict_access_set_env(&rset); /* set other environment */ env_put("DOVECOT_MASTER=1"); diff --git a/src/master/login-process.c b/src/master/login-process.c index 283a28f68a..1b6dcafc24 100644 --- a/src/master/login-process.c +++ b/src/master/login-process.c @@ -523,6 +523,7 @@ static void login_process_unref(struct login_process *p) static void login_process_init_env(struct login_group *group, pid_t pid) { struct master_settings *set = group->set; + struct restrict_access_settings rset; child_process_init_env(group->set); @@ -530,10 +531,12 @@ static void login_process_init_env(struct login_group *group, pid_t pid) clean_child_process() since it clears environment. Don't set user parameter since we don't want to call initgroups() for login processes. */ - restrict_access_set_env(NULL, set->login_uid, - set->server->login_gid, (gid_t)-1, - set->login_chroot ? set->login_dir : NULL, - 0, 0, NULL); + restrict_access_init(&rset); + rset.uid = set->login_uid; + rset.gid = set->server->login_gid; + if (set->login_chroot) + rset.chroot_dir = set->login_dir; + restrict_access_set_env(&rset); env_put("DOVECOT_MASTER=1"); diff --git a/src/master/mail-process.c b/src/master/mail-process.c index 3c112ebea4..1af63b41d1 100644 --- a/src/master/mail-process.c +++ b/src/master/mail-process.c @@ -285,6 +285,7 @@ create_mail_process(enum process_type process_type, struct master_settings *set, const char *p, *addr, *chroot_dir, *home_dir, *full_home_dir; const char *system_groups_user, *master_user, *key; struct mail_process_group *process_group; + struct restrict_access_settings rset; char title[1024]; struct log_io *log; string_t *str, *expanded_vars; @@ -484,11 +485,16 @@ create_mail_process(enum process_type process_type, struct master_settings *set, /* setup environment - set the most important environment first (paranoia about filling up environment without noticing) */ - restrict_access_set_env(system_groups_user, uid, gid, - set->mail_priv_gid_t, - dump_capability ? "" : chroot_dir, - set->first_valid_gid, set->last_valid_gid, - set->mail_access_groups); + restrict_access_init(&rset); + rset.uid = uid; + rset.gid = gid; + rset.privileged_gid = set->mail_priv_gid_t; + rset.extra_groups = set->mail_access_groups; + rset.system_groups_user = system_groups_user; + rset.first_valid_gid = set->first_valid_gid; + rset.last_valid_gid = set->last_valid_gid; + rset.chroot_dir = dump_capability ? NULL : chroot_dir; + restrict_access_set_env(&rset); restrict_process_size(set->mail_process_size, (unsigned int)-1); @@ -624,7 +630,7 @@ create_mail_process(enum process_type process_type, struct master_settings *set, fd_close_on_exec(i, FALSE); if (set->mail_drop_priv_before_exec) { - restrict_access_by_env(TRUE); + restrict_access_by_env(home_dir, TRUE); /* privileged GID is now only in saved-GID. if we want to preserve it across exec, it needs to be temporarily in effective gid. unfortunately this also causes kernel diff --git a/src/plugins/expire/auth-client.c b/src/plugins/expire/auth-client.c index be4a961b0a..bdae7424fc 100644 --- a/src/plugins/expire/auth-client.c +++ b/src/plugins/expire/auth-client.c @@ -8,6 +8,7 @@ #include "auth-client.h" #include "auth-master.h" +#include #include static uid_t current_uid = 0; @@ -43,7 +44,7 @@ static void auth_set_env(const char *user, struct auth_user_reply *reply) } /* change GID */ - restrict_access_by_env(FALSE); + restrict_access_by_env(getenv("HOME"), FALSE); /* we'll change only effective UID. This is a bit unfortunate since it allows reverting back to root, but we'll have to be able to diff --git a/src/pop3/main.c b/src/pop3/main.c index f4a261ea1c..ec5c5e184a 100644 --- a/src/pop3/main.c +++ b/src/pop3/main.c @@ -177,7 +177,7 @@ static void main_preinit(const struct pop3_settings **set_r, module_dir_load((*set_r)->mail_plugin_dir, (*set_r)->mail_plugins, TRUE, version); - restrict_access_by_env(!IS_STANDALONE()); + restrict_access_by_env(getenv("HOME"), !IS_STANDALONE()); restrict_access_allow_coredumps(TRUE); } diff --git a/src/util/rawlog.c b/src/util/rawlog.c index 77c39bcc9f..ca0b8655c5 100644 --- a/src/util/rawlog.c +++ b/src/util/rawlog.c @@ -320,7 +320,7 @@ static void rawlog_open(enum rawlog_flags flags) } (void)close(sfd[1]); - restrict_access_by_env(TRUE); + restrict_access_by_env(getenv("HOME"), TRUE); process_title_set(t_strdup_printf("[%s:%s rawlog]", getenv("USER"), dec2str(getppid())));