]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
acl: Added doveadm plugin supporting acl get/set/rights/debug commands.
authorTimo Sirainen <tss@iki.fi>
Fri, 4 Mar 2011 01:22:45 +0000 (03:22 +0200)
committerTimo Sirainen <tss@iki.fi>
Fri, 4 Mar 2011 01:22:45 +0000 (03:22 +0200)
src/plugins/acl/Makefile.am
src/plugins/acl/acl-api-private.h
src/plugins/acl/acl-api.c
src/plugins/acl/acl-backend-vfile.c
src/plugins/acl/acl-backend.c
src/plugins/acl/acl-lookup-dict.c
src/plugins/acl/acl-lookup-dict.h
src/plugins/acl/doveadm-acl.c [new file with mode: 0644]

index 63083df0da4249a64b656d41d79a689a8fec1829..ad3b407eec99469e50c81ea6acf593b28278133c 100644 (file)
@@ -1,11 +1,15 @@
+doveadm_moduledir = $(moduledir)/doveadm
+
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-dict \
        -I$(top_srcdir)/src/lib-mail \
        -I$(top_srcdir)/src/lib-imap \
        -I$(top_srcdir)/src/lib-index \
-       -I$(top_srcdir)/src/lib-storage
+       -I$(top_srcdir)/src/lib-storage \
+       -I$(top_srcdir)/src/doveadm
 
+lib10_doveadm_acl_plugin_la_LDFLAGS = -module -avoid-version
 lib01_acl_plugin_la_LDFLAGS = -module -avoid-version
 
 module_LTLIBRARIES = \
@@ -33,3 +37,9 @@ noinst_HEADERS = \
        acl-plugin.h \
        acl-shared-storage.h \
        acl-storage.h
+
+doveadm_module_LTLIBRARIES = \
+       lib10_doveadm_acl_plugin.la
+
+lib10_doveadm_acl_plugin_la_SOURCES = \
+       doveadm-acl.c
index 46478e9e62a0cedf88fc4cd2d3951e989d75ccc4..fc2211d2c0e0f42d7e72a57a143a3305482382ec 100644 (file)
@@ -74,6 +74,8 @@ struct acl_object_list_iter {
        unsigned int failed:1;
 };
 
+extern const char *const all_mailbox_rights[];
+
 const char *const *
 acl_backend_mask_get_names(struct acl_backend *backend,
                           const struct acl_mask *mask, pool_t pool);
@@ -82,4 +84,6 @@ int acl_backend_get_default_rights(struct acl_backend *backend,
 void acl_rights_write_id(string_t *dest, const struct acl_rights *right);
 bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights);
 
+int acl_identifier_parse(const char *line, struct acl_rights *rights);
+
 #endif
index 3c04e2e2b3b5c16228146e478ec79418dd3ff338..532c8822255cd09f9c63223c1734453261a9e5ba 100644 (file)
@@ -210,3 +210,30 @@ bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights)
        }
        return FALSE;
 }
