You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cfg_utils.cxx 73KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955
  1. /*
  2. * Copyright 2023 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "config.h"
  17. #include "lua/lua_common.h"
  18. #include "lua/lua_thread_pool.h"
  19. #include "cfg_file.h"
  20. #include "rspamd.h"
  21. #include "cfg_file_private.h"
  22. #include "maps/map.h"
  23. #include "maps/map_helpers.h"
  24. #include "maps/map_private.h"
  25. #include "dynamic_cfg.h"
  26. #include "utlist.h"
  27. #include "stat_api.h"
  28. #include "unix-std.h"
  29. #include "libutil/multipattern.h"
  30. #include "monitored.h"
  31. #include "ref.h"
  32. #include "cryptobox.h"
  33. #include "ssl_util.h"
  34. #include "contrib/libottery/ottery.h"
  35. #include "contrib/fastutf8/fastutf8.h"
  36. #ifdef SYS_ZSTD
  37. #include "zstd.h"
  38. #else
  39. #define ZSTD_STATIC_LINKING_ONLY
  40. #include "contrib/zstd/zstd.h"
  41. #endif
  42. #ifdef HAVE_OPENSSL
  43. #include <openssl/rand.h>
  44. #include <openssl/err.h>
  45. #include <openssl/evp.h>
  46. #include <openssl/ssl.h>
  47. #include <openssl/conf.h>
  48. #endif
  49. #ifdef HAVE_LOCALE_H
  50. #include <locale.h>
  51. #endif
  52. #ifdef HAVE_SYS_RESOURCE_H
  53. #include <sys/resource.h>
  54. #endif
  55. #include <math.h>
  56. #include "libserver/composites/composites.h"
  57. #include "blas-config.h"
  58. #include <string>
  59. #include <string_view>
  60. #include <vector>
  61. #include "fmt/core.h"
  62. #include "cxx/util.hxx"
  63. #include "frozen/unordered_map.h"
  64. #include "frozen/string.h"
  65. #include "contrib/ankerl/unordered_dense.h"
  66. #define DEFAULT_SCORE 10.0
  67. #define DEFAULT_RLIMIT_NOFILE 2048
  68. #define DEFAULT_RLIMIT_MAXCORE 0
  69. #define DEFAULT_MAP_TIMEOUT 60.0 * 5
  70. #define DEFAULT_MAP_FILE_WATCH_MULTIPLIER 1
  71. #define DEFAULT_MIN_WORD 0
  72. #define DEFAULT_MAX_WORD 40
  73. #define DEFAULT_WORDS_DECAY 600
  74. #define DEFAULT_MAX_MESSAGE (50 * 1024 * 1024)
  75. #define DEFAULT_MAX_PIC (1 * 1024 * 1024)
  76. #define DEFAULT_MAX_SHOTS 100
  77. #define DEFAULT_MAX_SESSIONS 100
  78. #define DEFAULT_MAX_WORKERS 4
  79. #define DEFAULT_MAX_HTML_SIZE DEFAULT_MAX_MESSAGE / 5 /* 10 Mb */
  80. /* Timeout for task processing */
  81. #define DEFAULT_TASK_TIMEOUT 8.0
  82. #define DEFAULT_LUA_GC_STEP 200
  83. #define DEFAULT_LUA_GC_PAUSE 200
  84. #define DEFAULT_GC_MAXITERS 0
  85. struct rspamd_ucl_map_cbdata {
  86. struct rspamd_config *cfg;
  87. std::string buf;
  88. explicit rspamd_ucl_map_cbdata(struct rspamd_config *cfg)
  89. : cfg(cfg)
  90. {
  91. }
  92. };
  93. static gchar *rspamd_ucl_read_cb(gchar *chunk,
  94. gint len,
  95. struct map_cb_data *data,
  96. gboolean final);
  97. static void rspamd_ucl_fin_cb(struct map_cb_data *data, void **target);
  98. static void rspamd_ucl_dtor_cb(struct map_cb_data *data);
  99. guint rspamd_config_log_id = (guint) -1;
  100. RSPAMD_CONSTRUCTOR(rspamd_config_log_init)
  101. {
  102. rspamd_config_log_id = rspamd_logger_add_debug_module("config");
  103. }
  104. struct rspamd_actions_list {
  105. using action_ptr = std::shared_ptr<rspamd_action>;
  106. std::vector<action_ptr> actions;
  107. ankerl::unordered_dense::map<std::string_view, action_ptr> actions_by_name;
  108. explicit rspamd_actions_list()
  109. {
  110. actions.reserve(METRIC_ACTION_MAX + 2);
  111. actions_by_name.reserve(METRIC_ACTION_MAX + 2);
  112. }
  113. void add_action(action_ptr action)
  114. {
  115. actions.push_back(action);
  116. actions_by_name[action->name] = action;
  117. sort();
  118. }
  119. void sort()
  120. {
  121. std::sort(actions.begin(), actions.end(), [](const action_ptr &a1, const action_ptr &a2) -> bool {
  122. if (!isnan(a1->threshold) && !isnan(a2->threshold)) {
  123. return a1->threshold < a2->threshold;
  124. }
  125. if (isnan(a1->threshold) && isnan(a2->threshold)) {
  126. return false;
  127. }
  128. else if (isnan(a1->threshold)) {
  129. return true;
  130. }
  131. return false;
  132. });
  133. }
  134. void clear()
  135. {
  136. actions.clear();
  137. actions_by_name.clear();
  138. }
  139. };
  140. #define RSPAMD_CFG_ACTIONS(cfg) (reinterpret_cast<rspamd_actions_list *>((cfg)->actions))
  141. gboolean
  142. rspamd_parse_bind_line(struct rspamd_config *cfg,
  143. struct rspamd_worker_conf *cf,
  144. const gchar *str)
  145. {
  146. struct rspamd_worker_bind_conf *cnf;
  147. const gchar *fdname;
  148. gboolean ret = TRUE;
  149. if (str == nullptr) {
  150. return FALSE;
  151. }
  152. cnf = rspamd_mempool_alloc0_type(cfg->cfg_pool, struct rspamd_worker_bind_conf);
  153. cnf->cnt = 1024;
  154. cnf->bind_line = rspamd_mempool_strdup(cfg->cfg_pool, str);
  155. auto bind_line = std::string_view{cnf->bind_line};
  156. if (bind_line.starts_with("systemd:")) {
  157. /* The actual socket will be passed by systemd environment */
  158. fdname = str + sizeof("systemd:") - 1;
  159. cnf->is_systemd = TRUE;
  160. cnf->addrs = g_ptr_array_new_full(1, nullptr);
  161. rspamd_mempool_add_destructor(cfg->cfg_pool,
  162. rspamd_ptr_array_free_hard, cnf->addrs);
  163. if (fdname[0]) {
  164. g_ptr_array_add(cnf->addrs, rspamd_mempool_strdup(cfg->cfg_pool, fdname));
  165. cnf->cnt = cnf->addrs->len;
  166. cnf->name = rspamd_mempool_strdup(cfg->cfg_pool, str);
  167. LL_PREPEND(cf->bind_conf, cnf);
  168. }
  169. else {
  170. msg_err_config("cannot parse bind line: %s", str);
  171. ret = FALSE;
  172. }
  173. }
  174. else {
  175. if (rspamd_parse_host_port_priority(str, &cnf->addrs,
  176. nullptr, &cnf->name, DEFAULT_BIND_PORT, TRUE, cfg->cfg_pool) == RSPAMD_PARSE_ADDR_FAIL) {
  177. msg_err_config("cannot parse bind line: %s", str);
  178. ret = FALSE;
  179. }
  180. else {
  181. cnf->cnt = cnf->addrs->len;
  182. LL_PREPEND(cf->bind_conf, cnf);
  183. }
  184. }
  185. return ret;
  186. }
  187. struct rspamd_config *
  188. rspamd_config_new(enum rspamd_config_init_flags flags)
  189. {
  190. struct rspamd_config *cfg;
  191. rspamd_mempool_t *pool;
  192. pool = rspamd_mempool_new(8 * 1024 * 1024, "cfg", 0);
  193. cfg = rspamd_mempool_alloc0_type(pool, struct rspamd_config);
  194. /* Allocate larger pool for cfg */
  195. cfg->cfg_pool = pool;
  196. cfg->dns_timeout = 1.0;
  197. cfg->dns_retransmits = 5;
  198. /* 16 sockets per DNS server */
  199. cfg->dns_io_per_server = 16;
  200. cfg->unknown_weight = NAN;
  201. cfg->actions = (void *) new rspamd_actions_list();
  202. /* Add all internal actions to keep compatibility */
  203. for (int i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i++) {
  204. auto &&action = std::make_shared<rspamd_action>();
  205. action->threshold = NAN;
  206. action->name = rspamd_mempool_strdup(cfg->cfg_pool,
  207. rspamd_action_to_str(static_cast<rspamd_action_type>(i)));
  208. action->action_type = static_cast<rspamd_action_type>(i);
  209. if (i == METRIC_ACTION_SOFT_REJECT) {
  210. action->flags |= RSPAMD_ACTION_NO_THRESHOLD | RSPAMD_ACTION_HAM;
  211. }
  212. else if (i == METRIC_ACTION_GREYLIST) {
  213. action->flags |= RSPAMD_ACTION_THRESHOLD_ONLY | RSPAMD_ACTION_HAM;
  214. }
  215. else if (i == METRIC_ACTION_NOACTION) {
  216. action->flags |= RSPAMD_ACTION_HAM;
  217. }
  218. RSPAMD_CFG_ACTIONS(cfg)->add_action(std::move(action));
  219. }
  220. /* Disable timeout */
  221. cfg->task_timeout = DEFAULT_TASK_TIMEOUT;
  222. rspamd_config_init_metric(cfg);
  223. cfg->composites_manager = rspamd_composites_manager_create(cfg);
  224. cfg->classifiers_symbols = g_hash_table_new(rspamd_str_hash,
  225. rspamd_str_equal);
  226. cfg->cfg_params = g_hash_table_new(rspamd_str_hash, rspamd_str_equal);
  227. cfg->debug_modules = g_hash_table_new(rspamd_str_hash, rspamd_str_equal);
  228. cfg->explicit_modules = g_hash_table_new(rspamd_str_hash, rspamd_str_equal);
  229. cfg->trusted_keys = g_hash_table_new(rspamd_str_hash,
  230. rspamd_str_equal);
  231. cfg->map_timeout = DEFAULT_MAP_TIMEOUT;
  232. cfg->map_file_watch_multiplier = DEFAULT_MAP_FILE_WATCH_MULTIPLIER;
  233. cfg->log_level = G_LOG_LEVEL_WARNING;
  234. cfg->log_flags = RSPAMD_LOG_FLAG_DEFAULT;
  235. cfg->check_text_attachements = TRUE;
  236. cfg->dns_max_requests = 64;
  237. cfg->history_rows = 200;
  238. cfg->log_error_elts = 10;
  239. cfg->log_error_elt_maxlen = 1000;
  240. cfg->log_task_max_elts = 7;
  241. cfg->cache_reload_time = 30.0;
  242. cfg->max_lua_urls = 1024;
  243. cfg->max_urls = cfg->max_lua_urls * 10;
  244. cfg->max_recipients = 1024;
  245. cfg->max_blas_threads = 1;
  246. cfg->max_opts_len = 4096;
  247. cfg->gtube_patterns_policy = RSPAMD_GTUBE_REJECT;
  248. /* Default log line */
  249. cfg->log_format_str = rspamd_mempool_strdup(cfg->cfg_pool,
  250. "id: <$mid>,$if_qid{ qid: <$>,}$if_ip{ ip: $,}"
  251. "$if_user{ user: $,}$if_smtp_from{ from: <$>,} (default: $is_spam "
  252. "($action): [$scores] [$symbols_scores_params]), len: $len, time: $time_real, "
  253. "dns req: $dns_req, digest: <$digest>"
  254. "$if_smtp_rcpts{ rcpts: <$>, }$if_mime_rcpt{ mime_rcpt: <$>, }");
  255. /* Allow non-mime input by default */
  256. cfg->allow_raw_input = TRUE;
  257. /* Default maximum words processed */
  258. cfg->words_decay = DEFAULT_WORDS_DECAY;
  259. cfg->min_word_len = DEFAULT_MIN_WORD;
  260. cfg->max_word_len = DEFAULT_MAX_WORD;
  261. cfg->max_html_len = DEFAULT_MAX_HTML_SIZE;
  262. /* GC limits */
  263. cfg->lua_gc_pause = DEFAULT_LUA_GC_PAUSE;
  264. cfg->lua_gc_step = DEFAULT_LUA_GC_STEP;
  265. cfg->full_gc_iters = DEFAULT_GC_MAXITERS;
  266. /* Default hyperscan cache */
  267. cfg->hs_cache_dir = rspamd_mempool_strdup(cfg->cfg_pool, RSPAMD_DBDIR "/");
  268. if (!(flags & RSPAMD_CONFIG_INIT_SKIP_LUA)) {
  269. cfg->lua_state = (void *) rspamd_lua_init(flags & RSPAMD_CONFIG_INIT_WIPE_LUA_MEM);
  270. cfg->own_lua_state = TRUE;
  271. cfg->lua_thread_pool = (void *) lua_thread_pool_new(RSPAMD_LUA_CFG_STATE(cfg));
  272. }
  273. cfg->cache = rspamd_symcache_new(cfg);
  274. cfg->ups_ctx = rspamd_upstreams_library_init();
  275. cfg->re_cache = rspamd_re_cache_new();
  276. cfg->doc_strings = ucl_object_typed_new(UCL_OBJECT);
  277. /*
  278. * Unless exim is fixed
  279. */
  280. cfg->enable_shutdown_workaround = TRUE;
  281. cfg->ssl_ciphers = rspamd_mempool_strdup(cfg->cfg_pool, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
  282. cfg->max_message = DEFAULT_MAX_MESSAGE;
  283. cfg->max_pic_size = DEFAULT_MAX_PIC;
  284. cfg->images_cache_size = 256;
  285. cfg->monitored_ctx = rspamd_monitored_ctx_init();
  286. cfg->neighbours = ucl_object_typed_new(UCL_OBJECT);
  287. cfg->redis_pool = rspamd_redis_pool_init();
  288. cfg->default_max_shots = DEFAULT_MAX_SHOTS;
  289. cfg->max_sessions_cache = DEFAULT_MAX_SESSIONS;
  290. cfg->maps_cache_dir = rspamd_mempool_strdup(cfg->cfg_pool, RSPAMD_DBDIR);
  291. cfg->c_modules = g_ptr_array_new();
  292. cfg->heartbeat_interval = 10.0;
  293. cfg->enable_css_parser = true;
  294. cfg->script_modules = g_ptr_array_new();
  295. REF_INIT_RETAIN(cfg, rspamd_config_free);
  296. return cfg;
  297. }
  298. void rspamd_config_free(struct rspamd_config *cfg)
  299. {
  300. struct rspamd_config_cfg_lua_script *sc, *sctmp;
  301. struct rspamd_config_settings_elt *set, *stmp;
  302. struct rspamd_worker_log_pipe *lp, *ltmp;
  303. rspamd_lua_run_config_unload(RSPAMD_LUA_CFG_STATE(cfg), cfg);
  304. /* Scripts part */
  305. DL_FOREACH_SAFE(cfg->on_term_scripts, sc, sctmp)
  306. {
  307. luaL_unref(RSPAMD_LUA_CFG_STATE(cfg), LUA_REGISTRYINDEX, sc->cbref);
  308. }
  309. DL_FOREACH_SAFE(cfg->on_load_scripts, sc, sctmp)
  310. {
  311. luaL_unref(RSPAMD_LUA_CFG_STATE(cfg), LUA_REGISTRYINDEX, sc->cbref);
  312. }
  313. DL_FOREACH_SAFE(cfg->post_init_scripts, sc, sctmp)
  314. {
  315. luaL_unref(RSPAMD_LUA_CFG_STATE(cfg), LUA_REGISTRYINDEX, sc->cbref);
  316. }
  317. DL_FOREACH_SAFE(cfg->config_unload_scripts, sc, sctmp)
  318. {
  319. luaL_unref(RSPAMD_LUA_CFG_STATE(cfg), LUA_REGISTRYINDEX, sc->cbref);
  320. }
  321. DL_FOREACH_SAFE(cfg->setting_ids, set, stmp)
  322. {
  323. REF_RELEASE(set);
  324. }
  325. rspamd_map_remove_all(cfg);
  326. rspamd_mempool_destructors_enforce(cfg->cfg_pool);
  327. g_list_free(cfg->classifiers);
  328. g_list_free(cfg->workers);
  329. rspamd_symcache_destroy(cfg->cache);
  330. ucl_object_unref(cfg->cfg_ucl_obj);
  331. ucl_object_unref(cfg->config_comments);
  332. ucl_object_unref(cfg->doc_strings);
  333. ucl_object_unref(cfg->neighbours);
  334. g_hash_table_remove_all(cfg->cfg_params);
  335. g_hash_table_unref(cfg->cfg_params);
  336. g_hash_table_unref(cfg->classifiers_symbols);
  337. g_hash_table_unref(cfg->debug_modules);
  338. g_hash_table_unref(cfg->explicit_modules);
  339. g_hash_table_unref(cfg->trusted_keys);
  340. rspamd_re_cache_unref(cfg->re_cache);
  341. g_ptr_array_free(cfg->c_modules, TRUE);
  342. g_ptr_array_free(cfg->script_modules, TRUE);
  343. if (cfg->monitored_ctx) {
  344. rspamd_monitored_ctx_destroy(cfg->monitored_ctx);
  345. }
  346. if (RSPAMD_LUA_CFG_STATE(cfg) && cfg->own_lua_state) {
  347. lua_thread_pool_free((struct lua_thread_pool *) cfg->lua_thread_pool);
  348. rspamd_lua_close(RSPAMD_LUA_CFG_STATE(cfg));
  349. }
  350. if (cfg->redis_pool) {
  351. rspamd_redis_pool_destroy(cfg->redis_pool);
  352. }
  353. rspamd_upstreams_library_unref(cfg->ups_ctx);
  354. delete RSPAMD_CFG_ACTIONS(cfg);
  355. rspamd_mempool_destructors_enforce(cfg->cfg_pool);
  356. if (cfg->checksum) {
  357. g_free(cfg->checksum);
  358. }
  359. REF_RELEASE(cfg->libs_ctx);
  360. DL_FOREACH_SAFE(cfg->log_pipes, lp, ltmp)
  361. {
  362. close(lp->fd);
  363. g_free(lp);
  364. }
  365. rspamd_mempool_delete(cfg->cfg_pool);
  366. }
  367. const ucl_object_t *
  368. rspamd_config_get_module_opt(struct rspamd_config *cfg,
  369. const gchar *module_name,
  370. const gchar *opt_name)
  371. {
  372. const ucl_object_t *res = nullptr, *sec;
  373. sec = ucl_obj_get_key(cfg->cfg_ucl_obj, module_name);
  374. if (sec != nullptr) {
  375. res = ucl_obj_get_key(sec, opt_name);
  376. }
  377. return res;
  378. }
  379. gint rspamd_config_parse_flag(const gchar *str, guint len)
  380. {
  381. gint c;
  382. if (!str || !*str) {
  383. return -1;
  384. }
  385. if (len == 0) {
  386. len = strlen(str);
  387. }
  388. switch (len) {
  389. case 1:
  390. c = g_ascii_tolower(*str);
  391. if (c == 'y' || c == '1') {
  392. return 1;
  393. }
  394. else if (c == 'n' || c == '0') {
  395. return 0;
  396. }
  397. break;
  398. case 2:
  399. if (g_ascii_strncasecmp(str, "no", len) == 0) {
  400. return 0;
  401. }
  402. else if (g_ascii_strncasecmp(str, "on", len) == 0) {
  403. return 1;
  404. }
  405. break;
  406. case 3:
  407. if (g_ascii_strncasecmp(str, "yes", len) == 0) {
  408. return 1;
  409. }
  410. else if (g_ascii_strncasecmp(str, "off", len) == 0) {
  411. return 0;
  412. }
  413. break;
  414. case 4:
  415. if (g_ascii_strncasecmp(str, "true", len) == 0) {
  416. return 1;
  417. }
  418. break;
  419. case 5:
  420. if (g_ascii_strncasecmp(str, "false", len) == 0) {
  421. return 0;
  422. }
  423. break;
  424. }
  425. return -1;
  426. }
  427. // A mapping between names and log format types + flags
  428. constexpr const auto config_vars = frozen::make_unordered_map<frozen::string, std::pair<rspamd_log_format_type, int>>({
  429. {"mid", {RSPAMD_LOG_MID, 0}},
  430. {"qid", {RSPAMD_LOG_QID, 0}},
  431. {"user", {RSPAMD_LOG_USER, 0}},
  432. {"ip", {RSPAMD_LOG_IP, 0}},
  433. {"len", {RSPAMD_LOG_LEN, 0}},
  434. {"dns_req", {RSPAMD_LOG_DNS_REQ, 0}},
  435. {"smtp_from", {RSPAMD_LOG_SMTP_FROM, 0}},
  436. {"mime_from", {RSPAMD_LOG_MIME_FROM, 0}},
  437. {"smtp_rcpt", {RSPAMD_LOG_SMTP_RCPT, 0}},
  438. {"mime_rcpt", {RSPAMD_LOG_MIME_RCPT, 0}},
  439. {"smtp_rcpts", {RSPAMD_LOG_SMTP_RCPTS, 0}},
  440. {"mime_rcpts", {RSPAMD_LOG_MIME_RCPTS, 0}},
  441. {"time_real", {RSPAMD_LOG_TIME_REAL, 0}},
  442. {"time_virtual", {RSPAMD_LOG_TIME_VIRTUAL, 0}},
  443. {"lua", {RSPAMD_LOG_LUA, 0}},
  444. {"digest", {RSPAMD_LOG_DIGEST, 0}},
  445. {"checksum", {RSPAMD_LOG_DIGEST, 0}},
  446. {"filename", {RSPAMD_LOG_FILENAME, 0}},
  447. {"forced_action", {RSPAMD_LOG_FORCED_ACTION, 0}},
  448. {"settings_id", {RSPAMD_LOG_SETTINGS_ID, 0}},
  449. {"mempool_size", {RSPAMD_LOG_MEMPOOL_SIZE, 0}},
  450. {"mempool_waste", {RSPAMD_LOG_MEMPOOL_WASTE, 0}},
  451. {"action", {RSPAMD_LOG_ACTION, 0}},
  452. {"scores", {RSPAMD_LOG_SCORES, 0}},
  453. {"symbols", {RSPAMD_LOG_SYMBOLS, 0}},
  454. {"symbols_scores", {RSPAMD_LOG_SYMBOLS, RSPAMD_LOG_FMT_FLAG_SYMBOLS_SCORES}},
  455. {"symbols_params", {RSPAMD_LOG_SYMBOLS, RSPAMD_LOG_FMT_FLAG_SYMBOLS_PARAMS}},
  456. {"symbols_scores_params", {RSPAMD_LOG_SYMBOLS, RSPAMD_LOG_FMT_FLAG_SYMBOLS_PARAMS | RSPAMD_LOG_FMT_FLAG_SYMBOLS_SCORES}},
  457. {"groups", {RSPAMD_LOG_GROUPS, 0}},
  458. {"public_groups", {RSPAMD_LOG_PUBLIC_GROUPS, 0}},
  459. {"is_spam", {RSPAMD_LOG_ISSPAM, 0}},
  460. });
  461. static gboolean
  462. rspamd_config_process_var(struct rspamd_config *cfg, const rspamd_ftok_t *var,
  463. const rspamd_ftok_t *content)
  464. {
  465. g_assert(var != nullptr);
  466. auto flags = 0;
  467. auto lc_var = std::string{var->begin, var->len};
  468. std::transform(lc_var.begin(), lc_var.end(), lc_var.begin(), g_ascii_tolower);
  469. auto tok = std::string_view{lc_var};
  470. if (var->len > 3 && tok.starts_with("if_")) {
  471. flags |= RSPAMD_LOG_FMT_FLAG_CONDITION;
  472. tok = tok.substr(3);
  473. }
  474. auto maybe_fmt_var = rspamd::find_map(config_vars, tok);
  475. if (maybe_fmt_var) {
  476. auto &fmt_var = maybe_fmt_var.value().get();
  477. auto *log_format = rspamd_mempool_alloc0_type(cfg->cfg_pool, rspamd_log_format);
  478. log_format->type = fmt_var.first;
  479. log_format->flags = fmt_var.second | flags;
  480. if (log_format->type != RSPAMD_LOG_LUA) {
  481. if (content && content->len > 0) {
  482. log_format->data = rspamd_mempool_alloc0(cfg->cfg_pool,
  483. sizeof(rspamd_ftok_t));
  484. memcpy(log_format->data, content, sizeof(*content));
  485. log_format->len = sizeof(*content);
  486. }
  487. }
  488. else {
  489. /* Load lua code and ensure that we have function ref returned */
  490. if (!content || content->len == 0) {
  491. msg_err_config("lua variable needs content: %T", &tok);
  492. return FALSE;
  493. }
  494. if (luaL_loadbuffer(RSPAMD_LUA_CFG_STATE(cfg), content->begin, content->len,
  495. "lua log variable") != 0) {
  496. msg_err_config("error loading lua code: '%T': %s", content,
  497. lua_tostring(RSPAMD_LUA_CFG_STATE(cfg), -1));
  498. return FALSE;
  499. }
  500. if (lua_pcall(RSPAMD_LUA_CFG_STATE(cfg), 0, 1, 0) != 0) {
  501. msg_err_config("error executing lua code: '%T': %s", content,
  502. lua_tostring(RSPAMD_LUA_CFG_STATE(cfg), -1));
  503. lua_pop(RSPAMD_LUA_CFG_STATE(cfg), 1);
  504. return FALSE;
  505. }
  506. if (lua_type(RSPAMD_LUA_CFG_STATE(cfg), -1) != LUA_TFUNCTION) {
  507. msg_err_config("lua variable should return function: %T", content);
  508. lua_pop(RSPAMD_LUA_CFG_STATE(cfg), 1);
  509. return FALSE;
  510. }
  511. auto id = luaL_ref(RSPAMD_LUA_CFG_STATE(cfg), LUA_REGISTRYINDEX);
  512. log_format->data = GINT_TO_POINTER(id);
  513. log_format->len = 0;
  514. }
  515. DL_APPEND(cfg->log_format, log_format);
  516. }
  517. else {
  518. std::string known_formats;
  519. for (const auto &v: config_vars) {
  520. known_formats += std::string_view{v.first.data(), v.first.size()};
  521. known_formats += ", ";
  522. }
  523. if (known_formats.size() > 2) {
  524. // Remove last comma
  525. known_formats.resize(known_formats.size() - 2);
  526. }
  527. msg_err_config("unknown log variable: %T, known vars are: \"%s\"", var, known_formats.c_str());
  528. return FALSE;
  529. }
  530. return TRUE;
  531. }
  532. static gboolean
  533. rspamd_config_parse_log_format(struct rspamd_config *cfg)
  534. {
  535. const gchar *p, *c, *end, *s;
  536. gchar *d;
  537. struct rspamd_log_format *lf = nullptr;
  538. rspamd_ftok_t var, var_content;
  539. enum {
  540. parse_str,
  541. parse_dollar,
  542. parse_var_name,
  543. parse_var_content,
  544. } state = parse_str;
  545. gint braces = 0;
  546. g_assert(cfg != nullptr);
  547. c = cfg->log_format_str;
  548. if (c == nullptr) {
  549. return FALSE;
  550. }
  551. p = c;
  552. end = p + strlen(p);
  553. while (p < end) {
  554. switch (state) {
  555. case parse_str:
  556. if (*p == '$') {
  557. state = parse_dollar;
  558. }
  559. else {
  560. p++;
  561. }
  562. break;
  563. case parse_dollar:
  564. if (p > c) {
  565. /* We have string element that we need to store */
  566. lf = rspamd_mempool_alloc0_type(cfg->cfg_pool, struct rspamd_log_format);
  567. lf->type = RSPAMD_LOG_STRING;
  568. lf->data = rspamd_mempool_alloc(cfg->cfg_pool, p - c + 1);
  569. /* Filter \r\n from the destination */
  570. s = c;
  571. d = (char *) lf->data;
  572. while (s < p) {
  573. if (*s != '\r' && *s != '\n') {
  574. *d++ = *s++;
  575. }
  576. else {
  577. *d++ = ' ';
  578. s++;
  579. }
  580. }
  581. *d = '\0';
  582. lf->len = d - (char *) lf->data;
  583. DL_APPEND(cfg->log_format, lf);
  584. lf = nullptr;
  585. }
  586. p++;
  587. c = p;
  588. state = parse_var_name;
  589. break;
  590. case parse_var_name:
  591. if (*p == '{') {
  592. var.begin = c;
  593. var.len = p - c;
  594. p++;
  595. c = p;
  596. state = parse_var_content;
  597. braces = 1;
  598. }
  599. else if (*p != '_' && *p != '-' && !g_ascii_isalnum(*p)) {
  600. /* Variable with no content */
  601. var.begin = c;
  602. var.len = p - c;
  603. c = p;
  604. if (!rspamd_config_process_var(cfg, &var, nullptr)) {
  605. return FALSE;
  606. }
  607. state = parse_str;
  608. }
  609. else {
  610. p++;
  611. }
  612. break;
  613. case parse_var_content:
  614. if (*p == '}' && --braces == 0) {
  615. var_content.begin = c;
  616. var_content.len = p - c;
  617. p++;
  618. c = p;
  619. if (!rspamd_config_process_var(cfg, &var, &var_content)) {
  620. return FALSE;
  621. }
  622. state = parse_str;
  623. }
  624. else if (*p == '{') {
  625. braces++;
  626. p++;
  627. }
  628. else {
  629. p++;
  630. }
  631. break;
  632. }
  633. }
  634. /* Last state */
  635. switch (state) {
  636. case parse_str:
  637. if (p > c) {
  638. /* We have string element that we need to store */
  639. lf = rspamd_mempool_alloc0_type(cfg->cfg_pool, struct rspamd_log_format);
  640. lf->type = RSPAMD_LOG_STRING;
  641. lf->data = rspamd_mempool_alloc(cfg->cfg_pool, p - c + 1);
  642. /* Filter \r\n from the destination */
  643. s = c;
  644. d = (char *) lf->data;
  645. while (s < p) {
  646. if (*s != '\r' && *s != '\n') {
  647. *d++ = *s++;
  648. }
  649. else {
  650. *d++ = ' ';
  651. s++;
  652. }
  653. }
  654. *d = '\0';
  655. lf->len = d - (char *) lf->data;
  656. DL_APPEND(cfg->log_format, lf);
  657. lf = nullptr;
  658. }
  659. break;
  660. case parse_var_name:
  661. var.begin = c;
  662. var.len = p - c;
  663. if (!rspamd_config_process_var(cfg, &var, nullptr)) {
  664. return FALSE;
  665. }
  666. break;
  667. case parse_dollar:
  668. case parse_var_content:
  669. msg_err_config("cannot parse log format %s: incomplete string",
  670. cfg->log_format_str);
  671. return FALSE;
  672. break;
  673. }
  674. return TRUE;
  675. }
  676. static void
  677. rspamd_urls_config_dtor(gpointer _unused)
  678. {
  679. rspamd_url_deinit();
  680. }
  681. static void
  682. rspamd_adjust_clocks_resolution(struct rspamd_config *cfg)
  683. {
  684. #ifdef HAVE_CLOCK_GETTIME
  685. struct timespec ts;
  686. #endif
  687. #ifdef HAVE_CLOCK_GETTIME
  688. #ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID
  689. clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts);
  690. #elif defined(HAVE_CLOCK_VIRTUAL)
  691. clock_getres(CLOCK_VIRTUAL, &ts);
  692. #else
  693. clock_getres(CLOCK_REALTIME, &ts);
  694. #endif
  695. cfg->clock_res = log10(1000000. / ts.tv_nsec);
  696. if (cfg->clock_res < 0) {
  697. cfg->clock_res = 0;
  698. }
  699. if (cfg->clock_res > 3) {
  700. cfg->clock_res = 3;
  701. }
  702. #else
  703. /* For gettimeofday */
  704. cfg->clock_res = 1;
  705. #endif
  706. }
  707. /*
  708. * Perform post load actions
  709. */
  710. gboolean
  711. rspamd_config_post_load(struct rspamd_config *cfg,
  712. enum rspamd_post_load_options opts)
  713. {
  714. auto ret = TRUE;
  715. rspamd_adjust_clocks_resolution(cfg);
  716. rspamd_logger_configure_modules(cfg->debug_modules);
  717. if (cfg->one_shot_mode) {
  718. msg_info_config("enabling one shot mode (was %d max shots)",
  719. cfg->default_max_shots);
  720. cfg->default_max_shots = 1;
  721. }
  722. #if defined(WITH_HYPERSCAN) && !defined(__aarch64__) && !defined(__powerpc64__)
  723. if (!cfg->disable_hyperscan) {
  724. if (!(cfg->libs_ctx->crypto_ctx->cpu_config & CPUID_SSSE3)) {
  725. msg_warn_config("CPU doesn't have SSSE3 instructions set "
  726. "required for hyperscan, disable it");
  727. cfg->disable_hyperscan = TRUE;
  728. }
  729. }
  730. #endif
  731. rspamd_regexp_library_init(cfg);
  732. rspamd_multipattern_library_init(cfg->hs_cache_dir);
  733. if (opts & RSPAMD_CONFIG_INIT_URL) {
  734. if (cfg->tld_file == nullptr) {
  735. /* Try to guess tld file */
  736. auto fpath = fmt::format("{0}{1}{2}", RSPAMD_SHAREDIR,
  737. G_DIR_SEPARATOR, "effective_tld_names.dat");
  738. if (access(fpath.c_str(), R_OK) != -1) {
  739. msg_debug_config("url_tld option is not specified but %s is available,"
  740. " therefore this file is assumed as TLD file for URL"
  741. " extraction",
  742. fpath.c_str());
  743. cfg->tld_file = rspamd_mempool_strdup(cfg->cfg_pool, fpath.c_str());
  744. }
  745. else {
  746. if (opts & RSPAMD_CONFIG_INIT_VALIDATE) {
  747. msg_err_config("no url_tld option has been specified");
  748. ret = FALSE;
  749. }
  750. }
  751. }
  752. else {
  753. if (access(cfg->tld_file, R_OK) == -1) {
  754. if (opts & RSPAMD_CONFIG_INIT_VALIDATE) {
  755. ret = FALSE;
  756. msg_err_config("cannot access tld file %s: %s", cfg->tld_file,
  757. strerror(errno));
  758. }
  759. else {
  760. msg_debug_config("cannot access tld file %s: %s", cfg->tld_file,
  761. strerror(errno));
  762. cfg->tld_file = nullptr;
  763. }
  764. }
  765. }
  766. if (opts & RSPAMD_CONFIG_INIT_NO_TLD) {
  767. rspamd_url_init(nullptr);
  768. }
  769. else {
  770. rspamd_url_init(cfg->tld_file);
  771. }
  772. rspamd_mempool_add_destructor(cfg->cfg_pool, rspamd_urls_config_dtor,
  773. nullptr);
  774. }
  775. init_dynamic_config(cfg);
  776. /* Insert classifiers symbols */
  777. rspamd_config_insert_classify_symbols(cfg);
  778. /* Parse format string that we have */
  779. if (!rspamd_config_parse_log_format(cfg)) {
  780. msg_err_config("cannot parse log format, task logging will not be available");
  781. if (opts & RSPAMD_CONFIG_INIT_VALIDATE) {
  782. ret = FALSE;
  783. }
  784. }
  785. if (opts & RSPAMD_CONFIG_INIT_SYMCACHE) {
  786. /* Init config cache */
  787. ret = rspamd_symcache_init(cfg->cache) && ret;
  788. /* Init re cache */
  789. rspamd_re_cache_init(cfg->re_cache, cfg);
  790. /* Try load Hypersan */
  791. auto hs_ret = rspamd_re_cache_load_hyperscan(cfg->re_cache,
  792. cfg->hs_cache_dir ? cfg->hs_cache_dir : RSPAMD_DBDIR "/",
  793. true);
  794. if (hs_ret == RSPAMD_HYPERSCAN_LOAD_ERROR) {
  795. msg_debug_config("cannot load hyperscan database, disable it");
  796. }
  797. }
  798. if (opts & RSPAMD_CONFIG_INIT_LIBS) {
  799. /* Config other libraries */
  800. ret = rspamd_config_libs(cfg->libs_ctx, cfg) && ret;
  801. if (!ret) {
  802. msg_err_config("cannot configure libraries, fatal error");
  803. return FALSE;
  804. }
  805. }
  806. /* Validate cache */
  807. if (opts & RSPAMD_CONFIG_INIT_VALIDATE) {
  808. /* Check for actions sanity */
  809. auto seen_controller = FALSE;
  810. auto *cur = cfg->workers;
  811. while (cur) {
  812. auto *wcf = (struct rspamd_worker_conf *) cur->data;
  813. if (wcf->type == g_quark_from_static_string("controller")) {
  814. seen_controller = TRUE;
  815. break;
  816. }
  817. cur = g_list_next(cur);
  818. }
  819. if (!seen_controller) {
  820. msg_warn_config("controller worker is unconfigured: learning,"
  821. " periodic scripts, maps watching and many other"
  822. " Rspamd features will be broken");
  823. }
  824. ret = rspamd_symcache_validate(cfg->cache, cfg, FALSE) && ret;
  825. }
  826. if (opts & RSPAMD_CONFIG_INIT_POST_LOAD_LUA) {
  827. rspamd_lua_run_config_post_init(RSPAMD_LUA_CFG_STATE(cfg), cfg);
  828. }
  829. if (opts & RSPAMD_CONFIG_INIT_PRELOAD_MAPS) {
  830. rspamd_map_preload(cfg);
  831. }
  832. return ret;
  833. }
  834. struct rspamd_classifier_config *
  835. rspamd_config_new_classifier(struct rspamd_config *cfg,
  836. struct rspamd_classifier_config *c)
  837. {
  838. if (c == nullptr) {
  839. c =
  840. rspamd_mempool_alloc0_type(cfg->cfg_pool,
  841. struct rspamd_classifier_config);
  842. c->min_prob_strength = 0.05;
  843. c->min_token_hits = 2;
  844. }
  845. if (c->labels == nullptr) {
  846. c->labels = g_hash_table_new_full(rspamd_str_hash,
  847. rspamd_str_equal,
  848. nullptr,
  849. (GDestroyNotify) g_list_free);
  850. rspamd_mempool_add_destructor(cfg->cfg_pool,
  851. (rspamd_mempool_destruct_t) g_hash_table_destroy,
  852. c->labels);
  853. }
  854. return c;
  855. }
  856. struct rspamd_statfile_config *
  857. rspamd_config_new_statfile(struct rspamd_config *cfg,
  858. struct rspamd_statfile_config *c)
  859. {
  860. if (c == nullptr) {
  861. c =
  862. rspamd_mempool_alloc0_type(cfg->cfg_pool, struct rspamd_statfile_config);
  863. }
  864. return c;
  865. }
  866. void rspamd_config_init_metric(struct rspamd_config *cfg)
  867. {
  868. cfg->grow_factor = 1.0;
  869. cfg->symbols = g_hash_table_new(rspamd_str_hash, rspamd_str_equal);
  870. cfg->groups = g_hash_table_new(rspamd_strcase_hash, rspamd_strcase_equal);
  871. cfg->subject = SPAM_SUBJECT;
  872. rspamd_mempool_add_destructor(cfg->cfg_pool,
  873. (rspamd_mempool_destruct_t) g_hash_table_unref,
  874. cfg->symbols);
  875. rspamd_mempool_add_destructor(cfg->cfg_pool,
  876. (rspamd_mempool_destruct_t) g_hash_table_unref,
  877. cfg->groups);
  878. }
  879. struct rspamd_symbols_group *
  880. rspamd_config_new_group(struct rspamd_config *cfg, const gchar *name)
  881. {
  882. struct rspamd_symbols_group *gr;
  883. gr = rspamd_mempool_alloc0_type(cfg->cfg_pool, struct rspamd_symbols_group);
  884. gr->symbols = g_hash_table_new(rspamd_strcase_hash,
  885. rspamd_strcase_equal);
  886. rspamd_mempool_add_destructor(cfg->cfg_pool,
  887. (rspamd_mempool_destruct_t) g_hash_table_unref, gr->symbols);
  888. gr->name = rspamd_mempool_strdup(cfg->cfg_pool, name);
  889. if (strcmp(gr->name, "ungrouped") == 0) {
  890. gr->flags |= RSPAMD_SYMBOL_GROUP_UNGROUPED;
  891. }
  892. g_hash_table_insert(cfg->groups, gr->name, gr);
  893. return gr;
  894. }
  895. static void
  896. rspamd_worker_conf_dtor(struct rspamd_worker_conf *wcf)
  897. {
  898. if (wcf) {
  899. ucl_object_unref(wcf->options);
  900. g_queue_free(wcf->active_workers);
  901. g_hash_table_unref(wcf->params);
  902. g_free(wcf);
  903. }
  904. }
  905. static void
  906. rspamd_worker_conf_cfg_fin(gpointer d)
  907. {
  908. auto *wcf = (struct rspamd_worker_conf *) d;
  909. REF_RELEASE(wcf);
  910. }
  911. struct rspamd_worker_conf *
  912. rspamd_config_new_worker(struct rspamd_config *cfg,
  913. struct rspamd_worker_conf *c)
  914. {
  915. if (c == nullptr) {
  916. c = g_new0(struct rspamd_worker_conf, 1);
  917. c->params = g_hash_table_new(rspamd_str_hash, rspamd_str_equal);
  918. c->active_workers = g_queue_new();
  919. #ifdef HAVE_SC_NPROCESSORS_ONLN
  920. auto nproc = sysconf(_SC_NPROCESSORS_ONLN);
  921. c->count = MIN(DEFAULT_MAX_WORKERS, MAX(1, nproc - 2));
  922. #else
  923. c->count = DEFAULT_MAX_WORKERS;
  924. #endif
  925. c->rlimit_nofile = 0;
  926. c->rlimit_maxcore = 0;
  927. c->enabled = TRUE;
  928. REF_INIT_RETAIN(c, rspamd_worker_conf_dtor);
  929. rspamd_mempool_add_destructor(cfg->cfg_pool,
  930. rspamd_worker_conf_cfg_fin, c);
  931. }
  932. return c;
  933. }
  934. static bool
  935. rspamd_include_map_handler(const guchar *data, gsize len,
  936. const ucl_object_t *args, void *ud)
  937. {
  938. auto *cfg = (struct rspamd_config *) ud;
  939. auto ftok = rspamd_ftok_t{.len = len + 1, .begin = (char *) data};
  940. auto *map_line = rspamd_mempool_ftokdup(cfg->cfg_pool, &ftok);
  941. auto *cbdata = new rspamd_ucl_map_cbdata{cfg};
  942. auto **pcbdata = new rspamd_ucl_map_cbdata *(cbdata);
  943. return rspamd_map_add(cfg,
  944. map_line,
  945. "ucl include",
  946. rspamd_ucl_read_cb,
  947. rspamd_ucl_fin_cb,
  948. rspamd_ucl_dtor_cb,
  949. (void **) pcbdata,
  950. nullptr, RSPAMD_MAP_DEFAULT) != nullptr;
  951. }
  952. /*
  953. * Variables:
  954. * $CONFDIR - configuration directory
  955. * $LOCAL_CONFDIR - local configuration directory
  956. * $RUNDIR - local states directory
  957. * $DBDIR - databases dir
  958. * $LOGDIR - logs dir
  959. * $PLUGINSDIR - plugins dir
  960. * $PREFIX - installation prefix
  961. * $VERSION - rspamd version
  962. */
  963. #define RSPAMD_CONFDIR_MACRO "CONFDIR"
  964. #define RSPAMD_LOCAL_CONFDIR_MACRO "LOCAL_CONFDIR"
  965. #define RSPAMD_RUNDIR_MACRO "RUNDIR"
  966. #define RSPAMD_DBDIR_MACRO "DBDIR"
  967. #define RSPAMD_LOGDIR_MACRO "LOGDIR"
  968. #define RSPAMD_PLUGINSDIR_MACRO "PLUGINSDIR"
  969. #define RSPAMD_SHAREDIR_MACRO "SHAREDIR"
  970. #define RSPAMD_RULESDIR_MACRO "RULESDIR"
  971. #define RSPAMD_WWWDIR_MACRO "WWWDIR"
  972. #define RSPAMD_PREFIX_MACRO "PREFIX"
  973. #define RSPAMD_VERSION_MACRO "VERSION"
  974. #define RSPAMD_VERSION_MAJOR_MACRO "VERSION_MAJOR"
  975. #define RSPAMD_VERSION_MINOR_MACRO "VERSION_MINOR"
  976. #define RSPAMD_BRANCH_VERSION_MACRO "BRANCH_VERSION"
  977. #define RSPAMD_HOSTNAME_MACRO "HOSTNAME"
  978. void rspamd_ucl_add_conf_variables(struct ucl_parser *parser, GHashTable *vars)
  979. {
  980. GHashTableIter it;
  981. gpointer k, v;
  982. ucl_parser_register_variable(parser,
  983. RSPAMD_CONFDIR_MACRO,
  984. RSPAMD_CONFDIR);
  985. ucl_parser_register_variable(parser,
  986. RSPAMD_LOCAL_CONFDIR_MACRO,
  987. RSPAMD_LOCAL_CONFDIR);
  988. ucl_parser_register_variable(parser, RSPAMD_RUNDIR_MACRO,
  989. RSPAMD_RUNDIR);
  990. ucl_parser_register_variable(parser, RSPAMD_DBDIR_MACRO,
  991. RSPAMD_DBDIR);
  992. ucl_parser_register_variable(parser, RSPAMD_LOGDIR_MACRO,
  993. RSPAMD_LOGDIR);
  994. ucl_parser_register_variable(parser,
  995. RSPAMD_PLUGINSDIR_MACRO,
  996. RSPAMD_PLUGINSDIR);
  997. ucl_parser_register_variable(parser,
  998. RSPAMD_SHAREDIR_MACRO,
  999. RSPAMD_SHAREDIR);
  1000. ucl_parser_register_variable(parser,
  1001. RSPAMD_RULESDIR_MACRO,
  1002. RSPAMD_RULESDIR);
  1003. ucl_parser_register_variable(parser, RSPAMD_WWWDIR_MACRO,
  1004. RSPAMD_WWWDIR);
  1005. ucl_parser_register_variable(parser, RSPAMD_PREFIX_MACRO,
  1006. RSPAMD_PREFIX);
  1007. ucl_parser_register_variable(parser, RSPAMD_VERSION_MACRO, RVERSION);
  1008. ucl_parser_register_variable(parser, RSPAMD_VERSION_MAJOR_MACRO,
  1009. RSPAMD_VERSION_MAJOR);
  1010. ucl_parser_register_variable(parser, RSPAMD_VERSION_MINOR_MACRO,
  1011. RSPAMD_VERSION_MINOR);
  1012. ucl_parser_register_variable(parser, RSPAMD_BRANCH_VERSION_MACRO,
  1013. RSPAMD_VERSION_BRANCH);
  1014. auto hostlen = sysconf(_SC_HOST_NAME_MAX);
  1015. if (hostlen <= 0) {
  1016. hostlen = 256;
  1017. }
  1018. else {
  1019. hostlen++;
  1020. }
  1021. auto hostbuf = std::string{};
  1022. hostbuf.resize(hostlen);
  1023. if (gethostname(hostbuf.data(), hostlen) != 0) {
  1024. hostbuf = "unknown";
  1025. }
  1026. /* UCL copies variables, so it is safe to pass an ephemeral buffer here */
  1027. ucl_parser_register_variable(parser, RSPAMD_HOSTNAME_MACRO,
  1028. hostbuf.c_str());
  1029. if (vars != nullptr) {
  1030. g_hash_table_iter_init(&it, vars);
  1031. while (g_hash_table_iter_next(&it, &k, &v)) {
  1032. ucl_parser_register_variable(parser, (const char *) k, (const char *) v);
  1033. }
  1034. }
  1035. }
  1036. void rspamd_ucl_add_conf_macros(struct ucl_parser *parser,
  1037. struct rspamd_config *cfg)
  1038. {
  1039. ucl_parser_register_macro(parser,
  1040. "include_map",
  1041. rspamd_include_map_handler,
  1042. cfg);
  1043. }
  1044. static void
  1045. symbols_classifiers_callback(gpointer key, gpointer value, gpointer ud)
  1046. {
  1047. auto *cfg = (struct rspamd_config *) ud;
  1048. /* Actually, statistics should act like any ordinary symbol */
  1049. rspamd_symcache_add_symbol(cfg->cache, (const char *) key, 0, nullptr, nullptr,
  1050. SYMBOL_TYPE_CLASSIFIER | SYMBOL_TYPE_NOSTAT, -1);
  1051. }
  1052. void rspamd_config_insert_classify_symbols(struct rspamd_config *cfg)
  1053. {
  1054. g_hash_table_foreach(cfg->classifiers_symbols,
  1055. symbols_classifiers_callback,
  1056. cfg);
  1057. }
  1058. struct rspamd_classifier_config *
  1059. rspamd_config_find_classifier(struct rspamd_config *cfg, const gchar *name)
  1060. {
  1061. if (name == nullptr) {
  1062. return nullptr;
  1063. }
  1064. auto *cur = cfg->classifiers;
  1065. while (cur) {
  1066. auto *cf = (struct rspamd_classifier_config *) cur->data;
  1067. if (g_ascii_strcasecmp(cf->name, name) == 0) {
  1068. return cf;
  1069. }
  1070. cur = g_list_next(cur);
  1071. }
  1072. return nullptr;
  1073. }
  1074. gboolean
  1075. rspamd_config_check_statfiles(struct rspamd_classifier_config *cf)
  1076. {
  1077. gboolean has_other = FALSE, res = FALSE, cur_class = FALSE;
  1078. /* First check classes directly */
  1079. auto *cur = cf->statfiles;
  1080. while (cur) {
  1081. auto *st = (struct rspamd_statfile_config *) cur->data;
  1082. if (!has_other) {
  1083. cur_class = st->is_spam;
  1084. has_other = TRUE;
  1085. }
  1086. else {
  1087. if (cur_class != st->is_spam) {
  1088. return TRUE;
  1089. }
  1090. }
  1091. cur = g_list_next(cur);
  1092. }
  1093. if (!has_other) {
  1094. /* We have only one statfile */
  1095. return FALSE;
  1096. }
  1097. /* We have not detected any statfile that has different class, so turn on heuristic based on symbol's name */
  1098. has_other = FALSE;
  1099. cur = cf->statfiles;
  1100. while (cur) {
  1101. auto *st = (struct rspamd_statfile_config *) cur->data;
  1102. if (rspamd_substring_search_caseless(st->symbol,
  1103. strlen(st->symbol), "spam", 4) != -1) {
  1104. st->is_spam = TRUE;
  1105. }
  1106. else if (rspamd_substring_search_caseless(st->symbol,
  1107. strlen(st->symbol), "ham", 3) != -1) {
  1108. st->is_spam = FALSE;
  1109. }
  1110. if (!has_other) {
  1111. cur_class = st->is_spam;
  1112. has_other = TRUE;
  1113. }
  1114. else {
  1115. if (cur_class != st->is_spam) {
  1116. res = TRUE;
  1117. }
  1118. }
  1119. cur = g_list_next(cur);
  1120. }
  1121. return res;
  1122. }
  1123. static gchar *
  1124. rspamd_ucl_read_cb(gchar *chunk,
  1125. gint len,
  1126. struct map_cb_data *data,
  1127. gboolean final)
  1128. {
  1129. auto *cbdata = (struct rspamd_ucl_map_cbdata *) data->cur_data;
  1130. auto *prev = (struct rspamd_ucl_map_cbdata *) data->prev_data;
  1131. if (cbdata == nullptr) {
  1132. cbdata = new rspamd_ucl_map_cbdata{prev->cfg};
  1133. data->cur_data = cbdata;
  1134. }
  1135. cbdata->buf.append(chunk, len);
  1136. /* Say not to copy any part of this buffer */
  1137. return nullptr;
  1138. }
  1139. static void
  1140. rspamd_ucl_fin_cb(struct map_cb_data *data, void **target)
  1141. {
  1142. auto *cbdata = (struct rspamd_ucl_map_cbdata *) data->cur_data;
  1143. auto *prev = (struct rspamd_ucl_map_cbdata *) data->prev_data;
  1144. auto *cfg = data->map->cfg;
  1145. if (cbdata == nullptr) {
  1146. msg_err_config("map fin error: new data is nullptr");
  1147. return;
  1148. }
  1149. /* New data available */
  1150. auto *parser = ucl_parser_new(0);
  1151. if (!ucl_parser_add_chunk(parser, (unsigned char *) cbdata->buf.data(),
  1152. cbdata->buf.size())) {
  1153. msg_err_config("cannot parse map %s: %s",
  1154. data->map->name,
  1155. ucl_parser_get_error(parser));
  1156. ucl_parser_free(parser);
  1157. }
  1158. else {
  1159. auto *obj = ucl_parser_get_object(parser);
  1160. ucl_object_iter_t it = nullptr;
  1161. for (auto *cur = ucl_object_iterate(obj, &it, true); cur != nullptr; cur = ucl_object_iterate(obj, &it, true)) {
  1162. ucl_object_replace_key(cbdata->cfg->cfg_ucl_obj, (ucl_object_t *) cur,
  1163. cur->key, cur->keylen, false);
  1164. }
  1165. ucl_parser_free(parser);
  1166. ucl_object_unref(obj);
  1167. }
  1168. if (target) {
  1169. *target = data->cur_data;
  1170. }
  1171. delete prev;
  1172. }
  1173. static void
  1174. rspamd_ucl_dtor_cb(struct map_cb_data *data)
  1175. {
  1176. auto *cbdata = (struct rspamd_ucl_map_cbdata *) data->cur_data;
  1177. delete cbdata;
  1178. }
  1179. gboolean
  1180. rspamd_check_module(struct rspamd_config *cfg, module_t *mod)
  1181. {
  1182. gboolean ret = TRUE;
  1183. if (mod != nullptr) {
  1184. if (mod->module_version != RSPAMD_CUR_MODULE_VERSION) {
  1185. msg_err_config("module %s has incorrect version %xd (%xd expected)",
  1186. mod->name, (gint) mod->module_version, RSPAMD_CUR_MODULE_VERSION);
  1187. ret = FALSE;
  1188. }
  1189. if (ret && mod->rspamd_version != RSPAMD_VERSION_NUM) {
  1190. msg_err_config("module %s has incorrect rspamd version %xL (%xL expected)",
  1191. mod->name, mod->rspamd_version, RSPAMD_VERSION_NUM);
  1192. ret = FALSE;
  1193. }
  1194. if (ret && strcmp(mod->rspamd_features, RSPAMD_FEATURES) != 0) {
  1195. msg_err_config("module %s has incorrect rspamd features '%s' ('%s' expected)",
  1196. mod->name, mod->rspamd_features, RSPAMD_FEATURES);
  1197. ret = FALSE;
  1198. }
  1199. }
  1200. else {
  1201. ret = FALSE;
  1202. }
  1203. return ret;
  1204. }
  1205. gboolean
  1206. rspamd_check_worker(struct rspamd_config *cfg, worker_t *wrk)
  1207. {
  1208. gboolean ret = TRUE;
  1209. if (wrk != nullptr) {
  1210. if (wrk->worker_version != RSPAMD_CUR_WORKER_VERSION) {
  1211. msg_err_config("worker %s has incorrect version %xd (%xd expected)",
  1212. wrk->name, wrk->worker_version, RSPAMD_CUR_WORKER_VERSION);
  1213. ret = FALSE;
  1214. }
  1215. if (ret && wrk->rspamd_version != RSPAMD_VERSION_NUM) {
  1216. msg_err_config("worker %s has incorrect rspamd version %xL (%xL expected)",
  1217. wrk->name, wrk->rspamd_version, RSPAMD_VERSION_NUM);
  1218. ret = FALSE;
  1219. }
  1220. if (ret && strcmp(wrk->rspamd_features, RSPAMD_FEATURES) != 0) {
  1221. msg_err_config("worker %s has incorrect rspamd features '%s' ('%s' expected)",
  1222. wrk->name, wrk->rspamd_features, RSPAMD_FEATURES);
  1223. ret = FALSE;
  1224. }
  1225. }
  1226. else {
  1227. ret = FALSE;
  1228. }
  1229. return ret;
  1230. }
  1231. gboolean
  1232. rspamd_init_filters(struct rspamd_config *cfg, bool reconfig, bool strict)
  1233. {
  1234. GList *cur;
  1235. module_t *mod, **pmod;
  1236. guint i = 0;
  1237. struct module_ctx *mod_ctx, *cur_ctx;
  1238. gboolean ret = TRUE;
  1239. /* Init all compiled modules */
  1240. for (pmod = cfg->compiled_modules; pmod != nullptr && *pmod != nullptr; pmod++) {
  1241. mod = *pmod;
  1242. if (rspamd_check_module(cfg, mod)) {
  1243. if (mod->module_init_func(cfg, &mod_ctx) == 0) {
  1244. g_assert(mod_ctx != nullptr);
  1245. g_ptr_array_add(cfg->c_modules, mod_ctx);
  1246. mod_ctx->mod = mod;
  1247. mod->ctx_offset = i++;
  1248. }
  1249. }
  1250. }
  1251. /* Now check what's enabled */
  1252. cur = g_list_first(cfg->filters);
  1253. while (cur) {
  1254. /* Perform modules configuring */
  1255. mod_ctx = nullptr;
  1256. PTR_ARRAY_FOREACH(cfg->c_modules, i, cur_ctx)
  1257. {
  1258. if (g_ascii_strcasecmp(cur_ctx->mod->name,
  1259. (const gchar *) cur->data) == 0) {
  1260. mod_ctx = cur_ctx;
  1261. break;
  1262. }
  1263. }
  1264. if (mod_ctx) {
  1265. mod = mod_ctx->mod;
  1266. mod_ctx->enabled = rspamd_config_is_module_enabled(cfg, mod->name);
  1267. if (reconfig) {
  1268. if (!mod->module_reconfig_func(cfg)) {
  1269. msg_err_config("reconfig of %s failed!", mod->name);
  1270. }
  1271. else {
  1272. msg_info_config("reconfig of %s", mod->name);
  1273. }
  1274. }
  1275. else {
  1276. if (!mod->module_config_func(cfg, strict)) {
  1277. msg_err_config("config of %s failed", mod->name);
  1278. ret = FALSE;
  1279. if (strict) {
  1280. return FALSE;
  1281. }
  1282. }
  1283. }
  1284. }
  1285. if (mod_ctx == nullptr) {
  1286. msg_warn_config("requested unknown module %s", cur->data);
  1287. }
  1288. cur = g_list_next(cur);
  1289. }
  1290. ret = rspamd_init_lua_filters(cfg, 0, strict) && ret;
  1291. return ret;
  1292. }
  1293. static void
  1294. rspamd_config_new_symbol(struct rspamd_config *cfg, const gchar *symbol,
  1295. gdouble score, const gchar *description, const gchar *group,
  1296. guint flags, guint priority, gint nshots)
  1297. {
  1298. struct rspamd_symbols_group *sym_group;
  1299. struct rspamd_symbol *sym_def;
  1300. double *score_ptr;
  1301. sym_def =
  1302. rspamd_mempool_alloc0_type(cfg->cfg_pool, struct rspamd_symbol);
  1303. score_ptr = rspamd_mempool_alloc_type(cfg->cfg_pool, double);
  1304. if (isnan(score)) {
  1305. /* In fact, it could be defined later */
  1306. msg_debug_config("score is not defined for symbol %s, set it to zero",
  1307. symbol);
  1308. score = 0.0;
  1309. /* Also set priority to 0 to allow override by anything */
  1310. sym_def->priority = 0;
  1311. flags |= RSPAMD_SYMBOL_FLAG_UNSCORED;
  1312. }
  1313. else {
  1314. sym_def->priority = priority;
  1315. }
  1316. *score_ptr = score;
  1317. sym_def->score = score;
  1318. sym_def->weight_ptr = score_ptr;
  1319. sym_def->name = rspamd_mempool_strdup(cfg->cfg_pool, symbol);
  1320. sym_def->flags = flags;
  1321. sym_def->nshots = nshots != 0 ? nshots : cfg->default_max_shots;
  1322. sym_def->groups = g_ptr_array_sized_new(1);
  1323. rspamd_mempool_add_destructor(cfg->cfg_pool, rspamd_ptr_array_free_hard,
  1324. sym_def->groups);
  1325. if (description) {
  1326. sym_def->description = rspamd_mempool_strdup(cfg->cfg_pool, description);
  1327. }
  1328. msg_debug_config("registered symbol %s with weight %.2f in and group %s",
  1329. sym_def->name, score, group);
  1330. g_hash_table_insert(cfg->symbols, sym_def->name, sym_def);
  1331. /* Search for symbol group */
  1332. if (group == nullptr) {
  1333. group = "ungrouped";
  1334. sym_def->flags |= RSPAMD_SYMBOL_FLAG_UNGROUPED;
  1335. }
  1336. else {
  1337. if (strcmp(group, "ungrouped") == 0) {
  1338. sym_def->flags |= RSPAMD_SYMBOL_FLAG_UNGROUPED;
  1339. }
  1340. }
  1341. sym_group = reinterpret_cast<rspamd_symbols_group *>(g_hash_table_lookup(cfg->groups, group));
  1342. if (sym_group == nullptr) {
  1343. /* Create new group */
  1344. sym_group = rspamd_config_new_group(cfg, group);
  1345. }
  1346. sym_def->gr = sym_group;
  1347. g_hash_table_insert(sym_group->symbols, sym_def->name, sym_def);
  1348. if (!(sym_def->flags & RSPAMD_SYMBOL_FLAG_UNGROUPED)) {
  1349. g_ptr_array_add(sym_def->groups, sym_group);
  1350. }
  1351. }
  1352. gboolean
  1353. rspamd_config_add_symbol(struct rspamd_config *cfg,
  1354. const gchar *symbol,
  1355. gdouble score,
  1356. const gchar *description,
  1357. const gchar *group,
  1358. guint flags,
  1359. guint priority,
  1360. gint nshots)
  1361. {
  1362. struct rspamd_symbol *sym_def;
  1363. struct rspamd_symbols_group *sym_group;
  1364. guint i;
  1365. g_assert(cfg != nullptr);
  1366. g_assert(symbol != nullptr);
  1367. sym_def = reinterpret_cast<rspamd_symbol *>(g_hash_table_lookup(cfg->symbols, symbol));
  1368. if (sym_def != nullptr) {
  1369. if (group != nullptr) {
  1370. gboolean has_group = FALSE;
  1371. PTR_ARRAY_FOREACH(sym_def->groups, i, sym_group)
  1372. {
  1373. if (g_ascii_strcasecmp(sym_group->name, group) == 0) {
  1374. /* Group is already here */
  1375. has_group = TRUE;
  1376. break;
  1377. }
  1378. }
  1379. if (!has_group) {
  1380. /* Non-empty group has a priority over non-grouped one */
  1381. sym_group = reinterpret_cast<rspamd_symbols_group *>(g_hash_table_lookup(cfg->groups, group));
  1382. if (sym_group == nullptr) {
  1383. /* Create new group */
  1384. sym_group = rspamd_config_new_group(cfg, group);
  1385. }
  1386. if ((!sym_def->gr) || (sym_def->flags & RSPAMD_SYMBOL_FLAG_UNGROUPED)) {
  1387. sym_def->gr = sym_group;
  1388. sym_def->flags &= ~RSPAMD_SYMBOL_FLAG_UNGROUPED;
  1389. }
  1390. g_hash_table_insert(sym_group->symbols, sym_def->name, sym_def);
  1391. sym_def->flags &= ~(RSPAMD_SYMBOL_FLAG_UNGROUPED);
  1392. g_ptr_array_add(sym_def->groups, sym_group);
  1393. }
  1394. }
  1395. if (sym_def->priority > priority &&
  1396. (isnan(score) || !(sym_def->flags & RSPAMD_SYMBOL_FLAG_UNSCORED))) {
  1397. msg_debug_config("symbol %s has been already registered with "
  1398. "priority %ud, do not override (new priority: %ud)",
  1399. symbol,
  1400. sym_def->priority,
  1401. priority);
  1402. /* But we can still add description */
  1403. if (!sym_def->description && description) {
  1404. sym_def->description = rspamd_mempool_strdup(cfg->cfg_pool,
  1405. description);
  1406. }
  1407. /* Or nshots in case of non-default setting */
  1408. if (nshots != 0 && sym_def->nshots == cfg->default_max_shots) {
  1409. sym_def->nshots = nshots;
  1410. }
  1411. return FALSE;
  1412. }
  1413. else {
  1414. if (!isnan(score)) {
  1415. msg_debug_config("symbol %s has been already registered with "
  1416. "priority %ud, override it with new priority: %ud, "
  1417. "old score: %.2f, new score: %.2f",
  1418. symbol,
  1419. sym_def->priority,
  1420. priority,
  1421. sym_def->score,
  1422. score);
  1423. *sym_def->weight_ptr = score;
  1424. sym_def->score = score;
  1425. sym_def->priority = priority;
  1426. sym_def->flags &= ~RSPAMD_SYMBOL_FLAG_UNSCORED;
  1427. }
  1428. sym_def->flags = flags;
  1429. if (nshots != 0) {
  1430. sym_def->nshots = nshots;
  1431. }
  1432. else {
  1433. /* Do not reset unless we have exactly lower priority */
  1434. if (sym_def->priority < priority) {
  1435. sym_def->nshots = cfg->default_max_shots;
  1436. }
  1437. }
  1438. if (description) {
  1439. sym_def->description = rspamd_mempool_strdup(cfg->cfg_pool,
  1440. description);
  1441. }
  1442. /* We also check group information in this case */
  1443. if (group != nullptr && sym_def->gr != nullptr &&
  1444. strcmp(group, sym_def->gr->name) != 0) {
  1445. sym_group = reinterpret_cast<rspamd_symbols_group *>(g_hash_table_lookup(cfg->groups, group));
  1446. if (sym_group == nullptr) {
  1447. /* Create new group */
  1448. sym_group = rspamd_config_new_group(cfg, group);
  1449. }
  1450. if (!(sym_group->flags & RSPAMD_SYMBOL_GROUP_UNGROUPED)) {
  1451. msg_debug_config("move symbol %s from group %s to %s",
  1452. sym_def->name, sym_def->gr->name, group);
  1453. g_hash_table_remove(sym_def->gr->symbols, sym_def->name);
  1454. sym_def->gr = sym_group;
  1455. g_hash_table_insert(sym_group->symbols, sym_def->name, sym_def);
  1456. }
  1457. }
  1458. return TRUE;
  1459. }
  1460. }
  1461. /* This is called merely when we have an undefined symbol */
  1462. rspamd_config_new_symbol(cfg, symbol, score, description,
  1463. group, flags, priority, nshots);
  1464. return TRUE;
  1465. }
  1466. gboolean
  1467. rspamd_config_add_symbol_group(struct rspamd_config *cfg,
  1468. const gchar *symbol,
  1469. const gchar *group)
  1470. {
  1471. struct rspamd_symbol *sym_def;
  1472. struct rspamd_symbols_group *sym_group;
  1473. guint i;
  1474. g_assert(cfg != nullptr);
  1475. g_assert(symbol != nullptr);
  1476. g_assert(group != nullptr);
  1477. sym_def = reinterpret_cast<rspamd_symbol *>(g_hash_table_lookup(cfg->symbols, symbol));
  1478. if (sym_def != nullptr) {
  1479. gboolean has_group = FALSE;
  1480. PTR_ARRAY_FOREACH(sym_def->groups, i, sym_group)
  1481. {
  1482. if (g_ascii_strcasecmp(sym_group->name, group) == 0) {
  1483. /* Group is already here */
  1484. has_group = TRUE;
  1485. break;
  1486. }
  1487. }
  1488. if (!has_group) {
  1489. /* Non-empty group has a priority over non-grouped one */
  1490. sym_group = reinterpret_cast<rspamd_symbols_group *>(g_hash_table_lookup(cfg->groups, group));
  1491. if (sym_group == nullptr) {
  1492. /* Create new group */
  1493. sym_group = rspamd_config_new_group(cfg, group);
  1494. }
  1495. if (!sym_def->gr) {
  1496. sym_def->gr = sym_group;
  1497. }
  1498. g_hash_table_insert(sym_group->symbols, sym_def->name, sym_def);
  1499. sym_def->flags &= ~(RSPAMD_SYMBOL_FLAG_UNGROUPED);
  1500. g_ptr_array_add(sym_def->groups, sym_group);
  1501. return TRUE;
  1502. }
  1503. }
  1504. return FALSE;
  1505. }
  1506. gboolean
  1507. rspamd_config_is_enabled_from_ucl(rspamd_mempool_t *pool,
  1508. const ucl_object_t *obj)
  1509. {
  1510. const ucl_object_t *enabled;
  1511. enabled = ucl_object_lookup(obj, "enabled");
  1512. if (enabled) {
  1513. if (ucl_object_type(enabled) == UCL_BOOLEAN) {
  1514. return ucl_object_toboolean(enabled);
  1515. }
  1516. else if (ucl_object_type(enabled) == UCL_STRING) {
  1517. gint ret = rspamd_config_parse_flag(ucl_object_tostring(enabled), 0);
  1518. if (ret == 0) {
  1519. return FALSE;
  1520. }
  1521. else if (ret == -1) {
  1522. msg_info_pool_check("wrong value for the `enabled` key");
  1523. return FALSE;
  1524. }
  1525. /* Default return is TRUE here */
  1526. }
  1527. }
  1528. const ucl_object_t *disabled;
  1529. disabled = ucl_object_lookup(obj, "disabled");
  1530. if (disabled) {
  1531. if (ucl_object_type(disabled) == UCL_BOOLEAN) {
  1532. return !ucl_object_toboolean(disabled);
  1533. }
  1534. else if (ucl_object_type(disabled) == UCL_STRING) {
  1535. gint ret = rspamd_config_parse_flag(ucl_object_tostring(disabled), 0);
  1536. if (ret == 0) {
  1537. return TRUE;
  1538. }
  1539. else if (ret == -1) {
  1540. msg_info_pool_check("wrong value for the `disabled` key");
  1541. return FALSE;
  1542. }
  1543. return FALSE;
  1544. }
  1545. }
  1546. return TRUE;
  1547. }
  1548. gboolean
  1549. rspamd_config_is_module_enabled(struct rspamd_config *cfg,
  1550. const gchar *module_name)
  1551. {
  1552. gboolean is_c = FALSE, enabled;
  1553. const ucl_object_t *conf;
  1554. GList *cur;
  1555. struct rspamd_symbols_group *gr;
  1556. lua_State *L = RSPAMD_LUA_CFG_STATE(cfg);
  1557. struct module_ctx *cur_ctx;
  1558. guint i;
  1559. PTR_ARRAY_FOREACH(cfg->c_modules, i, cur_ctx)
  1560. {
  1561. if (g_ascii_strcasecmp(cur_ctx->mod->name, module_name) == 0) {
  1562. is_c = TRUE;
  1563. break;
  1564. }
  1565. }
  1566. if (g_hash_table_lookup(cfg->explicit_modules, module_name) != nullptr) {
  1567. /* Always load module */
  1568. rspamd_plugins_table_push_elt(L, "enabled", module_name);
  1569. return TRUE;
  1570. }
  1571. if (is_c) {
  1572. gboolean found = FALSE;
  1573. cur = g_list_first(cfg->filters);
  1574. while (cur) {
  1575. if (strcmp((char *) cur->data, module_name) == 0) {
  1576. found = TRUE;
  1577. break;
  1578. }
  1579. cur = g_list_next(cur);
  1580. }
  1581. if (!found) {
  1582. msg_info_config("internal module %s is disable in `filters` line",
  1583. module_name);
  1584. rspamd_plugins_table_push_elt(L,
  1585. "disabled_explicitly", module_name);
  1586. return FALSE;
  1587. }
  1588. }
  1589. conf = ucl_object_lookup(cfg->cfg_ucl_obj, module_name);
  1590. if (conf == nullptr) {
  1591. rspamd_plugins_table_push_elt(L, "disabled_unconfigured", module_name);
  1592. msg_info_config("%s module %s is enabled but has not been configured",
  1593. is_c ? "internal" : "lua", module_name);
  1594. if (!is_c) {
  1595. msg_info_config("%s disabling unconfigured lua module", module_name);
  1596. return FALSE;
  1597. }
  1598. }
  1599. else {
  1600. enabled = rspamd_config_is_enabled_from_ucl(cfg->cfg_pool, conf);
  1601. if (!enabled) {
  1602. rspamd_plugins_table_push_elt(L,
  1603. "disabled_explicitly", module_name);
  1604. msg_info_config(
  1605. "%s module %s is disabled in the configuration",
  1606. is_c ? "internal" : "lua", module_name);
  1607. return FALSE;
  1608. }
  1609. }
  1610. /* Now we check symbols group */
  1611. gr = reinterpret_cast<rspamd_symbols_group *>(g_hash_table_lookup(cfg->groups, module_name));
  1612. if (gr) {
  1613. if (gr->flags & RSPAMD_SYMBOL_GROUP_DISABLED) {
  1614. rspamd_plugins_table_push_elt(L,
  1615. "disabled_explicitly", module_name);
  1616. msg_info_config("%s module %s is disabled in the configuration as "
  1617. "its group has been disabled",
  1618. is_c ? "internal" : "lua", module_name);
  1619. return FALSE;
  1620. }
  1621. }
  1622. rspamd_plugins_table_push_elt(L, "enabled", module_name);
  1623. return TRUE;
  1624. }
  1625. static gboolean
  1626. rspamd_config_action_from_ucl(struct rspamd_config *cfg,
  1627. struct rspamd_action *act,
  1628. const ucl_object_t *obj,
  1629. guint priority)
  1630. {
  1631. auto threshold = NAN;
  1632. int flags = 0;
  1633. auto obj_type = ucl_object_type(obj);
  1634. if (obj_type == UCL_OBJECT) {
  1635. obj_type = ucl_object_type(obj);
  1636. const auto *elt = ucl_object_lookup_any(obj, "score", "threshold", nullptr);
  1637. if (elt) {
  1638. threshold = ucl_object_todouble(elt);
  1639. }
  1640. elt = ucl_object_lookup(obj, "flags");
  1641. if (elt && ucl_object_type(elt) == UCL_ARRAY) {
  1642. const ucl_object_t *cur;
  1643. ucl_object_iter_t it = nullptr;
  1644. while ((cur = ucl_object_iterate(elt, &it, true)) != nullptr) {
  1645. if (ucl_object_type(cur) == UCL_STRING) {
  1646. const gchar *fl_str = ucl_object_tostring(cur);
  1647. if (g_ascii_strcasecmp(fl_str, "no_threshold") == 0) {
  1648. flags |= RSPAMD_ACTION_NO_THRESHOLD;
  1649. }
  1650. else if (g_ascii_strcasecmp(fl_str, "threshold_only") == 0) {
  1651. flags |= RSPAMD_ACTION_THRESHOLD_ONLY;
  1652. }
  1653. else if (g_ascii_strcasecmp(fl_str, "ham") == 0) {
  1654. flags |= RSPAMD_ACTION_HAM;
  1655. }
  1656. else {
  1657. msg_warn_config("unknown action flag: %s", fl_str);
  1658. }
  1659. }
  1660. }
  1661. }
  1662. elt = ucl_object_lookup(obj, "milter");
  1663. if (elt) {
  1664. const gchar *milter_action = ucl_object_tostring(elt);
  1665. if (strcmp(milter_action, "discard") == 0) {
  1666. flags |= RSPAMD_ACTION_MILTER;
  1667. act->action_type = METRIC_ACTION_DISCARD;
  1668. }
  1669. else if (strcmp(milter_action, "quarantine") == 0) {
  1670. flags |= RSPAMD_ACTION_MILTER;
  1671. act->action_type = METRIC_ACTION_QUARANTINE;
  1672. }
  1673. else {
  1674. msg_warn_config("unknown milter action: %s", milter_action);
  1675. }
  1676. }
  1677. }
  1678. else if (obj_type == UCL_FLOAT || obj_type == UCL_INT) {
  1679. threshold = ucl_object_todouble(obj);
  1680. }
  1681. /* TODO: add lua references support */
  1682. if (isnan(threshold) && !(flags & RSPAMD_ACTION_NO_THRESHOLD)) {
  1683. msg_err_config("action %s has no threshold being set and it is not"
  1684. " a no threshold action",
  1685. act->name);
  1686. return FALSE;
  1687. }
  1688. act->threshold = threshold;
  1689. act->flags = flags;
  1690. enum rspamd_action_type std_act;
  1691. if (!(flags & RSPAMD_ACTION_MILTER)) {
  1692. if (rspamd_action_from_str(act->name, &std_act)) {
  1693. act->action_type = std_act;
  1694. }
  1695. else {
  1696. act->action_type = METRIC_ACTION_CUSTOM;
  1697. }
  1698. }
  1699. return TRUE;
  1700. }
  1701. gboolean
  1702. rspamd_config_set_action_score(struct rspamd_config *cfg,
  1703. const gchar *action_name,
  1704. const ucl_object_t *obj)
  1705. {
  1706. enum rspamd_action_type std_act;
  1707. const ucl_object_t *elt;
  1708. guint priority = ucl_object_get_priority(obj), obj_type;
  1709. g_assert(cfg != nullptr);
  1710. g_assert(action_name != nullptr);
  1711. obj_type = ucl_object_type(obj);
  1712. if (obj_type == UCL_OBJECT) {
  1713. elt = ucl_object_lookup(obj, "priority");
  1714. if (elt) {
  1715. priority = ucl_object_toint(elt);
  1716. }
  1717. }
  1718. /* Here are dragons:
  1719. * We have `canonical` name for actions, such as `soft reject` and
  1720. * configuration names for actions (used to be more convenient), such
  1721. * as `soft_reject`. Unfortunately, we must have heuristic for this
  1722. * variance of names.
  1723. */
  1724. if (rspamd_action_from_str(action_name, &std_act)) {
  1725. action_name = rspamd_action_to_str(std_act);
  1726. }
  1727. auto actions = RSPAMD_CFG_ACTIONS(cfg);
  1728. auto existing_act_it = actions->actions_by_name.find(action_name);
  1729. if (existing_act_it != actions->actions_by_name.end()) {
  1730. auto *act = existing_act_it->second.get();
  1731. /* Existing element */
  1732. if (act->priority <= priority) {
  1733. /* We can replace data */
  1734. auto old_pri = act->priority;
  1735. auto old_thr = act->threshold;
  1736. if (rspamd_config_action_from_ucl(cfg, act, obj, priority)) {
  1737. msg_info_config("action %s has been already registered with "
  1738. "priority %ud, override it with new priority: %ud, "
  1739. "old threshold: %.2f, new threshold: %.2f",
  1740. action_name,
  1741. old_pri,
  1742. priority,
  1743. old_thr,
  1744. act->threshold);
  1745. actions->sort();
  1746. }
  1747. else {
  1748. return FALSE;
  1749. }
  1750. }
  1751. else {
  1752. msg_info_config("action %s has been already registered with "
  1753. "priority %ud, do not override (new priority: %ud)",
  1754. action_name,
  1755. act->priority,
  1756. priority);
  1757. }
  1758. }
  1759. else {
  1760. /* Add new element */
  1761. auto act = std::make_shared<rspamd_action>();
  1762. act->name = rspamd_mempool_strdup(cfg->cfg_pool, action_name);
  1763. if (rspamd_config_action_from_ucl(cfg, act.get(), obj, priority)) {
  1764. actions->add_action(std::move(act));
  1765. }
  1766. else {
  1767. return FALSE;
  1768. }
  1769. }
  1770. return TRUE;
  1771. }
  1772. gboolean
  1773. rspamd_config_maybe_disable_action(struct rspamd_config *cfg,
  1774. const gchar *action_name,
  1775. guint priority)
  1776. {
  1777. auto actions = RSPAMD_CFG_ACTIONS(cfg);
  1778. auto maybe_act = rspamd::find_map(actions->actions_by_name, action_name);
  1779. if (maybe_act) {
  1780. auto *act = maybe_act.value().get().get();
  1781. if (priority >= act->priority) {
  1782. msg_info_config("disable action %s; old priority: %ud, new priority: %ud",
  1783. action_name,
  1784. act->priority,
  1785. priority);
  1786. act->threshold = NAN;
  1787. act->priority = priority;
  1788. act->flags |= RSPAMD_ACTION_NO_THRESHOLD;
  1789. return TRUE;
  1790. }
  1791. else {
  1792. msg_info_config("action %s has been already registered with "
  1793. "priority %ud, cannot disable it with new priority: %ud",
  1794. action_name,
  1795. act->priority,
  1796. priority);
  1797. }
  1798. }
  1799. return FALSE;
  1800. }
  1801. struct rspamd_action *
  1802. rspamd_config_get_action(struct rspamd_config *cfg, const gchar *name)
  1803. {
  1804. auto actions = RSPAMD_CFG_ACTIONS(cfg);
  1805. auto maybe_act = rspamd::find_map(actions->actions_by_name, name);
  1806. if (maybe_act) {
  1807. return maybe_act.value().get().get();
  1808. }
  1809. return nullptr;
  1810. }
  1811. struct rspamd_action *
  1812. rspamd_config_get_action_by_type(struct rspamd_config *cfg,
  1813. enum rspamd_action_type type)
  1814. {
  1815. for (const auto &act: RSPAMD_CFG_ACTIONS(cfg)->actions) {
  1816. if (act->action_type == type) {
  1817. return act.get();
  1818. }
  1819. }
  1820. return nullptr;
  1821. }
  1822. void rspamd_config_actions_foreach(struct rspamd_config *cfg,
  1823. void (*func)(struct rspamd_action *act, void *d),
  1824. void *data)
  1825. {
  1826. for (const auto &act: RSPAMD_CFG_ACTIONS(cfg)->actions) {
  1827. func(act.get(), data);
  1828. }
  1829. }
  1830. void rspamd_config_actions_foreach_enumerate(struct rspamd_config *cfg,
  1831. void (*func)(int idx, struct rspamd_action *act, void *d),
  1832. void *data)
  1833. {
  1834. for (const auto &[idx, act]: rspamd::enumerate(RSPAMD_CFG_ACTIONS(cfg)->actions)) {
  1835. func(idx, act.get(), data);
  1836. }
  1837. }
  1838. gsize rspamd_config_actions_size(struct rspamd_config *cfg)
  1839. {
  1840. return RSPAMD_CFG_ACTIONS(cfg)->actions.size();
  1841. }
  1842. gboolean
  1843. rspamd_config_radix_from_ucl(struct rspamd_config *cfg, const ucl_object_t *obj, const gchar *description,
  1844. struct rspamd_radix_map_helper **target, GError **err,
  1845. struct rspamd_worker *worker, const gchar *map_name)
  1846. {
  1847. ucl_type_t type;
  1848. ucl_object_iter_t it = nullptr;
  1849. const ucl_object_t *cur, *cur_elt;
  1850. const gchar *str;
  1851. /* Cleanup */
  1852. *target = nullptr;
  1853. LL_FOREACH(obj, cur_elt)
  1854. {
  1855. type = ucl_object_type(cur_elt);
  1856. switch (type) {
  1857. case UCL_STRING:
  1858. /* Either map or a list of IPs */
  1859. str = ucl_object_tostring(cur_elt);
  1860. if (rspamd_map_is_map(str)) {
  1861. if (rspamd_map_add_from_ucl(cfg, cur_elt,
  1862. description,
  1863. rspamd_radix_read,
  1864. rspamd_radix_fin,
  1865. rspamd_radix_dtor,
  1866. (void **) target,
  1867. worker, RSPAMD_MAP_DEFAULT) == nullptr) {
  1868. g_set_error(err,
  1869. g_quark_from_static_string("rspamd-config"),
  1870. EINVAL, "bad map definition %s for %s", str,
  1871. ucl_object_key(obj));
  1872. return FALSE;
  1873. }
  1874. return TRUE;
  1875. }
  1876. else {
  1877. /* Just a list */
  1878. if (!*target) {
  1879. *target = rspamd_map_helper_new_radix(
  1880. rspamd_map_add_fake(cfg, description, map_name));
  1881. }
  1882. rspamd_map_helper_insert_radix_resolve(*target, str, "");
  1883. }
  1884. break;
  1885. case UCL_OBJECT:
  1886. /* Should be a map description */
  1887. if (rspamd_map_add_from_ucl(cfg, cur_elt,
  1888. description,
  1889. rspamd_radix_read,
  1890. rspamd_radix_fin,
  1891. rspamd_radix_dtor,
  1892. (void **) target,
  1893. worker, RSPAMD_MAP_DEFAULT) == nullptr) {
  1894. g_set_error(err,
  1895. g_quark_from_static_string("rspamd-config"),
  1896. EINVAL, "bad map object for %s", ucl_object_key(obj));
  1897. return FALSE;
  1898. }
  1899. return TRUE;
  1900. break;
  1901. case UCL_ARRAY:
  1902. /* List of IP addresses */
  1903. it = ucl_object_iterate_new(cur_elt);
  1904. while ((cur = ucl_object_iterate_safe(it, true)) != nullptr) {
  1905. if (ucl_object_type(cur) == UCL_STRING) {
  1906. str = ucl_object_tostring(cur);
  1907. if (!*target) {
  1908. *target = rspamd_map_helper_new_radix(
  1909. rspamd_map_add_fake(cfg, description, map_name));
  1910. }
  1911. rspamd_map_helper_insert_radix_resolve(*target, str, "");
  1912. }
  1913. else {
  1914. g_set_error(err,
  1915. g_quark_from_static_string("rspamd-config"),
  1916. EINVAL, "bad element inside array object for %s: expected string, got: %s",
  1917. ucl_object_key(obj), ucl_object_type_to_string(ucl_object_type(cur)));
  1918. ucl_object_iterate_free(it);
  1919. return FALSE;
  1920. }
  1921. }
  1922. ucl_object_iterate_free(it);
  1923. break;
  1924. default:
  1925. g_set_error(err, g_quark_from_static_string("rspamd-config"),
  1926. EINVAL, "bad map type %s for %s",
  1927. ucl_object_type_to_string(type),
  1928. ucl_object_key(obj));
  1929. return FALSE;
  1930. }
  1931. }
  1932. /* Destroy on cfg cleanup */
  1933. rspamd_mempool_add_destructor(cfg->cfg_pool,
  1934. (rspamd_mempool_destruct_t) rspamd_map_helper_destroy_radix,
  1935. *target);
  1936. return TRUE;
  1937. }
  1938. constexpr const auto action_types = frozen::make_unordered_map<frozen::string, enum rspamd_action_type>({
  1939. {"reject", METRIC_ACTION_REJECT},
  1940. {"greylist", METRIC_ACTION_GREYLIST},
  1941. {"add header", METRIC_ACTION_ADD_HEADER},
  1942. {"add_header", METRIC_ACTION_ADD_HEADER},
  1943. {"rewrite subject", METRIC_ACTION_REWRITE_SUBJECT},
  1944. {"rewrite_subject", METRIC_ACTION_REWRITE_SUBJECT},
  1945. {"soft reject", METRIC_ACTION_SOFT_REJECT},
  1946. {"soft_reject", METRIC_ACTION_SOFT_REJECT},
  1947. {"no action", METRIC_ACTION_NOACTION},
  1948. {"no_action", METRIC_ACTION_NOACTION},
  1949. {"accept", METRIC_ACTION_NOACTION},
  1950. {"quarantine", METRIC_ACTION_QUARANTINE},
  1951. {"discard", METRIC_ACTION_DISCARD},
  1952. });
  1953. gboolean
  1954. rspamd_action_from_str(const gchar *data, enum rspamd_action_type *result)
  1955. {
  1956. auto maybe_action = rspamd::find_map(action_types, std::string_view{data});
  1957. if (maybe_action) {
  1958. *result = maybe_action.value().get();
  1959. return true;
  1960. }
  1961. else {
  1962. return false;
  1963. }
  1964. }
  1965. const gchar *
  1966. rspamd_action_to_str(enum rspamd_action_type action)
  1967. {
  1968. switch (action) {
  1969. case METRIC_ACTION_REJECT:
  1970. return "reject";
  1971. case METRIC_ACTION_SOFT_REJECT:
  1972. return "soft reject";
  1973. case METRIC_ACTION_REWRITE_SUBJECT:
  1974. return "rewrite subject";
  1975. case METRIC_ACTION_ADD_HEADER:
  1976. return "add header";
  1977. case METRIC_ACTION_GREYLIST:
  1978. return "greylist";
  1979. case METRIC_ACTION_NOACTION:
  1980. return "no action";
  1981. case METRIC_ACTION_MAX:
  1982. return "invalid max action";
  1983. case METRIC_ACTION_CUSTOM:
  1984. return "custom";
  1985. case METRIC_ACTION_DISCARD:
  1986. return "discard";
  1987. case METRIC_ACTION_QUARANTINE:
  1988. return "quarantine";
  1989. }
  1990. return "unknown action";
  1991. }
  1992. const gchar *
  1993. rspamd_action_to_str_alt(enum rspamd_action_type action)
  1994. {
  1995. switch (action) {
  1996. case METRIC_ACTION_REJECT:
  1997. return "reject";
  1998. case METRIC_ACTION_SOFT_REJECT:
  1999. return "soft_reject";
  2000. case METRIC_ACTION_REWRITE_SUBJECT:
  2001. return "rewrite_subject";
  2002. case METRIC_ACTION_ADD_HEADER:
  2003. return "add_header";
  2004. case METRIC_ACTION_GREYLIST:
  2005. return "greylist";
  2006. case METRIC_ACTION_NOACTION:
  2007. return "no action";
  2008. case METRIC_ACTION_MAX:
  2009. return "invalid max action";
  2010. case METRIC_ACTION_CUSTOM:
  2011. return "custom";
  2012. case METRIC_ACTION_DISCARD:
  2013. return "discard";
  2014. case METRIC_ACTION_QUARANTINE:
  2015. return "quarantine";
  2016. }
  2017. return "unknown action";
  2018. }
  2019. static void
  2020. rspamd_config_settings_elt_dtor(struct rspamd_config_settings_elt *e)
  2021. {
  2022. if (e->symbols_enabled) {
  2023. ucl_object_unref(e->symbols_enabled);
  2024. }
  2025. if (e->symbols_disabled) {
  2026. ucl_object_unref(e->symbols_disabled);
  2027. }
  2028. }
  2029. uint32_t
  2030. rspamd_config_name_to_id(const gchar *name, gsize namelen)
  2031. {
  2032. uint64_t h;
  2033. h = rspamd_cryptobox_fast_hash_specific(RSPAMD_CRYPTOBOX_XXHASH64,
  2034. name, namelen, 0x0);
  2035. /* Take the lower part of hash as LE number */
  2036. return ((uint32_t) GUINT64_TO_LE(h));
  2037. }
  2038. struct rspamd_config_settings_elt *
  2039. rspamd_config_find_settings_id_ref(struct rspamd_config *cfg,
  2040. uint32_t id)
  2041. {
  2042. struct rspamd_config_settings_elt *cur;
  2043. DL_FOREACH(cfg->setting_ids, cur)
  2044. {
  2045. if (cur->id == id) {
  2046. REF_RETAIN(cur);
  2047. return cur;
  2048. }
  2049. }
  2050. return nullptr;
  2051. }
  2052. struct rspamd_config_settings_elt *rspamd_config_find_settings_name_ref(
  2053. struct rspamd_config *cfg,
  2054. const gchar *name, gsize namelen)
  2055. {
  2056. uint32_t id;
  2057. id = rspamd_config_name_to_id(name, namelen);
  2058. return rspamd_config_find_settings_id_ref(cfg, id);
  2059. }
  2060. void rspamd_config_register_settings_id(struct rspamd_config *cfg,
  2061. const gchar *name,
  2062. ucl_object_t *symbols_enabled,
  2063. ucl_object_t *symbols_disabled,
  2064. enum rspamd_config_settings_policy policy)
  2065. {
  2066. struct rspamd_config_settings_elt *elt;
  2067. uint32_t id;
  2068. id = rspamd_config_name_to_id(name, strlen(name));
  2069. elt = rspamd_config_find_settings_id_ref(cfg, id);
  2070. if (elt) {
  2071. /* Need to replace */
  2072. struct rspamd_config_settings_elt *nelt;
  2073. DL_DELETE(cfg->setting_ids, elt);
  2074. nelt = rspamd_mempool_alloc0_type(cfg->cfg_pool, struct rspamd_config_settings_elt);
  2075. nelt->id = id;
  2076. nelt->name = rspamd_mempool_strdup(cfg->cfg_pool, name);
  2077. if (symbols_enabled) {
  2078. nelt->symbols_enabled = ucl_object_ref(symbols_enabled);
  2079. }
  2080. if (symbols_disabled) {
  2081. nelt->symbols_disabled = ucl_object_ref(symbols_disabled);
  2082. }
  2083. nelt->policy = policy;
  2084. REF_INIT_RETAIN(nelt, rspamd_config_settings_elt_dtor);
  2085. msg_warn_config("replace settings id %ud (%s)", id, name);
  2086. rspamd_symcache_process_settings_elt(cfg->cache, elt);
  2087. DL_APPEND(cfg->setting_ids, nelt);
  2088. /*
  2089. * Need to unref old element twice as there are two reference holders:
  2090. * 1. Config structure as we call REF_INIT_RETAIN
  2091. * 2. rspamd_config_find_settings_id_ref also increases refcount
  2092. */
  2093. REF_RELEASE(elt);
  2094. REF_RELEASE(elt);
  2095. }
  2096. else {
  2097. elt = rspamd_mempool_alloc0_type(cfg->cfg_pool, struct rspamd_config_settings_elt);
  2098. elt->id = id;
  2099. elt->name = rspamd_mempool_strdup(cfg->cfg_pool, name);
  2100. if (symbols_enabled) {
  2101. elt->symbols_enabled = ucl_object_ref(symbols_enabled);
  2102. }
  2103. if (symbols_disabled) {
  2104. elt->symbols_disabled = ucl_object_ref(symbols_disabled);
  2105. }
  2106. elt->policy = policy;
  2107. msg_info_config("register new settings id %ud (%s)", id, name);
  2108. REF_INIT_RETAIN(elt, rspamd_config_settings_elt_dtor);
  2109. rspamd_symcache_process_settings_elt(cfg->cache, elt);
  2110. DL_APPEND(cfg->setting_ids, elt);
  2111. }
  2112. }
  2113. int rspamd_config_ev_backend_get(struct rspamd_config *cfg)
  2114. {
  2115. #define AUTO_BACKEND (ev_supported_backends() & ~EVBACKEND_IOURING)
  2116. if (cfg == nullptr || cfg->events_backend == nullptr) {
  2117. return AUTO_BACKEND;
  2118. }
  2119. if (strcmp(cfg->events_backend, "auto") == 0) {
  2120. return AUTO_BACKEND;
  2121. }
  2122. else if (strcmp(cfg->events_backend, "epoll") == 0) {
  2123. if (ev_supported_backends() & EVBACKEND_EPOLL) {
  2124. return EVBACKEND_EPOLL;
  2125. }
  2126. else {
  2127. msg_warn_config("unsupported events_backend: %s; defaulting to auto",
  2128. cfg->events_backend);
  2129. return AUTO_BACKEND;
  2130. }
  2131. }
  2132. else if (strcmp(cfg->events_backend, "iouring") == 0) {
  2133. if (ev_supported_backends() & EVBACKEND_IOURING) {
  2134. return EVBACKEND_IOURING;
  2135. }
  2136. else {
  2137. msg_warn_config("unsupported events_backend: %s; defaulting to auto",
  2138. cfg->events_backend);
  2139. return AUTO_BACKEND;
  2140. }
  2141. }
  2142. else if (strcmp(cfg->events_backend, "kqueue") == 0) {
  2143. if (ev_supported_backends() & EVBACKEND_KQUEUE) {
  2144. return EVBACKEND_KQUEUE;
  2145. }
  2146. else {
  2147. msg_warn_config("unsupported events_backend: %s; defaulting to auto",
  2148. cfg->events_backend);
  2149. return AUTO_BACKEND;
  2150. }
  2151. }
  2152. else if (strcmp(cfg->events_backend, "poll") == 0) {
  2153. return EVBACKEND_POLL;
  2154. }
  2155. else if (strcmp(cfg->events_backend, "select") == 0) {
  2156. return EVBACKEND_SELECT;
  2157. }
  2158. else {
  2159. msg_warn_config("unknown events_backend: %s; defaulting to auto",
  2160. cfg->events_backend);
  2161. }
  2162. return AUTO_BACKEND;
  2163. }
  2164. const gchar *
  2165. rspamd_config_ev_backend_to_string(int ev_backend, gboolean *effective)
  2166. {
  2167. #define SET_EFFECTIVE(b) \
  2168. do { \
  2169. if ((effective) != nullptr) *(effective) = b; \
  2170. } while (0)
  2171. if ((ev_backend & EVBACKEND_ALL) == EVBACKEND_ALL) {
  2172. SET_EFFECTIVE(TRUE);
  2173. return "auto";
  2174. }
  2175. if (ev_backend & EVBACKEND_IOURING) {
  2176. SET_EFFECTIVE(TRUE);
  2177. return "epoll+io_uring";
  2178. }
  2179. if (ev_backend & EVBACKEND_LINUXAIO) {
  2180. SET_EFFECTIVE(TRUE);
  2181. return "epoll+aio";
  2182. }
  2183. if (ev_backend & EVBACKEND_IOURING) {
  2184. SET_EFFECTIVE(TRUE);
  2185. return "epoll+io_uring";
  2186. }
  2187. if (ev_backend & EVBACKEND_LINUXAIO) {
  2188. SET_EFFECTIVE(TRUE);
  2189. return "epoll+aio";
  2190. }
  2191. if (ev_backend & EVBACKEND_EPOLL) {
  2192. SET_EFFECTIVE(TRUE);
  2193. return "epoll";
  2194. }
  2195. if (ev_backend & EVBACKEND_KQUEUE) {
  2196. SET_EFFECTIVE(TRUE);
  2197. return "kqueue";
  2198. }
  2199. if (ev_backend & EVBACKEND_POLL) {
  2200. SET_EFFECTIVE(FALSE);
  2201. return "poll";
  2202. }
  2203. if (ev_backend & EVBACKEND_SELECT) {
  2204. SET_EFFECTIVE(FALSE);
  2205. return "select";
  2206. }
  2207. SET_EFFECTIVE(FALSE);
  2208. return "unknown";
  2209. #undef SET_EFFECTIVE
  2210. }
  2211. struct rspamd_external_libs_ctx *
  2212. rspamd_init_libs(void)
  2213. {
  2214. struct rlimit rlim;
  2215. struct ottery_config *ottery_cfg;
  2216. auto *ctx = g_new0(struct rspamd_external_libs_ctx, 1);
  2217. ctx->crypto_ctx = rspamd_cryptobox_init();
  2218. ottery_cfg = (struct ottery_config *) g_malloc0(ottery_get_sizeof_config());
  2219. ottery_config_init(ottery_cfg);
  2220. ctx->ottery_cfg = ottery_cfg;
  2221. rspamd_openssl_maybe_init();
  2222. /* Check if we have rdrand */
  2223. if ((ctx->crypto_ctx->cpu_config & CPUID_RDRAND) == 0) {
  2224. ottery_config_disable_entropy_sources(ottery_cfg,
  2225. OTTERY_ENTROPY_SRC_RDRAND);
  2226. }
  2227. g_assert(ottery_init(ottery_cfg) == 0);
  2228. #if OPENSSL_VERSION_NUMBER >= 0x1000104fL && OPENSSL_VERSION_NUMBER < 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
  2229. RAND_set_rand_engine(nullptr);
  2230. #endif
  2231. /* Configure utf8 library */
  2232. guint utf8_flags = 0;
  2233. if ((ctx->crypto_ctx->cpu_config & CPUID_SSE41)) {
  2234. utf8_flags |= RSPAMD_FAST_UTF8_FLAG_SSE41;
  2235. }
  2236. if ((ctx->crypto_ctx->cpu_config & CPUID_AVX2)) {
  2237. utf8_flags |= RSPAMD_FAST_UTF8_FLAG_AVX2;
  2238. }
  2239. rspamd_fast_utf8_library_init(utf8_flags);
  2240. #ifdef HAVE_LOCALE_H
  2241. if (getenv("LANG") == nullptr) {
  2242. setlocale(LC_ALL, "C");
  2243. setlocale(LC_CTYPE, "C");
  2244. setlocale(LC_MESSAGES, "C");
  2245. setlocale(LC_TIME, "C");
  2246. }
  2247. else {
  2248. /* Just set the default locale */
  2249. setlocale(LC_ALL, "");
  2250. /* But for some issues we still want C locale */
  2251. setlocale(LC_NUMERIC, "C");
  2252. }
  2253. #endif
  2254. ctx->ssl_ctx = rspamd_init_ssl_ctx();
  2255. ctx->ssl_ctx_noverify = rspamd_init_ssl_ctx_noverify();
  2256. rspamd_random_seed_fast();
  2257. /* Set stack size for pcre */
  2258. getrlimit(RLIMIT_STACK, &rlim);
  2259. rlim.rlim_cur = 100 * 1024 * 1024;
  2260. rlim.rlim_max = rlim.rlim_cur;
  2261. setrlimit(RLIMIT_STACK, &rlim);
  2262. ctx->local_addrs = rspamd_inet_library_init();
  2263. REF_INIT_RETAIN(ctx, rspamd_deinit_libs);
  2264. return ctx;
  2265. }
  2266. static struct zstd_dictionary *
  2267. rspamd_open_zstd_dictionary(const char *path)
  2268. {
  2269. struct zstd_dictionary *dict;
  2270. dict = g_new0(zstd_dictionary, 1);
  2271. dict->dict = rspamd_file_xmap(path, PROT_READ, &dict->size, TRUE);
  2272. if (dict->dict == nullptr) {
  2273. g_free(dict);
  2274. return nullptr;
  2275. }
  2276. dict->id = -1;
  2277. if (dict->id == 0) {
  2278. g_free(dict);
  2279. return nullptr;
  2280. }
  2281. return dict;
  2282. }
  2283. static void
  2284. rspamd_free_zstd_dictionary(struct zstd_dictionary *dict)
  2285. {
  2286. if (dict) {
  2287. munmap(dict->dict, dict->size);
  2288. g_free(dict);
  2289. }
  2290. }
  2291. #ifdef HAVE_OPENBLAS_SET_NUM_THREADS
  2292. extern "C" void openblas_set_num_threads(int num_threads);
  2293. #endif
  2294. #ifdef HAVE_BLI_THREAD_SET_NUM_THREADS
  2295. extern "C" void bli_thread_set_num_threads(int num_threads);
  2296. #endif
  2297. gboolean
  2298. rspamd_config_libs(struct rspamd_external_libs_ctx *ctx,
  2299. struct rspamd_config *cfg)
  2300. {
  2301. size_t r;
  2302. gboolean ret = TRUE;
  2303. g_assert(cfg != nullptr);
  2304. if (ctx != nullptr) {
  2305. if (cfg->local_addrs) {
  2306. GError *err = nullptr;
  2307. ret = rspamd_config_radix_from_ucl(cfg, cfg->local_addrs,
  2308. "Local addresses",
  2309. (struct rspamd_radix_map_helper **) ctx->local_addrs,
  2310. &err,
  2311. nullptr, "local addresses");
  2312. if (!ret) {
  2313. msg_err_config("cannot load local addresses: %e", err);
  2314. g_error_free(err);
  2315. return ret;
  2316. }
  2317. }
  2318. rspamd_free_zstd_dictionary(ctx->in_dict);
  2319. rspamd_free_zstd_dictionary(ctx->out_dict);
  2320. if (ctx->out_zstream) {
  2321. ZSTD_freeCStream((ZSTD_CCtx *) ctx->out_zstream);
  2322. ctx->out_zstream = nullptr;
  2323. }
  2324. if (ctx->in_zstream) {
  2325. ZSTD_freeDStream((ZSTD_DCtx *) ctx->in_zstream);
  2326. ctx->in_zstream = nullptr;
  2327. }
  2328. if (cfg->zstd_input_dictionary) {
  2329. ctx->in_dict = rspamd_open_zstd_dictionary(
  2330. cfg->zstd_input_dictionary);
  2331. if (ctx->in_dict == nullptr) {
  2332. msg_err_config("cannot open zstd dictionary in %s",
  2333. cfg->zstd_input_dictionary);
  2334. }
  2335. }
  2336. if (cfg->zstd_output_dictionary) {
  2337. ctx->out_dict = rspamd_open_zstd_dictionary(
  2338. cfg->zstd_output_dictionary);
  2339. if (ctx->out_dict == nullptr) {
  2340. msg_err_config("cannot open zstd dictionary in %s",
  2341. cfg->zstd_output_dictionary);
  2342. }
  2343. }
  2344. if (cfg->fips_mode) {
  2345. #ifdef HAVE_FIPS_MODE
  2346. int mode = FIPS_mode();
  2347. unsigned long err = (unsigned long) -1;
  2348. /* Toggle FIPS mode */
  2349. if (mode == 0) {
  2350. #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
  2351. if (EVP_set_default_properties(nullptr, "fips=yes") != 1) {
  2352. #else
  2353. if (FIPS_mode_set(1) != 1) {
  2354. #endif
  2355. err = ERR_get_error();
  2356. }
  2357. }
  2358. else {
  2359. msg_info_config("OpenSSL FIPS mode is already enabled");
  2360. }
  2361. if (err != (unsigned long) -1) {
  2362. #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
  2363. msg_err_config("EVP_set_default_properties failed: %s",
  2364. #else
  2365. msg_err_config("FIPS_mode_set failed: %s",
  2366. #endif
  2367. ERR_error_string(err, nullptr));
  2368. ret = FALSE;
  2369. }
  2370. else {
  2371. msg_info_config("OpenSSL FIPS mode is enabled");
  2372. }
  2373. #else
  2374. msg_warn_config("SSL FIPS mode is enabled but not supported by OpenSSL library!");
  2375. #endif
  2376. }
  2377. rspamd_ssl_ctx_config(cfg, ctx->ssl_ctx);
  2378. rspamd_ssl_ctx_config(cfg, ctx->ssl_ctx_noverify);
  2379. /* Init decompression */
  2380. ctx->in_zstream = ZSTD_createDStream();
  2381. r = ZSTD_initDStream((ZSTD_DCtx *) ctx->in_zstream);
  2382. if (ZSTD_isError(r)) {
  2383. msg_err("cannot init decompression stream: %s",
  2384. ZSTD_getErrorName(r));
  2385. ZSTD_freeDStream((ZSTD_DCtx *) ctx->in_zstream);
  2386. ctx->in_zstream = nullptr;
  2387. }
  2388. /* Init compression */
  2389. ctx->out_zstream = ZSTD_createCStream();
  2390. r = ZSTD_initCStream((ZSTD_CCtx *) ctx->out_zstream, 1);
  2391. if (ZSTD_isError(r)) {
  2392. msg_err("cannot init compression stream: %s",
  2393. ZSTD_getErrorName(r));
  2394. ZSTD_freeCStream((ZSTD_CCtx *) ctx->out_zstream);
  2395. ctx->out_zstream = nullptr;
  2396. }
  2397. #ifdef HAVE_OPENBLAS_SET_NUM_THREADS
  2398. openblas_set_num_threads(cfg->max_blas_threads);
  2399. #endif
  2400. #ifdef HAVE_BLI_THREAD_SET_NUM_THREADS
  2401. bli_thread_set_num_threads(cfg->max_blas_threads);
  2402. #endif
  2403. }
  2404. return ret;
  2405. }
  2406. gboolean
  2407. rspamd_libs_reset_decompression(struct rspamd_external_libs_ctx *ctx)
  2408. {
  2409. gsize r;
  2410. if (ctx->in_zstream == nullptr) {
  2411. return FALSE;
  2412. }
  2413. else {
  2414. r = ZSTD_DCtx_reset((ZSTD_DCtx *) ctx->in_zstream, ZSTD_reset_session_only);
  2415. if (ZSTD_isError(r)) {
  2416. msg_err("cannot init decompression stream: %s",
  2417. ZSTD_getErrorName(r));
  2418. ZSTD_freeDStream((ZSTD_DCtx *) ctx->in_zstream);
  2419. ctx->in_zstream = nullptr;
  2420. return FALSE;
  2421. }
  2422. }
  2423. return TRUE;
  2424. }
  2425. gboolean
  2426. rspamd_libs_reset_compression(struct rspamd_external_libs_ctx *ctx)
  2427. {
  2428. gsize r;
  2429. if (ctx->out_zstream == nullptr) {
  2430. return FALSE;
  2431. }
  2432. else {
  2433. /* Dictionary will be reused automatically if specified */
  2434. r = ZSTD_CCtx_reset((ZSTD_CCtx *) ctx->out_zstream, ZSTD_reset_session_only);
  2435. if (!ZSTD_isError(r)) {
  2436. r = ZSTD_CCtx_setPledgedSrcSize((ZSTD_CCtx *) ctx->out_zstream, ZSTD_CONTENTSIZE_UNKNOWN);
  2437. }
  2438. if (ZSTD_isError(r)) {
  2439. msg_err("cannot init compression stream: %s",
  2440. ZSTD_getErrorName(r));
  2441. ZSTD_freeCStream((ZSTD_CCtx *) ctx->out_zstream);
  2442. ctx->out_zstream = nullptr;
  2443. return FALSE;
  2444. }
  2445. }
  2446. return TRUE;
  2447. }
  2448. void rspamd_deinit_libs(struct rspamd_external_libs_ctx *ctx)
  2449. {
  2450. if (ctx != nullptr) {
  2451. g_free(ctx->ottery_cfg);
  2452. #ifdef HAVE_OPENSSL
  2453. EVP_cleanup();
  2454. ERR_free_strings();
  2455. rspamd_ssl_ctx_free(ctx->ssl_ctx);
  2456. rspamd_ssl_ctx_free(ctx->ssl_ctx_noverify);
  2457. #endif
  2458. rspamd_inet_library_destroy();
  2459. rspamd_free_zstd_dictionary(ctx->in_dict);
  2460. rspamd_free_zstd_dictionary(ctx->out_dict);
  2461. if (ctx->out_zstream) {
  2462. ZSTD_freeCStream((ZSTD_CCtx *) ctx->out_zstream);
  2463. }
  2464. if (ctx->in_zstream) {
  2465. ZSTD_freeDStream((ZSTD_DCtx *) ctx->in_zstream);
  2466. }
  2467. rspamd_cryptobox_deinit(ctx->crypto_ctx);
  2468. g_free(ctx);
  2469. }
  2470. }
  2471. gboolean
  2472. rspamd_ip_is_local_cfg(struct rspamd_config *cfg,
  2473. const rspamd_inet_addr_t *addr)
  2474. {
  2475. struct rspamd_radix_map_helper *local_addrs = nullptr;
  2476. if (cfg && cfg->libs_ctx) {
  2477. local_addrs = *(struct rspamd_radix_map_helper **) cfg->libs_ctx->local_addrs;
  2478. }
  2479. if (rspamd_inet_address_is_local(addr)) {
  2480. return TRUE;
  2481. }
  2482. if (local_addrs) {
  2483. if (rspamd_match_radix_map_addr(local_addrs, addr) != nullptr) {
  2484. return TRUE;
  2485. }
  2486. }
  2487. return FALSE;
  2488. }