From abe286bb37c58a5c8425d4095d0e6736e7cc6044 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Wed, 16 Jun 2004 05:04:01 +0300 Subject: [PATCH] Added mail_extra_groups setting. --HG-- branch : HEAD --- dovecot-example.conf | 4 ++ src/lib/restrict-access.c | 106 +++++++++++++++++++++++++++++------ src/lib/restrict-access.h | 3 +- src/master/auth-process.c | 3 +- src/master/login-process.c | 2 +- src/master/mail-process.c | 3 +- src/master/master-settings.c | 2 + src/master/master-settings.h | 1 + 8 files changed, 102 insertions(+), 22 deletions(-) diff --git a/dovecot-example.conf b/dovecot-example.conf index d46e345cb8..464ef6ceb1 100644 --- a/dovecot-example.conf +++ b/dovecot-example.conf @@ -146,6 +146,10 @@ #first_valid_gid = 1 #last_valid_gid = 0 +# Grant access to these extra groups for mail processes. Typical use would be +# to give "mail" group write access to /var/mail to be able to create dotlocks. +#mail_extra_groups = + # ':' separated list of directories under which chrooting is allowed for mail # processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too). # This setting doesn't affect login_chroot or auth_chroot variables. diff --git a/src/lib/restrict-access.c b/src/lib/restrict-access.c index 032ede4fea..8831f20b6f 100644 --- a/src/lib/restrict-access.c +++ b/src/lib/restrict-access.c @@ -17,7 +17,8 @@ void restrict_access_set_env(const char *user, uid_t uid, gid_t gid, const char *chroot_dir, - gid_t first_valid_gid, gid_t last_valid_gid) + gid_t first_valid_gid, gid_t last_valid_gid, + const char *extra_groups) { if (user != NULL && *user != '\0') env_put(t_strconcat("RESTRICT_USER=", user, NULL)); @@ -26,6 +27,10 @@ void restrict_access_set_env(const char *user, uid_t uid, gid_t gid, env_put(t_strdup_printf("RESTRICT_SETUID=%s", dec2str(uid))); env_put(t_strdup_printf("RESTRICT_SETGID=%s", dec2str(gid))); + if (extra_groups != NULL && *extra_groups != '\0') { + env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=", + extra_groups, NULL)); + } if (first_valid_gid != 0) { env_put(t_strdup_printf("RESTRICT_GID_FIRST=%s", @@ -37,12 +42,34 @@ void restrict_access_set_env(const char *user, uid_t uid, gid_t gid, } } +static gid_t *get_groups_list(int *gid_count_r) +{ + /* @UNSAFE */ + gid_t *gid_list; + int ret, gid_count; + + gid_count = NGROUPS_MAX; + gid_list = t_buffer_get(sizeof(gid_t) * gid_count); + while ((ret = getgroups(gid_count, gid_list)) < 0) { + if (errno != EINVAL || + gid_count < HARD_MAX_GROUPS) + i_fatal("getgroups() failed: %m"); + + gid_count *= 2; + gid_list = t_buffer_reget(gid_list, sizeof(gid_t) * gid_count); + } + t_buffer_alloc(sizeof(gid_t) * ret); + + *gid_count_r = ret; + return gid_list; +} + static void drop_restricted_groups(void) { /* @UNSAFE */ const char *env; gid_t *gid_list, first_valid_gid, last_valid_gid; - int ret, i, gid_count; + int i, used, gid_count; env = getenv("RESTRICT_GID_FIRST"); first_valid_gid = env == NULL ? 0 : (gid_t)atol(env); @@ -52,29 +79,58 @@ static void drop_restricted_groups(void) if (first_valid_gid == 0 && last_valid_gid == 0) return; - gid_count = NGROUPS_MAX; - gid_list = t_buffer_get(sizeof(gid_t) * gid_count); - while ((ret = getgroups(gid_count, gid_list)) < 0) { - if (errno != EINVAL || - gid_count < HARD_MAX_GROUPS) - i_fatal("getgroups() failed: %m"); - - gid_count *= 2; - gid_list = t_buffer_reget(gid_list, sizeof(gid_t) * gid_count); - } + t_push(); + gid_list = get_groups_list(&gid_count); - gid_count = 0; - for (i = 0; i < ret; i++) { + for (i = 0, used = 0; i < gid_count; i++) { if (gid_list[i] >= first_valid_gid && (last_valid_gid == 0 || gid_list[i] <= last_valid_gid)) - gid_list[gid_count++] = gid_list[i]; + gid_list[used++] = gid_list[i]; } - if (ret != gid_count) { - /* it did contain 0, remove it */ + if (used != gid_count) { + /* it did contain restricted groups, remove it */ if (setgroups(gid_count, gid_list) < 0) i_fatal("setgroups() failed: %m"); } + t_pop(); +} + +static gid_t get_group_id(const char *name) +{ + struct group *group; + + if (is_numeric(name, '\0')) + return (gid_t)atol(name); + + group = getgrnam(name); + if (group == NULL) + i_fatal("unknown group name in extra_groups: %s", name); + return group->gr_gid; +} + +static void grant_extra_groups(const char *groups) +{ + const char *const *tmp; + gid_t *gid_list; + int gid_count; + + t_push(); + tmp = t_strsplit(groups, ", "); + gid_list = get_groups_list(&gid_count); + for (; *tmp != NULL; tmp++) { + if (**tmp == '\0') + continue; + + if (!t_try_realloc(gid_list, (gid_count+1) * sizeof(gid_t))) + i_panic("won't happen"); + gid_list[gid_count++] = get_group_id(*tmp); + } + + if (setgroups(gid_count, gid_list) < 0) + i_fatal("setgroups() failed: %m"); + + t_pop(); } void restrict_access_by_env(int disallow_root) @@ -109,9 +165,14 @@ void restrict_access_by_env(int disallow_root) } } + /* grant additional groups to process */ + env = getenv("RESTRICT_SETEXTRAGROUPS"); + if (env != NULL && *env != '\0') + grant_extra_groups(env); + /* chrooting */ env = getenv("RESTRICT_CHROOT"); - if (env != NULL) { + if (env != NULL && *env != '\0') { /* kludge: localtime() must be called before chroot(), or the timezone isn't known */ time_t t = 0; @@ -142,4 +203,13 @@ void restrict_access_by_env(int disallow_root) if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) i_fatal("We couldn't drop root group privileges"); } + + /* 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="); + env_put("RESTRICT_SETGID="); + env_put("RESTRICT_SETEXTRAGROUPS="); + env_put("RESTRICT_GID_FIRST="); + env_put("RESTRICT_GID_LAST="); } diff --git a/src/lib/restrict-access.h b/src/lib/restrict-access.h index b3ea5d7b77..9c4b11381c 100644 --- a/src/lib/restrict-access.h +++ b/src/lib/restrict-access.h @@ -5,7 +5,8 @@ restrict_access_by_env() */ void restrict_access_set_env(const char *user, uid_t uid, gid_t gid, const char *chroot_dir, - gid_t first_valid_gid, gid_t last_valid_gid); + gid_t first_valid_gid, gid_t last_valid_gid, + const char *extra_groups); /* chroot, setuid() and setgid() based on environment variables. If disallow_roots is TRUE, we'll kill ourself if we didn't have the diff --git a/src/master/auth-process.c b/src/master/auth-process.c index a444bb8d15..e281f34b7c 100644 --- a/src/master/auth-process.c +++ b/src/master/auth-process.c @@ -324,7 +324,8 @@ static pid_t create_auth_process(struct auth_process_group *group) /* setup access environment */ restrict_access_set_env(group->set->user, group->set->uid, - group->set->gid, group->set->chroot, 0, 0); + group->set->gid, group->set->chroot, + 0, 0, NULL); /* set other environment */ env_put(t_strconcat("AUTH_PROCESS=", dec2str(getpid()), NULL)); diff --git a/src/master/login-process.c b/src/master/login-process.c index f3a48bb355..9defa05ec6 100644 --- a/src/master/login-process.c +++ b/src/master/login-process.c @@ -383,7 +383,7 @@ static void login_process_init_env(struct login_group *group, pid_t pid) restrict_access_set_env(set->login_user, set->login_uid, set->server->login_gid, set->login_chroot ? set->login_dir : NULL, - 0, 0); + 0, 0, NULL); env_put("DOVECOT_MASTER=1"); diff --git a/src/master/mail-process.c b/src/master/mail-process.c index 0831e720ae..75ee0c7921 100644 --- a/src/master/mail-process.c +++ b/src/master/mail-process.c @@ -268,7 +268,8 @@ int create_mail_process(struct login_group *group, int socket, (paranoia about filling up environment without noticing) */ restrict_access_set_env(data + reply->system_user_idx, reply->uid, reply->gid, chroot_dir, - set->first_valid_gid, set->last_valid_gid); + set->first_valid_gid, set->last_valid_gid, + set->mail_extra_groups); restrict_process_size(group->set->mail_process_size, (unsigned int)-1); diff --git a/src/master/master-settings.c b/src/master/master-settings.c index c1f15ef428..88c2f9841e 100644 --- a/src/master/master-settings.c +++ b/src/master/master-settings.c @@ -79,6 +79,7 @@ static struct setting_def setting_defs[] = { DEF(SET_INT, last_valid_uid), DEF(SET_INT, first_valid_gid), DEF(SET_INT, last_valid_gid), + DEF(SET_STR, mail_extra_groups), DEF(SET_STR, default_mail_env), DEF(SET_STR, mail_cache_fields), @@ -210,6 +211,7 @@ struct settings default_settings = { MEMBER(last_valid_uid) 0, MEMBER(first_valid_gid) 1, MEMBER(last_valid_gid) 0, + MEMBER(mail_extra_groups) NULL, MEMBER(default_mail_env) NULL, MEMBER(mail_cache_fields) "MessagePart", diff --git a/src/master/master-settings.h b/src/master/master-settings.h index 2dfa36d22f..7d8199c72f 100644 --- a/src/master/master-settings.h +++ b/src/master/master-settings.h @@ -54,6 +54,7 @@ struct settings { unsigned int first_valid_uid, last_valid_uid; unsigned int first_valid_gid, last_valid_gid; + const char *mail_extra_groups; const char *default_mail_env; const char *mail_cache_fields; -- 2.47.3