dovecot-2.2: acl: Added ACL checks for attributes. Added ACL <->...

dovecot at dovecot.org dovecot at dovecot.org
Thu Mar 14 15:41:49 EET 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/3ba8fa6d3cc2
changeset: 16026:3ba8fa6d3cc2
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Mar 14 15:41:39 2013 +0200
description:
acl: Added ACL checks for attributes. Added ACL <-> mailbox attribute mapping.
The ACL checks will be useful once IMAP METADATA extension is finished. The
mapping is used by dsync to sync ACLs via generic attribute syncing.

diffstat:

 src/plugins/acl/Makefile.am            |    1 +
 src/plugins/acl/acl-api-private.h      |    5 +
 src/plugins/acl/acl-api.c              |  100 ++++++++++++++
 src/plugins/acl/acl-api.h              |    9 +
 src/plugins/acl/acl-attributes.c       |  234 +++++++++++++++++++++++++++++++++
 src/plugins/acl/acl-backend-vfile.c    |   22 +++
 src/plugins/acl/acl-mailbox.c          |   60 +++++++-
 src/plugins/acl/acl-plugin.h           |    8 +
 src/plugins/acl/acl-storage.h          |   19 ++
 src/plugins/acl/doveadm-acl.c          |  121 +++-------------
 src/plugins/imap-acl/imap-acl-plugin.c |   29 +++-
 11 files changed, 499 insertions(+), 109 deletions(-)

diffs (truncated from 820 to 300 lines):

diff -r c6082de4bf5b -r 3ba8fa6d3cc2 src/plugins/acl/Makefile.am
--- a/src/plugins/acl/Makefile.am	Thu Mar 14 15:41:09 2013 +0200
+++ b/src/plugins/acl/Makefile.am	Thu Mar 14 15:41:39 2013 +0200
@@ -18,6 +18,7 @@
 
 lib01_acl_plugin_la_SOURCES = \
 	acl-api.c \
+	acl-attributes.c \
 	acl-backend.c \
 	acl-backend-vfile.c \
 	acl-backend-vfile-acllist.c \
diff -r c6082de4bf5b -r 3ba8fa6d3cc2 src/plugins/acl/acl-api-private.h
--- a/src/plugins/acl/acl-api-private.h	Thu Mar 14 15:41:09 2013 +0200
+++ b/src/plugins/acl/acl-api-private.h	Thu Mar 14 15:41:39 2013 +0200
@@ -32,6 +32,7 @@
 	int (*object_refresh_cache)(struct acl_object *aclobj);
 	int (*object_update)(struct acl_object *aclobj,
 			     const struct acl_rights_update *update);
+	int (*last_changed)(struct acl_object *aclobj, time_t *last_changed_r);
 
 	struct acl_object_list_iter *
 		(*object_list_init)(struct acl_object *aclobj);
@@ -86,5 +87,9 @@
 bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights);
 
 int acl_identifier_parse(const char *line, struct acl_rights *rights);
+int acl_rights_update_import(struct acl_rights_update *update,
+			     const char *id, const char *const *rights,
+			     const char **error_r);
+const char *acl_rights_export(const struct acl_rights *rights);
 
 #endif
diff -r c6082de4bf5b -r 3ba8fa6d3cc2 src/plugins/acl/acl-api.c
--- a/src/plugins/acl/acl-api.c	Thu Mar 14 15:41:09 2013 +0200
+++ b/src/plugins/acl/acl-api.c	Thu Mar 14 15:41:39 2013 +0200
@@ -1,6 +1,7 @@
 /* Copyright (c) 2006-2013 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "array.h"
 #include "str.h"
 #include "hash.h"
 #include "mail-user.h"
@@ -122,6 +123,11 @@
 					  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)
 {
@@ -205,6 +211,100 @@
 	}
 }
 
+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_append(dest, &right, 1);
+			} else if (is_standard_right(right)) {
+				array_append(dest, &right, 1);
+			} else {
+				*error_r = t_strdup_printf("Invalid right '%s'",
+							   right);
+				return -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) {
+		array_append_zero(&dest_rights);
+		update->rights.rights = array_idx(&dest_rights, 0);
+	} 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_idx(&dest_neg_rights, 0);
+	} 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) {
+		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);
+}
+
 bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights)
 {
 	const char *const *p;
diff -r c6082de4bf5b -r 3ba8fa6d3cc2 src/plugins/acl/acl-api.h
--- a/src/plugins/acl/acl-api.h	Thu Mar 14 15:41:09 2013 +0200
+++ b/src/plugins/acl/acl-api.h	Thu Mar 14 15:41:39 2013 +0200
@@ -30,6 +30,9 @@
 /* Allow changing ACL state in this mailbox */
 #define MAIL_ACL_ADMIN		"admin"
 
+#define MAILBOX_ATTRIBUTE_PREFIX_ACL \
+	MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT"acl/"
+
 /* ACL identifiers in override order */
 enum acl_id_type {
 	/* Anyone's rights, including anonymous's.
@@ -138,6 +141,9 @@
 			     const char *const **rights_r);
 /* Returns the default rights for the object. */
 const char *const *acl_object_get_default_rights(struct acl_object *aclobj);
+/* Returns timestamp of when the ACLs were last changed for this object,
+   or 0 = never. */
+int acl_object_last_changed(struct acl_object *aclobj, time_t *last_changed_r);
 
 /* Update ACL of given object. */
 int acl_object_update(struct acl_object *aclobj,
@@ -149,4 +155,7 @@
                          struct acl_rights *rights_r);
 void acl_object_list_deinit(struct acl_object_list_iter **iter);
 
+/* Returns the canonical ID for the right. */
+const char *acl_rights_get_id(const struct acl_rights *right);
+
 #endif
diff -r c6082de4bf5b -r 3ba8fa6d3cc2 src/plugins/acl/acl-attributes.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/acl/acl-attributes.c	Thu Mar 14 15:41:39 2013 +0200
@@ -0,0 +1,234 @@
+/* Copyright (c) 2013 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 char *value)
+{
+	const char *id, *const *rights, *error;
+	struct acl_rights_update update;
+
+	/* for now allow only admin (=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->admin) {
+		mail_storage_set_error(t->box->storage, MAIL_ERROR_PERM,
+				       MAIL_ERRSTR_NO_PERMISSION);
+		return -1;
+	}
+
+	memset(&update, 0, sizeof(update));
+	update.modify_mode = ACL_MODIFY_MODE_REPLACE;
+	update.neg_modify_mode = ACL_MODIFY_MODE_REPLACE;
+	id = key + strlen(MAILBOX_ATTRIBUTE_PREFIX_ACL);
+	rights = value == NULL ? NULL : t_strsplit(value, " ");
+	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;
+
+	memset(value_r, 0, sizeof(*value_r));
+
+	if (!box->storage->user->admin) {
+		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);
+
+	memset(&wanted_rights, 0, sizeof(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 ((ret = acl_object_list_next(iter, &rights)) > 0) {
+		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);
+			break;
+		}
+	}
+	if (ret < 0)
+		mail_storage_set_internal_error(box->storage);
+	acl_object_list_deinit(&iter);
+	return ret;
+}
+
+static int acl_have_attribute_rights(struct mailbox *box)
+{
+	int ret;
+
+	/* RFC 5464:
+
+	   When the ACL extension [RFC4314] is present, users can only set and
+	   retrieve private or shared mailbox annotations on a mailbox on which
+	   they have the "l" right and any one of the "r", "s", "w", "i", or "p"
+	   rights.
+	*/
+	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,


More information about the dovecot-cvs mailing list