bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen#include "lib.h"
4c158400b046fefefce0194603951a6587f51867Timo Sirainen#include "array.h"
4c158400b046fefefce0194603951a6587f51867Timo Sirainen#include "ioloop.h"
4c158400b046fefefce0194603951a6587f51867Timo Sirainen#include "istream.h"
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen#include "strescape.h"
4c158400b046fefefce0194603951a6587f51867Timo Sirainen#include "wildcard-match.h"
4c158400b046fefefce0194603951a6587f51867Timo Sirainen#include "acl-api-private.h"
4c158400b046fefefce0194603951a6587f51867Timo Sirainen#include "acl-global-file.h"
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen#include <sys/stat.h>
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainenstruct acl_global_rights {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen const char *vpattern;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen ARRAY_TYPE(acl_rights) rights;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen};
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainenstruct acl_global_parse_rights {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen const char *vpattern;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen struct acl_rights rights;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen};
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainenstruct acl_global_file {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen char *path;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen struct stat prev_st;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen time_t last_refresh_time;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen pool_t rights_pool;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen ARRAY(struct acl_global_rights) rights;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen unsigned int refresh_interval_secs;
e2e6f40d58b85041ca77338026a17d6708f324efTimo Sirainen bool debug;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen};
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainenstruct acl_global_file *
e2e6f40d58b85041ca77338026a17d6708f324efTimo Sirainenacl_global_file_init(const char *path, unsigned int refresh_interval_secs,
e2e6f40d58b85041ca77338026a17d6708f324efTimo Sirainen bool debug)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen{
4c158400b046fefefce0194603951a6587f51867Timo Sirainen struct acl_global_file *file;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen file = i_new(struct acl_global_file, 1);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen file->path = i_strdup(path);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen file->refresh_interval_secs = refresh_interval_secs;
e2e6f40d58b85041ca77338026a17d6708f324efTimo Sirainen file->debug = debug;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen i_array_init(&file->rights, 32);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen file->rights_pool = pool_alloconly_create("acl global file rights", 1024);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen return file;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen}
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainenvoid acl_global_file_deinit(struct acl_global_file **_file)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen{
4c158400b046fefefce0194603951a6587f51867Timo Sirainen struct acl_global_file *file = *_file;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen *_file = NULL;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen array_free(&file->rights);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen pool_unref(&file->rights_pool);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen i_free(file->path);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen i_free(file);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen}
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainenstatic int acl_global_parse_rights_cmp(const struct acl_global_parse_rights *r1,
4c158400b046fefefce0194603951a6587f51867Timo Sirainen const struct acl_global_parse_rights *r2)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen{
4c158400b046fefefce0194603951a6587f51867Timo Sirainen return strcmp(r1->vpattern, r2->vpattern);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen}
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainenstruct acl_global_file_parse_ctx {
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen struct acl_global_file *file;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen ARRAY(struct acl_global_parse_rights) parse_rights;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen};
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainenstatic int
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainenacl_global_file_parse_line(struct acl_global_file_parse_ctx *ctx,
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen const char *line, const char **error_r)
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen{
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen struct acl_global_parse_rights *pright;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen const char *p, *vpattern;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen if (*line == '"') {
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen line++;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen if (str_unescape_next(&line, &vpattern) < 0) {
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen *error_r = "Missing '\"'";
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen return -1;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen }
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen if (line[0] != ' ') {
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen *error_r = "Expecting space after '\"'";
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen return -1;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen }
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen line++;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen } else {
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen p = strchr(line, ' ');
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen if (p == NULL) {
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen *error_r = "Missing ACL rights";
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen return -1;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen }
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen if (p == line) {
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen *error_r = "Empty ACL pattern";
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen return -1;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen }
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen vpattern = t_strdup_until(line, p);
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen line = p + 1;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen }
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen pright = array_append_space(&ctx->parse_rights);
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen pright->vpattern = p_strdup(ctx->file->rights_pool, vpattern);
ad9a6d27cd065a502d165e4929e7227abd19063dTimo Sirainen if (acl_rights_parse_line(line, ctx->file->rights_pool,
ad9a6d27cd065a502d165e4929e7227abd19063dTimo Sirainen &pright->rights, error_r) < 0)
ad9a6d27cd065a502d165e4929e7227abd19063dTimo Sirainen return -1;
ad9a6d27cd065a502d165e4929e7227abd19063dTimo Sirainen pright->rights.global = TRUE;
ad9a6d27cd065a502d165e4929e7227abd19063dTimo Sirainen return 0;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen}
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainenstatic int acl_global_file_read(struct acl_global_file *file)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen{
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen struct acl_global_file_parse_ctx ctx;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen struct acl_global_parse_rights *pright;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen struct acl_global_rights *right;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen struct istream *input;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen const char *line, *error, *prev_vpattern;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen unsigned int linenum = 0;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen int ret = 0;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen array_clear(&file->rights);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen p_clear(file->rights_pool);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&ctx);
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen ctx.file = file;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen i_array_init(&ctx.parse_rights, 32);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen input = i_stream_create_file(file->path, (size_t)-1);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen while ((line = i_stream_read_next_line(input)) != NULL) {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen linenum++;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (line[0] == '\0' || line[0] == '#')
4c158400b046fefefce0194603951a6587f51867Timo Sirainen continue;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen T_BEGIN {
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen ret = acl_global_file_parse_line(&ctx, line, &error);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (ret < 0) {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen i_error("Global ACL file %s line %u: %s",
4c158400b046fefefce0194603951a6587f51867Timo Sirainen file->path, linenum, error);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen }
4c158400b046fefefce0194603951a6587f51867Timo Sirainen } T_END;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (ret < 0)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen break;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen }
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (ret == 0 && input->stream_errno != 0) {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen i_error("Couldn't read global ACL file %s: %s",
4c158400b046fefefce0194603951a6587f51867Timo Sirainen file->path, i_stream_get_error(input));
4c158400b046fefefce0194603951a6587f51867Timo Sirainen ret = -1;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen }
587cca00ff7ec4980bfa7c069cc65e0e409d2a5fTimo Sirainen if (ret == 0) {
587cca00ff7ec4980bfa7c069cc65e0e409d2a5fTimo Sirainen const struct stat *st;
587cca00ff7ec4980bfa7c069cc65e0e409d2a5fTimo Sirainen
587cca00ff7ec4980bfa7c069cc65e0e409d2a5fTimo Sirainen if (i_stream_stat(input, TRUE, &st) < 0) {
587cca00ff7ec4980bfa7c069cc65e0e409d2a5fTimo Sirainen i_error("Couldn't stat global ACL file %s: %s",
587cca00ff7ec4980bfa7c069cc65e0e409d2a5fTimo Sirainen file->path, i_stream_get_error(input));
f4d6aeaa415a9fd7bf65fc7843be087adc136c11Martti Rannanjärvi ret = -1;
f4d6aeaa415a9fd7bf65fc7843be087adc136c11Martti Rannanjärvi } else {
f4d6aeaa415a9fd7bf65fc7843be087adc136c11Martti Rannanjärvi file->prev_st = *st;
587cca00ff7ec4980bfa7c069cc65e0e409d2a5fTimo Sirainen }
587cca00ff7ec4980bfa7c069cc65e0e409d2a5fTimo Sirainen }
4c158400b046fefefce0194603951a6587f51867Timo Sirainen i_stream_destroy(&input);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen /* sort all parsed rights */
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen array_sort(&ctx.parse_rights, acl_global_parse_rights_cmp);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen /* combine identical patterns into same structs */
4c158400b046fefefce0194603951a6587f51867Timo Sirainen prev_vpattern = ""; right = NULL;
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen array_foreach_modifiable(&ctx.parse_rights, pright) {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (right == NULL ||
4c158400b046fefefce0194603951a6587f51867Timo Sirainen strcmp(prev_vpattern, pright->vpattern) != 0) {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen right = array_append_space(&file->rights);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen right->vpattern = pright->vpattern;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen p_array_init(&right->rights, file->rights_pool, 4);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen }
4c158400b046fefefce0194603951a6587f51867Timo Sirainen array_append(&right->rights, &pright->rights, 1);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen }
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
fa96f616b3e940b8835d923a7544acb1e6d7669fTimo Sirainen array_free(&ctx.parse_rights);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen return ret;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen}
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainenint acl_global_file_refresh(struct acl_global_file *file)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen{
4c158400b046fefefce0194603951a6587f51867Timo Sirainen struct stat st;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (file->last_refresh_time + (time_t)file->refresh_interval_secs > ioloop_time)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen return 0;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (file->last_refresh_time != 0) {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (stat(file->path, &st) < 0) {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen i_error("stat(%s) failed: %m", file->path);
4c158400b046fefefce0194603951a6587f51867Timo Sirainen return -1;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen }
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (st.st_ino == file->prev_st.st_ino &&
4c158400b046fefefce0194603951a6587f51867Timo Sirainen st.st_size == file->prev_st.st_size &&
4c158400b046fefefce0194603951a6587f51867Timo Sirainen CMP_ST_MTIME(&st, &file->prev_st)) {
4c158400b046fefefce0194603951a6587f51867Timo Sirainen /* no change to the file */
4c158400b046fefefce0194603951a6587f51867Timo Sirainen file->last_refresh_time = ioloop_time;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen return 0;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen }
4c158400b046fefefce0194603951a6587f51867Timo Sirainen }
4c158400b046fefefce0194603951a6587f51867Timo Sirainen if (acl_global_file_read(file) < 0)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen return -1;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen file->last_refresh_time = ioloop_time;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen return 0;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen}
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainenvoid acl_global_file_last_stat(struct acl_global_file *file, struct stat *st_r)
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen{
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen *st_r = file->prev_st;
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen}
af134287071b6d8a9e12c3f11a50fe9ad824d89aTimo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainenvoid acl_global_file_get(struct acl_global_file *file, const char *vname,
4c158400b046fefefce0194603951a6587f51867Timo Sirainen pool_t pool, ARRAY_TYPE(acl_rights) *rights_r)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen{
4c158400b046fefefce0194603951a6587f51867Timo Sirainen struct acl_global_rights *global_rights;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen const struct acl_rights *rights;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen struct acl_rights *new_rights;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen array_foreach_modifiable(&file->rights, global_rights) {
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen if (!wildcard_match(vname, global_rights->vpattern))
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen continue;
e2e6f40d58b85041ca77338026a17d6708f324efTimo Sirainen if (file->debug) {
e2e6f40d58b85041ca77338026a17d6708f324efTimo Sirainen i_debug("Mailbox '%s' matches global ACL pattern '%s'",
e2e6f40d58b85041ca77338026a17d6708f324efTimo Sirainen vname, global_rights->vpattern);
e2e6f40d58b85041ca77338026a17d6708f324efTimo Sirainen }
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen array_foreach(&global_rights->rights, rights) {
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen new_rights = array_append_space(rights_r);
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen acl_rights_dup(rights, pool, new_rights);
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen }
4c158400b046fefefce0194603951a6587f51867Timo Sirainen }
4c158400b046fefefce0194603951a6587f51867Timo Sirainen}
4c158400b046fefefce0194603951a6587f51867Timo Sirainen
4c158400b046fefefce0194603951a6587f51867Timo Sirainenbool acl_global_file_have_any(struct acl_global_file *file, const char *vname)
4c158400b046fefefce0194603951a6587f51867Timo Sirainen{
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen struct acl_global_rights *rights;
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen i_assert(file->last_refresh_time != 0);
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen array_foreach_modifiable(&file->rights, rights) {
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen if (wildcard_match(vname, rights->vpattern))
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen return TRUE;
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen }
3b65d5b306f25bb80660bcb4f9eda9e22d38ab52Timo Sirainen return FALSE;
4c158400b046fefefce0194603951a6587f51867Timo Sirainen}