imap-acl-plugin.c revision eacce2276278ce6a8176a9a100807dba50bbfb36
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2008-2011 Dovecot authors, see the included COPYING file */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#define ERROR_NOT_ADMIN "["IMAP_RESP_CODE_NOPERM"] " \
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen "You lack administrator privileges on this mailbox."
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic const struct imap_acl_letter_map imap_acl_letter_map[] = {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenconst char *imap_acl_plugin_version = DOVECOT_VERSION;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic void (*next_hook_client_created)(struct client **client);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic struct mailbox *
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenacl_mailbox_open_as_admin(struct client_command_context *cmd, const char *name)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen enum mailbox_existence existence = MAILBOX_EXISTENCE_NONE;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (ACL_USER_CONTEXT(cmd->client->user) == NULL) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen client_send_command_error(cmd, "ACLs disabled.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* Force opening the mailbox so that we can give a nicer error message
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if mailbox isn't selectable but is listable. */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen box = mailbox_alloc(ns->list, name, MAILBOX_FLAG_READONLY |
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (mailbox_exists(box, TRUE, &existence) == 0 &&
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_ADMIN);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* mailbox doesn't exist / not an administrator. */
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP) <= 0) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_tagline(cmd, "NO "ERROR_NOT_ADMIN);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic const struct imap_acl_letter_map *
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen unsigned int i;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen for (i = 0; imap_acl_letter_map[i].name != NULL; i++) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (strcmp(imap_acl_letter_map[i].name, name) == 0)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenimap_acl_write_rights_list(string_t *dest, const char *const *rights)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* write only letters */
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainenimap_acl_write_right(string_t *dest, string_t *tmp,
0bf3eac1110a902e7ec7e695c64e8e46c114e623Timo Sirainen const char *const *rights = neg ? right->neg_rights : right->rights;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen str_append(tmp, IMAP_ACL_GROUP_OVERRIDE_PREFIX);
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen imap_quote_append(dest, str_data(tmp), str_len(tmp), FALSE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenacl_rights_is_owner(struct acl_backend *backend,
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic bool have_positive_owner_rights(struct acl_backend *backend,
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen while ((ret = acl_object_list_next(iter, &rights)) > 0) {
8d131435ba4648c8821160ec38d508c97177c715Timo Sirainenimap_acl_write_aclobj(string_t *dest, struct acl_backend *backend,
b6612c334604eeb27e1ca2bd804ac66dcbc2eaadTimo Sirainen struct acl_object *aclobj, bool convert_owner,
e64d7b6f388fecd0c83a4f2acb54e30d5ac98c6cTimo Sirainen bool seen_owner = FALSE, seen_positive_owner = FALSE;
e64d7b6f388fecd0c83a4f2acb54e30d5ac98c6cTimo Sirainen username = acl_backend_get_acl_username(backend);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen while ((ret = acl_object_list_next(iter, &rights)) > 0) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (rights.id_type == ACL_ID_OWNER && convert_owner) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* oops, we have both owner and user=myself.
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen can't do the conversion, so try again. */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen imap_acl_write_right(dest, tmp, &rights, FALSE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen imap_acl_write_right(dest, tmp, &rights, TRUE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (!seen_positive_owner && username != NULL && add_default) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* no positive owner rights returned, write default ACLs */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen rights.rights = acl_object_get_default_rights(aclobj);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen imap_acl_write_right(dest, tmp, &rights, FALSE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic bool cmd_getacl(struct client_command_context *cmd)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (!client_read_string_args(cmd, 1, &mailbox))
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen box = acl_mailbox_open_as_admin(cmd, mailbox);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen imap_quote_append_string(str, mailbox, FALSE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen backend = acl_mailbox_list_get_backend(ns->list);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_tagline(cmd, "OK Getacl completed.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_tagline(cmd, "NO "MAIL_ERRSTR_CRITICAL_MSG);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic bool cmd_myrights(struct client_command_context *cmd)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen const char *const *rights;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (!client_read_string_args(cmd, 1, &mailbox))
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (ACL_USER_CONTEXT(cmd->client->user) == NULL) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_command_error(cmd, "ACLs disabled.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen MAILBOX_FLAG_READONLY | MAILBOX_FLAG_IGNORE_ACLS);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (acl_object_get_my_rights(acl_mailbox_get_aclobj(box),
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_tagline(cmd, "NO "MAIL_ERRSTR_CRITICAL_MSG);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* Post right alone doesn't give permissions to see if the mailbox
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen exists or not. Only mail deliveries care about that. */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen (strcmp(*rights, MAIL_ACL_POST) == 0 && rights[1] == NULL)) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen imap_quote_append_string(str, mailbox, FALSE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_tagline(cmd, "OK Myrights completed.");
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainenstatic bool cmd_listrights(struct client_command_context *cmd)
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen if (!client_read_string_args(cmd, 2, &mailbox, &identifier))
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen box = acl_mailbox_open_as_admin(cmd, mailbox);
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen imap_quote_append_string(str, mailbox, FALSE);
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen imap_quote_append_string(str, identifier, FALSE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen str_append(str, "\"\" l r w s t p i e k x a c d");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_tagline(cmd, "OK Listrights completed.");
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainenimap_acl_letters_parse(const char *letters, const char *const **rights_r,
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen const char **error_r)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen static const char *acl_t = MAIL_ACL_WRITE_DELETED;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen unsigned int i;
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen for (i = 0; imap_acl_letter_map[i].name != NULL; i++) {
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen if (imap_acl_letter_map[i].letter == *letters) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* Handling of obsolete rights as virtual
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen rights according to RFC 4314 */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic bool acl_anyone_allow(struct mail_user *user)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen const char *env;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen env = mail_user_plugin_getenv(user, "acl_anyone");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen return env != NULL && strcmp(env, "allow") == 0;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenimap_acl_identifier_parse(struct client_command_context *cmd,
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen *error_r = t_strdup_printf("Global ACLs can't be modified: %s",
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (check_anyone && !acl_anyone_allow(user)) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen *error_r = "'anyone' identifier is disallowed";
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen } else if (strcmp(id, IMAP_ACL_AUTHENTICATED) == 0) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (check_anyone && !acl_anyone_allow(user)) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen *error_r = "'authenticated' identifier is disallowed";
2e99f3f3bb35715ce5e0a75a2f2a9bac3ab4224bTimo Sirainen rights->identifier = id + strlen(IMAP_ACL_GROUP_PREFIX);
2e99f3f3bb35715ce5e0a75a2f2a9bac3ab4224bTimo Sirainen } else if (strncmp(id, IMAP_ACL_GROUP_OVERRIDE_PREFIX,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen strlen(IMAP_ACL_GROUP_OVERRIDE_PREFIX)) == 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void imap_acl_update_ensure_keep_admins(struct acl_backend *backend,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen static const char *acl_admin = MAIL_ACL_ADMIN;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen const char *const *rights = update->rights.rights;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen const char *const *default_rights;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen unsigned int i;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (have_positive_owner_rights(backend, aclobj))
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* adding initial rights for a user. we need to add
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen the defaults also. don't worry about duplicates. */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen default_rights = acl_object_get_default_rights(aclobj);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen array_append(&new_rights, &default_rights[i], 1);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* skip over the ADMIN removal and add the rest */
04b8a90af181cc4c7959266855e8ed50a22ed413Timo Sirainen /* add the missing ADMIN right */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen update->rights.rights = array_idx(&new_rights, 0);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic bool cmd_setacl(struct client_command_context *cmd)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen const char *mailbox, *identifier, *rights, *error;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (!client_read_string_args(cmd, 3, &mailbox, &identifier, &rights))
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen client_send_command_error(cmd, "Invalid arguments.");
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (imap_acl_identifier_parse(cmd, identifier, &update.rights,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (imap_acl_letters_parse(rights, &update.rights.rights, &error) < 0) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen box = acl_mailbox_open_as_admin(cmd, mailbox);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen backend = acl_mailbox_list_get_backend(ns->list);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (ns->type == NAMESPACE_PUBLIC && r->id_type == ACL_ID_OWNER) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen client_send_tagline(cmd, "NO Public namespaces have no owner");
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen update.rights.neg_rights = update.rights.rights;
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen } else if (ns->type == NAMESPACE_PRIVATE && r->rights != NULL &&
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen acl_backend_user_name_equals(backend, r->identifier)) ||
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* make sure client doesn't (accidentally) remove admin
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen privileges from its own mailboxes */
629600d9a85e8025c15a5eaeb80329e116e022c9Timo Sirainen imap_acl_update_ensure_keep_admins(backend, aclobj, &update);
629600d9a85e8025c15a5eaeb80329e116e022c9Timo Sirainen client_send_tagline(cmd, "NO "MAIL_ERRSTR_CRITICAL_MSG);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen client_send_tagline(cmd, "OK Setacl complete.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic bool cmd_deleteacl(struct client_command_context *cmd)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (!client_read_string_args(cmd, 2, &mailbox, &identifier))
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_command_error(cmd, "Invalid arguments.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen update.neg_modify_mode = ACL_MODIFY_MODE_CLEAR;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (imap_acl_identifier_parse(cmd, identifier, &update.rights,
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen box = acl_mailbox_open_as_admin(cmd, mailbox);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (acl_object_update(acl_mailbox_get_aclobj(box), &update) < 0)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen client_send_tagline(cmd, "NO "MAIL_ERRSTR_CRITICAL_MSG);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_tagline(cmd, "OK Deleteacl complete.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic void imap_acl_client_created(struct client **client)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (mail_user_is_plugin_loaded((*client)->user, imap_acl_module))
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen str_append((*client)->capability_string, " ACL RIGHTS=texk");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenvoid imap_acl_plugin_init(struct module *module)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen command_register("LISTRIGHTS", cmd_listrights, 0);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen command_register("MYRIGHTS", cmd_myrights, 0);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen command_register("DELETEACL", cmd_deleteacl, 0);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen imap_client_created_hook_set(imap_acl_client_created);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen imap_client_created_hook_set(next_hook_client_created);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenconst char *imap_acl_plugin_dependencies[] = { "acl", NULL };
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenconst char imap_acl_plugin_binary_dependency[] = "imap";