diff options
Diffstat (limited to 'src/plugins/acl/acl-api.c')
-rw-r--r-- | src/plugins/acl/acl-api.c | 847 |
1 files changed, 847 insertions, 0 deletions
diff --git a/src/plugins/acl/acl-api.c b/src/plugins/acl/acl-api.c new file mode 100644 index 0000000..2e422ea --- /dev/null +++ b/src/plugins/acl/acl-api.c @@ -0,0 +1,847 @@ +/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "strescape.h" +#include "hash.h" +#include "mail-user.h" +#include "mailbox-list.h" +#include "acl-global-file.h" +#include "acl-cache.h" +#include "acl-api-private.h" + +struct acl_letter_map { + char letter; + const char *name; +}; + +static const struct acl_letter_map acl_letter_map[] = { + { 'l', MAIL_ACL_LOOKUP }, + { 'r', MAIL_ACL_READ }, + { 'w', MAIL_ACL_WRITE }, + { 's', MAIL_ACL_WRITE_SEEN }, + { 't', MAIL_ACL_WRITE_DELETED }, + { 'i', MAIL_ACL_INSERT }, + { 'p', MAIL_ACL_POST }, + { 'e', MAIL_ACL_EXPUNGE }, + { 'k', MAIL_ACL_CREATE }, + { 'x', MAIL_ACL_DELETE }, + { 'a', MAIL_ACL_ADMIN }, + { '\0', NULL } +}; + +struct acl_object *acl_object_init_from_name(struct acl_backend *backend, + const char *name) +{ + return backend->v.object_init(backend, name); +} + +struct acl_object *acl_object_init_from_parent(struct acl_backend *backend, + const char *child_name) +{ + return backend->v.object_init_parent(backend, child_name); +} + +void acl_object_deinit(struct acl_object **_aclobj) +{ + struct acl_object *aclobj = *_aclobj; + + *_aclobj = NULL; + aclobj->backend->v.object_deinit(aclobj); +} + +int acl_object_have_right(struct acl_object *aclobj, unsigned int right_idx) +{ + struct acl_backend *backend = aclobj->backend; + const struct acl_mask *have_mask; + unsigned int read_idx; + + if (backend->v.object_refresh_cache(aclobj) < 0) + return -1; + + have_mask = acl_cache_get_my_rights(backend->cache, aclobj->name); + if (have_mask == NULL) { + if (acl_backend_get_default_rights(backend, &have_mask) < 0) + return -1; + } + + if (acl_cache_mask_isset(have_mask, right_idx)) + return 1; + + if (mailbox_list_get_user(aclobj->backend->list)->dsyncing) { + /* when dsync is running on a shared mailbox, it must be able + to do everything inside it. however, dsync shouldn't touch + mailboxes where user doesn't have any read access, because + that could make them readable on the replica. */ + read_idx = acl_backend_lookup_right(aclobj->backend, + MAIL_ACL_READ); + if (acl_cache_mask_isset(have_mask, read_idx)) + return 1; + } + return 0; +} + +const char *const * +acl_backend_mask_get_names(struct acl_backend *backend, + const struct acl_mask *mask, pool_t pool) +{ + const char *const *names; + const char **buf, **rights; + unsigned int names_count, count, i, j, name_idx; + + names = acl_cache_get_names(backend->cache, &names_count); + buf = t_new(const char *, (mask->size * CHAR_BIT) + 1); + count = 0; + for (i = 0, name_idx = 0; i < mask->size; i++) { + if (mask->mask[i] == 0) + name_idx += CHAR_BIT; + else { + for (j = 1; j < (1 << CHAR_BIT); j <<= 1, name_idx++) { + if ((mask->mask[i] & j) == 0) + continue; + + /* @UNSAFE */ + i_assert(name_idx < names_count); + buf[count++] = p_strdup(pool, names[name_idx]); + } + } + } + + /* @UNSAFE */ + rights = p_new(pool, const char *, count + 1); + memcpy(rights, buf, count * sizeof(const char *)); + return rights; +} + +static int acl_object_get_my_rights_real(struct acl_object *aclobj, pool_t pool, + const char *const **rights_r) +{ + struct acl_backend *backend = aclobj->backend; + const struct acl_mask *mask; + + if (backend->v.object_refresh_cache(aclobj) < 0) + return -1; + + mask = acl_cache_get_my_rights(backend->cache, aclobj->name); + if (mask == NULL) { + if (acl_backend_get_default_rights(backend, &mask) < 0) + return -1; + } + + *rights_r = acl_backend_mask_get_names(backend, mask, pool); + return 0; +} + +int acl_object_get_my_rights(struct acl_object *aclobj, pool_t pool, + const char *const **rights_r) +{ + int ret; + + if (pool->datastack_pool) + return acl_object_get_my_rights_real(aclobj, pool, rights_r); + T_BEGIN { + ret = acl_object_get_my_rights_real(aclobj, pool, rights_r); + } T_END; + return ret; +} + +const char *const *acl_object_get_default_rights(struct acl_object *aclobj) +{ + return acl_backend_mask_get_names(aclobj->backend, + aclobj->backend->default_aclmask, + pool_datastack_create()); +} + +int acl_object_last_changed(struct acl_object *aclobj, time_t *last_changed_r) +{ + return aclobj->backend->v.last_changed(aclobj, last_changed_r); +} + +int acl_object_update(struct acl_object *aclobj, + const struct acl_rights_update *update) +{ + return aclobj->backend->v.object_update(aclobj, update); +} + +struct acl_object_list_iter *acl_object_list_init(struct acl_object *aclobj) +{ + return aclobj->backend->v.object_list_init(aclobj); +} + +bool acl_object_list_next(struct acl_object_list_iter *iter, + struct acl_rights *rights_r) +{ + if (iter->failed) + return FALSE; + + return iter->aclobj->backend->v.object_list_next(iter, rights_r); +} + +int acl_object_list_deinit(struct acl_object_list_iter **_iter) +{ + struct acl_object_list_iter *iter = *_iter; + + *_iter = NULL; + return iter->aclobj->backend->v.object_list_deinit(iter); +} + +struct acl_object_list_iter * +acl_default_object_list_init(struct acl_object *aclobj) +{ + struct acl_object_list_iter *iter; + const struct acl_rights *aclobj_rights; + unsigned int i; + pool_t pool; + + pool = pool_alloconly_create("acl object list", 512); + iter = p_new(pool, struct acl_object_list_iter, 1); + iter->pool = pool; + iter->aclobj = aclobj; + + if (!array_is_created(&aclobj->rights)) { + /* we may have the object cached, but we don't have all the + rights read into memory */ + acl_cache_flush(aclobj->backend->cache, aclobj->name); + } + + if (aclobj->backend->v.object_refresh_cache(aclobj) < 0) + iter->failed = TRUE; + + aclobj_rights = array_get(&aclobj->rights, &iter->count); + if (iter->count > 0) { + iter->rights = p_new(pool, struct acl_rights, iter->count); + for (i = 0; i < iter->count; i++) + acl_rights_dup(&aclobj_rights[i], pool, &iter->rights[i]); + } else + iter->empty = TRUE; + return iter; +} + +bool acl_default_object_list_next(struct acl_object_list_iter *iter, + struct acl_rights *rights_r) +{ + if (iter->failed) + return FALSE; + + if (iter->idx == iter->count) + return FALSE; + *rights_r = iter->rights[iter->idx++]; + return TRUE; +} + +int acl_default_object_list_deinit(struct acl_object_list_iter *iter) +{ + int ret = 0; + if (iter->failed) + ret = -1; + else if (iter->empty) + ret = 0; + else + ret = 1; + + pool_unref(&iter->pool); + return ret; +} + +struct acl_mailbox_list_context * +acl_backend_nonowner_lookups_iter_init(struct acl_backend *backend) +{ + return backend->v.nonowner_lookups_iter_init(backend); +} + +bool acl_backend_nonowner_lookups_iter_next(struct acl_mailbox_list_context *ctx, + const char **name_r) +{ + return ctx->backend->v.nonowner_lookups_iter_next(ctx, name_r); +} + +int acl_backend_nonowner_lookups_iter_deinit(struct acl_mailbox_list_context **_ctx) +{ + struct acl_mailbox_list_context *ctx = *_ctx; + + *_ctx = NULL; + return ctx->backend->v.nonowner_lookups_iter_deinit(ctx); +} + +int acl_backend_nonowner_lookups_rebuild(struct acl_backend *backend) +{ + return backend->v.nonowner_lookups_rebuild(backend); +} + +void acl_rights_write_id(string_t *dest, const struct acl_rights *right) +{ + switch (right->id_type) { + case ACL_ID_ANYONE: + str_append(dest, ACL_ID_NAME_ANYONE); + break; + case ACL_ID_AUTHENTICATED: + str_append(dest, ACL_ID_NAME_AUTHENTICATED); + break; + case ACL_ID_OWNER: + str_append(dest, ACL_ID_NAME_OWNER); + break; + case ACL_ID_USER: + str_append(dest, ACL_ID_NAME_USER_PREFIX); + str_append(dest, right->identifier); + break; + case ACL_ID_GROUP: + str_append(dest, ACL_ID_NAME_GROUP_PREFIX); + str_append(dest, right->identifier); + break; + case ACL_ID_GROUP_OVERRIDE: + str_append(dest, ACL_ID_NAME_GROUP_OVERRIDE_PREFIX); + str_append(dest, right->identifier); + break; + case ACL_ID_TYPE_COUNT: + i_unreached(); + } +} + +const char *acl_rights_get_id(const struct acl_rights *right) +{ + string_t *str = t_str_new(32); + + acl_rights_write_id(str, right); + return str_c(str); +} + +static bool is_standard_right(const char *name) +{ + unsigned int i; + + for (i = 0; all_mailbox_rights[i] != NULL; i++) { + if (strcmp(all_mailbox_rights[i], name) == 0) + return TRUE; + } + return FALSE; +} + +int acl_rights_update_import(struct acl_rights_update *update, + const char *id, const char *const *rights, + const char **error_r) +{ + ARRAY_TYPE(const_string) dest_rights, dest_neg_rights, *dest; + unsigned int i, j; + + if (acl_identifier_parse(id, &update->rights) < 0) { + *error_r = t_strdup_printf("Invalid ID: %s", id); + return -1; + } + if (rights == NULL) { + update->modify_mode = ACL_MODIFY_MODE_CLEAR; + update->neg_modify_mode = ACL_MODIFY_MODE_CLEAR; + return 0; + } + + t_array_init(&dest_rights, 8); + t_array_init(&dest_neg_rights, 8); + for (i = 0; rights[i] != NULL; i++) { + const char *right = rights[i]; + + if (right[0] != '-') + dest = &dest_rights; + else { + right++; + dest = &dest_neg_rights; + } + if (strcmp(right, "all") != 0) { + if (*right == ':') { + /* non-standard right */ + right++; + array_push_back(dest, &right); + } else if (is_standard_right(right)) { + array_push_back(dest, &right); + } else { + *error_r = t_strdup_printf("Invalid right '%s'", + right); + return -1; + } + } else { + for (j = 0; all_mailbox_rights[j] != NULL; j++) + array_push_back(dest, &all_mailbox_rights[j]); + } + } + if (array_count(&dest_rights) > 0) { + array_append_zero(&dest_rights); + update->rights.rights = array_front(&dest_rights); + } else if (update->modify_mode == ACL_MODIFY_MODE_REPLACE) { + update->modify_mode = ACL_MODIFY_MODE_CLEAR; + } + if (array_count(&dest_neg_rights) > 0) { + array_append_zero(&dest_neg_rights); + update->rights.neg_rights = array_front(&dest_neg_rights); + } else if (update->neg_modify_mode == ACL_MODIFY_MODE_REPLACE) { + update->neg_modify_mode = ACL_MODIFY_MODE_CLEAR; + } + return 0; +} + +const char *acl_rights_export(const struct acl_rights *rights) +{ + string_t *str = t_str_new(128); + + if (rights->rights != NULL) + str_append(str, t_strarray_join(rights->rights, " ")); + if (rights->neg_rights != NULL && rights->neg_rights[0] != NULL) { + if (str_len(str) > 0) + str_append_c(str, ' '); + str_append_c(str, '-'); + str_append(str, t_strarray_join(rights->neg_rights, " -")); + } + return str_c(str); +} + +int acl_rights_parse_line(const char *line, pool_t pool, + struct acl_rights *rights_r, const char **error_r) +{ + const char *id_str, *const *right_names, *error = NULL; + + /* <id> [<imap acls>] [:<named acls>] */ + if (*line == '"') { + line++; + if (str_unescape_next(&line, &id_str) < 0 || + (line[0] != ' ' && line[0] != '\0')) { + *error_r = "Invalid quoted ID"; + return -1; + } + if (line[0] == ' ') + line++; + } else { + id_str = line; + line = strchr(id_str, ' '); + if (line == NULL) + line = ""; + else + id_str = t_strdup_until(id_str, line++); + } + + i_zero(rights_r); + + right_names = acl_right_names_parse(pool, line, &error); + if (*id_str != '-') + rights_r->rights = right_names; + else { + id_str++; + rights_r->neg_rights = right_names; + } + + if (acl_identifier_parse(id_str, rights_r) < 0) + error = t_strdup_printf("Unknown ID '%s'", id_str); + + if (error != NULL) { + *error_r = error; + return -1; + } + + rights_r->identifier = p_strdup(pool, rights_r->identifier); + return 0; +} + +void acl_rights_dup(const struct acl_rights *src, + pool_t pool, struct acl_rights *dest_r) +{ + i_zero(dest_r); + dest_r->id_type = src->id_type; + dest_r->identifier = p_strdup(pool, src->identifier); + dest_r->rights = src->rights == NULL ? NULL : + p_strarray_dup(pool, src->rights); + dest_r->neg_rights = src->neg_rights == NULL ? NULL : + p_strarray_dup(pool, src->neg_rights); + dest_r->global = src->global; +} + +int acl_rights_cmp(const struct acl_rights *r1, const struct acl_rights *r2) +{ + int ret; + + if (r1->global != r2->global) { + /* globals have higher priority than locals */ + return r1->global ? 1 : -1; + } + + ret = (int)r1->id_type - (int)r2->id_type; + if (ret != 0) + return ret; + + return null_strcmp(r1->identifier, r2->identifier); +} + +void acl_rights_sort(struct acl_object *aclobj) +{ + struct acl_rights *rights; + unsigned int i, dest, count; + + if (!array_is_created(&aclobj->rights)) + return; + + array_sort(&aclobj->rights, acl_rights_cmp); + + /* merge identical identifiers */ + rights = array_get_modifiable(&aclobj->rights, &count); + for (dest = 0, i = 1; i < count; i++) { + if (acl_rights_cmp(&rights[i], &rights[dest]) == 0) { + /* add i's rights to dest and delete i */ + acl_right_names_merge(aclobj->rights_pool, + &rights[dest].rights, + rights[i].rights, FALSE); + acl_right_names_merge(aclobj->rights_pool, + &rights[dest].neg_rights, + rights[i].neg_rights, FALSE); + } else { + if (++dest != i) + rights[dest] = rights[i]; + } + } + if (++dest < count) + array_delete(&aclobj->rights, dest, count - dest); +} + +bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights) +{ + const char *const *p; + + if (rights->id_type == ACL_ID_OWNER) { + /* ignore owner rights */ + return FALSE; + } + + if (rights->rights == NULL) + return FALSE; + + for (p = rights->rights; *p != NULL; p++) { + if (strcmp(*p, MAIL_ACL_LOOKUP) == 0) + return TRUE; + } + return FALSE; +} + +int acl_identifier_parse(const char *line, struct acl_rights *rights) +{ + if (str_begins(line, ACL_ID_NAME_USER_PREFIX)) { + rights->id_type = ACL_ID_USER; + rights->identifier = line + 5; + } else if (strcmp(line, ACL_ID_NAME_OWNER) == 0) { + rights->id_type = ACL_ID_OWNER; + } else if (str_begins(line, ACL_ID_NAME_GROUP_PREFIX)) { + rights->id_type = ACL_ID_GROUP; + rights->identifier = line + 6; + } else if (str_begins(line, ACL_ID_NAME_GROUP_OVERRIDE_PREFIX)) { + rights->id_type = ACL_ID_GROUP_OVERRIDE; + rights->identifier = line + 15; + } else if (strcmp(line, ACL_ID_NAME_AUTHENTICATED) == 0) { + rights->id_type = ACL_ID_AUTHENTICATED; + } else if (strcmp(line, ACL_ID_NAME_ANYONE) == 0 || + strcmp(line, "anonymous") == 0) { + rights->id_type = ACL_ID_ANYONE; + } else { + return -1; + } + return 0; +} + +static const char *const * +acl_right_names_alloc(pool_t pool, ARRAY_TYPE(const_string) *rights_arr, + bool dup_strings) +{ + const char **ret, *const *rights; + unsigned int i, dest, count; + + /* sort the rights first so we can easily drop duplicates */ + array_sort(rights_arr, i_strcmp_p); + + /* @UNSAFE */ + rights = array_get(rights_arr, &count); + ret = p_new(pool, const char *, count + 1); + if (count > 0) { + ret[0] = rights[0]; + for (i = dest = 1; i < count; i++) { + if (strcmp(rights[i-1], rights[i]) != 0) + ret[dest++] = rights[i]; + } + ret[dest] = NULL; + if (dup_strings) { + for (i = 0; i < dest; i++) + ret[i] = p_strdup(pool, ret[i]); + } + } + return ret; +} + +const char *const * +acl_right_names_parse(pool_t pool, const char *acl, const char **error_r) +{ + ARRAY_TYPE(const_string) rights; + const char *const *names; + unsigned int i; + + /* parse IMAP ACL list */ + while (*acl == ' ' || *acl == '\t') + acl++; + + t_array_init(&rights, 64); + while (*acl != '\0' && *acl != ' ' && *acl != '\t' && *acl != ':') { + for (i = 0; acl_letter_map[i].letter != '\0'; i++) { + if (acl_letter_map[i].letter == *acl) + break; + } + + if (acl_letter_map[i].letter == '\0') { + *error_r = t_strdup_printf("Unknown ACL '%c'", *acl); + return NULL; + } + + array_push_back(&rights, &acl_letter_map[i].name); + acl++; + } + while (*acl == ' ' || *acl == '\t') acl++; + + if (*acl != '\0') { + /* parse our own extended ACLs */ + if (*acl != ':') { + *error_r = "Missing ':' prefix in ACL extensions"; + return NULL; + } + + names = t_strsplit_spaces(acl + 1, ", \t"); + for (; *names != NULL; names++) { + const char *name = p_strdup(pool, *names); + array_push_back(&rights, &name); + } + } + + return acl_right_names_alloc(pool, &rights, FALSE); +} + +void acl_right_names_write(string_t *dest, const char *const *rights) +{ + char c2[2]; + unsigned int i, j, pos; + + c2[1] = '\0'; + pos = str_len(dest); + for (i = 0; rights[i] != NULL; i++) { + /* use letters if possible */ + for (j = 0; acl_letter_map[j].name != NULL; j++) { + if (strcmp(rights[i], acl_letter_map[j].name) == 0) { + c2[0] = acl_letter_map[j].letter; + str_insert(dest, pos, c2); + pos++; + break; + } + } + if (acl_letter_map[j].name == NULL) { + /* fallback to full name */ + str_append_c(dest, ' '); + str_append(dest, rights[i]); + } + } + if (pos + 1 < str_len(dest)) { + c2[0] = ':'; + str_insert(dest, pos + 1, c2); + } +} + +void acl_right_names_merge(pool_t pool, const char *const **destp, + const char *const *src, bool dup_strings) +{ + const char *const *dest = *destp; + ARRAY_TYPE(const_string) rights; + unsigned int i; + + t_array_init(&rights, 64); + if (dest != NULL) { + for (i = 0; dest[i] != NULL; i++) + array_push_back(&rights, &dest[i]); + } + if (src != NULL) { + for (i = 0; src[i] != NULL; i++) + array_push_back(&rights, &src[i]); + } + + *destp = acl_right_names_alloc(pool, &rights, dup_strings); +} + +bool acl_right_names_modify(pool_t pool, + const char *const **rightsp, + const char *const *modify_rights, + enum acl_modify_mode modify_mode) +{ + const char *const *old_rights = *rightsp; + const char *const *new_rights = NULL; + const char *null = NULL; + ARRAY_TYPE(const_string) rights; + unsigned int i, j; + + if (modify_rights == NULL && modify_mode != ACL_MODIFY_MODE_CLEAR) { + /* nothing to do here */ + return FALSE; + } + + switch (modify_mode) { + case ACL_MODIFY_MODE_REMOVE: + if (old_rights == NULL || *old_rights == NULL) { + /* nothing to do */ + return FALSE; + } + t_array_init(&rights, 64); + for (i = 0; old_rights[i] != NULL; i++) { + for (j = 0; modify_rights[j] != NULL; j++) { + if (strcmp(old_rights[i], modify_rights[j]) == 0) + break; + } + if (modify_rights[j] == NULL) + array_push_back(&rights, &old_rights[i]); + } + new_rights = &null; + modify_rights = array_count(&rights) == 0 ? NULL : + array_front(&rights); + acl_right_names_merge(pool, &new_rights, modify_rights, TRUE); + break; + case ACL_MODIFY_MODE_ADD: + new_rights = old_rights; + acl_right_names_merge(pool, &new_rights, modify_rights, TRUE); + break; + case ACL_MODIFY_MODE_REPLACE: + new_rights = &null; + acl_right_names_merge(pool, &new_rights, modify_rights, TRUE); + break; + case ACL_MODIFY_MODE_CLEAR: + if (*rightsp == NULL) { + /* ACL didn't exist before either */ + return FALSE; + } + *rightsp = NULL; + return TRUE; + } + i_assert(new_rights != NULL); + *rightsp = new_rights; + + if (old_rights == NULL) + return new_rights[0] != NULL; + + /* see if anything changed */ + for (i = 0; old_rights[i] != NULL && new_rights[i] != NULL; i++) { + if (strcmp(old_rights[i], new_rights[i]) != 0) + return TRUE; + } + return old_rights[i] != NULL || new_rights[i] != NULL; +} + +static void apply_owner_default_rights(struct acl_object *aclobj) +{ + struct acl_rights_update ru; + const char *null = NULL; + + i_zero(&ru); + ru.modify_mode = ACL_MODIFY_MODE_REPLACE; + ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; + ru.rights.id_type = ACL_ID_OWNER; + ru.rights.rights = aclobj->backend->default_rights; + ru.rights.neg_rights = &null; + acl_cache_update(aclobj->backend->cache, aclobj->name, &ru); +} + +void acl_object_rebuild_cache(struct acl_object *aclobj) +{ + struct acl_rights_update ru; + enum acl_modify_mode add_mode; + const struct acl_rights *rights, *prev_match = NULL; + unsigned int i, count; + bool first_global = TRUE; + + acl_cache_flush(aclobj->backend->cache, aclobj->name); + + if (!array_is_created(&aclobj->rights)) + return; + + /* Rights are sorted by their 1) locals first, globals next, + 2) acl_id_type. We'll apply only the rights matching ourself. + + Every time acl_id_type or local/global changes, the new ACLs will + replace all of the existing ACLs. Basically this means that if + user belongs to multiple matching groups or group-overrides, their + ACLs are merged. In all other situations the ACLs are replaced + (because there aren't duplicate rights entries and a user can't + match multiple usernames). */ + i_zero(&ru); + rights = array_get(&aclobj->rights, &count); + if (!acl_backend_user_is_owner(aclobj->backend)) + i = 0; + else { + /* we're the owner. skip over all rights entries until we + reach ACL_ID_OWNER or higher, or alternatively when we + reach a global ACL (even ACL_ID_ANYONE overrides owner's + rights if it's global) */ + for (i = 0; i < count; i++) { + if (rights[i].id_type >= ACL_ID_OWNER || + rights[i].global) + break; + } + apply_owner_default_rights(aclobj); + /* now continue applying the rest of the rights, + if there are any */ + } + for (; i < count; i++) { + if (!acl_backend_rights_match_me(aclobj->backend, &rights[i])) + continue; + + if (prev_match == NULL || + prev_match->id_type != rights[i].id_type || + prev_match->global != rights[i].global) { + /* replace old ACLs */ + add_mode = ACL_MODIFY_MODE_REPLACE; + } else { + /* merging to existing ACLs */ + i_assert(rights[i].id_type == ACL_ID_GROUP || + rights[i].id_type == ACL_ID_GROUP_OVERRIDE); + add_mode = ACL_MODIFY_MODE_ADD; + } + prev_match = &rights[i]; + + /* If [neg_]rights is NULL it needs to be ignored. + The easiest way to do that is to just mark it with + REMOVE mode */ + ru.modify_mode = rights[i].rights == NULL ? + ACL_MODIFY_MODE_REMOVE : add_mode; + ru.neg_modify_mode = rights[i].neg_rights == NULL ? + ACL_MODIFY_MODE_REMOVE : add_mode; + ru.rights = rights[i]; + if (rights[i].global && first_global) { + /* first global: reset negative ACLs so local ACLs + can't mess things up via them */ + first_global = FALSE; + ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; + } + acl_cache_update(aclobj->backend->cache, aclobj->name, &ru); + } +} + +void acl_object_remove_all_access(struct acl_object *aclobj) +{ + static const char *null = NULL; + struct acl_rights rights; + + i_zero(&rights); + rights.id_type = ACL_ID_ANYONE; + rights.rights = &null; + array_push_back(&aclobj->rights, &rights); + + rights.id_type = ACL_ID_OWNER; + rights.rights = &null; + array_push_back(&aclobj->rights, &rights); +} + +void acl_object_add_global_acls(struct acl_object *aclobj) +{ + struct acl_backend *backend = aclobj->backend; + const char *vname, *error; + + if (mailbox_list_is_valid_name(backend->list, aclobj->name, &error)) + vname = mailbox_list_get_vname(backend->list, aclobj->name); + else + vname = ""; + + acl_global_file_get(backend->global_file, vname, + aclobj->rights_pool, &aclobj->rights); +} |