diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/fuzzy_storage.c | 88 |
1 files changed, 82 insertions, 6 deletions
diff --git a/src/fuzzy_storage.c b/src/fuzzy_storage.c index bc69be98c..00f226532 100644 --- a/src/fuzzy_storage.c +++ b/src/fuzzy_storage.c @@ -1,5 +1,5 @@ /* - * Copyright 2024 Vsevolod Stakhov + * Copyright 2025 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -124,6 +124,11 @@ fuzzy_kp_equal(gconstpointer a, gconstpointer b) return (memcmp(pa, pb, RSPAMD_FUZZY_KEYLEN) == 0); } +enum fuzzy_key_op { + FUZZY_KEY_READ = 0x1u << 0, + FUZZY_KEY_WRITE = 0x1u << 1, + FUZZY_KEY_DELETE = 0x1u << 2, +}; KHASH_SET_INIT_INT(fuzzy_key_ids_set); KHASH_INIT(fuzzy_key_flag_stat, int, struct fuzzy_key_stat, 1, kh_int_hash_func, kh_int_hash_equal); @@ -135,10 +140,12 @@ struct fuzzy_key { khash_t(fuzzy_key_flag_stat) * flags_stat; khash_t(fuzzy_key_ids_set) * forbidden_ids; struct rspamd_leaky_bucket_elt *rl_bucket; + ucl_object_t *extensions; double burst; double rate; ev_tstamp expire; bool expired; + int flags; /* enum fuzzy_key_op */ ref_entry_t ref; }; @@ -624,12 +631,15 @@ rspamd_fuzzy_check_client(struct rspamd_fuzzy_storage_ctx *ctx, } static gboolean -rspamd_fuzzy_check_write(struct fuzzy_session *session) +rspamd_fuzzy_check_write(struct fuzzy_session *session, uint8_t cmd) { if (session->ctx->read_only) { return FALSE; } + /* + * Check IP first + */ if (session->ctx->update_ips != NULL && session->addr) { if (rspamd_inet_address_get_af(session->addr) == AF_UNIX) { return TRUE; @@ -643,6 +653,9 @@ rspamd_fuzzy_check_write(struct fuzzy_session *session) } } + /* + * Check global list of the update keys + */ if (session->ctx->update_keys != NULL && session->key->stat && session->key->key) { static char base32_buf[rspamd_cryptobox_HASHBYTES * 2 + 1]; unsigned int raw_len; @@ -657,6 +670,15 @@ rspamd_fuzzy_check_write(struct fuzzy_session *session) } } + if (session->key) { + if (cmd == FUZZY_WRITE && session->key->flags & FUZZY_KEY_WRITE) { + return TRUE; + } + else if (cmd == FUZZY_DEL && session->key->flags & FUZZY_KEY_DELETE) { + return TRUE; + } + } + return FALSE; } @@ -711,6 +733,10 @@ fuzzy_key_dtor(gpointer p) g_free(key->name); } + if (key->extensions) { + ucl_object_unref(key->extensions); + } + g_free(key); } } @@ -1628,6 +1654,14 @@ rspamd_fuzzy_process_command(struct fuzzy_session *session) } } + /* Key is not allowed to read */ + if (session->key && !(session->key->flags & FUZZY_KEY_READ)) { + result.v1.value = 503; + result.v1.prob = 0.0f; + rspamd_fuzzy_make_reply(cmd, &result, session, send_flags); + return; + } + if (is_rate_allowed) { REF_RETAIN(session); rspamd_fuzzy_backend_check(session->ctx->backend, cmd, @@ -1655,7 +1689,7 @@ rspamd_fuzzy_process_command(struct fuzzy_session *session) rspamd_fuzzy_make_reply(cmd, &result, session, send_flags); } else { - if (rspamd_fuzzy_check_write(session)) { + if (rspamd_fuzzy_check_write(session, cmd->cmd)) { /* Check whitelist */ if (session->ctx->skip_hashes && cmd->cmd == FUZZY_WRITE) { rspamd_encode_hex_buf(cmd->digest, sizeof(cmd->digest), @@ -2942,6 +2976,8 @@ fuzzy_add_keypair_from_ucl(struct rspamd_config *cfg, const ucl_object_t *obj, key->rate = NAN; key->expire = NAN; key->rl_bucket = NULL; + /* Allow read by default */ + key->flags = FUZZY_KEY_READ; /* Preallocate some space for flags */ kh_resize(fuzzy_key_flag_stat, key->flags_stat, 8); const unsigned char *pk = rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_PK, @@ -2973,6 +3009,7 @@ fuzzy_add_keypair_from_ucl(struct rspamd_config *cfg, const ucl_object_t *obj, const ucl_object_t *extensions = rspamd_keypair_get_extensions(kp); if (extensions) { + key->extensions = ucl_object_ref(extensions); lua_State *L = RSPAMD_LUA_CFG_STATE(cfg); const ucl_object_t *forbidden_ids = ucl_object_lookup(extensions, "forbidden_ids"); @@ -3052,9 +3089,48 @@ fuzzy_add_keypair_from_ucl(struct rspamd_config *cfg, const ucl_object_t *obj, if (name && ucl_object_type(name) == UCL_STRING) { key->name = g_strdup(ucl_object_tostring(name)); } + + /* Check permissions */ + const ucl_object_t *read_only = ucl_object_lookup(extensions, "read_only"); + if (read_only && ucl_object_type(read_only) == UCL_BOOLEAN) { + if (ucl_object_toboolean(read_only)) { + key->flags &= ~(FUZZY_KEY_WRITE | FUZZY_KEY_DELETE); + } + else { + key->flags |= (FUZZY_KEY_WRITE | FUZZY_KEY_DELETE); + } + } + + const ucl_object_t *allowed_ops = ucl_object_lookup(extensions, "allowed_ops"); + if (allowed_ops && ucl_object_type(allowed_ops) == UCL_ARRAY) { + const ucl_object_t *cur; + ucl_object_iter_t it = NULL; + /* Reset to only allowed */ + key->flags = 0; + + while ((cur = ucl_object_iterate(allowed_ops, &it, true)) != NULL) { + if (ucl_object_type(cur) == UCL_STRING) { + const char *op = ucl_object_tostring(cur); + + if (g_ascii_strcasecmp(op, "read") == 0) { + key->flags |= FUZZY_KEY_READ; + } + else if (g_ascii_strcasecmp(op, "write") == 0) { + key->flags |= FUZZY_KEY_WRITE; + } + else if (g_ascii_strcasecmp(op, "delete") == 0) { + key->flags |= FUZZY_KEY_DELETE; + } + else { + msg_warn_config("invalid operation: %s", op); + } + } + } + } } - msg_debug("loaded keypair %*bs; expire=%f; rate=%f; burst=%f; name=%s", (int) crypto_box_publickeybytes(), pk, + msg_debug("loaded keypair %*bs; expire=%f; rate=%f; burst=%f; name=%s", + (int) crypto_box_publickeybytes(), pk, key->expire, key->rate, key->burst, key->name); return key; @@ -3677,12 +3753,12 @@ start_fuzzy(struct rspamd_worker *worker) .func = lua_fuzzy_add_pre_handler, }; rspamd_lua_add_metamethod(ctx->cfg->lua_state, rspamd_worker_classname, &fuzzy_lua_reg); - fuzzy_lua_reg = (luaL_Reg){ + fuzzy_lua_reg = (luaL_Reg) { .name = "add_fuzzy_post_handler", .func = lua_fuzzy_add_post_handler, }; rspamd_lua_add_metamethod(ctx->cfg->lua_state, rspamd_worker_classname, &fuzzy_lua_reg); - fuzzy_lua_reg = (luaL_Reg){ + fuzzy_lua_reg = (luaL_Reg) { .name = "add_fuzzy_blacklist_handler", .func = lua_fuzzy_add_blacklist_handler, }; |