changeset 8165:459c4c496983 HEAD

acl: Fixed handling 'k' right. Although box/child creation was prevented, box/child/child wasn't.
author Timo Sirainen <tss@iki.fi>
date Sun, 07 Sep 2008 15:18:29 +0300
parents 314fab62b3d1
children a1f6c93afbc2
files src/plugins/acl/acl-api-private.h src/plugins/acl/acl-api.c src/plugins/acl/acl-api.h src/plugins/acl/acl-backend-vfile.c src/plugins/acl/acl-mailbox-list.c src/plugins/acl/acl-plugin.h src/plugins/acl/acl-storage.c
diffstat 7 files changed, 169 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/src/plugins/acl/acl-api-private.h	Sun Sep 07 10:54:46 2008 +0300
+++ b/src/plugins/acl/acl-api-private.h	Sun Sep 07 15:18:29 2008 +0300
@@ -18,6 +18,9 @@
 	struct acl_object *(*object_init)(struct acl_backend *backend,
 					  struct mail_storage *storage,
 					  const char *name);
+	struct acl_object *(*object_init_parent)(struct acl_backend *backend,
+						 struct mail_storage *storage,
+						 const char *child_name);
 	void (*object_deinit)(struct acl_object *aclobj);
 
 	int (*object_refresh_cache)(struct acl_object *aclobj);
--- a/src/plugins/acl/acl-api.c	Sun Sep 07 10:54:46 2008 +0300
+++ b/src/plugins/acl/acl-api.c	Sun Sep 07 15:18:29 2008 +0300
@@ -12,6 +12,13 @@
 	return backend->v.object_init(backend, storage, name);
 }
 
