bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainen#define ACL_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT
3809b9691c46926aa54968ac8e418d04361e1efaTimo Sirainenstatic struct acl_backend *acl_backend_vfile_alloc(void)
3809b9691c46926aa54968ac8e418d04361e1efaTimo Sirainen pool = pool_alloconly_create("ACL backend", 512);
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainen backend = p_new(pool, struct acl_backend_vfile, 1);
3809b9691c46926aa54968ac8e418d04361e1efaTimo Sirainenacl_backend_vfile_init(struct acl_backend *_backend, const char *data)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen backend->global_path = p_strdup_empty(_backend->pool, *tmp);
7dc2c953377fdf3f98e392c2eb7c9caa3dfc5d4fTimo Sirainen backend->cache_secs = ACL_VFILE_DEFAULT_CACHE_SECS;
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen if (str_to_uint(*tmp + 11, &backend->cache_secs) < 0) {
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen i_error("acl vfile: Invalid cache_secs value: %s",
9e808b253bf5c20878fedfb44e4f2273db31da7cTimo Sirainen i_error("acl vfile: Unknown parameter: %s", *tmp);
e2e6f40d58b85041ca77338026a17d6708f324efTimo Sirainen acl_global_file_init(backend->global_path, backend->cache_secs,
4c158400b046fefefce0194603951a6587f51867Timo Sirainen i_debug("acl vfile: Global ACL legacy directory: %s",
395ad9a760c494a2106fada1392c5432a1629de6Timo Sirainenstatic void acl_backend_vfile_deinit(struct acl_backend *_backend)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen acl_global_file_deinit(&_backend->global_file);
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainenstatic const char *
949fa97a4ab5c62e4db73c3973e35ae3b73a2b23Timo Sirainenacl_backend_vfile_get_local_dir(struct acl_backend *backend,
1cfa65c0b9df2508b084774d349af58b5c6c1941Timo Sirainen struct mail_namespace *ns = mailbox_list_get_namespace(backend->list);
8d9540a4536e294e69beb3d1f5b378eb06ba8bdaTimo Sirainen /* ACL files are very important. try to keep them among the main
8d9540a4536e294e69beb3d1f5b378eb06ba8bdaTimo Sirainen mail files. that's not possible though with a) if the mailbox is
8d9540a4536e294e69beb3d1f5b378eb06ba8bdaTimo Sirainen a file or b) if the mailbox path doesn't point to filesystem. */
949fa97a4ab5c62e4db73c3973e35ae3b73a2b23Timo Sirainen if (mailbox_list_get_storage(&list, vname, &storage) < 0)
949fa97a4ab5c62e4db73c3973e35ae3b73a2b23Timo Sirainen type = mail_storage_is_mailbox_file(storage) ||
949fa97a4ab5c62e4db73c3973e35ae3b73a2b23Timo Sirainen (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0 ?
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_MAILBOX;
949fa97a4ab5c62e4db73c3973e35ae3b73a2b23Timo Sirainen if (!mailbox_list_get_root_path(list, type, &dir))
949fa97a4ab5c62e4db73c3973e35ae3b73a2b23Timo Sirainen if (mailbox_list_get_path(list, name, type, &dir) <= 0)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen /* verify that the directory isn't same as INBOX's directory.
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen this is mainly for Maildir. */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX, &inbox) > 0 &&
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen /* can't have default ACLs with this setup */
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainenstatic struct acl_object *
e7ca5f820d6a1a8fe549a2966ac707a60e055ef4Timo Sirainenacl_backend_vfile_object_init(struct acl_backend *_backend,
4c158400b046fefefce0194603951a6587f51867Timo Sirainen mailbox_list_is_valid_name(_backend->list, name, &error)) {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen dir = acl_backend_vfile_get_local_dir(_backend, name, vname);
f87daf83d4fc2db884e071475a21c54be9307689Timo Sirainen i_strconcat(backend->global_path, "/", vname, NULL);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen /* Invalid mailbox name, just use the default
4c158400b046fefefce0194603951a6587f51867Timo Sirainen global ACL files */
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainenstatic const char *
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenget_parent_mailbox(struct acl_backend *backend, const char *name)
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen const char *p;
6a764a456e4d0789e9cc0b6a95b72001737e819aTimo Sirainen p = strrchr(name, mailbox_list_get_hierarchy_sep(backend->list));
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen return p == NULL ? NULL : t_strdup_until(name, p);
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainenacl_backend_vfile_exists(struct acl_backend_vfile *backend, const char *path,
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen if (validity->last_check + (time_t)backend->cache_secs > ioloop_time) {
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen /* use the cached value */
f0339f522dc9c8e2e8a29ef9a3f937c431c6bd1bTimo Sirainen return validity->last_mtime != ACL_VFILE_VALIDITY_MTIME_NOTFOUND ? 1 : 0;
71966291d5f91a87394ad2e321250e1d02670a84Timo Sirainen validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOTFOUND;
71966291d5f91a87394ad2e321250e1d02670a84Timo Sirainen validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOACCESS;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenacl_backend_vfile_has_acl(struct acl_backend *_backend, const char *name)
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen struct acl_backend_vfile_validity *old_validity, new_validity;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen const char *path, *local_path, *global_path, *dir, *vname = "";
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen old_validity = acl_cache_get_validity(_backend->cache, name);
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen /* See if the mailbox exists. If we wanted recursive lookups we could
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen skip this, but at least for now we assume that if an existing
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen mailbox has no ACL it's equivalent to default ACLs. */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(_backend->list, name,
4c158400b046fefefce0194603951a6587f51867Timo Sirainen mailbox_list_is_valid_name(_backend->list, name, &error))) {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen dir = acl_backend_vfile_get_local_dir(_backend, name, vname);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen local_path = t_strconcat(dir, "/", name, NULL);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen ret = acl_backend_vfile_exists(backend, local_path,
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (ret == 0 && backend->global_path != NULL) {
22e74047a660ee770427920d755d9cda564c2babTimo Sirainen ret = acl_global_file_refresh(_backend->global_file);
22e74047a660ee770427920d755d9cda564c2babTimo Sirainen if (ret == 0 && acl_global_file_have_any(_backend->global_file, vname))
4c158400b046fefefce0194603951a6587f51867Timo Sirainen global_path = t_strconcat(backend->global_path, "/", name, NULL);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen ret = acl_backend_vfile_exists(backend, global_path,
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen acl_cache_set_validity(_backend->cache, name, &new_validity);
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainenstatic struct acl_object *
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainenacl_backend_vfile_object_init_parent(struct acl_backend *backend,
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen /* stop at the first parent that
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen a) has global ACL file
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen b) has local ACL file
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen while ((parent = get_parent_mailbox(backend, child_name)) != NULL) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (acl_backend_vfile_has_acl(backend, parent))
da9f6acdcb303d0fe5160b669668aedf39c8f45aTimo Sirainen /* use the root */
515d649c1802beb48433b90125518c00d0a1fbb4Timo Sirainen parent = acl_backend_get_default_object(backend)->name;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return acl_backend_vfile_object_init(backend, parent);
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainenstatic void acl_backend_vfile_object_deinit(struct acl_object *_aclobj)
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainen struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj;
086c52e4bcdc950e47ee331e1e07c9c10982a670Timo Sirainenacl_backend_vfile_read(struct acl_object *aclobj, bool global, const char *path,
96182e7a3a64a967c0a1d718024c2063f77d371fTimo Sirainen struct acl_vfile_validity *validity, bool try_retry,
71966291d5f91a87394ad2e321250e1d02670a84Timo Sirainen validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOTFOUND;
71966291d5f91a87394ad2e321250e1d02670a84Timo Sirainen validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOACCESS;
96182e7a3a64a967c0a1d718024c2063f77d371fTimo Sirainen /* we opened a directory. */
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi input = i_stream_create_fd(fd, (size_t)-1);
d4854db53e9c141db3d02821ed857bea101b1cc2Timo Sirainen i_stream_set_return_partial_line(input, TRUE);
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainen while ((line = i_stream_read_next_line(input)) != NULL) {
15f43b172d2c626aa03c921979c49821a55c7e5eTimo Sirainen ret = acl_rights_parse_line(line, aclobj->rights_pool,
ec238b744181bc41796d84d8e0ef5a23bca73bf2Timo Sirainen /* parsing failure */
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainen if (input->stream_errno == ESTALE && try_retry)
086c52e4bcdc950e47ee331e1e07c9c10982a670Timo Sirainenacl_backend_vfile_read_with_retry(struct acl_object *aclobj,
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainen unsigned int i;
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainen for (i = 0;; i++) {
0d7b2e0750386fe1646a17d83a803d1d5eb3d3a0Timo Sirainen ret = acl_backend_vfile_read(aclobj, global, path, validity,
96182e7a3a64a967c0a1d718024c2063f77d371fTimo Sirainen /* opened a directory. use dir/.DEFAULT instead */
96182e7a3a64a967c0a1d718024c2063f77d371fTimo Sirainen /* ESTALE - try again */
f988345bc29ef022ae73f96b3ec841d39c46d30aTimo Sirainenacl_vfile_validity_has_changed(struct acl_backend_vfile *backend,
f988345bc29ef022ae73f96b3ec841d39c46d30aTimo Sirainen /* same timestamp, but if it was modified within the
f988345bc29ef022ae73f96b3ec841d39c46d30aTimo Sirainen same second we want to refresh it again later (but
f988345bc29ef022ae73f96b3ec841d39c46d30aTimo Sirainen do it only after a couple of seconds so we don't
f988345bc29ef022ae73f96b3ec841d39c46d30aTimo Sirainen keep re-reading it all the time within those
f988345bc29ef022ae73f96b3ec841d39c46d30aTimo Sirainen (st->st_mtime < validity->last_read_time - cache_secs ||
f988345bc29ef022ae73f96b3ec841d39c46d30aTimo Sirainen ioloop_time - validity->last_read_time <= cache_secs))
08a0b7b0d0444875001847ef2b1b7b76122620abTimo Sirainenacl_backend_vfile_refresh(struct acl_object *aclobj, const char *path,
ff4bb2dfb5714eeb0408d3bb862de1646351d097Timo Sirainen validity->last_check + (time_t)backend->cache_secs > ioloop_time)
bd2b326267f9fcaeee8455c581394eabcf377759Timo Sirainen /* it's a directory. use dir/.DEFAULT instead */
08a0b7b0d0444875001847ef2b1b7b76122620abTimo Sirainen /* if the file used to exist, we have to re-read it */
f0339f522dc9c8e2e8a29ef9a3f937c431c6bd1bTimo Sirainen return validity->last_mtime != ACL_VFILE_VALIDITY_MTIME_NOTFOUND ? 1 : 0;
f0339f522dc9c8e2e8a29ef9a3f937c431c6bd1bTimo Sirainen return validity->last_mtime != ACL_VFILE_VALIDITY_MTIME_NOACCESS ? 1 : 0;
f988345bc29ef022ae73f96b3ec841d39c46d30aTimo Sirainen return acl_vfile_validity_has_changed(backend, validity, &st) ? 1 : 0;
2f122b4db3f0d4eeb59ff9d306e54b2009d72cf9Timo Sirainenint acl_backend_vfile_object_get_mtime(struct acl_object *aclobj,
2f122b4db3f0d4eeb59ff9d306e54b2009d72cf9Timo Sirainen validity = acl_cache_get_validity(aclobj->backend->cache, aclobj->name);
2f122b4db3f0d4eeb59ff9d306e54b2009d72cf9Timo Sirainen *mtime_r = validity->local_validity.last_mtime;
2f122b4db3f0d4eeb59ff9d306e54b2009d72cf9Timo Sirainen else if (validity->global_validity.last_mtime != 0)
2f122b4db3f0d4eeb59ff9d306e54b2009d72cf9Timo Sirainen *mtime_r = validity->global_validity.last_mtime;
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainenacl_backend_global_file_refresh(struct acl_object *_aclobj,
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen if (acl_global_file_refresh(_aclobj->backend->global_file) < 0)
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen acl_global_file_last_stat(_aclobj->backend->global_file, &st);
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen return acl_vfile_validity_has_changed(backend, validity, &st) ? 1 : 0;
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainenstatic int acl_backend_vfile_object_refresh_cache(struct acl_object *_aclobj)
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainen struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj;
e50c1eb31159d8ccc116d8fb487a5e231d2033a1Timo Sirainen struct acl_backend_vfile_validity *old_validity;
08a0b7b0d0444875001847ef2b1b7b76122620abTimo Sirainen old_validity = acl_cache_get_validity(_aclobj->backend->cache,
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen acl_backend_global_file_refresh(_aclobj, old_validity == NULL ? NULL :
4c158400b046fefefce0194603951a6587f51867Timo Sirainen acl_backend_vfile_refresh(_aclobj, aclobj->global_path,
08a0b7b0d0444875001847ef2b1b7b76122620abTimo Sirainen ret = acl_backend_vfile_refresh(_aclobj, aclobj->local_path,
7fb70daba4e571eab5b64f496d20b9e37e31141bTimo Sirainen /* either global or local ACLs changed, need to re-read both */
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen acl_global_file_last_stat(_aclobj->backend->global_file, &st);
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen validity.global_validity.last_read_time = ioloop_time;
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen validity.global_validity.last_mtime = st.st_mtime;
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen validity.global_validity.last_size = st.st_size;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (acl_backend_vfile_read_with_retry(_aclobj, TRUE, aclobj->global_path,
086c52e4bcdc950e47ee331e1e07c9c10982a670Timo Sirainen if (acl_backend_vfile_read_with_retry(_aclobj, FALSE, aclobj->local_path,
0d7b2e0750386fe1646a17d83a803d1d5eb3d3a0Timo Sirainen /* update cache only after we've successfully read everything */
08a0b7b0d0444875001847ef2b1b7b76122620abTimo Sirainen acl_cache_set_validity(_aclobj->backend->cache,
2f122b4db3f0d4eeb59ff9d306e54b2009d72cf9Timo Sirainen if (acl_backend_vfile_object_get_mtime(_aclobj, &mtime) == 0)
2f122b4db3f0d4eeb59ff9d306e54b2009d72cf9Timo Sirainen acl_backend_vfile_acllist_verify(backend, _aclobj->name, mtime);
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainenstatic int acl_backend_vfile_object_last_changed(struct acl_object *_aclobj,
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen struct acl_backend_vfile_validity *old_validity;
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen old_validity = acl_cache_get_validity(_aclobj->backend->cache,
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen if (acl_backend_vfile_object_refresh_cache(_aclobj) < 0)
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen old_validity = acl_cache_get_validity(_aclobj->backend->cache,
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen *last_changed_r = old_validity->local_validity.last_mtime;