From: Timo Sirainen Date: Fri, 17 Jan 2014 22:33:47 +0000 (-0500) Subject: acl: Moved acl_rights array from vfile-specific code to generic struct acl_object. X-Git-Tag: 2.2.11~26 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=086c52e4bcdc950e47ee331e1e07c9c10982a670;p=thirdparty%2Fdovecot%2Fcore.git acl: Moved acl_rights array from vfile-specific code to generic struct acl_object. --- diff --git a/src/plugins/acl/acl-api-private.h b/src/plugins/acl/acl-api-private.h index 3e84141302..d792a911cd 100644 --- a/src/plugins/acl/acl-api-private.h +++ b/src/plugins/acl/acl-api-private.h @@ -67,6 +67,9 @@ struct acl_mailbox_list_context { struct acl_object { struct acl_backend *backend; char *name; + + pool_t rights_pool; + ARRAY(struct acl_rights) rights; }; struct acl_object_list_iter { @@ -78,6 +81,12 @@ struct acl_object_list_iter { extern const char *const all_mailbox_rights[]; +struct acl_object_list_iter * +acl_default_object_list_init(struct acl_object *aclobj); +int acl_default_object_list_next(struct acl_object_list_iter *iter, + struct acl_rights *rights_r); +void acl_default_object_list_deinit(struct acl_object_list_iter *iter); + const char *const * acl_backend_mask_get_names(struct acl_backend *backend, const struct acl_mask *mask, pool_t pool); @@ -95,6 +104,7 @@ const char *acl_rights_export(const struct acl_rights *rights); int acl_rights_parse_line(const char *line, pool_t pool, struct acl_rights *rights_r, const char **error_r); int acl_rights_cmp(const struct acl_rights *r1, const struct acl_rights *r2); +void acl_rights_sort(struct acl_object *aclobj); const char *const * acl_right_names_parse(pool_t pool, const char *acl, const char **error_r); @@ -105,5 +115,7 @@ bool acl_right_names_modify(pool_t pool, const char *const **rightsp, const char *const *modify_rights, enum acl_modify_mode modify_mode); +void acl_object_rebuild_cache(struct acl_object *aclobj); +void acl_object_remove_all_access(struct acl_object *aclobj); #endif diff --git a/src/plugins/acl/acl-api.c b/src/plugins/acl/acl-api.c index c3c5229213..358a68922d 100644 --- a/src/plugins/acl/acl-api.c +++ b/src/plugins/acl/acl-api.c @@ -185,6 +185,43 @@ void acl_object_list_deinit(struct acl_object_list_iter **_iter) iter->aclobj->backend->v.object_list_deinit(iter); } +struct acl_object_list_iter * +acl_default_object_list_init(struct acl_object *aclobj) +{ + struct acl_object_list_iter *iter; + + iter = i_new(struct acl_object_list_iter, 1); + iter->aclobj = aclobj; + + if (!array_is_created(&aclobj->rights)) { + /* we may have the object cached, but we don't have all the + rights read into memory */ + acl_cache_flush(aclobj->backend->cache, aclobj->name); + } + + if (aclobj->backend->v.object_refresh_cache(aclobj) < 0) + iter->failed = TRUE; + return iter; +} + +int acl_default_object_list_next(struct acl_object_list_iter *iter, + struct acl_rights *rights_r) +{ + const struct acl_rights *rights; + + if (iter->idx == array_count(&iter->aclobj->rights)) + return 0; + + rights = array_idx(&iter->aclobj->rights, iter->idx++); + *rights_r = *rights; + return 1; +} + +void acl_default_object_list_deinit(struct acl_object_list_iter *iter) +{ + i_free(iter); +} + struct acl_mailbox_list_context * acl_backend_nonowner_lookups_iter_init(struct acl_backend *backend) { @@ -399,6 +436,35 @@ int acl_rights_cmp(const struct acl_rights *r1, const struct acl_rights *r2) return null_strcmp(r1->identifier, r2->identifier); } +void acl_rights_sort(struct acl_object *aclobj) +{ + struct acl_rights *rights; + unsigned int i, dest, count; + + if (!array_is_created(&aclobj->rights)) + return; + + array_sort(&aclobj->rights, acl_rights_cmp); + + /* merge identical identifiers */ + rights = array_get_modifiable(&aclobj->rights, &count); + for (dest = 0, i = 1; i < count; i++) { + if (acl_rights_cmp(&rights[i], &rights[dest]) == 0) { + /* add i's rights to dest and delete i */ + acl_right_names_merge(aclobj->rights_pool, + &rights[dest].rights, + rights[i].rights, FALSE); + acl_right_names_merge(aclobj->rights_pool, + &rights[dest].neg_rights, + rights[i].neg_rights, FALSE); + } else { + if (++dest != i) + rights[dest] = rights[i]; + } + } + if (++dest != count) + array_delete(&aclobj->rights, dest, count - dest); +} bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights) { @@ -633,3 +699,107 @@ bool acl_right_names_modify(pool_t pool, } return old_rights[i] != NULL || new_rights[i] != NULL; } + +static void apply_owner_default_rights(struct acl_object *aclobj) +{ + struct acl_rights_update ru; + const char *null = NULL; + + memset(&ru, 0, sizeof(ru)); + ru.modify_mode = ACL_MODIFY_MODE_REPLACE; + ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; + ru.rights.id_type = ACL_ID_OWNER; + ru.rights.rights = aclobj->backend->default_rights; + ru.rights.neg_rights = &null; + acl_cache_update(aclobj->backend->cache, aclobj->name, &ru); +} + +void acl_object_rebuild_cache(struct acl_object *aclobj) +{ + struct acl_rights_update ru; + enum acl_modify_mode add_mode; + const struct acl_rights *rights, *prev_match = NULL; + unsigned int i, count; + bool first_global = TRUE; + + acl_cache_flush(aclobj->backend->cache, aclobj->name); + + if (!array_is_created(&aclobj->rights)) + return; + + /* Rights are sorted by their 1) locals first, globals next, + 2) acl_id_type. We'll apply only the rights matching ourself. + + Every time acl_id_type or local/global changes, the new ACLs will + replace all of the existing ACLs. Basically this means that if + user belongs to multiple matching groups or group-overrides, their + ACLs are merged. In all other situations the ACLs are replaced + (because there aren't duplicate rights entries and a user can't + match multiple usernames). */ + memset(&ru, 0, sizeof(ru)); + rights = array_get(&aclobj->rights, &count); + if (!acl_backend_user_is_owner(aclobj->backend)) + i = 0; + else { + /* we're the owner. skip over all rights entries until we + reach ACL_ID_OWNER or higher, or alternatively when we + reach a global ACL (even ACL_ID_ANYONE overrides owner's + rights if it's global) */ + for (i = 0; i < count; i++) { + if (rights[i].id_type >= ACL_ID_OWNER || + rights[i].global) + break; + } + apply_owner_default_rights(aclobj); + /* now continue applying the rest of the rights, + if there are any */ + } + for (; i < count; i++) { + if (!acl_backend_rights_match_me(aclobj->backend, &rights[i])) + continue; + + if (prev_match == NULL || + prev_match->id_type != rights[i].id_type || + prev_match->global != rights[i].global) { + /* replace old ACLs */ + add_mode = ACL_MODIFY_MODE_REPLACE; + } else { + /* merging to existing ACLs */ + i_assert(rights[i].id_type == ACL_ID_GROUP || + rights[i].id_type == ACL_ID_GROUP_OVERRIDE); + add_mode = ACL_MODIFY_MODE_ADD; + } + prev_match = &rights[i]; + + /* If [neg_]rights is NULL it needs to be ignored. + The easiest way to do that is to just mark it with + REMOVE mode */ + ru.modify_mode = rights[i].rights == NULL ? + ACL_MODIFY_MODE_REMOVE : add_mode; + ru.neg_modify_mode = rights[i].neg_rights == NULL ? + ACL_MODIFY_MODE_REMOVE : add_mode; + ru.rights = rights[i]; + if (rights[i].global && first_global) { + /* first global: reset negative ACLs so local ACLs + can't mess things up via them */ + first_global = FALSE; + ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; + } + acl_cache_update(aclobj->backend->cache, aclobj->name, &ru); + } +} + +void acl_object_remove_all_access(struct acl_object *aclobj) +{ + static const char *null = NULL; + struct acl_rights rights; + + memset(&rights, 0, sizeof(rights)); + rights.id_type = ACL_ID_ANYONE; + rights.rights = &null; + array_append(&aclobj->rights, &rights, 1); + + rights.id_type = ACL_ID_OWNER; + rights.rights = &null; + array_append(&aclobj->rights, &rights, 1); +} diff --git a/src/plugins/acl/acl-backend-vfile-update.c b/src/plugins/acl/acl-backend-vfile-update.c index 9be468d10f..6cef9f6185 100644 --- a/src/plugins/acl/acl-backend-vfile-update.c +++ b/src/plugins/acl/acl-backend-vfile-update.c @@ -56,7 +56,7 @@ static int acl_backend_vfile_update_begin(struct acl_object_vfile *aclobj, } static bool -vfile_object_modify_right(struct acl_object_vfile *aclobj, unsigned int idx, +vfile_object_modify_right(struct acl_object *aclobj, unsigned int idx, const struct acl_rights_update *update) { struct acl_rights *right; @@ -78,7 +78,7 @@ vfile_object_modify_right(struct acl_object_vfile *aclobj, unsigned int idx, } static bool -vfile_object_add_right(struct acl_object_vfile *aclobj, unsigned int idx, +vfile_object_add_right(struct acl_object *aclobj, unsigned int idx, const struct acl_rights_update *update) { struct acl_rights right; @@ -129,7 +129,7 @@ vfile_write_right(string_t *dest, const struct acl_rights *right, } static int -acl_backend_vfile_update_write(struct acl_object_vfile *aclobj, +acl_backend_vfile_update_write(struct acl_object *aclobj, int fd, const char *path) { struct ostream *output; @@ -193,7 +193,8 @@ static void acl_backend_vfile_update_cache(struct acl_object *_aclobj, int fd) int acl_backend_vfile_object_update(struct acl_object *_aclobj, const struct acl_rights_update *update) { - struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj; + struct acl_object_vfile *aclobj = + (struct acl_object_vfile *)_aclobj; struct acl_backend_vfile *backend = (struct acl_backend_vfile *)_aclobj->backend; struct acl_backend_vfile_validity *validity; @@ -212,11 +213,11 @@ int acl_backend_vfile_object_update(struct acl_object *_aclobj, if (fd == -1) return -1; - if (!array_bsearch_insert_pos(&aclobj->rights, &update->rights, + if (!array_bsearch_insert_pos(&_aclobj->rights, &update->rights, acl_rights_cmp, &i)) - changed = vfile_object_add_right(aclobj, i, update); + changed = vfile_object_add_right(_aclobj, i, update); else - changed = vfile_object_modify_right(aclobj, i, update); + changed = vfile_object_modify_right(_aclobj, i, update); if (!changed) { file_dotlock_delete(&dotlock); return 0; @@ -228,7 +229,7 @@ int acl_backend_vfile_object_update(struct acl_object *_aclobj, /* ACLs were really changed, write the new ones */ path = file_dotlock_get_lock_path(dotlock); - if (acl_backend_vfile_update_write(aclobj, fd, path) < 0) { + if (acl_backend_vfile_update_write(_aclobj, fd, path) < 0) { file_dotlock_delete(&dotlock); acl_cache_flush(_aclobj->backend->cache, _aclobj->name); return -1; diff --git a/src/plugins/acl/acl-backend-vfile.c b/src/plugins/acl/acl-backend-vfile.c index 2396a2e524..047f2373c8 100644 --- a/src/plugins/acl/acl-backend-vfile.c +++ b/src/plugins/acl/acl-backend-vfile.c @@ -253,35 +253,19 @@ static void acl_backend_vfile_object_deinit(struct acl_object *_aclobj) { struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj; - if (array_is_created(&aclobj->rights)) - array_free(&aclobj->rights); - if (aclobj->rights_pool != NULL) - pool_unref(&aclobj->rights_pool); - i_free(aclobj->local_path); i_free(aclobj->global_path); + + if (array_is_created(&aclobj->aclobj.rights)) + array_free(&aclobj->aclobj.rights); + if (aclobj->aclobj.rights_pool != NULL) + pool_unref(&aclobj->aclobj.rights_pool); i_free(aclobj->aclobj.name); i_free(aclobj); } -static void acl_backend_remove_all_access(struct acl_object_vfile *aclobj) -{ - static const char *null = NULL; - struct acl_rights rights; - - memset(&rights, 0, sizeof(rights)); - rights.id_type = ACL_ID_ANYONE; - rights.rights = &null; - array_append(&aclobj->rights, &rights, 1); - - rights.id_type = ACL_ID_OWNER; - rights.rights = &null; - array_append(&aclobj->rights, &rights, 1); -} - static int -acl_backend_vfile_read(struct acl_object_vfile *aclobj, - bool global, const char *path, +acl_backend_vfile_read(struct acl_object *aclobj, bool global, const char *path, struct acl_vfile_validity *validity, bool try_retry, bool *is_dir_r) { @@ -297,15 +281,15 @@ acl_backend_vfile_read(struct acl_object_vfile *aclobj, fd = nfs_safe_open(path, O_RDONLY); if (fd == -1) { if (errno == ENOENT || errno == ENOTDIR) { - if (aclobj->aclobj.backend->debug) + if (aclobj->backend->debug) i_debug("acl vfile: file %s not found", path); validity->last_mtime = VALIDITY_MTIME_NOTFOUND; } else if (errno == EACCES) { - if (aclobj->aclobj.backend->debug) + if (aclobj->backend->debug) i_debug("acl vfile: no access to file %s", path); - acl_backend_remove_all_access(aclobj); + acl_object_remove_all_access(aclobj); validity->last_mtime = VALIDITY_MTIME_NOACCESS; } else { i_error("open(%s) failed: %m", path); @@ -334,7 +318,7 @@ acl_backend_vfile_read(struct acl_object_vfile *aclobj, return 0; } - if (aclobj->aclobj.backend->debug) + if (aclobj->backend->debug) i_debug("acl vfile: reading file %s", path); input = i_stream_create_fd(fd, (size_t)-1, FALSE); @@ -394,7 +378,7 @@ acl_backend_vfile_read(struct acl_object_vfile *aclobj, } static int -acl_backend_vfile_read_with_retry(struct acl_object_vfile *aclobj, +acl_backend_vfile_read_with_retry(struct acl_object *aclobj, bool global, const char *path, struct acl_vfile_validity *validity) { @@ -493,126 +477,6 @@ int acl_backend_vfile_object_get_mtime(struct acl_object *aclobj, return 0; } -static void acl_backend_vfile_rights_sort(struct acl_object_vfile *aclobj) -{ - struct acl_rights *rights; - unsigned int i, dest, count; - - if (!array_is_created(&aclobj->rights)) - return; - - array_sort(&aclobj->rights, acl_rights_cmp); - - /* merge identical identifiers */ - rights = array_get_modifiable(&aclobj->rights, &count); - for (dest = 0, i = 1; i < count; i++) { - if (acl_rights_cmp(&rights[i], &rights[dest]) == 0) { - /* add i's rights to dest and delete i */ - acl_right_names_merge(aclobj->rights_pool, - &rights[dest].rights, - rights[i].rights, FALSE); - acl_right_names_merge(aclobj->rights_pool, - &rights[dest].neg_rights, - rights[i].neg_rights, FALSE); - } else { - if (++dest != i) - rights[dest] = rights[i]; - } - } - if (++dest != count) - array_delete(&aclobj->rights, dest, count - dest); -} - -static void apply_owner_default_rights(struct acl_object *_aclobj) -{ - struct acl_rights_update ru; - const char *null = NULL; - - memset(&ru, 0, sizeof(ru)); - ru.modify_mode = ACL_MODIFY_MODE_REPLACE; - ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; - ru.rights.id_type = ACL_ID_OWNER; - ru.rights.rights = _aclobj->backend->default_rights; - ru.rights.neg_rights = &null; - acl_cache_update(_aclobj->backend->cache, _aclobj->name, &ru); -} - -static void acl_backend_vfile_cache_rebuild(struct acl_object_vfile *aclobj) -{ - struct acl_object *_aclobj = &aclobj->aclobj; - struct acl_rights_update ru; - enum acl_modify_mode add_mode; - const struct acl_rights *rights, *prev_match = NULL; - unsigned int i, count; - bool first_global = TRUE; - - acl_cache_flush(_aclobj->backend->cache, _aclobj->name); - - if (!array_is_created(&aclobj->rights)) - return; - - /* Rights are sorted by their 1) locals first, globals next, - 2) acl_id_type. We'll apply only the rights matching ourself. - - Every time acl_id_type or local/global changes, the new ACLs will - replace all of the existing ACLs. Basically this means that if - user belongs to multiple matching groups or group-overrides, their - ACLs are merged. In all other situations the ACLs are replaced - (because there aren't duplicate rights entries and a user can't - match multiple usernames). */ - memset(&ru, 0, sizeof(ru)); - rights = array_get(&aclobj->rights, &count); - if (!acl_backend_user_is_owner(_aclobj->backend)) - i = 0; - else { - /* we're the owner. skip over all rights entries until we - reach ACL_ID_OWNER or higher, or alternatively when we - reach a global ACL (even ACL_ID_ANYONE overrides owner's - rights if it's global) */ - for (i = 0; i < count; i++) { - if (rights[i].id_type >= ACL_ID_OWNER || - rights[i].global) - break; - } - apply_owner_default_rights(_aclobj); - /* now continue applying the rest of the rights, - if there are any */ - } - for (; i < count; i++) { - if (!acl_backend_rights_match_me(_aclobj->backend, &rights[i])) - continue; - - if (prev_match == NULL || - prev_match->id_type != rights[i].id_type || - prev_match->global != rights[i].global) { - /* replace old ACLs */ - add_mode = ACL_MODIFY_MODE_REPLACE; - } else { - /* merging to existing ACLs */ - i_assert(rights[i].id_type == ACL_ID_GROUP || - rights[i].id_type == ACL_ID_GROUP_OVERRIDE); - add_mode = ACL_MODIFY_MODE_ADD; - } - prev_match = &rights[i]; - - /* If [neg_]rights is NULL it needs to be ignored. - The easiest way to do that is to just mark it with - REMOVE mode */ - ru.modify_mode = rights[i].rights == NULL ? - ACL_MODIFY_MODE_REMOVE : add_mode; - ru.neg_modify_mode = rights[i].neg_rights == NULL ? - ACL_MODIFY_MODE_REMOVE : add_mode; - ru.rights = rights[i]; - if (rights[i].global && first_global) { - /* first global: reset negative ACLs so local ACLs - can't mess things up via them */ - first_global = FALSE; - ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE; - } - acl_cache_update(_aclobj->backend->cache, _aclobj->name, &ru); - } -} - static int acl_backend_vfile_object_refresh_cache(struct acl_object *_aclobj) { struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj; @@ -637,26 +501,26 @@ static int acl_backend_vfile_object_refresh_cache(struct acl_object *_aclobj) return ret; /* either global or local ACLs changed, need to re-read both */ - if (!array_is_created(&aclobj->rights)) { - aclobj->rights_pool = + if (!array_is_created(&_aclobj->rights)) { + _aclobj->rights_pool = pool_alloconly_create("acl rights", 256); - i_array_init(&aclobj->rights, 16); + i_array_init(&_aclobj->rights, 16); } else { - array_clear(&aclobj->rights); - p_clear(aclobj->rights_pool); + array_clear(&_aclobj->rights); + p_clear(_aclobj->rights_pool); } memset(&validity, 0, sizeof(validity)); - if (acl_backend_vfile_read_with_retry(aclobj, TRUE, aclobj->global_path, + if (acl_backend_vfile_read_with_retry(_aclobj, TRUE, aclobj->global_path, &validity.global_validity) < 0) return -1; - if (acl_backend_vfile_read_with_retry(aclobj, FALSE, aclobj->local_path, + if (acl_backend_vfile_read_with_retry(_aclobj, FALSE, aclobj->local_path, &validity.local_validity) < 0) return -1; - acl_backend_vfile_rights_sort(aclobj); + acl_rights_sort(_aclobj); /* update cache only after we've successfully read everything */ - acl_backend_vfile_cache_rebuild(aclobj); + acl_object_rebuild_cache(_aclobj); acl_cache_set_validity(_aclobj->backend->cache, _aclobj->name, &validity); @@ -686,49 +550,6 @@ static int acl_backend_vfile_object_last_changed(struct acl_object *_aclobj, return 0; } -static struct acl_object_list_iter * -acl_backend_vfile_object_list_init(struct acl_object *_aclobj) -{ - struct acl_object_vfile *aclobj = - (struct acl_object_vfile *)_aclobj; - struct acl_object_list_iter *iter; - - iter = i_new(struct acl_object_list_iter, 1); - iter->aclobj = _aclobj; - - if (!array_is_created(&aclobj->rights)) { - /* we may have the object cached, but we don't have all the - rights read into memory */ - acl_cache_flush(_aclobj->backend->cache, _aclobj->name); - } - - if (_aclobj->backend->v.object_refresh_cache(_aclobj) < 0) - iter->failed = TRUE; - return iter; -} - -static int -acl_backend_vfile_object_list_next(struct acl_object_list_iter *iter, - struct acl_rights *rights_r) -{ - struct acl_object_vfile *aclobj = - (struct acl_object_vfile *)iter->aclobj; - const struct acl_rights *rights; - - if (iter->idx == array_count(&aclobj->rights)) - return 0; - - rights = array_idx(&aclobj->rights, iter->idx++); - *rights_r = *rights; - return 1; -} - -static void -acl_backend_vfile_object_list_deinit(struct acl_object_list_iter *iter) -{ - i_free(iter); -} - struct acl_backend_vfuncs acl_backend_vfile = { acl_backend_vfile_alloc, acl_backend_vfile_init, @@ -743,7 +564,7 @@ struct acl_backend_vfuncs acl_backend_vfile = { acl_backend_vfile_object_refresh_cache, acl_backend_vfile_object_update, acl_backend_vfile_object_last_changed, - acl_backend_vfile_object_list_init, - acl_backend_vfile_object_list_next, - acl_backend_vfile_object_list_deinit + acl_default_object_list_init, + acl_default_object_list_next, + acl_default_object_list_deinit }; diff --git a/src/plugins/acl/acl-backend-vfile.h b/src/plugins/acl/acl-backend-vfile.h index 5c182d7a51..8803e46724 100644 --- a/src/plugins/acl/acl-backend-vfile.h +++ b/src/plugins/acl/acl-backend-vfile.h @@ -25,9 +25,6 @@ struct acl_backend_vfile_validity { struct acl_object_vfile { struct acl_object aclobj; - pool_t rights_pool; - ARRAY(struct acl_rights) rights; - char *global_path, *local_path; };