+struct acl_object *acl_object_init_from_parent(struct acl_backend *backend,
+					       struct mail_storage *storage,
+					       const char *child_name)
+{
+	return backend->v.object_init_parent(backend, storage, child_name);
+}
+
 void acl_object_deinit(struct acl_object **_aclobj)
 {
 	struct acl_object *aclobj = *_aclobj;
--- a/src/plugins/acl/acl-api.h	Sun Sep 07 10:54:46 2008 +0300
+++ b/src/plugins/acl/acl-api.h	Sun Sep 07 15:18:29 2008 +0300
@@ -108,6 +108,9 @@
 struct acl_object *acl_object_init_from_name(struct acl_backend *backend,
 					     struct mail_storage *storage,
 					     const char *name);
+struct acl_object *acl_object_init_from_parent(struct acl_backend *backend,
+					       struct mail_storage *storage,
+					       const char *child_name);
 void acl_object_deinit(struct acl_object **aclobj);
 
 /* Returns 1 if we have the requested rights, 0 if not, or -1 if internal
--- a/src/plugins/acl/acl-backend-vfile.c	Sun Sep 07 10:54:46 2008 +0300
+++ b/src/plugins/acl/acl-backend-vfile.c	Sun Sep 07 15:18:29 2008 +0300
@@ -30,6 +30,7 @@
 
 struct acl_backend_vfile_validity {
 	struct acl_vfile_validity global_validity, local_validity;
+	struct acl_vfile_validity mailbox_validity;
 };
 
 struct acl_letter_map {
@@ -106,6 +107,20 @@
 	pool_unref(&backend->backend.pool);
 }
 
+static const char *
+acl_backend_vfile_get_local_dir(struct mail_storage *storage, const char *name)
+{
+	const char *dir;
+	bool is_file;
+
+	dir = mail_storage_get_mailbox_path(storage, name, &is_file);
+	if (is_file) {
+		dir = mailbox_list_get_path(storage->list, name,
+					    MAILBOX_LIST_PATH_TYPE_CONTROL);
+	}
+	return dir;
+}
+
 static struct acl_object *
 acl_backend_vfile_object_init(struct acl_backend *_backend,
 			      struct mail_storage *storage, const char *name)
@@ -114,7 +129,6 @@
 		(struct acl_backend_vfile *)_backend;
 	struct acl_object_vfile *aclobj;
 	const char *dir;
-	bool is_file;
 
 	aclobj = i_new(struct acl_object_vfile, 1);
 	aclobj->aclobj.backend = _backend;
@@ -127,16 +141,113 @@
 		dir = mailbox_list_get_path(_backend->list, NULL,
 					    MAILBOX_LIST_PATH_TYPE_DIR);
 	} else {
-		dir = mail_storage_get_mailbox_path(storage, name, &is_file);
-		if (is_file) {
-			dir = mailbox_list_get_path(_backend->list, name,
-					MAILBOX_LIST_PATH_TYPE_CONTROL);
-		}
+		dir = acl_backend_vfile_get_local_dir(storage, name);
 	}
 	aclobj->local_path = i_strconcat(dir, "/"ACL_FILENAME, NULL);
 	return &aclobj->aclobj;
 }
 
+static const char *
+get_parent_mailbox(struct mail_storage *storage, const char *name)
+{
+	const char *p;
+	char sep;
+
+	sep = mailbox_list_get_hierarchy_sep(storage->list);
+	p = strrchr(name, sep);
+	return p == NULL ? NULL : t_strdup_until(name, p);
+}
+
+static int
+acl_backend_vfile_exists(struct acl_backend_vfile *backend, const char *path,
+			 struct acl_vfile_validity *validity)
+{
+	struct stat st;
+
+	if (validity->last_check + (time_t)backend->cache_secs > ioloop_time) {
+		/* use the cached value */
+		return validity->last_mtime != VALIDITY_MTIME_NOTFOUND;
+	}
+
+	validity->last_check = ioloop_time;
+	if (stat(path, &st) < 0) {
+		if (errno == ENOENT) {
+			validity->last_mtime = VALIDITY_MTIME_NOTFOUND;
+			return 0;
+		}
+		if (errno == EACCES) {
+			validity->last_mtime = VALIDITY_MTIME_NOACCESS;
+			return 1;
+		}
+		i_error("stat(%s) failed: %m", path);
+		return -1;
+	}
+	validity->last_mtime = st.st_mtime;
+	validity->last_size = st.st_size;
+	return 1;
+}
+
+static bool
+acl_backend_vfile_has_acl(struct acl_backend *_backend,
+			  struct mail_storage *storage, const char *name)
+{
+	struct acl_backend_vfile *backend =
+		(struct acl_backend_vfile *)_backend;
+	struct acl_backend_vfile_validity *old_validity, new_validity;
+	const char *path, *local_path, *global_path, *dir;
+	int ret;
+
+	old_validity = acl_cache_get_validity(_backend->cache, name);
+	if (old_validity != NULL)
+		new_validity = *old_validity;
+	else
+		memset(&new_validity, 0, sizeof(new_validity));
+
+	/* See if the mailbox exists. If we wanted recursive lookups we could
+	   skip this, but at least for now we assume that if an existing
+	   mailbox has no ACL it's equivalent to default ACLs. */
+	path = mailbox_list_get_path(storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	ret = acl_backend_vfile_exists(backend, path,
+				       &new_validity.mailbox_validity);
+	if (ret == 0) {
+		dir = acl_backend_vfile_get_local_dir(storage, name);
+		local_path = t_strconcat(dir, "/", name, NULL);
+		ret = acl_backend_vfile_exists(backend, local_path,
+					       &new_validity.local_validity);
+	}
+	if (ret == 0 && backend->global_dir != NULL) {
+		global_path = t_strconcat(backend->global_dir, "/", name, NULL);
+		ret = acl_backend_vfile_exists(backend, global_path,
+					       &new_validity.global_validity);
+	}
+	acl_cache_set_validity(_backend->cache, name, &new_validity);
+	return ret > 0;
+}
+
+static struct acl_object *
+acl_backend_vfile_object_init_parent(struct acl_backend *backend,
+				     struct mail_storage *storage,
+				     const char *child_name)
+{
+	const char *parent;
+
+	/* stop at the first parent that
+	   a) has global ACL file
+	   b) has local ACL file
+	   c) exists */
+	while ((parent = get_parent_mailbox(storage, child_name)) != NULL) {
+		if (acl_backend_vfile_has_acl(backend, storage, parent))
+			break;
+		child_name = parent;
+	}
+	if (parent == NULL) {
+		/* use the root */
+		parent = "";
+	}
+	return acl_backend_vfile_object_init(backend, storage, parent);
+}
+
 static void acl_backend_vfile_object_deinit(struct acl_object *_aclobj)
 {
 	struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj;
@@ -470,8 +581,9 @@
 		   seconds) */
 		time_t cache_secs = backend->cache_secs;
 
-		if (st.st_mtime < validity->last_read_time - cache_secs ||
-		    ioloop_time - validity->last_read_time <= cache_secs)
+		if (validity->last_read_time != 0 &&
+		    (st.st_mtime < validity->last_read_time - cache_secs ||
+		     ioloop_time - validity->last_read_time <= cache_secs))
 			return 0;
 	}
 
@@ -591,6 +703,7 @@
 	acl_backend_vfile_nonowner_iter_next,
 	acl_backend_vfile_nonowner_iter_deinit,
 	acl_backend_vfile_object_init,
