From 189005c0e85b2d976dde3307265a7170486e851f Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Thu, 5 Jun 2025 11:27:20 +0100 Subject: [PATCH] [Fix] Use safe parsers everywhere except configuration --- contrib/libucl/lua_ucl.c | 4 ++-- contrib/libucl/ucl.h | 20 ++++++++++++++++++++ src/client/rspamdclient.c | 4 ++-- src/controller.c | 4 ++-- src/fuzzy_storage.c | 10 ++++++---- src/libmime/lang_detection.c | 6 +++--- src/libserver/cfg_rcl.cxx | 2 +- src/libserver/cfg_utils.cxx | 2 +- src/libserver/dynamic_cfg.c | 4 ++-- src/libserver/roll_history.c | 8 ++++---- src/libserver/rspamd_control.c | 4 ++-- src/libserver/symcache/symcache_impl.cxx | 2 +- src/libserver/worker_util.c | 2 +- src/lua/lua_cryptobox.c | 2 +- src/rspamadm/control.c | 8 ++++---- src/rspamadm/signtool.c | 4 ++-- src/rspamd_proxy.c | 2 +- 17 files changed, 55 insertions(+), 33 deletions(-) diff --git a/contrib/libucl/lua_ucl.c b/contrib/libucl/lua_ucl.c index 13c5355cd3..13306b9425 100644 --- a/contrib/libucl/lua_ucl.c +++ b/contrib/libucl/lua_ucl.c @@ -700,7 +700,7 @@ lua_ucl_parser_init(lua_State *L) * files. Macros in the parser are very dangerous and should be used * for trusted data only. */ - int flags = UCL_PARSER_NO_FILEVARS|UCL_PARSER_DISABLE_MACRO; + int flags = UCL_PARSER_SAFE_FLAGS; if (lua_gettop(L) >= 1) { flags = lua_tonumber(L, 1); @@ -1097,7 +1097,7 @@ lua_ucl_parser_validate(lua_State *L) } } else if (lua_type(L, 2) == LUA_TSTRING) { - schema_parser = ucl_parser_new(0); + schema_parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); schema_file = luaL_checkstring(L, 2); if (!ucl_parser_add_file(schema_parser, schema_file)) { diff --git a/contrib/libucl/ucl.h b/contrib/libucl/ucl.h index b6b9f44c01..8c2ac59a49 100644 --- a/contrib/libucl/ucl.h +++ b/contrib/libucl/ucl.h @@ -1,3 +1,19 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /* Copyright (c) 2013-2015, Vsevolod Stakhov * All rights reserved. * @@ -159,6 +175,10 @@ typedef enum ucl_parser_flags { UCL_PARSER_NO_FILEVARS = (1 << 6) /** Do not set file vars */ } ucl_parser_flags_t; +#define UCL_PARSER_SAFE_FLAGS (UCL_PARSER_NO_TIME | \ + UCL_PARSER_NO_IMPLICIT_ARRAYS | \ + UCL_PARSER_DISABLE_MACRO | \ + UCL_PARSER_NO_FILEVARS) /** * String conversion flags, that are used in #ucl_object_fromstring_common function. */ diff --git a/src/client/rspamdclient.c b/src/client/rspamdclient.c index d07b243328..4d79590c52 100644 --- a/src/client/rspamdclient.c +++ b/src/client/rspamdclient.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. @@ -231,7 +231,7 @@ rspamd_client_finish_handler(struct rspamd_http_connection *conn, } } - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_chunk_full(parser, start, len, ucl_parser_get_default_priority(parser), UCL_DUPLICATE_APPEND, UCL_PARSE_AUTO)) { diff --git a/src/controller.c b/src/controller.c index c64835038b..ae2098282c 100644 --- a/src/controller.c +++ b/src/controller.c @@ -2311,7 +2311,7 @@ rspamd_controller_handle_saveactions( return 0; } - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_chunk(parser, msg->body_buf.begin, msg->body_buf.len)) { if ((error = ucl_parser_get_error(parser)) != NULL) { msg_err_session("cannot parse input: %s", error); @@ -2434,7 +2434,7 @@ rspamd_controller_handle_savesymbols( return 0; } - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_chunk(parser, msg->body_buf.begin, msg->body_buf.len)) { if ((error = ucl_parser_get_error(parser)) != NULL) { msg_err_session("cannot parse input: %s", error); diff --git a/src/fuzzy_storage.c b/src/fuzzy_storage.c index 7ed34ef695..58d123712d 100644 --- a/src/fuzzy_storage.c +++ b/src/fuzzy_storage.c @@ -342,7 +342,7 @@ ucl_keymap_fin_cb(struct map_cb_data *data, void **target) return; } - parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_chunk(parser, jb->buf->str, jb->buf->len)) { msg_err_config("cannot load ucl data: parse error %s", @@ -1339,7 +1339,8 @@ rspamd_fuzzy_check_callback(struct rspamd_fuzzy_reply *result, void *ud) /* We push shingles merely for commands that modify content to avoid extra work */ if (is_shingle && cmd->cmd != FUZZY_CHECK) { lua_newshingle(L, &session->cmd.sgl); - } else { + } + else { lua_pushnil(L); } @@ -1528,7 +1529,8 @@ rspamd_fuzzy_process_command(struct fuzzy_session *session) /* We push shingles merely for commands that modify content to avoid extra work */ if (is_shingle && cmd->cmd != FUZZY_CHECK) { lua_newshingle(L, &session->cmd.sgl); - } else { + } + else { lua_pushnil(L); } @@ -2663,7 +2665,7 @@ rspamd_fuzzy_maybe_load_ratelimits(struct rspamd_fuzzy_storage_ctx *ctx) RSPAMD_DBDIR); if (access(path, R_OK) != -1) { - struct ucl_parser *parser = ucl_parser_new(UCL_PARSER_NO_IMPLICIT_ARRAYS | UCL_PARSER_DISABLE_MACRO); + struct ucl_parser *parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (ucl_parser_add_file(parser, path)) { ucl_object_t *obj = ucl_parser_get_object(parser); int loaded = 0; diff --git a/src/libmime/lang_detection.c b/src/libmime/lang_detection.c index 6e180ea664..07ecff76db 100644 --- a/src/libmime/lang_detection.c +++ b/src/libmime/lang_detection.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. @@ -363,7 +363,7 @@ rspamd_language_detector_read_file(struct rspamd_config *cfg, double mean = 0, std = 0, delta = 0, delta2 = 0, m2 = 0; enum rspamd_language_category cat = RSPAMD_LANGUAGE_MAX; - parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_file(parser, path)) { msg_warn_config("cannot parse file %s: %s", path, ucl_parser_get_error(parser)); @@ -825,7 +825,7 @@ rspamd_language_detector_init(struct rspamd_config *cfg) languages_pattern = g_string_sized_new(PATH_MAX); rspamd_printf_gstring(languages_pattern, "%s/stop_words", languages_path); - parser = ucl_parser_new(UCL_PARSER_DEFAULT); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (ucl_parser_add_file(parser, languages_pattern->str)) { stop_words = ucl_parser_get_object(parser); diff --git a/src/libserver/cfg_rcl.cxx b/src/libserver/cfg_rcl.cxx index f383669080..b42a40499b 100644 --- a/src/libserver/cfg_rcl.cxx +++ b/src/libserver/cfg_rcl.cxx @@ -3640,7 +3640,7 @@ rspamd_config_parse_ucl(struct rspamd_config *cfg, /* Try to load keyfile if available */ auto keyfile_name = fmt::format("{}.key", filename); rspamd::util::raii_file::open(keyfile_name, O_RDONLY).map([&](const auto &keyfile) { - auto *kp_parser = ucl_parser_new(0); + auto *kp_parser = ucl_parser_new(UCL_PARSER_DEFAULT); if (ucl_parser_add_fd(kp_parser, keyfile.get_fd())) { auto *kp_obj = ucl_parser_get_object(kp_parser); diff --git a/src/libserver/cfg_utils.cxx b/src/libserver/cfg_utils.cxx index dfbdc6bee2..b430a5fcab 100644 --- a/src/libserver/cfg_utils.cxx +++ b/src/libserver/cfg_utils.cxx @@ -1363,7 +1363,7 @@ rspamd_ucl_fin_cb(struct map_cb_data *data, void **target) } /* New data available */ - auto *parser = ucl_parser_new(0); + auto *parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_chunk(parser, (unsigned char *) cbdata->buf.data(), cbdata->buf.size())) { msg_err_config("cannot parse map %s: %s", diff --git a/src/libserver/dynamic_cfg.c b/src/libserver/dynamic_cfg.c index 9845170742..6d648d7451 100644 --- a/src/libserver/dynamic_cfg.c +++ b/src/libserver/dynamic_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright 2023 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. @@ -195,7 +195,7 @@ json_config_fin_cb(struct map_cb_data *data, void **target) return; } - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_chunk(parser, jb->buf->str, jb->buf->len)) { msg_err("cannot load json data: parse error %s", diff --git a/src/libserver/roll_history.c b/src/libserver/roll_history.c index 66a53a5971..d0f145d8f0 100644 --- a/src/libserver/roll_history.c +++ b/src/libserver/roll_history.c @@ -1,11 +1,11 @@ -/*- - * Copyright 2016 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. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -231,7 +231,7 @@ rspamd_roll_history_load(struct roll_history *history, const char *filename) return FALSE; } - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_fd(parser, fd)) { msg_warn("cannot parse history file %s: %s", filename, diff --git a/src/libserver/rspamd_control.c b/src/libserver/rspamd_control.c index 1bff2ff12b..9e35cb5755 100644 --- a/src/libserver/rspamd_control.c +++ b/src/libserver/rspamd_control.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. @@ -214,7 +214,7 @@ rspamd_control_write_reply(struct rspamd_control_session *session) case RSPAMD_CONTROL_FUZZY_STAT: if (elt->attached_fd != -1) { /* We have some data to parse */ - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); ucl_object_insert_key(cur, ucl_object_fromint( elt->reply.reply.fuzzy_stat.status), diff --git a/src/libserver/symcache/symcache_impl.cxx b/src/libserver/symcache/symcache_impl.cxx index c0278cfc13..c1ca2a6ed4 100644 --- a/src/libserver/symcache/symcache_impl.cxx +++ b/src/libserver/symcache/symcache_impl.cxx @@ -274,7 +274,7 @@ auto symcache::load_items() -> bool return false; } - auto *parser = ucl_parser_new(0); + auto *parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); const auto *p = (const std::uint8_t *) (hdr + 1); if (!ucl_parser_add_chunk(parser, p, cached_map->get_size() - sizeof(*hdr))) { diff --git a/src/libserver/worker_util.c b/src/libserver/worker_util.c index d0ac8d8d3a..685ee9cd2d 100644 --- a/src/libserver/worker_util.c +++ b/src/libserver/worker_util.c @@ -2138,7 +2138,7 @@ rspamd_controller_load_saved_stats(struct rspamd_main *rspamd_main, return; } - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_file(parser, cfg->stats_file)) { msg_err_config("cannot parse controller stats from %s: %s", diff --git a/src/lua/lua_cryptobox.c b/src/lua/lua_cryptobox.c index 721d712564..2c2254920e 100644 --- a/src/lua/lua_cryptobox.c +++ b/src/lua/lua_cryptobox.c @@ -404,7 +404,7 @@ lua_cryptobox_keypair_load(lua_State *L) if (lua_type(L, 1) == LUA_TSTRING) { buf = luaL_checklstring(L, 1, &len); if (buf != NULL) { - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_chunk(parser, buf, len)) { msg_err("cannot open keypair from data: %s", diff --git a/src/rspamadm/control.c b/src/rspamadm/control.c index 381bdaa7a7..cd550c04ee 100644 --- a/src/rspamadm/control.c +++ b/src/rspamadm/control.c @@ -1,11 +1,11 @@ -/*- - * Copyright 2016 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. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -112,7 +112,7 @@ rspamd_control_finish_handler(struct rspamd_http_connection *conn, struct rspamadm_control_cbdata *cbdata = conn->ud; body = rspamd_http_message_get_body(msg, &body_len); - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!body || !ucl_parser_add_chunk(parser, body, body_len)) { rspamd_fprintf(stderr, "cannot parse server's reply: %s\n", diff --git a/src/rspamadm/signtool.c b/src/rspamadm/signtool.c index 6d60e6700e..538767b19b 100644 --- a/src/rspamadm/signtool.c +++ b/src/rspamadm/signtool.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. @@ -573,7 +573,7 @@ rspamadm_signtool(int argc, char **argv, const struct rspamadm_command *cmd) else { g_assert(keypair_file != NULL); - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_file(parser, keypair_file) || (top = ucl_parser_get_object(parser)) == NULL) { diff --git a/src/rspamd_proxy.c b/src/rspamd_proxy.c index 8e69298e63..04603eb469 100644 --- a/src/rspamd_proxy.c +++ b/src/rspamd_proxy.c @@ -1012,7 +1012,7 @@ proxy_backend_parse_results(struct rspamd_proxy_session *session, RSPAMD_FTOK_ASSIGN(&json_ct, "application/json"); if (ct && rspamd_ftok_casecmp(ct, &json_ct) == 0) { - parser = ucl_parser_new(0); + parser = ucl_parser_new(UCL_PARSER_SAFE_FLAGS); if (!ucl_parser_add_chunk(parser, in, inlen)) { char *encoded; -- 2.47.3