int acl_shared_namespaces_add(struct mail_namespace *ns) { struct acl_user *auser = ACL_USER_CONTEXT(ns->user); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ns->list); struct mail_storage *storage = mail_namespace_get_default_storage(ns); struct acl_lookup_dict_iter *iter; const char *name; i_assert(ns->type == MAIL_NAMESPACE_TYPE_SHARED); i_assert(strcmp(storage->name, MAIL_SHARED_STORAGE_NAME) == 0); if (ioloop_time < alist->last_shared_add_check + SHARED_NS_RETRY_SECS) { /* already added, don't bother rechecking */ return 0; } alist->last_shared_add_check = ioloop_time; iter = acl_lookup_dict_iterate_visible_init(auser->acl_lookup_dict); while ((name = acl_lookup_dict_iterate_visible_next(iter)) != NULL) { T_BEGIN { acl_shared_namespace_add(ns, storage, name); } T_END; } return acl_lookup_dict_iterate_visible_deinit(&iter); }
int acl_mailbox_list_have_right(struct mailbox_list *list, const char *name, bool parent, unsigned int acl_storage_right_idx, bool *can_see_r) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); struct acl_backend *backend = alist->rights.backend; const unsigned int *idx_arr = alist->rights.acl_storage_right_idx; struct acl_object *aclobj; int ret, ret2; aclobj = !parent ? acl_object_init_from_name(backend, name) : acl_object_init_from_parent(backend, name); ret = acl_object_have_right(aclobj, idx_arr[acl_storage_right_idx]); if (can_see_r != NULL) { ret2 = acl_object_have_right(aclobj, idx_arr[ACL_STORAGE_RIGHT_LOOKUP]); if (ret2 < 0) ret = -1; *can_see_r = ret2 > 0; } acl_object_deinit(&aclobj); if (ret < 0) mailbox_list_set_internal_error(list); return ret; }
void acl_mail_namespace_storage_added(struct mail_namespace *ns) { struct acl_user *auser = ACL_USER_CONTEXT(ns->user); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ns->list); struct acl_backend *backend; const char *current_username, *owner_username; bool owner = TRUE; if (alist == NULL) return; owner_username = ns->user->username; current_username = auser->acl_user; if (current_username == NULL) current_username = owner_username; else owner = strcmp(current_username, owner_username) == 0; /* We don't care about the username for non-private mailboxes. It's used only when checking if we're the mailbox owner. We never are for shared/public mailboxes. */ if (ns->type != MAIL_NAMESPACE_TYPE_PRIVATE) owner = FALSE; /* we need to know the storage when initializing backend */ backend = acl_backend_init(auser->acl_env, ns->list, current_username, auser->groups, owner); if (backend == NULL) i_fatal("ACL backend initialization failed"); acl_storage_rights_ctx_init(&alist->rights, backend); }
static void acl_mailbox_list_deinit(struct mailbox_list *list) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); if (alist->rights.backend != NULL) acl_backend_deinit(&alist->rights.backend); alist->module_ctx.super.deinit(list); }
static void acl_mailbox_try_list_fast(struct acl_mailbox_list_iterate_context *ctx) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ctx->ctx.list); struct acl_backend *backend = alist->rights.backend; const unsigned int *idxp; const struct acl_mask *acl_mask; struct acl_mailbox_list_context *nonowner_list_ctx; struct mail_namespace *ns = ctx->ctx.list->ns; struct mailbox_list_iter_update_context update_ctx; const char *name; int ret; if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_RAW_LIST | MAILBOX_LIST_ITER_SELECT_SUBSCRIBED)) != 0) return; if (ns->type == MAIL_NAMESPACE_TYPE_PUBLIC) { /* mailboxes in public namespace should all be listable to someone. we don't benefit from fast listing. */ return; } /* if this namespace's default rights contain LOOKUP, we'll need to go through all mailboxes in any case. */ idxp = alist->rights.acl_storage_right_idx + ACL_STORAGE_RIGHT_LOOKUP; if (acl_backend_get_default_rights(backend, &acl_mask) < 0 || acl_cache_mask_isset(acl_mask, *idxp)) return; /* no LOOKUP right by default, we can optimize this */ memset(&update_ctx, 0, sizeof(update_ctx)); update_ctx.iter_ctx = &ctx->ctx; update_ctx.glob = imap_match_init(pool_datastack_create(), "*", (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0, ctx->sep); update_ctx.match_parents = TRUE; update_ctx.tree_ctx = mailbox_tree_init(ctx->sep); nonowner_list_ctx = acl_backend_nonowner_lookups_iter_init(backend); while ((ret = acl_backend_nonowner_lookups_iter_next(nonowner_list_ctx, &name)) > 0) { T_BEGIN { const char *vname = mailbox_list_get_vname(ns->list, name); mailbox_list_iter_update(&update_ctx, vname); } T_END; } acl_backend_nonowner_lookups_iter_deinit(&nonowner_list_ctx); if (ret == 0) ctx->lookup_boxes = update_ctx.tree_ctx; else mailbox_tree_deinit(&update_ctx.tree_ctx); }
static struct mailbox_list_iterate_context * acl_mailbox_list_iter_init(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); struct acl_mailbox_list_iterate_context *ctx; pool_t pool; const char *p; unsigned int i; bool inboxcase; pool = pool_alloconly_create("mailbox list acl iter", 1024); ctx = p_new(pool, struct acl_mailbox_list_iterate_context, 1); ctx->ctx.pool = pool; ctx->ctx.list = list; ctx->ctx.flags = flags; if (list->ns->type != MAIL_NAMESPACE_TYPE_PRIVATE && (list->ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0) { /* non-private namespace with subscriptions=yes. this could be a site-global subscriptions file, so hide subscriptions for mailboxes the user doesn't see. */ ctx->hide_nonlistable_subscriptions = TRUE; } inboxcase = (list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0; ctx->sep = mail_namespace_get_sep(list->ns); ctx->ctx.glob = imap_match_init_multiple(pool, patterns, inboxcase, ctx->sep); /* see if all patterns have only a single '*' and it's at the end. we can use it to do some optimizations. */ ctx->simple_star_glob = TRUE; for (i = 0; patterns[i] != NULL; i++) { p = strchr(patterns[i], '*'); if (p == NULL || p[1] != '\0') { ctx->simple_star_glob = FALSE; break; } } /* Try to avoid reading ACLs from all mailboxes by getting a smaller list of mailboxes that have even potential to be visible. If we couldn't get such a list, we'll go through all mailboxes. */ T_BEGIN { acl_mailbox_try_list_fast(ctx); } T_END; ctx->super_ctx = alist->module_ctx.super. iter_init(list, patterns, flags); return &ctx->ctx; }
static int acl_mailbox_list_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct acl_mailbox_list_iterate_context *ctx = (struct acl_mailbox_list_iterate_context *)_ctx; struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(_ctx->list); int ret = ctx->ctx.failed ? -1 : 0; if (alist->module_ctx.super.iter_deinit(ctx->super_ctx) < 0) ret = -1; if (ctx->lookup_boxes != NULL) mailbox_tree_deinit(&ctx->lookup_boxes); pool_unref(&_ctx->pool); return ret; }
void acl_mailbox_allocated(struct mailbox *box) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(box->list); struct mailbox_vfuncs *v = box->vlast; struct acl_mailbox *abox; if (alist == NULL) { /* ACLs disabled */ return; } if (box->list->ns->type == MAIL_NAMESPACE_TYPE_SHARED && (box->list->ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) { /* this is the root shared namespace, which itself doesn't have any existing mailboxes. */ return; } abox = p_new(box->pool, struct acl_mailbox, 1); abox->module_ctx.super = *v; box->vlast = &abox->module_ctx.super; /* aclobj can be used for setting ACLs, even when mailbox is opened with IGNORE_ACLS flag */ abox->aclobj = acl_object_init_from_name(alist->rights.backend, mailbox_get_name(box)); v->free = acl_mailbox_free; if ((box->flags & MAILBOX_FLAG_IGNORE_ACLS) == 0) { abox->acl_enabled = TRUE; v->is_readonly = acl_is_readonly; v->exists = acl_mailbox_exists; v->open = acl_mailbox_open; v->get_status = acl_mailbox_get_status; v->create_box = acl_mailbox_create; v->update_box = acl_mailbox_update; v->delete_box = acl_mailbox_delete; v->rename_box = acl_mailbox_rename; v->save_begin = acl_save_begin; v->copy = acl_copy; v->transaction_commit = acl_transaction_commit; v->attribute_set = acl_attribute_set; v->attribute_get = acl_attribute_get; v->attribute_iter_init = acl_attribute_iter_init; v->attribute_iter_next = acl_attribute_iter_next; v->attribute_iter_deinit = acl_attribute_iter_deinit; } MODULE_CONTEXT_SET(box, acl_storage_module, abox); }
static struct mailbox_list_iterate_context * acl_mailbox_list_iter_init_shared(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); struct mailbox_list_iterate_context *ctx; int ret; /* before listing anything add namespaces for all users who may have visible mailboxes */ ret = acl_shared_namespaces_add(list->ns); ctx = alist->module_ctx.super.iter_init(list, patterns, flags); if (ret < 0) ctx->failed = TRUE; return ctx; }
static int acl_mailbox_open_check_acl(struct mailbox *box) { struct acl_mailbox *abox = ACL_CONTEXT(box); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(box->list); const unsigned int *idx_arr = alist->rights.acl_storage_right_idx; enum acl_storage_rights open_right; int ret; /* mailbox can be opened either for reading or appending new messages */ if ((box->flags & MAILBOX_FLAG_IGNORE_ACLS) != 0 || (box->list->ns->flags & NAMESPACE_FLAG_NOACL) != 0 || abox->skip_acl_checks) return 0; if ((box->flags & MAILBOX_FLAG_SAVEONLY) != 0) { open_right = (box->flags & MAILBOX_FLAG_POST_SESSION) != 0 ? ACL_STORAGE_RIGHT_POST : ACL_STORAGE_RIGHT_INSERT; } else if (box->deleting) { open_right = ACL_STORAGE_RIGHT_DELETE; } else { open_right = ACL_STORAGE_RIGHT_READ; } ret = acl_object_have_right(abox->aclobj, idx_arr[open_right]); if (ret <= 0) { if (ret == 0) { /* no access. */ acl_mailbox_fail_not_found(box); } return -1; } if (open_right != ACL_STORAGE_RIGHT_READ) { ret = acl_object_have_right(abox->aclobj, idx_arr[ACL_STORAGE_RIGHT_READ]); if (ret < 0) return -1; if (ret == 0) abox->no_read_right = TRUE; } return 0; }
static const struct mailbox_info * acl_mailbox_list_iter_next_info(struct acl_mailbox_list_iterate_context *ctx) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ctx->ctx.list); const struct mailbox_info *info; while ((info = alist->module_ctx.super.iter_next(ctx->super_ctx)) != NULL) { /* if we've a list of mailboxes with LOOKUP rights, skip the mailboxes not in the list (since we know they can't be visible to us). */ if (ctx->lookup_boxes == NULL || mailbox_tree_lookup(ctx->lookup_boxes, info->vname) != NULL) break; if (ctx->ctx.list->ns->user->mail_debug) { i_debug("acl: Mailbox not in dovecot-acl-list: %s", info->vname); } } return info; }
int acl_mailbox_right_lookup(struct mailbox *box, unsigned int right_idx) { struct acl_mailbox *abox = ACL_CONTEXT(box); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(box->list); int ret; if (abox->skip_acl_checks) return 1; ret = acl_object_have_right(abox->aclobj, alist->rights.acl_storage_right_idx[right_idx]); if (ret > 0) return 1; if (ret < 0) { mail_storage_set_internal_error(box->storage); return -1; } mail_storage_set_error(box->storage, MAIL_ERROR_PERM, MAIL_ERRSTR_NO_PERMISSION); return 0; }
static void acl_mailbox_copy_acls_from_parent(struct mailbox *box) { struct acl_mailbox *abox = ACL_CONTEXT(box); struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(box->list); struct acl_object *parent_aclobj; struct acl_object_list_iter *iter; struct acl_rights_update update; memset(&update, 0, sizeof(update)); update.modify_mode = ACL_MODIFY_MODE_REPLACE; update.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; parent_aclobj = acl_object_init_from_parent(alist->rights.backend, box->name); iter = acl_object_list_init(parent_aclobj); while (acl_object_list_next(iter, &update.rights) > 0) { /* don't copy global ACL rights. */ if (!update.rights.global) (void)acl_object_update(abox->aclobj, &update); } acl_object_list_deinit(&iter); acl_object_deinit(&parent_aclobj); }
struct acl_backend *acl_mailbox_list_get_backend(struct mailbox_list *list) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); return alist->rights.backend; }