+	acl_backend_vfile_object_init_parent,
 	acl_backend_vfile_object_deinit,
 	acl_backend_vfile_object_refresh_cache,
 	acl_backend_vfile_object_update,
--- a/src/plugins/acl/acl-mailbox-list.c	Sun Sep 07 10:54:46 2008 +0300
+++ b/src/plugins/acl/acl-mailbox-list.c	Sun Sep 07 15:18:29 2008 +0300
@@ -42,24 +42,19 @@
 	return alist->rights.backend;
 }
 
-const char *acl_mailbox_list_get_parent_mailbox_name(struct mailbox_list *list,
-						     const char *name)
-{
-	const char *p;
-	char sep;
-
-	sep = mailbox_list_get_hierarchy_sep(list);
-	p = strrchr(name, sep);
-	return p == NULL ? "" : t_strdup_until(name, p);
-}
-
 static int
-acl_mailbox_list_have_right(struct acl_mailbox_list *alist, const char *name,
+acl_mailbox_list_have_right(struct mailbox_list *list, const char *name,
 			    unsigned int acl_storage_right_idx, bool *can_see_r)
 {
-	return acl_storage_rights_ctx_have_right(&alist->rights, name,
-						 acl_storage_right_idx,
-						 can_see_r);
+	struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
+	int ret;
+
+	ret = acl_storage_rights_ctx_have_right(&alist->rights, name, FALSE,
+						acl_storage_right_idx,
+						can_see_r);
+	if (ret < 0)
+		mailbox_list_set_internal_error(list);
+	return ret;
 }
 
 static void
@@ -185,7 +180,6 @@
 acl_mailbox_list_info_is_visible(struct acl_mailbox_list_iterate_context *ctx,
 				 const struct mailbox_info *info)
 {
-	struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ctx->ctx.list);
 	const char *acl_name;
 	int ret;
 
@@ -195,7 +189,7 @@
 	}
 
 	acl_name = acl_mailbox_list_iter_get_name(&ctx->ctx, info->name);
