From 0441d265f2bb9da249c7abf333f0f771fadb4ab5 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 19:36:47 +0200 Subject: Adding upstream version 1:2.3.21+dfsg1. Signed-off-by: Daniel Baumann --- src/plugins/acl/acl-attributes.c | 233 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 src/plugins/acl/acl-attributes.c (limited to 'src/plugins/acl/acl-attributes.c') diff --git a/src/plugins/acl/acl-attributes.c b/src/plugins/acl/acl-attributes.c new file mode 100644 index 0000000..515ff42 --- /dev/null +++ b/src/plugins/acl/acl-attributes.c @@ -0,0 +1,233 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "mail-storage-private.h" +#include "acl-api-private.h" +#include "acl-plugin.h" +#include "acl-storage.h" + +struct acl_mailbox_attribute_iter { + struct mailbox_attribute_iter iter; + struct mailbox_attribute_iter *super; + + struct acl_object_list_iter *acl_iter; + string_t *acl_name; + + bool failed; +}; + +static int +acl_attribute_update_acl(struct mailbox_transaction_context *t, const char *key, + const struct mail_attribute_value *value) +{ + const char *value_str, *id, *const *rights, *error; + struct acl_rights_update update; + + /* for now allow only dsync to update ACLs this way. + if this check is removed, it should be replaced by a setting, since + some admins may still have configured Dovecot using dovecot-acl + files directly that they don't want users to update. and in any case + ACL_STORAGE_RIGHT_ADMIN must be checked then. */ + if (!t->box->storage->user->dsyncing) { + mail_storage_set_error(t->box->storage, MAIL_ERROR_PERM, + MAIL_ERRSTR_NO_PERMISSION); + return -1; + } + + if (mailbox_attribute_value_to_string(t->box->storage, value, + &value_str) < 0) + return -1; + + i_zero(&update); + update.modify_mode = ACL_MODIFY_MODE_REPLACE; + update.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; + update.last_change = value->last_change; + id = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_ACL); + rights = value_str == NULL ? NULL : t_strsplit(value_str, " "); + if (acl_rights_update_import(&update, id, rights, &error) < 0) { + mail_storage_set_error(t->box->storage, MAIL_ERROR_PARAMS, error); + return -1; + } + /* FIXME: this should actually be done only at commit().. */ + return acl_mailbox_update_acl(t, &update); +} + +static int acl_attribute_get_acl(struct mailbox *box, const char *key, + struct mail_attribute_value *value_r) +{ + struct acl_object *aclobj = acl_mailbox_get_aclobj(box); + struct acl_object_list_iter *iter; + struct acl_rights rights, wanted_rights; + const char *id; + int ret = 0; + + i_zero(value_r); + + if (!box->storage->user->dsyncing) { + mail_storage_set_error(box->storage, MAIL_ERROR_PERM, + MAIL_ERRSTR_NO_PERMISSION); + return -1; + } + /* set last_change for all ACL objects, even if they don't exist + (because they could have been removed by the last change, and dsync + can use this information) */ + (void)acl_object_last_changed(aclobj, &value_r->last_change); + + i_zero(&wanted_rights); + id = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_ACL); + if (acl_identifier_parse(id, &wanted_rights) < 0) { + mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, + t_strdup_printf("Invalid ID: %s", id)); + return -1; + } + + iter = acl_object_list_init(aclobj); + while (acl_object_list_next(iter, &rights)) { + if (!rights.global && + rights.id_type == wanted_rights.id_type && + null_strcmp(rights.identifier, wanted_rights.identifier) == 0) { + value_r->value = acl_rights_export(&rights); + ret = 1; + break; + } + } + /* the return value here cannot be used, because this function + needs to return whether it actually matched something + or not */ + if (acl_object_list_deinit(&iter) < 0) { + mail_storage_set_internal_error(box->storage); + ret = -1; + } + return ret; +} + +static int acl_have_attribute_rights(struct mailbox *box) +{ + int ret; + + if (box->deleting) { + /* deleting attributes during mailbox deletion */ + return 1; + } + + ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP); + if (ret <= 0) { + if (ret < 0) + return -1; + mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, + T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); + return -1; + } + + return acl_mailbox_have_extra_attribute_rights(box) ? 0 : -1; +} + +int acl_attribute_set(struct mailbox_transaction_context *t, + enum mail_attribute_type type, const char *key, + const struct mail_attribute_value *value) +{ + struct acl_mailbox *abox = ACL_CONTEXT_REQUIRE(t->box); + + if (acl_have_attribute_rights(t->box) < 0) + return -1; + if (str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_ACL)) + return acl_attribute_update_acl(t, key, value); + return abox->module_ctx.super.attribute_set(t, type, key, value); +} + +int acl_attribute_get(struct mailbox *box, + enum mail_attribute_type type, const char *key, + struct mail_attribute_value *value_r) +{ + struct acl_mailbox *abox = ACL_CONTEXT_REQUIRE(box); + + if (acl_have_attribute_rights(box) < 0) + return -1; + if (str_begins(key, MAILBOX_ATTRIBUTE_PREFIX_ACL)) + return acl_attribute_get_acl(box, key, value_r); + return abox->module_ctx.super.attribute_get(box, type, key, value_r); +} + +struct mailbox_attribute_iter * +acl_attribute_iter_init(struct mailbox *box, enum mail_attribute_type type, + const char *prefix) +{ + struct acl_mailbox *abox = ACL_CONTEXT_REQUIRE(box); + struct acl_mailbox_attribute_iter *aiter; + + aiter = i_new(struct acl_mailbox_attribute_iter, 1); + aiter->iter.box = box; + if (acl_have_attribute_rights(box) < 0) + aiter->failed = TRUE; + else { + aiter->super = abox->module_ctx.super. + attribute_iter_init(box, type, prefix); + if (box->storage->user->dsyncing && + type == MAIL_ATTRIBUTE_TYPE_SHARED && + str_begins(MAILBOX_ATTRIBUTE_PREFIX_ACL, prefix)) { + aiter->acl_iter = acl_object_list_init(abox->aclobj); + aiter->acl_name = str_new(default_pool, 128); + str_append(aiter->acl_name, MAILBOX_ATTRIBUTE_PREFIX_ACL); + } + } + return &aiter->iter; +} + +static const char * +acl_attribute_iter_next_acl(struct acl_mailbox_attribute_iter *aiter) +{ + struct acl_rights rights; + + if (aiter->failed) + return NULL; + + while (acl_object_list_next(aiter->acl_iter, &rights)) { + if (rights.global) + continue; + str_truncate(aiter->acl_name, strlen(MAILBOX_ATTRIBUTE_PREFIX_ACL)); + acl_rights_write_id(aiter->acl_name, &rights); + return str_c(aiter->acl_name); + } + if (acl_object_list_deinit(&aiter->acl_iter) < 0) { + mail_storage_set_internal_error(aiter->iter.box->storage); + aiter->failed = TRUE; + } + return NULL; +} + +const char *acl_attribute_iter_next(struct mailbox_attribute_iter *iter) +{ + struct acl_mailbox_attribute_iter *aiter = + (struct acl_mailbox_attribute_iter *)iter; + struct acl_mailbox *abox = ACL_CONTEXT_REQUIRE(iter->box); + const char *key; + + if (aiter->super == NULL) + return NULL; + if (aiter->acl_iter != NULL) { + if ((key = acl_attribute_iter_next_acl(aiter)) != NULL) + return key; + } + return abox->module_ctx.super.attribute_iter_next(aiter->super); +} + +int acl_attribute_iter_deinit(struct mailbox_attribute_iter *iter) +{ + struct acl_mailbox_attribute_iter *aiter = + (struct acl_mailbox_attribute_iter *)iter; + struct acl_mailbox *abox = ACL_CONTEXT_REQUIRE(iter->box); + int ret = aiter->failed ? -1 : 0; + + if (aiter->super != NULL) { + if (abox->module_ctx.super.attribute_iter_deinit(aiter->super) < 0) + ret = -1; + } + if (aiter->acl_iter != NULL && acl_object_list_deinit(&aiter->acl_iter) < 0) { + mail_storage_set_internal_error(aiter->iter.box->storage); + ret = -1; + } + str_free(&aiter->acl_name); + i_free(aiter); + return ret; +} -- cgit v1.2.3