+
+int acl_identifier_parse(const char *line, struct acl_rights *rights)
+{
+       if (strncmp(line, ACL_ID_NAME_USER_PREFIX,
+                   strlen(ACL_ID_NAME_USER_PREFIX)) == 0) {
+               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 (strncmp(line, ACL_ID_NAME_GROUP_PREFIX,
+                          strlen(ACL_ID_NAME_GROUP_PREFIX)) == 0) {
+               rights->id_type = ACL_ID_GROUP;
+               rights->identifier = line + 6;
+       } else if (strncmp(line, ACL_ID_NAME_GROUP_OVERRIDE_PREFIX,
+                          strlen(ACL_ID_NAME_GROUP_OVERRIDE_PREFIX)) == 0) {
+               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;
+}
index 98800c89b7790cb0711e809cb57393b5d371192a..c64bc7e3f361e24e73dae493ca130d0d52866901 100644 (file)
@@ -419,44 +419,8 @@ acl_object_vfile_parse_line(struct acl_object_vfile *aclobj, bool global,
                rights.neg_rights = right_names;
        }
 
-       switch (*line) {
-       case 'u':
-               if (strncmp(line, ACL_ID_NAME_USER_PREFIX,
-                           strlen(ACL_ID_NAME_USER_PREFIX)) == 0) {
-                       rights.id_type = ACL_ID_USER;
-                       rights.identifier = line + 5;
-                       break;
-               }
-       case 'o':
-               if (strcmp(line, ACL_ID_NAME_OWNER) == 0) {
-                       rights.id_type = ACL_ID_OWNER;
-                       break;
-               }
-       case 'g':
-               if (strncmp(line, ACL_ID_NAME_GROUP_PREFIX,
-                           strlen(ACL_ID_NAME_GROUP_PREFIX)) == 0) {
-                       rights.id_type = ACL_ID_GROUP;
-                       rights.identifier = line + 6;
-                       break;
-               } else if (strncmp(line, ACL_ID_NAME_GROUP_OVERRIDE_PREFIX,
-                                  strlen(ACL_ID_NAME_GROUP_OVERRIDE_PREFIX)) == 0) {
-                       rights.id_type = ACL_ID_GROUP_OVERRIDE;
-                       rights.identifier = line + 15;
-                       break;
-               }
-       case 'a':
-               if (strcmp(line, ACL_ID_NAME_AUTHENTICATED) == 0) {
-                       rights.id_type = ACL_ID_AUTHENTICATED;
-                       break;
-               } else if (strcmp(line, ACL_ID_NAME_ANYONE) == 0 ||
-                          strcmp(line, "anonymous") == 0) {
-                       rights.id_type = ACL_ID_ANYONE;
-                       break;
-               }
-       default:
+       if (acl_identifier_parse(line, &rights) < 0)
                error = t_strdup_printf("Unknown ID '%s'", line);
-               break;
-       }
 
        if (error != NULL) {
                i_error("ACL file %s line %u: %s", path, linenum, error);
@@ -1110,12 +1074,16 @@ acl_backend_vfile_update_write(struct acl_object_vfile *aclobj,
           first global */
        rights = array_get(&aclobj->rights, &count);
        for (i = 0; i < count && !rights[i].global; i++) {
-               if (rights[i].rights != NULL)
+               if (rights[i].rights != NULL) {
                        vfile_write_right(str, &rights[i], FALSE);
-               if (rights[i].neg_rights != NULL)
+                       o_stream_send(output, str_data(str), str_len(str));
+                       str_truncate(str, 0);
+               }
+               if (rights[i].neg_rights != NULL) {
                        vfile_write_right(str, &rights[i], TRUE);
-               o_stream_send(output, str_data(str), str_len(str));
-               str_truncate(str, 0);
+                       o_stream_send(output, str_data(str), str_len(str));
+                       str_truncate(str, 0);
+               }
        }
        str_free(&str);
        if (o_stream_flush(output) < 0) {
index 99e6ed21d97ddbf87d442073b828f051b139446b..d01f0d627e511a3fbeb34d7a206469508e823dfb 100644 (file)
@@ -12,7 +12,7 @@
 
 extern struct acl_backend_vfuncs acl_backend_vfile;
 
-static const char *const owner_mailbox_rights[] = {
+const char *const all_mailbox_rights[] = {
        MAIL_ACL_LOOKUP,
        MAIL_ACL_READ,
        MAIL_ACL_WRITE,
@@ -27,6 +27,7 @@ static const char *const owner_mailbox_rights[] = {
        NULL
 };
 
+static const char *const *owner_mailbox_rights = all_mailbox_rights;
 static const char *const non_owner_mailbox_rights[] = { NULL };
 
 struct acl_backend *
index 8e168613232a96f189a4ad8aa1ff817dcde1d4e2..42b9e133254e0fb5f9d1f79508c72e78e3972e7c 100644 (file)
@@ -63,6 +63,11 @@ void acl_lookup_dict_deinit(struct acl_lookup_dict **_dict)
        i_free(dict);
 }
 
+bool acl_lookup_dict_is_enabled(struct acl_lookup_dict *dict)
+{
+       return dict->dict != NULL;
+}
+
 static void
 acl_lookup_dict_write_rights_id(string_t *dest, const struct acl_rights *right)
 {
index 8fca8c0095ffcf0b1e9f08dc28549b6ebea8f25c..856aaf45769a2e0ef288d2020eaf0c19386665c7 100644 (file)
@@ -4,6 +4,8 @@
 struct acl_lookup_dict *acl_lookup_dict_init(struct mail_user *user);
 void acl_lookup_dict_deinit(struct acl_lookup_dict **dict);
 
+bool acl_lookup_dict_is_enabled(struct acl_lookup_dict *dict);
+
 int acl_lookup_dict_rebuild(struct acl_lookup_dict *dict);
 
 struct acl_lookup_dict_iter *
diff --git a/src/plugins/acl/doveadm-acl.c b/src/plugins/acl/doveadm-acl.c
new file mode 100644 (file)
index 0000000..f9000d0
--- /dev/null
@@ -0,0 +1,421 @@
+/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "module-dir.h"
+#include "acl-plugin.h"
+#include "acl-api-private.h"
+#include "acl-lookup-dict.h"
+#include "doveadm-print.h"
+#include "doveadm-mail.h"
+
+struct doveadm_acl_cmd_context {
+       struct doveadm_mail_cmd_context ctx;
+       bool get_match_me;
+};
+
+const char *doveadm_acl_plugin_version = DOVECOT_VERSION;
+
+void doveadm_acl_plugin_init(struct module *module);
+void doveadm_acl_plugin_deinit(void);
+
+static int
+cmd_acl_mailbox_open(struct mail_user *user, const char *mailbox,
+                    struct mailbox **box_r)
+{
+       struct acl_user *auser = ACL_USER_CONTEXT(user);
+       struct mail_namespace *ns;
+       struct mailbox *box;
+       const char *storage_name;
+
+       if (auser == NULL) {
+               i_error("ACL not enabled for %s", user->username);
+               return -1;
+       }
+
+       storage_name = mailbox;
+       ns = mail_namespace_find(user->namespaces, &storage_name);
+       if (ns == NULL) {
+               i_error("No namespace found for mailbox %s", mailbox);
+               return -1;
+       }
+       box = mailbox_alloc(ns->list, storage_name,
+                           MAILBOX_FLAG_READONLY | MAILBOX_FLAG_KEEP_RECENT |
+                           MAILBOX_FLAG_IGNORE_ACLS);
+       if (mailbox_open(box) < 0) {
+               i_error("Can't open mailbox %s: %s", mailbox,
+                       mail_storage_get_last_error(box->storage, NULL));
+               mailbox_free(&box);
+               return -1;
+       }
+       *box_r = box;
+       return 0;
+}
+
+static void cmd_acl_get_right(const struct acl_rights *rights)
+{
+       const char *id = "";
+       string_t *str;
+
+       switch (rights->id_type) {
+       case ACL_ID_ANYONE:
+               id = ACL_ID_NAME_ANYONE;
+               break;
+       case ACL_ID_AUTHENTICATED:
+               id = ACL_ID_NAME_AUTHENTICATED;
+               break;
+       case ACL_ID_OWNER:
+               id = ACL_ID_NAME_OWNER;
+               break;
+       case ACL_ID_USER:
+               id = t_strconcat(ACL_ID_NAME_USER_PREFIX,
+                                rights->identifier, NULL);
+               break;
+       case ACL_ID_GROUP:
+               id = t_strconcat(ACL_ID_NAME_GROUP_PREFIX,
+                                rights->identifier, NULL);
+               break;
+       case ACL_ID_GROUP_OVERRIDE:
+               id = t_strconcat(ACL_ID_NAME_GROUP_OVERRIDE_PREFIX,
+                                rights->identifier, NULL);
+               break;
+       case ACL_ID_TYPE_COUNT:
+               i_unreached();
+       }
+       doveadm_print(id);
+
+       if (rights->global)
+               doveadm_print("global");
+       else
+               doveadm_print("");
+
+       str = t_str_new(256);
+       if (rights->rights != NULL)
+               str_append(str, t_strarray_join(rights->rights, " "));
+       if (rights->neg_rights != NULL) {
+               if (str_len(str) > 0)
+                       str_append_c(str, ' ');
+               str_append_c(str, '-');
+               str_append(str, t_strarray_join(rights->neg_rights, " -"));
+       }
+       doveadm_print(str_c(str));
+}
+
+static void cmd_acl_get_mailbox(struct doveadm_acl_cmd_context *ctx,
+                               struct mailbox *box)
+{
+       struct acl_object *aclobj = acl_mailbox_get_aclobj(box);
+       struct acl_backend *backend;
+       struct acl_object_list_iter *iter;
+       struct acl_rights rights;
+       int ret;
+
+       backend = acl_mailbox_list_get_backend(box->list);
+
+       iter = acl_object_list_init(aclobj);
+       while ((ret = acl_object_list_next(iter, &rights)) > 0) T_BEGIN {
+               if (!ctx->get_match_me ||
+                   acl_backend_rights_match_me(backend, &rights))
+                       cmd_acl_get_right(&rights);
+       } T_END;
+       acl_object_list_deinit(&iter);
+
+       if (ret < 0)
+               i_error("ACL iteration failed");
+}
+
+static void
+cmd_acl_get_run(struct doveadm_mail_cmd_context *_ctx,
+               struct mail_user *user)
+{
+       struct doveadm_acl_cmd_context *ctx =
+               (struct doveadm_acl_cmd_context *)_ctx;
+       const char *mailbox = _ctx->args[0];
+       struct mailbox *box;
+
+       if (cmd_acl_mailbox_open(user, mailbox, &box) < 0)
+               return;
+
+       cmd_acl_get_mailbox(ctx, box);
+       mailbox_free(&box);
+}
+
+static bool cmd_acl_get_parse_arg(struct doveadm_mail_cmd_context *_ctx, int c)
+{
+       struct doveadm_acl_cmd_context *ctx =
+               (struct doveadm_acl_cmd_context *)_ctx;
+
+       switch (c) {
+       case 'm':
+               ctx->get_match_me = TRUE;
+               break;
+       default:
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static void cmd_acl_get_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED,
+                            const char *const args[])
+{
+       if (args[0] == NULL)
+               doveadm_mail_help_name("acl get");
+       doveadm_print_header("id", "ID", 0);
+       doveadm_print_header("global", "Global", 0);
+       doveadm_print_header("rights", "Rights", 0);
+}
+
+static struct doveadm_mail_cmd_context *
+cmd_acl_get_alloc(void)
+{
+       struct doveadm_acl_cmd_context *ctx;
+
+       ctx = doveadm_mail_cmd_alloc(struct doveadm_acl_cmd_context);
+       ctx->ctx.getopt_args = "m";
+       ctx->ctx.v.parse_arg = cmd_acl_get_parse_arg;
+       ctx->ctx.v.run = cmd_acl_get_run;
+       ctx->ctx.v.init = cmd_acl_get_init;
+       doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE);
+       return &ctx->ctx;
+}
+
+static void
+cmd_acl_rights_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user)
+{
+       const char *mailbox = ctx->args[0];
+       struct mailbox *box;
+       struct acl_object *aclobj;
+       const char *const *rights;
+
+       if (cmd_acl_mailbox_open(user, mailbox, &box) < 0)
+               return;
+
+       aclobj = acl_mailbox_get_aclobj(box);
+       if (acl_object_get_my_rights(aclobj, pool_datastack_create(),
+                                    &rights) < 0)
+               i_error("Failed to get rights");
+       else
+               doveadm_print(t_strarray_join(rights, " "));
+       mailbox_free(&box);
+}
+
+static void cmd_acl_rights_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED,
+                               const char *const args[])
+{
+       if (args[0] == NULL)
+               doveadm_mail_help_name("acl rights");
+       doveadm_print_header("rights", "Rights", 0);
+}
+
+static struct doveadm_mail_cmd_context *
+cmd_acl_rights_alloc(void)
+{
+       struct doveadm_mail_cmd_context *ctx;
+
+       ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context);
+       ctx->v.run = cmd_acl_rights_run;
+       ctx->v.init = cmd_acl_rights_init;
+       doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE);
+       return ctx;
+}
+
+static void
+cmd_acl_set_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user)
+{
+       const char *mailbox = ctx->args[0], *id = ctx->args[1];
+       const char *const *rights = ctx->args + 2;
+       ARRAY_TYPE(const_string) dest_rights, dest_neg_rights, *dest;
+       struct mailbox *box;
+       struct acl_object *aclobj;
+       struct acl_rights_update update;
+       unsigned int i, j;
+
+       if (cmd_acl_mailbox_open(user, mailbox, &box) < 0)
+               return;
+
+       memset(&update, 0, sizeof(update));
+       update.modify_mode = ACL_MODIFY_MODE_REPLACE;
+       update.neg_modify_mode = ACL_MODIFY_MODE_REPLACE;
+
+       if (acl_identifier_parse(id, &update.rights) < 0)
+               i_fatal("Invalid ID: %s", id);
+
+       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)
+                       array_append(dest, &right, 1);
+               else {
+                       for (j = 0; all_mailbox_rights[j] != NULL; j++)
+                               array_append(dest, &all_mailbox_rights[j], 1);
+               }
+       }
+       if (array_count(&dest_rights) > 0) {
+               (void)array_append_space(&dest_rights);
+               update.rights.rights = array_idx(&dest_rights, 0);
+       }
+       if (array_count(&dest_neg_rights) > 0) {
+               (void)array_append_space(&dest_neg_rights);
+               update.rights.neg_rights = array_idx(&dest_neg_rights, 0);
+       }
+
+       aclobj = acl_mailbox_get_aclobj(box);
+       if (acl_object_update(aclobj, &update) < 0)
+               i_error("Failed to set ACL");
+       mailbox_free(&box);
+}
+
+static void cmd_acl_set_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED,
+                            const char *const args[])
+{
+       if (str_array_length(args) < 3)
+               doveadm_mail_help_name("acl set");
+}
+
+static struct doveadm_mail_cmd_context *
+cmd_acl_set_alloc(void)
+{
+       struct doveadm_mail_cmd_context *ctx;
+
+       ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context);
+       ctx->v.run = cmd_acl_set_run;
+       ctx->v.init = cmd_acl_set_init;
+       return ctx;
+}
+
+static bool cmd_acl_debug_mailbox(struct mailbox *box)
+{
+       struct mail_namespace *ns = mailbox_get_namespace(box);
+       struct acl_user *auser = ACL_USER_CONTEXT(ns->user);
+       struct acl_object *aclobj = acl_mailbox_get_aclobj(box);
+       struct acl_backend *backend = acl_mailbox_list_get_backend(box->list);
+       struct acl_mailbox_list_context *iter;
+       struct acl_lookup_dict_iter *diter;
+       const char *const *rights, *name;
+       int ret;
+
+       /* check if user has lookup right */
+       if (acl_object_get_my_rights(aclobj, pool_datastack_create(),
+                                    &rights) < 0)
+               i_fatal("Failed to get rights");
+
+       if (rights == NULL || rights[0] == NULL)
+               i_info("User %s has no rights for mailbox", ns->user->username);
+       else {
+               i_info("User %s has rights: %s",
+                      ns->user->username, t_strarray_join(rights, " "));
+       }
+       if (!str_array_find(rights, MAIL_ACL_LOOKUP)) {
+               i_error("User %s is missing 'lookup' right",
+                       ns->user->username);
+               return FALSE;
+       }
+
+       /* check if mailbox is listable */
+       if (ns->type == NAMESPACE_PRIVATE) {
+               i_info("Mailbox in user's private namespace");
+               return TRUE;
+       }
+
+       iter = acl_backend_nonowner_lookups_iter_init(backend);
+       while ((ret = acl_backend_nonowner_lookups_iter_next(iter, &name)) > 0) {
+               if (strcmp(name, box->name) == 0)
+                       break;
+       }
+       acl_backend_nonowner_lookups_iter_deinit(&iter);
+       if (ret < 0)
+               i_fatal("ACL non-owner iteration failed");
+       if (ret == 0) {
+               i_error("Mailbox not found from dovecot-acl-list");
+               return FALSE;
+       }
+       i_info("Mailbox found from dovecot-acl-list");
+
+       if (ns->type == NAMESPACE_PUBLIC) {
+               i_info("Mailbox is in public namespace");
+               return TRUE;
+       }
+
+       if (!acl_lookup_dict_is_enabled(auser->acl_lookup_dict)) {
+               i_error("acl_lookup_dict not enabled");
+               return FALSE;
+       }
+
+       /* shared namespace. see if it's in acl lookup dict */
+       diter = acl_lookup_dict_iterate_visible_init(auser->acl_lookup_dict);
+       while ((name = acl_lookup_dict_iterate_visible_next(diter)) != NULL) {
+               if (strcmp(name, ns->owner->username) == 0)
+                       break;
+       }
+       if (acl_lookup_dict_iterate_visible_deinit(&diter) < 0)
+               i_fatal("ACL shared dict iteration failed");
+       if (name == NULL) {
+               i_error("User %s not found from ACL shared dict",
+                       ns->owner->username);
+               return FALSE;
+       }
+
+       i_info("User %s found from ACL shared dict", ns->owner->username);
+       return TRUE;
+}
+
+static void
+cmd_acl_debug_run(struct doveadm_mail_cmd_context *ctx, struct mail_user *user)
+{
+       const char *mailbox = ctx->args[0];
+       struct mailbox *box;
+
+       if (cmd_acl_mailbox_open(user, mailbox, &box) < 0)
+               return;
+
+       if (cmd_acl_debug_mailbox(box))
+               i_info("Mailbox %s is visible in LIST", box->vname);
+       else
+               i_info("Mailbox %s is NOT visible in LIST", box->vname);
+       mailbox_free(&box);
+}
+
+static void cmd_acl_debug_init(struct doveadm_mail_cmd_context *ctx ATTR_UNUSED,
+                              const char *const args[])
+{
+       if (args[0] == NULL)
+               doveadm_mail_help_name("acl debug");
+}
+
+static struct doveadm_mail_cmd_context *
+cmd_acl_debug_alloc(void)
+{
+       struct doveadm_mail_cmd_context *ctx;
+
+       ctx = doveadm_mail_cmd_alloc(struct doveadm_mail_cmd_context);
+       ctx->v.run = cmd_acl_debug_run;
+       ctx->v.init = cmd_acl_debug_init;
+       return ctx;
+}
+
+static struct doveadm_mail_cmd acl_commands[] = {
+       { cmd_acl_get_alloc, "acl get", "[-m] <mailbox>" },
+       { cmd_acl_rights_alloc, "acl rights", "<mailbox>" },
+       { cmd_acl_set_alloc, "acl set", "<mailbox> <id> <rights>" },
+       { cmd_acl_debug_alloc, "acl debug", "<mailbox>" }
+};
+
+void doveadm_acl_plugin_init(struct module *module ATTR_UNUSED)
+{
+       unsigned int i;
+
+       for (i = 0; i < N_ELEMENTS(acl_commands); i++)
+               doveadm_mail_register_cmd(&acl_commands[i]);
+}
+
+void doveadm_acl_plugin_deinit(void)
+{
+}