From: Timo Sirainen Date: Mon, 12 Dec 2011 04:36:31 +0000 (+0200) Subject: lib-storage: Handle %{uid} and %{gid} expansion without relying on process's euid... X-Git-Tag: 2.1.rc2~52 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=13b063ba3ea51256fd97d7fa883f14cb08842b0d;p=thirdparty%2Fdovecot%2Fcore.git lib-storage: Handle %{uid} and %{gid} expansion without relying on process's euid/egid. --- diff --git a/src/lib-storage/mail-storage-service.c b/src/lib-storage/mail-storage-service.c index bb89437838..059c416918 100644 --- a/src/lib-storage/mail-storage-service.c +++ b/src/lib-storage/mail-storage-service.c @@ -38,6 +38,15 @@ #define ERRSTR_INVALID_USER_SETTINGS \ "Invalid user settings. Refer to server log for more information." +struct mail_storage_service_privileges { + uid_t uid; + gid_t gid; + const char *uid_source, *gid_source; + + const char *home; + const char *chroot; +}; + struct mail_storage_service_ctx { pool_t pool; struct master_service *service; @@ -348,57 +357,142 @@ static bool parse_gid(const char *str, gid_t *gid_r, const char **error_r) } } +static const struct var_expand_table * +get_var_expand_table(struct master_service *service, + struct mail_storage_service_input *input, + struct mail_storage_service_privileges *priv) +{ + static struct var_expand_table static_tab[] = { + { 'u', NULL, "user" }, + { 'n', NULL, "username" }, + { 'd', NULL, "domain" }, + { 's', NULL, "service" }, + { 'l', NULL, "lip" }, + { 'r', NULL, "rip" }, + { 'p', NULL, "pid" }, + { 'i', NULL, "uid" }, + { '\0', NULL, "gid" }, + { '\0', NULL, NULL } + }; + struct var_expand_table *tab; + + tab = t_malloc(sizeof(static_tab)); + memcpy(tab, static_tab, sizeof(static_tab)); + + tab[0].value = input->username; + tab[1].value = t_strcut(input->username, '@'); + tab[2].value = strchr(input->username, '@'); + if (tab[2].value != NULL) tab[2].value++; + tab[3].value = service->name; + tab[4].value = net_ip2addr(&input->local_ip); + tab[5].value = net_ip2addr(&input->remote_ip); + tab[6].value = my_pid; + tab[7].value = dec2str(priv->uid == (uid_t)-1 ? geteuid() : priv->uid); + tab[8].value = dec2str(priv->gid == (gid_t)-1 ? getegid() : priv->gid); + return tab; +} + +static const char * +user_expand_varstr(struct master_service *service, + struct mail_storage_service_input *input, + struct mail_storage_service_privileges *priv, + const char *str) +{ + string_t *ret; + + if (*str == SETTING_STRVAR_EXPANDED[0]) + return str + 1; + + i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]); + + ret = t_str_new(256); + var_expand(ret, str + 1, get_var_expand_table(service, input, priv)); + return str_c(ret); +} + static int -service_drop_privileges(struct mail_storage_service_user *user, - const struct mail_user_settings *set, - const char *home, const char *chroot, - bool disallow_root, bool keep_setuid_root, - bool setenv_only, const char **error_r) +service_parse_privileges(struct mail_storage_service_ctx *ctx, + struct mail_storage_service_user *user, + struct mail_storage_service_privileges *priv_r, + const char **error_r) { - struct restrict_access_settings rset; - uid_t current_euid, setuid_uid = 0; - const char *cur_chroot, *error; + const struct mail_user_settings *set = user->user_set; + uid_t uid = (uid_t)-1; + gid_t gid = (gid_t)-1; - current_euid = geteuid(); - restrict_access_init(&rset); - restrict_access_get_env(&rset); + memset(priv_r, 0, sizeof(*priv_r)); if (*set->mail_uid != '\0') { - if (!parse_uid(set->mail_uid, &rset.uid, &error)) { - *error_r = t_strdup_printf("%s (from %s)", error, + if (!parse_uid(set->mail_uid, &uid, error_r)) { + *error_r = t_strdup_printf("%s (from %s)", *error_r, user->uid_source); return -1; } - if (rset.uid < (uid_t)set->first_valid_uid || + if (uid < (uid_t)set->first_valid_uid || (set->last_valid_uid != 0 && - rset.uid > (uid_t)set->last_valid_uid)) { + uid > (uid_t)set->last_valid_uid)) { *error_r = t_strdup_printf( "Mail access for users with UID %s not permitted " "(see first_valid_uid in config file, uid from %s).", - dec2str(rset.uid), user->uid_source); + dec2str(uid), user->uid_source); return -1; } - rset.uid_source = user->uid_source; - } else if (rset.uid == (uid_t)-1 && - disallow_root && current_euid == 0) { - *error_r = "User is missing UID (see mail_uid setting)"; - return -1; } + priv_r->uid = uid; + priv_r->uid_source = user->uid_source; + if (*set->mail_gid != '\0') { - if (!parse_gid(set->mail_gid, &rset.gid, &error)) { - *error_r = t_strdup_printf("%s (from %s)", error, + if (!parse_gid(set->mail_gid, &gid, error_r)) { + *error_r = t_strdup_printf("%s (from %s)", *error_r, user->gid_source); return -1; } - if (rset.gid < (gid_t)set->first_valid_gid || + if (gid < (gid_t)set->first_valid_gid || (set->last_valid_gid != 0 && - rset.gid > (gid_t)set->last_valid_gid)) { + gid > (gid_t)set->last_valid_gid)) { *error_r = t_strdup_printf( "Mail access for users with GID %s not permitted " "(see first_valid_gid in config file, gid from %s).", - dec2str(rset.gid), user->gid_source); + dec2str(gid), user->gid_source); return -1; } - rset.gid_source = user->gid_source; + } + priv_r->gid = gid; + priv_r->gid_source = user->gid_source; + + /* variable strings are expanded in mail_user_init(), + but we need the home and chroot sooner so do them separately here. */ + priv_r->home = user_expand_varstr(ctx->service, &user->input, priv_r, + user->user_set->mail_home); + priv_r->chroot = user_expand_varstr(ctx->service, &user->input, priv_r, + user->user_set->mail_chroot); + return 0; +} + +static int +service_drop_privileges(struct mail_storage_service_user *user, + struct mail_storage_service_privileges *priv, + bool disallow_root, bool keep_setuid_root, + bool setenv_only, const char **error_r) +{ + const struct mail_user_settings *set = user->user_set; + struct restrict_access_settings rset; + uid_t current_euid, setuid_uid = 0; + const char *cur_chroot, *error; + + current_euid = geteuid(); + restrict_access_init(&rset); + restrict_access_get_env(&rset); + if (priv->uid != (uid_t)-1) { + rset.uid = priv->uid; + rset.uid_source = priv->uid_source; + } else if (rset.uid == (uid_t)-1 && + disallow_root && current_euid == 0) { + *error_r = "User is missing UID (see mail_uid setting)"; + return -1; + } + if (priv->gid != (gid_t)-1) { + rset.gid = priv->gid; + rset.gid_source = priv->gid_source; } else if (rset.gid == (gid_t)-1 && disallow_root && set->first_valid_gid > 0 && getegid() == 0) { *error_r = "User is missing GID (see mail_gid setting)"; @@ -419,7 +513,7 @@ service_drop_privileges(struct mail_storage_service_user *user, rset.first_valid_gid = set->first_valid_gid; rset.last_valid_gid = set->last_valid_gid; - rset.chroot_dir = *chroot == '\0' ? NULL : chroot; + rset.chroot_dir = *priv->chroot == '\0' ? NULL : priv->chroot; rset.system_groups_user = user->system_groups_user; cur_chroot = restrict_access_get_current_chroot(); @@ -433,7 +527,7 @@ service_drop_privileges(struct mail_storage_service_user *user, if (strcmp(rset.chroot_dir, cur_chroot) != 0) { *error_r = t_strdup_printf( "Process is already chrooted to %s, " - "can't chroot to %s", cur_chroot, chroot); + "can't chroot to %s", cur_chroot, priv->chroot); return -1; } /* chrooting to same directory where we're already chrooted */ @@ -460,7 +554,7 @@ service_drop_privileges(struct mail_storage_service_user *user, disallow_root = FALSE; } if (!setenv_only) { - restrict_access(&rset, *home == '\0' ? NULL : home, + restrict_access(&rset, *priv->home == '\0' ? NULL : priv->home, disallow_root); } else { restrict_access_set_env(&rset); @@ -475,17 +569,21 @@ service_drop_privileges(struct mail_storage_service_user *user, static int mail_storage_service_init_post(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, - const char *home, struct mail_user **mail_user_r, + struct mail_storage_service_privileges *priv, + struct mail_user **mail_user_r, const char **error_r) { const struct mail_storage_settings *mail_set; + const char *home = priv->home; struct mail_user *mail_user; mail_user = mail_user_alloc(user->input.username, user->user_info, user->user_set); mail_user_set_home(mail_user, *home == '\0' ? NULL : home); - mail_user_set_vars(mail_user, geteuid(), getegid(), ctx->service->name, + mail_user_set_vars(mail_user, ctx->service->name, &user->input.local_ip, &user->input.remote_ip); + mail_user->uid = priv->uid == (uid_t)-1 ? geteuid() : priv->uid; + mail_user->gid = priv->gid == (gid_t)-1 ? getegid() : priv->gid; mail_set = mail_user_set_get_storage_set(mail_user); @@ -528,54 +626,6 @@ mail_storage_service_init_post(struct mail_storage_service_ctx *ctx, return 0; } -static const struct var_expand_table * -get_var_expand_table(struct master_service *service, - struct mail_storage_service_input *input) -{ - static struct var_expand_table static_tab[] = { - { 'u', NULL, "user" }, - { 'n', NULL, "username" }, - { 'd', NULL, "domain" }, - { 's', NULL, "service" }, - { 'l', NULL, "lip" }, - { 'r', NULL, "rip" }, - { 'p', NULL, "pid" }, - { 'i', NULL, "uid" }, - { '\0', NULL, NULL } - }; - struct var_expand_table *tab; - - tab = t_malloc(sizeof(static_tab)); - memcpy(tab, static_tab, sizeof(static_tab)); - - tab[0].value = input->username; - tab[1].value = t_strcut(input->username, '@'); - tab[2].value = strchr(input->username, '@'); - if (tab[2].value != NULL) tab[2].value++; - tab[3].value = service->name; - tab[4].value = net_ip2addr(&input->local_ip); - tab[5].value = net_ip2addr(&input->remote_ip); - tab[6].value = my_pid; - tab[7].value = dec2str(geteuid()); - return tab; -} - -static const char * -user_expand_varstr(struct master_service *service, - struct mail_storage_service_input *input, const char *str) -{ - string_t *ret; - - if (*str == SETTING_STRVAR_EXPANDED[0]) - return str + 1; - - i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]); - - ret = t_str_new(256); - var_expand(ret, str + 1, get_var_expand_table(service, input)); - return str_c(ret); -} - static void mail_storage_service_io_activate(void *context) { struct mail_storage_service_user *user = context; @@ -592,7 +642,8 @@ static void mail_storage_service_io_deactivate(void *context) static void mail_storage_service_init_log(struct mail_storage_service_ctx *ctx, - struct mail_storage_service_user *user) + struct mail_storage_service_user *user, + struct mail_storage_service_privileges *priv) { ctx->log_initialized = TRUE; T_BEGIN { @@ -600,7 +651,7 @@ mail_storage_service_init_log(struct mail_storage_service_ctx *ctx, str = t_str_new(256); var_expand(str, user->user_set->mail_log_prefix, - get_var_expand_table(ctx->service, &user->input)); + get_var_expand_table(ctx->service, &user->input, priv)); user->log_prefix = p_strdup(user->pool, str_c(str)); } T_END; @@ -950,8 +1001,8 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, struct mail_user **mail_user_r) { - const struct mail_user_settings *user_set = user->user_set; - const char *home, *chroot, *error; + struct mail_storage_service_privileges priv; + const char *error; unsigned int len; bool disallow_root = (user->flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0; @@ -959,17 +1010,15 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx, (user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0; bool use_chroot; - /* variable strings are expanded in mail_user_init(), - but we need the home and chroot sooner so do them separately here. */ - home = user_expand_varstr(ctx->service, &user->input, - user_set->mail_home); - chroot = user_expand_varstr(ctx->service, &user->input, - user_set->mail_chroot); + if (service_parse_privileges(ctx, user, &priv, &error) < 0) { + i_error("user %s: %s", user->input.username, error); + return -2; + } - if (*home != '/' && *home != '\0') { + if (*priv.home != '/' && *priv.home != '\0') { i_error("user %s: " "Relative home directory paths not supported: %s", - user->input.username, home); + user->input.username, priv.home); return -2; } @@ -980,36 +1029,36 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx, use_chroot = !temp_priv_drop || restrict_access_get_current_chroot() != NULL; - len = strlen(chroot); - if (len > 2 && strcmp(chroot + len - 2, "/.") == 0 && - strncmp(home, chroot, len - 2) == 0) { + len = strlen(priv.chroot); + if (len > 2 && strcmp(priv.chroot + len - 2, "/.") == 0 && + strncmp(priv.home, priv.chroot, len - 2) == 0) { /* mail_chroot = /chroot/. means that the home dir already contains the chroot dir. remove it from home. */ if (use_chroot) { - home += len - 2; - if (*home == '\0') - home = "/"; - chroot = t_strndup(chroot, len - 2); + priv.home += len - 2; + if (*priv.home == '\0') + priv.home = "/"; + priv.chroot = t_strndup(priv.chroot, len - 2); - set_keyval(ctx, user, "mail_home", home); - set_keyval(ctx, user, "mail_chroot", chroot); + set_keyval(ctx, user, "mail_home", priv.home); + set_keyval(ctx, user, "mail_chroot", priv.chroot); } } else if (len > 0 && !use_chroot) { /* we're not going to chroot. fix home directory so we can access it. */ - if (*home == '\0' || strcmp(home, "/") == 0) - home = chroot; + if (*priv.home == '\0' || strcmp(priv.home, "/") == 0) + priv.home = priv.chroot; else - home = t_strconcat(chroot, home, NULL); - chroot = ""; - set_keyval(ctx, user, "mail_home", home); + priv.home = t_strconcat(priv.chroot, priv.home, NULL); + priv.chroot = ""; + set_keyval(ctx, user, "mail_home", priv.home); } if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) - mail_storage_service_init_log(ctx, user); + mail_storage_service_init_log(ctx, user, &priv); if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) { - if (service_drop_privileges(user, user_set, home, chroot, + if (service_drop_privileges(user, &priv, disallow_root, temp_priv_drop, FALSE, &error) < 0) { i_error("user %s: Couldn't drop privileges: %s", @@ -1025,7 +1074,7 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx, initialized yet. */ module_dir_init(mail_storage_service_modules); - if (mail_storage_service_init_post(ctx, user, home, + if (mail_storage_service_init_post(ctx, user, &priv, mail_user_r, &error) < 0) { i_error("user %s: Initialization failed: %s", user->input.username, error); @@ -1037,18 +1086,14 @@ int mail_storage_service_next(struct mail_storage_service_ctx *ctx, void mail_storage_service_restrict_setenv(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user) { - const struct mail_user_settings *user_set = user->user_set; - const char *home, *chroot, *error; - - home = user_expand_varstr(ctx->service, &user->input, - user_set->mail_home); - chroot = user_expand_varstr(ctx->service, &user->input, - user_set->mail_chroot); + struct mail_storage_service_privileges priv; + const char *error; - if (service_drop_privileges(user, user_set, home, chroot, - FALSE, FALSE, TRUE, - &error) < 0) - i_fatal("%s", error); + if (service_parse_privileges(ctx, user, &priv, &error) < 0) + i_fatal("user %s: %s", user->input.username, error); + if (service_drop_privileges(user, &priv, + FALSE, FALSE, TRUE, &error) < 0) + i_fatal("user %s: %s", user->input.username, error); } int mail_storage_service_lookup_next(struct mail_storage_service_ctx *ctx, diff --git a/src/lib-storage/mail-user.c b/src/lib-storage/mail-user.c index 4ea4778aa2..5fb740f991 100644 --- a/src/lib-storage/mail-user.c +++ b/src/lib-storage/mail-user.c @@ -159,12 +159,10 @@ struct mail_user *mail_user_find(struct mail_user *user, const char *name) return NULL; } -void mail_user_set_vars(struct mail_user *user, uid_t uid, gid_t gid, - const char *service, const struct ip_addr *local_ip, +void mail_user_set_vars(struct mail_user *user, const char *service, + const struct ip_addr *local_ip, const struct ip_addr *remote_ip) { - user->uid = uid; - user->gid = gid; user->service = p_strdup(user->pool, service); if (local_ip != NULL && local_ip->family != 0) { user->local_ip = p_new(user->pool, struct ip_addr, 1); diff --git a/src/lib-storage/mail-user.h b/src/lib-storage/mail-user.h index c626c8b71b..b4c4a0796c 100644 --- a/src/lib-storage/mail-user.h +++ b/src/lib-storage/mail-user.h @@ -78,8 +78,8 @@ void mail_user_unref(struct mail_user **user); struct mail_user *mail_user_find(struct mail_user *user, const char *name); /* Specify mail location %variable expansion data. */ -void mail_user_set_vars(struct mail_user *user, uid_t uid, gid_t gid, - const char *service, const struct ip_addr *local_ip, +void mail_user_set_vars(struct mail_user *user, const char *service, + const struct ip_addr *local_ip, const struct ip_addr *remote_ip); /* Return %variable expansion table for the user. */ const struct var_expand_table *