-	ret = acl_mailbox_list_have_right(alist, acl_name,
+	ret = acl_mailbox_list_have_right(ctx->ctx.list, acl_name,
 					  ACL_STORAGE_RIGHT_LOOKUP,
 					  NULL);
 	if (ret != 0)
@@ -255,7 +249,7 @@
 		return ret;
 
 	mailbox_name = acl_mailbox_list_iter_get_name(ctx, mailbox_name);
-	return acl_mailbox_list_have_right(alist, mailbox_name,
+	return acl_mailbox_list_have_right(ctx->list, mailbox_name,
 					   ACL_STORAGE_RIGHT_LOOKUP, NULL);
 }
 
@@ -283,14 +277,14 @@
 	struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
 	int ret;
 
-	ret = acl_mailbox_list_have_right(alist, name, ACL_STORAGE_RIGHT_LOOKUP,
+	ret = acl_mailbox_list_have_right(list, name, ACL_STORAGE_RIGHT_LOOKUP,
 					  NULL);
 	if (ret < 0)
 		return -1;
 	if (ret == 0) {
 		/* If we have INSERT right for the mailbox, we'll need to
 		   reveal its existence so that APPEND and COPY works. */
-		ret = acl_mailbox_list_have_right(alist, name,
+		ret = acl_mailbox_list_have_right(list, name,
 						  ACL_STORAGE_RIGHT_INSERT,
 						  NULL);
 		if (ret < 0)
@@ -314,16 +308,14 @@
 	case MAILBOX_NAME_NOINFERIORS:
 		/* have to check if we are allowed to see the parent */
 		T_BEGIN {
-			const char *parent;
-
-			parent = acl_mailbox_list_get_parent_mailbox_name(list,
-									  name);
-			ret = acl_mailbox_list_have_right(alist, parent,
-						ACL_STORAGE_RIGHT_LOOKUP, NULL);
+			ret = acl_storage_rights_ctx_have_right(&alist->rights, name,
+					TRUE, ACL_STORAGE_RIGHT_LOOKUP, NULL);
 		} T_END;
 
-		if (ret < 0)
+		if (ret < 0) {
+			mailbox_list_set_internal_error(list);
 			return -1;
+		}
 		if (ret == 0) {
 			/* no permission to see the parent */
 			*status = MAILBOX_NAME_VALID;
@@ -340,7 +332,7 @@
 	bool can_see;
 	int ret;
 
-	ret = acl_mailbox_list_have_right(alist, name, ACL_STORAGE_RIGHT_DELETE,
+	ret = acl_mailbox_list_have_right(list, name, ACL_STORAGE_RIGHT_DELETE,
 					  &can_see);
 	if (ret <= 0) {
 		if (ret < 0)
@@ -367,7 +359,7 @@
 	int ret;
 
 	/* renaming requires rights to delete the old mailbox */
-	ret = acl_mailbox_list_have_right(alist, oldname,
+	ret = acl_mailbox_list_have_right(list, oldname,
 					  ACL_STORAGE_RIGHT_DELETE, &can_see);
 	if (ret <= 0) {
 		if (ret < 0)
@@ -384,9 +376,8 @@
 
 	/* and create the new one under the parent mailbox */
 	T_BEGIN {
-		ret = acl_mailbox_list_have_right(alist,
-			acl_mailbox_list_get_parent_mailbox_name(list, newname),
-			ACL_STORAGE_RIGHT_CREATE, NULL);
+		ret = acl_storage_rights_ctx_have_right(&alist->rights, newname,
+				TRUE, ACL_STORAGE_RIGHT_CREATE, NULL);
 	} T_END;
 
 	if (ret <= 0) {
@@ -396,6 +387,8 @@
 			   existence. Can't help it. */
 			mailbox_list_set_error(list, MAIL_ERROR_PERM,
 					       MAIL_ERRSTR_NO_PERMISSION);
+		} else {
+			mailbox_list_set_internal_error(list);
 		}
 		return -1;
 	}
--- a/src/plugins/acl/acl-plugin.h	Sun Sep 07 10:54:46 2008 +0300
+++ b/src/plugins/acl/acl-plugin.h	Sun Sep 07 15:18:29 2008 +0300
@@ -44,13 +44,11 @@
 void acl_storage_rights_ctx_init(struct acl_storage_rights_context *ctx,
 				 struct acl_backend *backend);
 int acl_storage_rights_ctx_have_right(struct acl_storage_rights_context *ctx,
-				      const char *name,
+				      const char *name, bool parent,
 				      unsigned int acl_storage_right_idx,
 				      bool *can_see_r);
 
 struct acl_backend *acl_mailbox_list_get_backend(struct mailbox_list *list);
-const char *acl_mailbox_list_get_parent_mailbox_name(struct mailbox_list *list,
-						     const char *name);
 
 void acl_plugin_init(void);
 void acl_plugin_deinit(void);
--- a/src/plugins/acl/acl-storage.c	Sun Sep 07 10:54:46 2008 +0300
+++ b/src/plugins/acl/acl-storage.c	Sun Sep 07 15:18:29 2008 +0300
@@ -38,7 +38,7 @@
 }
 
 int acl_storage_rights_ctx_have_right(struct acl_storage_rights_context *ctx,
-				      const char *name,
+				      const char *name, bool parent,
 				      unsigned int acl_storage_right_idx,
 				      bool *can_see_r)
 {
@@ -48,7 +48,9 @@
 	int ret, ret2;
 
 	ns = mailbox_list_get_namespace(ctx->backend->list);
-	aclobj = acl_object_init_from_name(ctx->backend, ns->storage, name);
+	aclobj = !parent ?
+		acl_object_init_from_name(ctx->backend, ns->storage, name) :
+		acl_object_init_from_parent(ctx->backend, ns->storage, name);
 	ret = acl_object_have_right(aclobj, idx_arr[acl_storage_right_idx]);
 
 	if (can_see_r != NULL) {
@@ -70,7 +72,7 @@
 	struct acl_mail_storage *astorage = ACL_CONTEXT(storage);
 	int ret;
 
-	ret = acl_storage_rights_ctx_have_right(&astorage->rights, name,
+	ret = acl_storage_rights_ctx_have_right(&astorage->rights, name, FALSE,
 						acl_storage_right_idx,
 						can_see_r);
 	if (ret < 0) 
@@ -131,13 +133,11 @@
 			      bool directory)
 {
 	struct acl_mail_storage *astorage = ACL_CONTEXT(storage);
-	struct mailbox_list *list = mail_storage_get_list(storage);
 	int ret;
 
 	T_BEGIN {
-		ret = acl_storage_have_right(storage,
-			acl_mailbox_list_get_parent_mailbox_name(list, name),
-			ACL_STORAGE_RIGHT_CREATE, NULL);
+		ret = acl_storage_rights_ctx_have_right(&astorage->rights, name,
+				TRUE, ACL_STORAGE_RIGHT_CREATE, NULL);
 	} T_END;
 
 	if (ret <= 0) {
@@ -147,6 +147,8 @@
 			   existence. Can't help it. */
 			mail_storage_set_error(storage, MAIL_ERROR_PERM,
 					       MAIL_ERRSTR_NO_PERMISSION);
+		} else {
+			mail_storage_set_internal_error(storage);
 		}
 		return -1;
 	}