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.

controller.c 70KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683
  1. /*-
  2. * Copyright 2016 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 "libserver/dynamic_cfg.h"
  18. #include "libutil/rrd.h"
  19. #include "libutil/map.h"
  20. #include "libstat/stat_api.h"
  21. #include "rspamd.h"
  22. #include "libserver/worker_util.h"
  23. #include "cryptobox.h"
  24. #include "ottery.h"
  25. #include "libutil/rrd.h"
  26. #include "unix-std.h"
  27. #include <math.h>
  28. /* 60 seconds for worker's IO */
  29. #define DEFAULT_WORKER_IO_TIMEOUT 60000
  30. #define DEFAULT_STATS_PATH RSPAMD_DBDIR "/stats.ucl"
  31. /* HTTP paths */
  32. #define PATH_AUTH "/auth"
  33. #define PATH_SYMBOLS "/symbols"
  34. #define PATH_ACTIONS "/actions"
  35. #define PATH_MAPS "/maps"
  36. #define PATH_GET_MAP "/getmap"
  37. #define PATH_GRAPH "/graph"
  38. #define PATH_PIE_CHART "/pie"
  39. #define PATH_HISTORY "/history"
  40. #define PATH_HISTORY_RESET "/historyreset"
  41. #define PATH_LEARN_SPAM "/learnspam"
  42. #define PATH_LEARN_HAM "/learnham"
  43. #define PATH_SAVE_ACTIONS "/saveactions"
  44. #define PATH_SAVE_SYMBOLS "/savesymbols"
  45. #define PATH_SAVE_MAP "/savemap"
  46. #define PATH_SCAN "/scan"
  47. #define PATH_CHECK "/check"
  48. #define PATH_STAT "/stat"
  49. #define PATH_STAT_RESET "/statreset"
  50. #define PATH_COUNTERS "/counters"
  51. #define msg_err_session(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
  52. session->pool->tag.tagname, session->pool->tag.uid, \
  53. G_STRFUNC, \
  54. __VA_ARGS__)
  55. #define msg_warn_session(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
  56. session->pool->tag.tagname, session->pool->tag.uid, \
  57. G_STRFUNC, \
  58. __VA_ARGS__)
  59. #define msg_info_session(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \
  60. session->pool->tag.tagname, session->pool->tag.uid, \
  61. G_STRFUNC, \
  62. __VA_ARGS__)
  63. #define msg_debug_session(...) rspamd_default_log_function (G_LOG_LEVEL_DEBUG, \
  64. session->pool->tag.tagname, session->pool->tag.uid, \
  65. G_STRFUNC, \
  66. __VA_ARGS__)
  67. #define msg_err_ctx(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
  68. "controller", ctx->cfg->cfg_pool->tag.uid, \
  69. G_STRFUNC, \
  70. __VA_ARGS__)
  71. #define msg_warn_ctx(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
  72. "controller", ctx->cfg->cfg_pool->tag.uid, \
  73. G_STRFUNC, \
  74. __VA_ARGS__)
  75. #define msg_info_ctx(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \
  76. "controller", ctx->cfg->cfg_pool->tag.uid, \
  77. G_STRFUNC, \
  78. __VA_ARGS__)
  79. #define msg_debug_ctx(...) rspamd_default_log_function (G_LOG_LEVEL_DEBUG, \
  80. "controller", ctx->cfg->cfg_pool->tag.uid, \
  81. G_STRFUNC, \
  82. __VA_ARGS__)
  83. /* Graph colors */
  84. #define COLOR_CLEAN "#58A458"
  85. #define COLOR_PROBABLE_SPAM "#D67E7E"
  86. #define COLOR_GREYLIST "#A0A0A0"
  87. #define COLOR_REJECT "#CB4B4B"
  88. #define COLOR_TOTAL "#9440ED"
  89. const struct timeval rrd_update_time = {
  90. .tv_sec = 1,
  91. .tv_usec = 0
  92. };
  93. gpointer init_controller_worker (struct rspamd_config *cfg);
  94. void start_controller_worker (struct rspamd_worker *worker);
  95. worker_t controller_worker = {
  96. "controller", /* Name */
  97. init_controller_worker, /* Init function */
  98. start_controller_worker, /* Start function */
  99. TRUE, /* Has socket */
  100. TRUE, /* Non unique */
  101. FALSE, /* Non threaded */
  102. TRUE, /* Killable */
  103. SOCK_STREAM, /* TCP socket */
  104. RSPAMD_WORKER_VER /* Version info */
  105. };
  106. /*
  107. * Worker's context
  108. */
  109. struct rspamd_controller_worker_ctx {
  110. guint32 timeout;
  111. struct timeval io_tv;
  112. /* DNS resolver */
  113. struct rspamd_dns_resolver *resolver;
  114. /* Events base */
  115. struct event_base *ev_base;
  116. /* Whether we use ssl for this server */
  117. gboolean use_ssl;
  118. /* Webui password */
  119. gchar *password;
  120. /* Privilleged password */
  121. gchar *enable_password;
  122. /* Cached versions of the passwords */
  123. rspamd_ftok_t cached_password;
  124. rspamd_ftok_t cached_enable_password;
  125. /* HTTP server */
  126. struct rspamd_http_connection_router *http;
  127. /* Server's start time */
  128. time_t start_time;
  129. /* Main server */
  130. struct rspamd_main *srv;
  131. /* Configuration */
  132. struct rspamd_config *cfg;
  133. /* SSL cert */
  134. gchar *ssl_cert;
  135. /* SSL private key */
  136. gchar *ssl_key;
  137. /* A map of secure IP */
  138. GList *secure_ip;
  139. radix_compressed_t *secure_map;
  140. /* Static files dir */
  141. gchar *static_files_dir;
  142. /* Saved statistics path */
  143. gchar *saved_stats_path;
  144. /* Custom commands registered by plugins */
  145. GHashTable *custom_commands;
  146. /* Worker */
  147. struct rspamd_worker *worker;
  148. /* Local keypair */
  149. gpointer key;
  150. struct event *rrd_event;
  151. struct rspamd_rrd_file *rrd;
  152. };
  153. static gboolean
  154. rspamd_is_encrypted_password (const gchar *password,
  155. struct rspamd_controller_pbkdf const **pbkdf)
  156. {
  157. const gchar *start, *end;
  158. gint64 id;
  159. gsize size;
  160. gboolean ret = FALSE;
  161. if (password[0] == '$') {
  162. /* Parse id */
  163. start = password + 1;
  164. end = start;
  165. size = 0;
  166. while (*end != '\0' && g_ascii_isdigit (*end)) {
  167. size++;
  168. end++;
  169. }
  170. if (size > 0) {
  171. gchar *endptr;
  172. id = strtoul (start, &endptr, 10);
  173. if ((endptr == NULL || *endptr == *end) && id == RSPAMD_PBKDF_ID_V1) {
  174. ret = TRUE;
  175. if (pbkdf != NULL) {
  176. *pbkdf = &pbkdf_list[0];
  177. }
  178. }
  179. }
  180. }
  181. return ret;
  182. }
  183. static const gchar *
  184. rspamd_encrypted_password_get_str (const gchar * password, gsize skip,
  185. gsize * length)
  186. {
  187. const gchar *str, *start, *end;
  188. gsize size;
  189. start = password + skip;
  190. end = start;
  191. size = 0;
  192. while (*end != '\0' && g_ascii_isalnum (*end)) {
  193. size++;
  194. end++;
  195. }
  196. if (size) {
  197. str = start;
  198. *length = size;
  199. }
  200. else {
  201. str = NULL;
  202. }
  203. return str;
  204. }
  205. static gboolean
  206. rspamd_check_encrypted_password (struct rspamd_controller_worker_ctx *ctx,
  207. const rspamd_ftok_t * password, const gchar * check,
  208. const struct rspamd_controller_pbkdf *pbkdf,
  209. gboolean is_enable)
  210. {
  211. const gchar *salt, *hash;
  212. gchar *salt_decoded, *key_decoded;
  213. gsize salt_len = 0, key_len = 0;
  214. gboolean ret = TRUE;
  215. guchar *local_key;
  216. rspamd_ftok_t *cache;
  217. gpointer m;
  218. /* First of all check cached versions to save resources */
  219. if (is_enable && ctx->cached_enable_password.len != 0) {
  220. if (password->len != ctx->cached_enable_password.len ||
  221. !rspamd_constant_memcmp (password->begin,
  222. ctx->cached_enable_password.begin, password->len)) {
  223. msg_info_ctx ("incorrect or absent enable password has been specified");
  224. return FALSE;
  225. }
  226. return TRUE;
  227. }
  228. else if (!is_enable && ctx->cached_password.len != 0) {
  229. if (password->len != ctx->cached_password.len ||
  230. !rspamd_constant_memcmp (password->begin,
  231. ctx->cached_password.begin, password->len)) {
  232. msg_info_ctx ("incorrect or absent password has been specified");
  233. return FALSE;
  234. }
  235. return TRUE;
  236. }
  237. g_assert (pbkdf != NULL);
  238. /* get salt */
  239. salt = rspamd_encrypted_password_get_str (check, 3, &salt_len);
  240. /* get hash */
  241. hash = rspamd_encrypted_password_get_str (check, 3 + salt_len + 1,
  242. &key_len);
  243. if (salt != NULL && hash != NULL) {
  244. /* decode salt */
  245. salt_decoded = rspamd_decode_base32 (salt, salt_len, &salt_len);
  246. if (salt_decoded == NULL || salt_len != pbkdf->salt_len) {
  247. /* We have some unknown salt here */
  248. msg_info_ctx ("incorrect salt: %z, while %z expected",
  249. salt_len, pbkdf->salt_len);
  250. return FALSE;
  251. }
  252. key_decoded = rspamd_decode_base32 (hash, key_len, &key_len);
  253. if (key_decoded == NULL || key_len != pbkdf->key_len) {
  254. /* We have some unknown salt here */
  255. msg_info_ctx ("incorrect key: %z, while %z expected",
  256. key_len, pbkdf->key_len);
  257. return FALSE;
  258. }
  259. local_key = g_alloca (pbkdf->key_len);
  260. rspamd_cryptobox_pbkdf (password->begin, password->len,
  261. salt_decoded, salt_len,
  262. local_key, pbkdf->key_len, pbkdf->rounds);
  263. if (!rspamd_constant_memcmp (key_decoded, local_key, pbkdf->key_len)) {
  264. msg_info_ctx ("incorrect or absent password has been specified");
  265. ret = FALSE;
  266. }
  267. g_free (salt_decoded);
  268. g_free (key_decoded);
  269. }
  270. if (ret) {
  271. /* Save cached version */
  272. cache = is_enable ? &ctx->cached_enable_password : &ctx->cached_password;
  273. if (cache->len == 0) {
  274. /* Mmap region */
  275. m = mmap (NULL, password->len, PROT_WRITE,
  276. MAP_PRIVATE | MAP_ANON, -1, 0);
  277. memcpy (m, password->begin, password->len);
  278. (void)mprotect (m, password->len, PROT_READ);
  279. (void)mlock (m, password->len);
  280. cache->begin = m;
  281. cache->len = password->len;
  282. }
  283. }
  284. return ret;
  285. }
  286. /**
  287. * Checks for X-Forwarded-For header and update client's address if needed
  288. *
  289. * This function is intended to be called for a trusted client to ensure that
  290. * a request is not proxied through it
  291. * @return 0 if no forwarded found, 1 if forwarded found and it is yet trusted
  292. * and -1 if forwarded is denied
  293. */
  294. static gint
  295. rspamd_controller_check_forwarded (struct rspamd_controller_session *session,
  296. struct rspamd_http_message *msg,
  297. struct rspamd_controller_worker_ctx *ctx)
  298. {
  299. const rspamd_ftok_t *hdr;
  300. const gchar *comma;
  301. const char *hdr_name = "X-Forwarded-For", *alt_hdr_name = "X-Real-IP";
  302. char ip_buf[INET6_ADDRSTRLEN + 1];
  303. rspamd_inet_addr_t *addr = NULL;
  304. gint ret = 0;
  305. hdr = rspamd_http_message_find_header (msg, hdr_name);
  306. if (hdr) {
  307. /*
  308. * We need to parse and update the header
  309. * X-Forwarded-For: client, proxy1, proxy2
  310. */
  311. comma = memchr (hdr->begin, ',', hdr->len);
  312. if (comma != NULL) {
  313. if (rspamd_parse_inet_address (&addr, hdr->begin,
  314. comma - hdr->begin)) {
  315. /* We have addr now, so check if it is still trusted */
  316. if (ctx->secure_map &&
  317. radix_find_compressed_addr (ctx->secure_map,
  318. addr) != RADIX_NO_VALUE) {
  319. /* rspamd_inet_address_to_string is not reentrant */
  320. rspamd_strlcpy (ip_buf, rspamd_inet_address_to_string (addr),
  321. sizeof (ip_buf));
  322. msg_info_session ("allow unauthorized proxied connection "
  323. "from a trusted IP %s via %s",
  324. ip_buf,
  325. rspamd_inet_address_to_string (session->from_addr));
  326. ret = 1;
  327. }
  328. else {
  329. ret = -1;
  330. }
  331. rspamd_inet_address_destroy (addr);
  332. }
  333. }
  334. }
  335. else {
  336. /* Try also X-Real-IP */
  337. hdr = rspamd_http_message_find_header (msg, alt_hdr_name);
  338. if (hdr) {
  339. if (rspamd_parse_inet_address (&addr, hdr->begin, hdr->len)) {
  340. /* We have addr now, so check if it is still trusted */
  341. if (ctx->secure_map &&
  342. radix_find_compressed_addr (ctx->secure_map,
  343. addr) != RADIX_NO_VALUE) {
  344. /* rspamd_inet_address_to_string is not reentrant */
  345. rspamd_strlcpy (ip_buf, rspamd_inet_address_to_string (addr),
  346. sizeof (ip_buf));
  347. msg_info_session ("allow unauthorized proxied connection "
  348. "from a trusted IP %s via %s",
  349. ip_buf,
  350. rspamd_inet_address_to_string (session->from_addr));
  351. ret = 1;
  352. }
  353. else {
  354. ret = -1;
  355. }
  356. rspamd_inet_address_destroy (addr);
  357. }
  358. }
  359. }
  360. return ret;
  361. }
  362. /* Check for password if it is required by configuration */
  363. static gboolean rspamd_controller_check_password(
  364. struct rspamd_http_connection_entry *entry,
  365. struct rspamd_controller_session *session,
  366. struct rspamd_http_message *msg, gboolean is_enable)
  367. {
  368. const gchar *check;
  369. const rspamd_ftok_t *password;
  370. rspamd_ftok_t lookup;
  371. GHashTable *query_args = NULL;
  372. struct rspamd_controller_worker_ctx *ctx = session->ctx;
  373. gboolean check_normal = TRUE, check_enable = TRUE, ret = TRUE,
  374. use_enable = FALSE;
  375. const struct rspamd_controller_pbkdf *pbkdf = NULL;
  376. /* Access list logic */
  377. if (rspamd_inet_address_get_af (session->from_addr) == AF_UNIX) {
  378. ret = rspamd_controller_check_forwarded (session, msg, ctx);
  379. if (ret == 1) {
  380. return TRUE;
  381. }
  382. else if (ret == 0) {
  383. /* No forwarded found */
  384. msg_info_session ("allow unauthorized connection from a unix socket");
  385. return TRUE;
  386. }
  387. }
  388. else if (ctx->secure_map
  389. && radix_find_compressed_addr (ctx->secure_map, session->from_addr)
  390. != RADIX_NO_VALUE) {
  391. ret = rspamd_controller_check_forwarded (session, msg, ctx);
  392. if (ret == 1) {
  393. return TRUE;
  394. }
  395. else if (ret == 0) {
  396. /* No forwarded found */
  397. msg_info_session ("allow unauthorized connection from a trusted IP %s",
  398. rspamd_inet_address_to_string (session->from_addr));
  399. return TRUE;
  400. }
  401. }
  402. /* Password logic */
  403. password = rspamd_http_message_find_header (msg, "Password");
  404. if (password == NULL) {
  405. /* Try to get password from query args */
  406. query_args = rspamd_http_message_parse_query (msg);
  407. lookup.begin = (gchar *)"password";
  408. lookup.len = sizeof ("password") - 1;
  409. password = g_hash_table_lookup (query_args, &lookup);
  410. }
  411. if (password == NULL) {
  412. if (ctx->secure_map == NULL) {
  413. if (ctx->password == NULL && !is_enable) {
  414. return TRUE;
  415. }
  416. else if (is_enable && (ctx->password == NULL &&
  417. ctx->enable_password == NULL)) {
  418. return TRUE;
  419. }
  420. }
  421. msg_info_session ("absent password has been specified");
  422. ret = FALSE;
  423. }
  424. else {
  425. if (is_enable) {
  426. /* For privileged commands we strictly require enable password */
  427. if (ctx->enable_password != NULL) {
  428. check = ctx->enable_password;
  429. use_enable = TRUE;
  430. }
  431. else {
  432. /* Use just a password (legacy mode) */
  433. msg_info(
  434. "using password as enable_password for a privileged command");
  435. check = ctx->password;
  436. }
  437. if (check != NULL) {
  438. if (!rspamd_is_encrypted_password (check, &pbkdf)) {
  439. ret = rspamd_constant_memcmp (password->begin, check, password->len);
  440. }
  441. else {
  442. ret = rspamd_check_encrypted_password (ctx, password, check,
  443. pbkdf, use_enable);
  444. }
  445. }
  446. else {
  447. msg_warn_session (
  448. "no password to check while executing a privileged command");
  449. if (ctx->secure_map) {
  450. msg_info("deny unauthorized connection");
  451. ret = FALSE;
  452. }
  453. ret = FALSE;
  454. }
  455. }
  456. else {
  457. /* Accept both normal and enable passwords */
  458. if (ctx->password != NULL) {
  459. check = ctx->password;
  460. if (!rspamd_is_encrypted_password (check, &pbkdf)) {
  461. check_normal = rspamd_constant_memcmp (password->begin, check,
  462. password->len);
  463. }
  464. else {
  465. check_normal = rspamd_check_encrypted_password (ctx,
  466. password,
  467. check, pbkdf, FALSE);
  468. }
  469. }
  470. else {
  471. check_normal = FALSE;
  472. }
  473. if (ctx->enable_password != NULL) {
  474. check = ctx->enable_password;
  475. if (!rspamd_is_encrypted_password (check, &pbkdf)) {
  476. check_enable = rspamd_constant_memcmp (password->begin, check,
  477. password->len);
  478. }
  479. else {
  480. check_enable = rspamd_check_encrypted_password (ctx,
  481. password,
  482. check, pbkdf, TRUE);
  483. }
  484. }
  485. else {
  486. check_enable = FALSE;
  487. }
  488. }
  489. }
  490. if (query_args != NULL) {
  491. g_hash_table_unref (query_args);
  492. }
  493. if (check_normal == FALSE && check_enable == FALSE) {
  494. msg_info("absent or incorrect password has been specified");
  495. ret = FALSE;
  496. }
  497. if (!ret) {
  498. rspamd_controller_send_error (entry, 403, "Unauthorized");
  499. }
  500. return ret;
  501. }
  502. /* Command handlers */
  503. /*
  504. * Auth command handler:
  505. * request: /auth
  506. * headers: Password
  507. * reply: json {"auth": "ok", "version": "0.5.2", "uptime": "some uptime", "error": "none"}
  508. */
  509. static int
  510. rspamd_controller_handle_auth (struct rspamd_http_connection_entry *conn_ent,
  511. struct rspamd_http_message *msg)
  512. {
  513. struct rspamd_controller_session *session = conn_ent->ud;
  514. struct rspamd_stat *st;
  515. int64_t uptime;
  516. gulong data[4];
  517. ucl_object_t *obj;
  518. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  519. return 0;
  520. }
  521. obj = ucl_object_typed_new (UCL_OBJECT);
  522. st = session->ctx->srv->stat;
  523. data[0] = st->actions_stat[METRIC_ACTION_NOACTION];
  524. data[1] = st->actions_stat[METRIC_ACTION_ADD_HEADER] +
  525. st->actions_stat[METRIC_ACTION_REWRITE_SUBJECT];
  526. data[2] = st->actions_stat[METRIC_ACTION_GREYLIST];
  527. data[3] = st->actions_stat[METRIC_ACTION_REJECT];
  528. /* Get uptime */
  529. uptime = time (NULL) - session->ctx->start_time;
  530. ucl_object_insert_key (obj, ucl_object_fromstring (
  531. RVERSION), "version", 0, false);
  532. ucl_object_insert_key (obj, ucl_object_fromstring (
  533. "ok"), "auth", 0, false);
  534. ucl_object_insert_key (obj, ucl_object_fromint (
  535. uptime), "uptime", 0, false);
  536. ucl_object_insert_key (obj, ucl_object_fromint (
  537. data[0]), "clean", 0, false);
  538. ucl_object_insert_key (obj, ucl_object_fromint (
  539. data[1]), "probable", 0, false);
  540. ucl_object_insert_key (obj, ucl_object_fromint (
  541. data[2]), "greylist", 0, false);
  542. ucl_object_insert_key (obj, ucl_object_fromint (
  543. data[3]), "reject", 0, false);
  544. ucl_object_insert_key (obj, ucl_object_fromint (
  545. st->messages_scanned), "scanned", 0, false);
  546. ucl_object_insert_key (obj, ucl_object_fromint (
  547. st->messages_learned), "learned", 0, false);
  548. rspamd_controller_send_ucl (conn_ent, obj);
  549. ucl_object_unref (obj);
  550. return 0;
  551. }
  552. /*
  553. * Symbols command handler:
  554. * request: /symbols
  555. * reply: json [{
  556. * "name": "group_name",
  557. * "symbols": [
  558. * {
  559. * "name": "name",
  560. * "weight": 0.1,
  561. * "description": "description of symbol"
  562. * },
  563. * {...}
  564. * },
  565. * {...}]
  566. */
  567. static int
  568. rspamd_controller_handle_symbols (struct rspamd_http_connection_entry *conn_ent,
  569. struct rspamd_http_message *msg)
  570. {
  571. struct rspamd_controller_session *session = conn_ent->ud;
  572. GHashTableIter it, sit;
  573. struct rspamd_symbols_group *gr;
  574. struct rspamd_symbol_def *sym;
  575. struct metric *metric;
  576. ucl_object_t *obj, *top, *sym_obj, *group_symbols;
  577. gpointer k, v;
  578. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  579. return 0;
  580. }
  581. top = ucl_object_typed_new (UCL_ARRAY);
  582. /* Go through all symbols groups in the default metric */
  583. metric = g_hash_table_lookup (session->ctx->cfg->metrics, DEFAULT_METRIC);
  584. g_assert (metric != NULL);
  585. g_hash_table_iter_init (&it, metric->groups);
  586. while (g_hash_table_iter_next (&it, &k, &v)) {
  587. gr = v;
  588. obj = ucl_object_typed_new (UCL_OBJECT);
  589. ucl_object_insert_key (obj, ucl_object_fromstring (
  590. gr->name), "group", 0, false);
  591. /* Iterate through all symbols */
  592. g_hash_table_iter_init (&sit, gr->symbols);
  593. group_symbols = ucl_object_typed_new (UCL_ARRAY);
  594. while (g_hash_table_iter_next (&sit, &k, &v)) {
  595. sym = v;
  596. sym_obj = ucl_object_typed_new (UCL_OBJECT);
  597. ucl_object_insert_key (sym_obj, ucl_object_fromstring (sym->name),
  598. "symbol", 0, false);
  599. ucl_object_insert_key (sym_obj,
  600. ucl_object_fromdouble (*sym->weight_ptr),
  601. "weight", 0, false);
  602. if (sym->description) {
  603. ucl_object_insert_key (sym_obj,
  604. ucl_object_fromstring (sym->description),
  605. "description", 0, false);
  606. }
  607. ucl_array_append (group_symbols, sym_obj);
  608. }
  609. ucl_object_insert_key (obj, group_symbols, "rules", 0, false);
  610. ucl_array_append (top, obj);
  611. }
  612. rspamd_controller_send_ucl (conn_ent, top);
  613. ucl_object_unref (top);
  614. return 0;
  615. }
  616. /*
  617. * Actions command handler:
  618. * request: /actions
  619. * reply: json [{
  620. * "action": "no action",
  621. * "value": 1.1
  622. * },
  623. * {...}]
  624. */
  625. static int
  626. rspamd_controller_handle_actions (struct rspamd_http_connection_entry *conn_ent,
  627. struct rspamd_http_message *msg)
  628. {
  629. struct rspamd_controller_session *session = conn_ent->ud;
  630. struct metric *metric;
  631. struct metric_action *act;
  632. gint i;
  633. ucl_object_t *obj, *top;
  634. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  635. return 0;
  636. }
  637. top = ucl_object_typed_new (UCL_ARRAY);
  638. /* Get actions for default metric */
  639. metric = g_hash_table_lookup (session->ctx->cfg->metrics, DEFAULT_METRIC);
  640. if (metric != NULL) {
  641. for (i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i++) {
  642. act = &metric->actions[i];
  643. if (act->score >= 0) {
  644. obj = ucl_object_typed_new (UCL_OBJECT);
  645. ucl_object_insert_key (obj,
  646. ucl_object_fromstring (rspamd_action_to_str (
  647. act->action)), "action", 0, false);
  648. ucl_object_insert_key (obj, ucl_object_fromdouble (
  649. act->score), "value", 0, false);
  650. ucl_array_append (top, obj);
  651. }
  652. }
  653. }
  654. rspamd_controller_send_ucl (conn_ent, top);
  655. ucl_object_unref (top);
  656. return 0;
  657. }
  658. /*
  659. * Maps command handler:
  660. * request: /maps
  661. * headers: Password
  662. * reply: json [
  663. * {
  664. * "map": "name",
  665. * "description": "description",
  666. * "editable": true
  667. * },
  668. * {...}
  669. * ]
  670. */
  671. static int
  672. rspamd_controller_handle_maps (struct rspamd_http_connection_entry *conn_ent,
  673. struct rspamd_http_message *msg)
  674. {
  675. struct rspamd_controller_session *session = conn_ent->ud;
  676. GList *cur, *tmp = NULL;
  677. struct rspamd_map *map;
  678. gboolean editable;
  679. ucl_object_t *obj, *top;
  680. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  681. return 0;
  682. }
  683. top = ucl_object_typed_new (UCL_ARRAY);
  684. /* Iterate over all maps */
  685. cur = session->ctx->cfg->maps;
  686. while (cur) {
  687. map = cur->data;
  688. if (map->protocol == MAP_PROTO_FILE) {
  689. if (access (map->uri, R_OK) == 0) {
  690. tmp = g_list_prepend (tmp, map);
  691. }
  692. }
  693. cur = g_list_next (cur);
  694. }
  695. /* Iterate over selected maps */
  696. cur = tmp;
  697. while (cur) {
  698. map = cur->data;
  699. editable = (access (map->uri, W_OK) == 0);
  700. obj = ucl_object_typed_new (UCL_OBJECT);
  701. ucl_object_insert_key (obj, ucl_object_fromint (map->id),
  702. "map", 0, false);
  703. if (map->description) {
  704. ucl_object_insert_key (obj, ucl_object_fromstring (map->description),
  705. "description", 0, false);
  706. }
  707. ucl_object_insert_key (obj, ucl_object_frombool (editable),
  708. "editable", 0, false);
  709. ucl_array_append (top, obj);
  710. cur = g_list_next (cur);
  711. }
  712. if (tmp) {
  713. g_list_free (tmp);
  714. }
  715. rspamd_controller_send_ucl (conn_ent, top);
  716. ucl_object_unref (top);
  717. return 0;
  718. }
  719. /*
  720. * Get map command handler:
  721. * request: /getmap
  722. * headers: Password, Map
  723. * reply: plain-text
  724. */
  725. static int
  726. rspamd_controller_handle_get_map (struct rspamd_http_connection_entry *conn_ent,
  727. struct rspamd_http_message *msg)
  728. {
  729. struct rspamd_controller_session *session = conn_ent->ud;
  730. GList *cur;
  731. struct rspamd_map *map;
  732. const rspamd_ftok_t *idstr;
  733. struct stat st;
  734. gint fd;
  735. gulong id;
  736. gboolean found = FALSE;
  737. struct rspamd_http_message *reply;
  738. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  739. return 0;
  740. }
  741. idstr = rspamd_http_message_find_header (msg, "Map");
  742. if (idstr == NULL) {
  743. msg_info_session ("absent map id");
  744. rspamd_controller_send_error (conn_ent, 400, "400 id header missing");
  745. return 0;
  746. }
  747. if (!rspamd_strtoul (idstr->begin, idstr->len, &id)) {
  748. msg_info_session ("invalid map id");
  749. rspamd_controller_send_error (conn_ent, 400, "400 invalid map id");
  750. return 0;
  751. }
  752. /* Now let's be sure that we have map defined in configuration */
  753. cur = session->ctx->cfg->maps;
  754. while (cur) {
  755. map = cur->data;
  756. if (map->id == id && map->protocol == MAP_PROTO_FILE) {
  757. found = TRUE;
  758. break;
  759. }
  760. cur = g_list_next (cur);
  761. }
  762. if (!found) {
  763. msg_info_session ("map not found");
  764. rspamd_controller_send_error (conn_ent, 404, "404 map not found");
  765. return 0;
  766. }
  767. if (stat (map->uri, &st) == -1 || (fd = open (map->uri, O_RDONLY)) == -1) {
  768. msg_err_session ("cannot open map %s: %s", map->uri, strerror (errno));
  769. rspamd_controller_send_error (conn_ent, 500, "500 map open error");
  770. return 0;
  771. }
  772. reply = rspamd_http_new_message (HTTP_RESPONSE);
  773. reply->date = time (NULL);
  774. reply->code = 200;
  775. reply->body = rspamd_fstring_sized_new (st.st_size);
  776. /* Read the whole buffer */
  777. if (read (fd, reply->body->str, st.st_size) == -1) {
  778. close (fd);
  779. rspamd_http_message_free (reply);
  780. msg_err_session ("cannot read map %s: %s", map->uri, strerror (errno));
  781. rspamd_controller_send_error (conn_ent, 500, "500 map read error");
  782. return 0;
  783. }
  784. reply->body->len = st.st_size;
  785. close (fd);
  786. rspamd_http_connection_reset (conn_ent->conn);
  787. rspamd_http_connection_write_message (conn_ent->conn, reply, NULL,
  788. "text/plain", conn_ent, conn_ent->conn->fd,
  789. conn_ent->rt->ptv, conn_ent->rt->ev_base);
  790. conn_ent->is_reply = TRUE;
  791. return 0;
  792. }
  793. static ucl_object_t *
  794. rspamd_controller_pie_element (enum rspamd_metric_action action,
  795. const char *label, gdouble data)
  796. {
  797. ucl_object_t *res = ucl_object_typed_new (UCL_OBJECT);
  798. const char *colors[METRIC_ACTION_MAX] = {
  799. [METRIC_ACTION_REJECT] = "#FF0000",
  800. [METRIC_ACTION_SOFT_REJECT] = "#cc9966",
  801. [METRIC_ACTION_REWRITE_SUBJECT] = "#ff6600",
  802. [METRIC_ACTION_ADD_HEADER] = "#FFD700",
  803. [METRIC_ACTION_GREYLIST] = "#436EEE",
  804. [METRIC_ACTION_NOACTION] = "#66cc00"
  805. };
  806. ucl_object_insert_key (res, ucl_object_fromstring (colors[action]),
  807. "color", 0, false);
  808. ucl_object_insert_key (res, ucl_object_fromstring (label), "label", 0, false);
  809. ucl_object_insert_key (res, ucl_object_fromdouble (data), "data", 0, false);
  810. ucl_object_insert_key (res, ucl_object_fromdouble (data), "value", 0, false);
  811. return res;
  812. }
  813. /*
  814. * Pie chart command handler:
  815. * request: /pie
  816. * headers: Password
  817. * reply: json [
  818. * { label: "Foo", data: 11 },
  819. * { label: "Bar", data: 20 },
  820. * {...}
  821. * ]
  822. */
  823. static int
  824. rspamd_controller_handle_pie_chart (
  825. struct rspamd_http_connection_entry *conn_ent,
  826. struct rspamd_http_message *msg)
  827. {
  828. struct rspamd_controller_session *session = conn_ent->ud;
  829. struct rspamd_controller_worker_ctx *ctx;
  830. gdouble data[5], total;
  831. ucl_object_t *top;
  832. ctx = session->ctx;
  833. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  834. return 0;
  835. }
  836. top = ucl_object_typed_new (UCL_ARRAY);
  837. total = ctx->srv->stat->messages_scanned;
  838. if (total != 0) {
  839. data[0] = ctx->srv->stat->actions_stat[METRIC_ACTION_NOACTION];
  840. data[1] = ctx->srv->stat->actions_stat[METRIC_ACTION_SOFT_REJECT];
  841. data[2] = (ctx->srv->stat->actions_stat[METRIC_ACTION_ADD_HEADER] +
  842. ctx->srv->stat->actions_stat[METRIC_ACTION_REWRITE_SUBJECT]);
  843. data[3] = ctx->srv->stat->actions_stat[METRIC_ACTION_GREYLIST];
  844. data[4] = ctx->srv->stat->actions_stat[METRIC_ACTION_REJECT];
  845. }
  846. else {
  847. memset (data, 0, sizeof (data));
  848. }
  849. ucl_array_append (top, rspamd_controller_pie_element (
  850. METRIC_ACTION_NOACTION, "Clean", data[0]));
  851. ucl_array_append (top, rspamd_controller_pie_element (
  852. METRIC_ACTION_SOFT_REJECT, "Temporary rejected", data[1]));
  853. ucl_array_append (top, rspamd_controller_pie_element (
  854. METRIC_ACTION_ADD_HEADER, "Probable spam", data[2]));
  855. ucl_array_append (top, rspamd_controller_pie_element (
  856. METRIC_ACTION_GREYLIST, "Greylisted", data[3]));
  857. ucl_array_append (top, rspamd_controller_pie_element (
  858. METRIC_ACTION_REJECT, "Rejected", data[4]));
  859. rspamd_controller_send_ucl (conn_ent, top);
  860. ucl_object_unref (top);
  861. return 0;
  862. }
  863. /*
  864. * Graph command handler:
  865. * request: /graph?type=<hourly|daily|weekly|monthly>
  866. * headers: Password
  867. * reply: json [
  868. * { label: "Foo", data: 11 },
  869. * { label: "Bar", data: 20 },
  870. * {...}
  871. * ]
  872. */
  873. static int
  874. rspamd_controller_handle_graph (
  875. struct rspamd_http_connection_entry *conn_ent,
  876. struct rspamd_http_message *msg)
  877. {
  878. GHashTable *query;
  879. struct rspamd_controller_session *session = conn_ent->ud;
  880. struct rspamd_controller_worker_ctx *ctx;
  881. rspamd_ftok_t srch, *value;
  882. struct rspamd_rrd_query_result *rrd_result;
  883. gulong i, j, ts, start_row, cnt, t;
  884. ucl_object_t *res, *elt[4], *data_elt;
  885. enum {
  886. rra_hourly = 0,
  887. rra_daily,
  888. rra_weekly,
  889. rra_monthly,
  890. rra_invalid
  891. } rra_num = rra_invalid;
  892. ctx = session->ctx;
  893. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  894. return 0;
  895. }
  896. if (ctx->rrd == NULL) {
  897. msg_err_session ("no rrd configured");
  898. rspamd_controller_send_error (conn_ent, 404, "no rrd configured for graphs");
  899. return 0;
  900. }
  901. query = rspamd_http_message_parse_query (msg);
  902. srch.begin = (gchar *)"type";
  903. srch.len = 4;
  904. if (query == NULL || (value = g_hash_table_lookup (query, &srch)) == NULL) {
  905. msg_err_session ("absent graph type query");
  906. rspamd_controller_send_error (conn_ent, 400, "absent graph type");
  907. if (query) {
  908. g_hash_table_unref (query);
  909. }
  910. return 0;
  911. }
  912. if (strncmp (value->begin, "hourly", value->len) == 0) {
  913. rra_num = rra_hourly;
  914. }
  915. else if (strncmp (value->begin, "daily", value->len) == 0) {
  916. rra_num = rra_hourly;
  917. }
  918. else if (strncmp (value->begin, "weekly", value->len) == 0) {
  919. rra_num = rra_hourly;
  920. }
  921. else if (strncmp (value->begin, "monthly", value->len) == 0) {
  922. rra_num = rra_monthly;
  923. }
  924. g_hash_table_unref (query);
  925. if (rra_num == rra_invalid) {
  926. msg_err_session ("invalid graph type query");
  927. rspamd_controller_send_error (conn_ent, 400, "invalid graph type");
  928. return 0;
  929. }
  930. rrd_result = rspamd_rrd_query (ctx->rrd, rra_num);
  931. if (rrd_result == NULL) {
  932. msg_err_session ("cannot query rrd");
  933. rspamd_controller_send_error (conn_ent, 500, "cannot query rrd");
  934. return 0;
  935. }
  936. g_assert (rrd_result->ds_count == G_N_ELEMENTS (elt));
  937. res = ucl_object_typed_new (UCL_ARRAY);
  938. /* How much full updates happened since the last update */
  939. ts = rrd_result->last_update / rrd_result->pdp_per_cdp - rrd_result->rra_rows;
  940. for (i = 0; i < rrd_result->ds_count; i ++) {
  941. elt[i] = ucl_object_typed_new (UCL_ARRAY);
  942. }
  943. start_row = rrd_result->cur_row == rrd_result->rra_rows - 1 ?
  944. 0 : rrd_result->cur_row;
  945. for (i = start_row, cnt = 0; cnt < rrd_result->rra_rows; cnt ++) {
  946. for (j = 0; j < rrd_result->ds_count; j++) {
  947. data_elt = ucl_object_typed_new (UCL_OBJECT);
  948. t = ts * rrd_result->pdp_per_cdp;
  949. ucl_object_insert_key (data_elt,
  950. ucl_object_fromint (t),
  951. "x", 1,
  952. false);
  953. ucl_object_insert_key (data_elt,
  954. ucl_object_fromdouble (rrd_result->data[i *
  955. rrd_result->ds_count + j]),
  956. "y", 1,
  957. false);
  958. ucl_array_append (elt[j], data_elt);
  959. }
  960. i = start_row == 0 ? i + 1 : (i + 1) % start_row;
  961. ts ++;
  962. }
  963. for (i = 0; i < rrd_result->ds_count; i++) {
  964. ucl_array_append (res, elt[i]);
  965. }
  966. rspamd_controller_send_ucl (conn_ent, res);
  967. ucl_object_unref (res);
  968. return 0;
  969. }
  970. /*
  971. * History command handler:
  972. * request: /history
  973. * headers: Password
  974. * reply: json [
  975. * { label: "Foo", data: 11 },
  976. * { label: "Bar", data: 20 },
  977. * {...}
  978. * ]
  979. */
  980. static int
  981. rspamd_controller_handle_history (struct rspamd_http_connection_entry *conn_ent,
  982. struct rspamd_http_message *msg)
  983. {
  984. struct rspamd_controller_session *session = conn_ent->ud;
  985. struct rspamd_controller_worker_ctx *ctx;
  986. struct roll_history_row *row, *copied_rows;
  987. guint i, rows_proc, row_num;
  988. struct tm *tm;
  989. gchar timebuf[32];
  990. ucl_object_t *top, *obj;
  991. ctx = session->ctx;
  992. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  993. return 0;
  994. }
  995. top = ucl_object_typed_new (UCL_ARRAY);
  996. /* Set lock on history */
  997. copied_rows = g_slice_alloc (sizeof (*copied_rows) * ctx->srv->history->nrows);
  998. memcpy (copied_rows, ctx->srv->history->rows,
  999. sizeof (*copied_rows) * ctx->srv->history->nrows);
  1000. /* Go through all rows */
  1001. row_num = ctx->srv->history->cur_row;
  1002. for (i = 0, rows_proc = 0; i < ctx->srv->history->nrows; i++, row_num++) {
  1003. if (row_num == ctx->srv->history->nrows) {
  1004. row_num = 0;
  1005. }
  1006. row = &copied_rows[row_num];
  1007. /* Get only completed rows */
  1008. if (row->completed) {
  1009. tm = localtime (&row->tv.tv_sec);
  1010. strftime (timebuf, sizeof (timebuf) - 1, "%Y-%m-%d %H:%M:%S", tm);
  1011. obj = ucl_object_typed_new (UCL_OBJECT);
  1012. ucl_object_insert_key (obj, ucl_object_fromstring (
  1013. timebuf), "time", 0, false);
  1014. ucl_object_insert_key (obj, ucl_object_fromint (
  1015. row->tv.tv_sec), "unix_time", 0, false);
  1016. ucl_object_insert_key (obj, ucl_object_fromstring (
  1017. row->message_id), "id", 0, false);
  1018. ucl_object_insert_key (obj, ucl_object_fromstring (row->from_addr),
  1019. "ip", 0, false);
  1020. ucl_object_insert_key (obj,
  1021. ucl_object_fromstring (rspamd_action_to_str (
  1022. row->action)), "action", 0, false);
  1023. ucl_object_insert_key (obj, ucl_object_fromdouble (
  1024. row->score), "score", 0, false);
  1025. ucl_object_insert_key (obj,
  1026. ucl_object_fromdouble (
  1027. row->required_score), "required_score", 0, false);
  1028. ucl_object_insert_key (obj, ucl_object_fromstring (
  1029. row->symbols), "symbols", 0, false);
  1030. ucl_object_insert_key (obj, ucl_object_fromint (
  1031. row->len), "size", 0, false);
  1032. ucl_object_insert_key (obj, ucl_object_fromdouble (
  1033. row->scan_time), "scan_time", 0, false);
  1034. if (row->user[0] != '\0') {
  1035. ucl_object_insert_key (obj, ucl_object_fromstring (
  1036. row->user), "user", 0, false);
  1037. }
  1038. if (row->from_addr[0] != '\0') {
  1039. ucl_object_insert_key (obj, ucl_object_fromstring (
  1040. row->from_addr), "from", 0, false);
  1041. }
  1042. ucl_array_append (top, obj);
  1043. rows_proc++;
  1044. }
  1045. }
  1046. rspamd_controller_send_ucl (conn_ent, top);
  1047. ucl_object_unref (top);
  1048. return 0;
  1049. }
  1050. static int
  1051. rspamd_controller_handle_history_reset (struct rspamd_http_connection_entry *conn_ent,
  1052. struct rspamd_http_message *msg)
  1053. {
  1054. struct rspamd_controller_session *session = conn_ent->ud;
  1055. struct rspamd_controller_worker_ctx *ctx;
  1056. struct roll_history_row *row;
  1057. guint start_row, i, t;
  1058. ctx = session->ctx;
  1059. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1060. return 0;
  1061. }
  1062. /* Clean from start to the current row */
  1063. start_row = g_atomic_int_get (&ctx->srv->history->cur_row);
  1064. for (i = 0; i < start_row; i ++) {
  1065. t = g_atomic_int_get (&ctx->srv->history->cur_row);
  1066. /* We somehow come to the race condition */
  1067. if (i >= t) {
  1068. break;
  1069. }
  1070. row = &ctx->srv->history->rows[i];
  1071. memset (row, 0, sizeof (*row));
  1072. }
  1073. start_row = g_atomic_int_get (&ctx->srv->history->cur_row);
  1074. /* Optimistically set all bytes to zero (might cause race) */
  1075. memset (ctx->srv->history->rows,
  1076. 0,
  1077. sizeof (*row) * (ctx->srv->history->nrows - start_row));
  1078. msg_info_session ("<%s> reseted history",
  1079. rspamd_inet_address_to_string (session->from_addr));
  1080. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1081. return 0;
  1082. }
  1083. static gboolean
  1084. rspamd_controller_learn_fin_task (void *ud)
  1085. {
  1086. struct rspamd_task *task = ud;
  1087. struct rspamd_controller_session *session;
  1088. struct rspamd_http_connection_entry *conn_ent;
  1089. conn_ent = task->fin_arg;
  1090. session = conn_ent->ud;
  1091. if (task->err != NULL) {
  1092. msg_info_session ("cannot learn <%s>: %e", task->message_id, task->err);
  1093. rspamd_controller_send_error (conn_ent, task->err->code,
  1094. task->err->message);
  1095. return TRUE;
  1096. }
  1097. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  1098. /* Successful learn */
  1099. msg_info_session ("<%s> learned message as %s: %s",
  1100. rspamd_inet_address_to_string (session->from_addr),
  1101. session->is_spam ? "spam" : "ham",
  1102. task->message_id);
  1103. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1104. return TRUE;
  1105. }
  1106. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_LEARN)) {
  1107. msg_info_session ("cannot learn <%s>: %e", task->message_id, task->err);
  1108. if (task->err) {
  1109. rspamd_controller_send_error (conn_ent, task->err->code,
  1110. task->err->message);
  1111. }
  1112. else {
  1113. rspamd_controller_send_error (conn_ent, 500,
  1114. "Internal error");
  1115. }
  1116. }
  1117. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  1118. if (task->err) {
  1119. rspamd_controller_send_error (conn_ent, task->err->code,
  1120. task->err->message);
  1121. }
  1122. else {
  1123. msg_info_session ("<%s> learned message as %s: %s",
  1124. rspamd_inet_address_to_string (session->from_addr),
  1125. session->is_spam ? "spam" : "ham",
  1126. task->message_id);
  1127. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1128. }
  1129. return TRUE;
  1130. }
  1131. /* One more iteration */
  1132. return FALSE;
  1133. }
  1134. static void
  1135. rspamd_controller_scan_reply (struct rspamd_task *task)
  1136. {
  1137. struct rspamd_http_message *msg;
  1138. struct rspamd_http_connection_entry *conn_ent = task->fin_arg;
  1139. conn_ent = task->fin_arg;
  1140. msg = rspamd_http_new_message (HTTP_RESPONSE);
  1141. msg->date = time (NULL);
  1142. msg->code = 200;
  1143. rspamd_protocol_http_reply (msg, task);
  1144. rspamd_http_connection_reset (conn_ent->conn);
  1145. rspamd_http_connection_write_message (conn_ent->conn, msg, NULL,
  1146. "application/json", conn_ent, conn_ent->conn->fd, conn_ent->rt->ptv,
  1147. conn_ent->rt->ev_base);
  1148. conn_ent->is_reply = TRUE;
  1149. }
  1150. static gboolean
  1151. rspamd_controller_check_fin_task (void *ud)
  1152. {
  1153. struct rspamd_task *task = ud;
  1154. msg_debug_task ("finish task");
  1155. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  1156. rspamd_controller_scan_reply (task);
  1157. return TRUE;
  1158. }
  1159. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL)) {
  1160. rspamd_controller_scan_reply (task);
  1161. return TRUE;
  1162. }
  1163. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  1164. rspamd_controller_scan_reply (task);
  1165. return TRUE;
  1166. }
  1167. /* One more iteration */
  1168. return FALSE;
  1169. }
  1170. static int
  1171. rspamd_controller_handle_learn_common (
  1172. struct rspamd_http_connection_entry *conn_ent,
  1173. struct rspamd_http_message *msg,
  1174. gboolean is_spam)
  1175. {
  1176. struct rspamd_controller_session *session = conn_ent->ud;
  1177. struct rspamd_controller_worker_ctx *ctx;
  1178. struct rspamd_task *task;
  1179. const rspamd_ftok_t *cl_header;
  1180. ctx = session->ctx;
  1181. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1182. return 0;
  1183. }
  1184. if (msg->body == NULL || msg->body->len == 0) {
  1185. msg_err_session ("got zero length body, cannot continue");
  1186. rspamd_controller_send_error (conn_ent,
  1187. 400,
  1188. "Empty body is not permitted");
  1189. return 0;
  1190. }
  1191. task = rspamd_task_new (session->ctx->worker, session->cfg);
  1192. task->resolver = ctx->resolver;
  1193. task->ev_base = ctx->ev_base;
  1194. task->s = rspamd_session_create (session->pool,
  1195. rspamd_controller_learn_fin_task,
  1196. NULL,
  1197. (event_finalizer_t )rspamd_task_free,
  1198. task);
  1199. task->fin_arg = conn_ent;
  1200. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
  1201. task->sock = -1;
  1202. session->task = task;
  1203. cl_header = rspamd_http_message_find_header (msg, "classifier");
  1204. if (cl_header) {
  1205. session->classifier = rspamd_mempool_ftokdup (session->pool, cl_header);
  1206. }
  1207. else {
  1208. session->classifier = NULL;
  1209. }
  1210. if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
  1211. rspamd_controller_send_error (conn_ent, task->err->code, task->err->message);
  1212. return 0;
  1213. }
  1214. rspamd_learn_task_spam (task, is_spam, session->classifier, NULL);
  1215. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_LEARN)) {
  1216. msg_warn_session ("<%s> message cannot be processed", task->message_id);
  1217. rspamd_controller_send_error (conn_ent, task->err->code, task->err->message);
  1218. return 0;
  1219. }
  1220. session->is_spam = is_spam;
  1221. rspamd_session_pending (task->s);
  1222. return 0;
  1223. }
  1224. /*
  1225. * Learn spam command handler:
  1226. * request: /learnspam
  1227. * headers: Password
  1228. * input: plaintext data
  1229. * reply: json {"success":true} or {"error":"error message"}
  1230. */
  1231. static int
  1232. rspamd_controller_handle_learnspam (
  1233. struct rspamd_http_connection_entry *conn_ent,
  1234. struct rspamd_http_message *msg)
  1235. {
  1236. return rspamd_controller_handle_learn_common (conn_ent, msg, TRUE);
  1237. }
  1238. /*
  1239. * Learn ham command handler:
  1240. * request: /learnham
  1241. * headers: Password
  1242. * input: plaintext data
  1243. * reply: json {"success":true} or {"error":"error message"}
  1244. */
  1245. static int
  1246. rspamd_controller_handle_learnham (
  1247. struct rspamd_http_connection_entry *conn_ent,
  1248. struct rspamd_http_message *msg)
  1249. {
  1250. return rspamd_controller_handle_learn_common (conn_ent, msg, FALSE);
  1251. }
  1252. /*
  1253. * Scan command handler:
  1254. * request: /scan
  1255. * headers: Password
  1256. * input: plaintext data
  1257. * reply: json {scan data} or {"error":"error message"}
  1258. */
  1259. static int
  1260. rspamd_controller_handle_scan (struct rspamd_http_connection_entry *conn_ent,
  1261. struct rspamd_http_message *msg)
  1262. {
  1263. struct rspamd_controller_session *session = conn_ent->ud;
  1264. struct rspamd_controller_worker_ctx *ctx;
  1265. struct rspamd_task *task;
  1266. ctx = session->ctx;
  1267. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  1268. return 0;
  1269. }
  1270. if (msg->body == NULL || msg->body->len == 0) {
  1271. msg_err_session ("got zero length body, cannot continue");
  1272. rspamd_controller_send_error (conn_ent,
  1273. 400,
  1274. "Empty body is not permitted");
  1275. return 0;
  1276. }
  1277. task = rspamd_task_new (session->ctx->worker, session->cfg);
  1278. task->ev_base = session->ctx->ev_base;
  1279. task->resolver = ctx->resolver;
  1280. task->ev_base = ctx->ev_base;
  1281. task->s = rspamd_session_create (session->pool,
  1282. rspamd_controller_check_fin_task,
  1283. NULL,
  1284. (event_finalizer_t )rspamd_task_free,
  1285. task);
  1286. task->fin_arg = conn_ent;
  1287. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);
  1288. task->sock = conn_ent->conn->fd;
  1289. task->flags |= RSPAMD_TASK_FLAG_MIME;
  1290. task->resolver = ctx->resolver;
  1291. if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
  1292. rspamd_controller_send_error (conn_ent, task->err->code, task->err->message);
  1293. rspamd_session_destroy (task->s);
  1294. return 0;
  1295. }
  1296. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL)) {
  1297. msg_warn_session ("message cannot be processed for %s", task->message_id);
  1298. rspamd_controller_send_error (conn_ent, task->err->code, task->err->message);
  1299. rspamd_session_destroy (task->s);
  1300. return 0;
  1301. }
  1302. session->task = task;
  1303. rspamd_session_pending (task->s);
  1304. return 0;
  1305. }
  1306. /*
  1307. * Save actions command handler:
  1308. * request: /saveactions
  1309. * headers: Password
  1310. * input: json array [<spam>,<probable spam>,<greylist>]
  1311. * reply: json {"success":true} or {"error":"error message"}
  1312. */
  1313. static int
  1314. rspamd_controller_handle_saveactions (
  1315. struct rspamd_http_connection_entry *conn_ent,
  1316. struct rspamd_http_message *msg)
  1317. {
  1318. struct rspamd_controller_session *session = conn_ent->ud;
  1319. struct ucl_parser *parser;
  1320. struct metric *metric;
  1321. ucl_object_t *obj;
  1322. const ucl_object_t *cur;
  1323. struct rspamd_controller_worker_ctx *ctx;
  1324. const gchar *error;
  1325. gdouble score;
  1326. gint i, added = 0;
  1327. enum rspamd_metric_action act;
  1328. ucl_object_iter_t it = NULL;
  1329. ctx = session->ctx;
  1330. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1331. return 0;
  1332. }
  1333. if (msg->body == NULL || msg->body->len == 0) {
  1334. msg_err_session ("got zero length body, cannot continue");
  1335. rspamd_controller_send_error (conn_ent,
  1336. 400,
  1337. "Empty body is not permitted");
  1338. return 0;
  1339. }
  1340. metric = g_hash_table_lookup (ctx->cfg->metrics, DEFAULT_METRIC);
  1341. if (metric == NULL) {
  1342. msg_err_session ("cannot find default metric");
  1343. rspamd_controller_send_error (conn_ent, 500,
  1344. "Default metric is absent");
  1345. return 0;
  1346. }
  1347. /* Now check for dynamic config */
  1348. if (!ctx->cfg->dynamic_conf) {
  1349. msg_err_session ("dynamic conf has not been defined");
  1350. rspamd_controller_send_error (conn_ent,
  1351. 500,
  1352. "No dynamic_rules setting defined");
  1353. return 0;
  1354. }
  1355. parser = ucl_parser_new (0);
  1356. ucl_parser_add_chunk (parser, msg->body_buf.begin, msg->body_buf.len);
  1357. if ((error = ucl_parser_get_error (parser)) != NULL) {
  1358. msg_err_session ("cannot parse input: %s", error);
  1359. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1360. ucl_parser_free (parser);
  1361. return 0;
  1362. }
  1363. obj = ucl_parser_get_object (parser);
  1364. ucl_parser_free (parser);
  1365. if (obj->type != UCL_ARRAY || obj->len != 3) {
  1366. msg_err_session ("input is not an array of 3 elements");
  1367. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1368. ucl_object_unref (obj);
  1369. return 0;
  1370. }
  1371. for (i = 0; i < 3; i++) {
  1372. cur = ucl_iterate_object (obj, &it, TRUE);
  1373. if (cur == NULL) {
  1374. break;
  1375. }
  1376. switch (i) {
  1377. case 0:
  1378. act = METRIC_ACTION_REJECT;
  1379. break;
  1380. case 1:
  1381. act = METRIC_ACTION_ADD_HEADER;
  1382. break;
  1383. case 2:
  1384. act = METRIC_ACTION_GREYLIST;
  1385. break;
  1386. }
  1387. score = ucl_object_todouble (cur);
  1388. if (metric->actions[act].score != score) {
  1389. add_dynamic_action (ctx->cfg, DEFAULT_METRIC, act, score);
  1390. added ++;
  1391. }
  1392. }
  1393. if (dump_dynamic_config (ctx->cfg)) {
  1394. msg_info_session ("<%s> modified %d actions",
  1395. rspamd_inet_address_to_string (session->from_addr),
  1396. added);
  1397. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1398. }
  1399. else {
  1400. rspamd_controller_send_error (conn_ent, 500, "Save error");
  1401. }
  1402. ucl_object_unref (obj);
  1403. return 0;
  1404. }
  1405. /*
  1406. * Save symbols command handler:
  1407. * request: /savesymbols
  1408. * headers: Password
  1409. * input: json data
  1410. * reply: json {"success":true} or {"error":"error message"}
  1411. */
  1412. static int
  1413. rspamd_controller_handle_savesymbols (
  1414. struct rspamd_http_connection_entry *conn_ent,
  1415. struct rspamd_http_message *msg)
  1416. {
  1417. struct rspamd_controller_session *session = conn_ent->ud;
  1418. struct ucl_parser *parser;
  1419. struct metric *metric;
  1420. ucl_object_t *obj;
  1421. const ucl_object_t *cur, *jname, *jvalue;
  1422. ucl_object_iter_t iter = NULL;
  1423. struct rspamd_controller_worker_ctx *ctx;
  1424. const gchar *error;
  1425. gdouble val;
  1426. struct rspamd_symbol_def *sym;
  1427. int added = 0;
  1428. ctx = session->ctx;
  1429. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1430. return 0;
  1431. }
  1432. if (msg->body == NULL || msg->body->len == 0) {
  1433. msg_err_session ("got zero length body, cannot continue");
  1434. rspamd_controller_send_error (conn_ent,
  1435. 400,
  1436. "Empty body is not permitted");
  1437. return 0;
  1438. }
  1439. metric = g_hash_table_lookup (ctx->cfg->metrics, DEFAULT_METRIC);
  1440. if (metric == NULL) {
  1441. msg_err_session ("cannot find default metric");
  1442. rspamd_controller_send_error (conn_ent, 500,
  1443. "Default metric is absent");
  1444. return 0;
  1445. }
  1446. /* Now check for dynamic config */
  1447. if (!ctx->cfg->dynamic_conf) {
  1448. msg_err_session ("dynamic conf has not been defined");
  1449. rspamd_controller_send_error (conn_ent,
  1450. 500,
  1451. "No dynamic_rules setting defined");
  1452. return 0;
  1453. }
  1454. parser = ucl_parser_new (0);
  1455. ucl_parser_add_chunk (parser, msg->body_buf.begin, msg->body_buf.len);
  1456. if ((error = ucl_parser_get_error (parser)) != NULL) {
  1457. msg_err_session ("cannot parse input: %s", error);
  1458. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1459. ucl_parser_free (parser);
  1460. return 0;
  1461. }
  1462. obj = ucl_parser_get_object (parser);
  1463. ucl_parser_free (parser);
  1464. if (obj->type != UCL_ARRAY) {
  1465. msg_err_session ("input is not an array");
  1466. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1467. ucl_object_unref (obj);
  1468. return 0;
  1469. }
  1470. while ((cur = ucl_iterate_object (obj, &iter, true))) {
  1471. if (cur->type != UCL_OBJECT) {
  1472. msg_err_session ("json array data error");
  1473. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1474. ucl_object_unref (obj);
  1475. return 0;
  1476. }
  1477. jname = ucl_object_find_key (cur, "name");
  1478. jvalue = ucl_object_find_key (cur, "value");
  1479. val = ucl_object_todouble (jvalue);
  1480. sym =
  1481. g_hash_table_lookup (metric->symbols, ucl_object_tostring (jname));
  1482. if (sym && fabs (*sym->weight_ptr - val) > 0.01) {
  1483. if (!add_dynamic_symbol (ctx->cfg, DEFAULT_METRIC,
  1484. ucl_object_tostring (jname), val)) {
  1485. msg_err_session ("add symbol failed for %s",
  1486. ucl_object_tostring (jname));
  1487. rspamd_controller_send_error (conn_ent, 506,
  1488. "Add symbol failed");
  1489. ucl_object_unref (obj);
  1490. return 0;
  1491. }
  1492. added ++;
  1493. }
  1494. }
  1495. if (added > 0) {
  1496. if (dump_dynamic_config (ctx->cfg)) {
  1497. msg_info_session ("<%s> modified %d symbols",
  1498. rspamd_inet_address_to_string (session->from_addr),
  1499. added);
  1500. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1501. }
  1502. else {
  1503. rspamd_controller_send_error (conn_ent, 500, "Save error");
  1504. }
  1505. }
  1506. else {
  1507. msg_err_session ("no symbols to save");
  1508. rspamd_controller_send_error (conn_ent, 404, "No symbols to save");
  1509. }
  1510. ucl_object_unref (obj);
  1511. return 0;
  1512. }
  1513. /*
  1514. * Save map command handler:
  1515. * request: /savemap
  1516. * headers: Password, Map
  1517. * input: plaintext data
  1518. * reply: json {"success":true} or {"error":"error message"}
  1519. */
  1520. static int
  1521. rspamd_controller_handle_savemap (struct rspamd_http_connection_entry *conn_ent,
  1522. struct rspamd_http_message *msg)
  1523. {
  1524. struct rspamd_controller_session *session = conn_ent->ud;
  1525. GList *cur;
  1526. struct rspamd_map *map;
  1527. struct rspamd_controller_worker_ctx *ctx;
  1528. const rspamd_ftok_t *idstr;
  1529. gulong id;
  1530. gboolean found = FALSE;
  1531. gint fd;
  1532. ctx = session->ctx;
  1533. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1534. return 0;
  1535. }
  1536. if (msg->body == NULL || msg->body->len == 0) {
  1537. msg_err_session ("got zero length body, cannot continue");
  1538. rspamd_controller_send_error (conn_ent,
  1539. 400,
  1540. "Empty body is not permitted");
  1541. return 0;
  1542. }
  1543. idstr = rspamd_http_message_find_header (msg, "Map");
  1544. if (idstr == NULL) {
  1545. msg_info_session ("absent map id");
  1546. rspamd_controller_send_error (conn_ent, 400, "Map id not specified");
  1547. return 0;
  1548. }
  1549. if (!rspamd_strtoul (idstr->begin, idstr->len, &id)) {
  1550. msg_info_session ("invalid map id: %T", idstr);
  1551. rspamd_controller_send_error (conn_ent, 400, "Map id is invalid");
  1552. return 0;
  1553. }
  1554. /* Now let's be sure that we have map defined in configuration */
  1555. cur = ctx->cfg->maps;
  1556. while (cur) {
  1557. map = cur->data;
  1558. if (map->id == id && map->protocol == MAP_PROTO_FILE) {
  1559. found = TRUE;
  1560. break;
  1561. }
  1562. cur = g_list_next (cur);
  1563. }
  1564. if (!found) {
  1565. msg_info_session ("map not found: %L", id);
  1566. rspamd_controller_send_error (conn_ent, 404, "Map id not found");
  1567. return 0;
  1568. }
  1569. if (g_atomic_int_get (map->locked)) {
  1570. msg_info_session ("map locked: %s", map->uri);
  1571. rspamd_controller_send_error (conn_ent, 404, "Map is locked");
  1572. return 0;
  1573. }
  1574. /* Set lock */
  1575. g_atomic_int_set (map->locked, 1);
  1576. fd = open (map->uri, O_WRONLY | O_TRUNC);
  1577. if (fd == -1) {
  1578. g_atomic_int_set (map->locked, 0);
  1579. msg_info_session ("map %s open error: %s", map->uri, strerror (errno));
  1580. rspamd_controller_send_error (conn_ent, 404, "Map id not found");
  1581. return 0;
  1582. }
  1583. if (write (fd, msg->body_buf.begin, msg->body_buf.len) == -1) {
  1584. msg_info_session ("map %s write error: %s", map->uri, strerror (errno));
  1585. close (fd);
  1586. g_atomic_int_set (map->locked, 0);
  1587. rspamd_controller_send_error (conn_ent, 500, "Map write error");
  1588. return 0;
  1589. }
  1590. msg_info_session ("<%s>, map %s saved",
  1591. rspamd_inet_address_to_string (session->from_addr),
  1592. map->uri);
  1593. /* Close and unlock */
  1594. close (fd);
  1595. g_atomic_int_set (map->locked, 0);
  1596. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1597. return 0;
  1598. }
  1599. struct rspamd_stat_cbdata {
  1600. struct rspamd_http_connection_entry *conn_ent;
  1601. ucl_object_t *top;
  1602. ucl_object_t *stat;
  1603. struct rspamd_task *task;
  1604. guint64 learned;
  1605. };
  1606. static gboolean
  1607. rspamd_controller_stat_fin_task (void *ud)
  1608. {
  1609. struct rspamd_stat_cbdata *cbdata = ud;
  1610. struct rspamd_http_connection_entry *conn_ent;
  1611. ucl_object_t *top;
  1612. conn_ent = cbdata->conn_ent;
  1613. top = cbdata->top;
  1614. ucl_object_insert_key (top,
  1615. ucl_object_fromint (cbdata->learned), "total_learns", 0, false);
  1616. if (cbdata->stat) {
  1617. ucl_object_insert_key (top, cbdata->stat, "statfiles", 0, false);
  1618. }
  1619. rspamd_controller_send_ucl (conn_ent, top);
  1620. return TRUE;
  1621. }
  1622. static void
  1623. rspamd_controller_stat_cleanup_task (void *ud)
  1624. {
  1625. struct rspamd_stat_cbdata *cbdata = ud;
  1626. rspamd_task_free (cbdata->task);
  1627. ucl_object_unref (cbdata->top);
  1628. }
  1629. /*
  1630. * Stat command handler:
  1631. * request: /stat (/resetstat)
  1632. * headers: Password
  1633. * reply: json data
  1634. */
  1635. static int
  1636. rspamd_controller_handle_stat_common (
  1637. struct rspamd_http_connection_entry *conn_ent,
  1638. struct rspamd_http_message *msg,
  1639. gboolean do_reset)
  1640. {
  1641. struct rspamd_controller_session *session = conn_ent->ud;
  1642. ucl_object_t *top, *sub;
  1643. gint i;
  1644. guint64 spam = 0, ham = 0;
  1645. rspamd_mempool_stat_t mem_st;
  1646. struct rspamd_stat *stat, stat_copy;
  1647. struct rspamd_controller_worker_ctx *ctx;
  1648. struct rspamd_task *task;
  1649. struct rspamd_stat_cbdata *cbdata;
  1650. rspamd_mempool_stat (&mem_st);
  1651. memcpy (&stat_copy, session->ctx->worker->srv->stat, sizeof (stat_copy));
  1652. stat = &stat_copy;
  1653. task = rspamd_task_new (session->ctx->worker, session->cfg);
  1654. ctx = session->ctx;
  1655. task->resolver = ctx->resolver;
  1656. task->ev_base = ctx->ev_base;
  1657. cbdata = rspamd_mempool_alloc0 (session->pool, sizeof (*cbdata));
  1658. cbdata->conn_ent = conn_ent;
  1659. cbdata->task = task;
  1660. top = ucl_object_typed_new (UCL_OBJECT);
  1661. cbdata->top = top;
  1662. task->s = rspamd_session_create (session->pool,
  1663. rspamd_controller_stat_fin_task,
  1664. NULL,
  1665. rspamd_controller_stat_cleanup_task,
  1666. cbdata);
  1667. task->fin_arg = cbdata;
  1668. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
  1669. task->sock = conn_ent->conn->fd;
  1670. ucl_object_insert_key (top, ucl_object_fromint (
  1671. stat->messages_scanned), "scanned", 0, false);
  1672. ucl_object_insert_key (top, ucl_object_fromint (
  1673. stat->messages_learned), "learned", 0, false);
  1674. if (stat->messages_scanned > 0) {
  1675. sub = ucl_object_typed_new (UCL_OBJECT);
  1676. for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
  1677. ucl_object_insert_key (sub,
  1678. ucl_object_fromint (stat->actions_stat[i]),
  1679. rspamd_action_to_str (i), 0, false);
  1680. if (i < METRIC_ACTION_GREYLIST) {
  1681. spam += stat->actions_stat[i];
  1682. }
  1683. else {
  1684. ham += stat->actions_stat[i];
  1685. }
  1686. if (do_reset) {
  1687. #ifndef HAVE_ATOMIC_BUILTINS
  1688. session->ctx->worker->srv->stat->actions_stat[i] = 0;
  1689. #else
  1690. __atomic_store_n(&session->ctx->worker->srv->stat->actions_stat[i],
  1691. 0, __ATOMIC_RELEASE);
  1692. #endif
  1693. }
  1694. }
  1695. ucl_object_insert_key (top, sub, "actions", 0, false);
  1696. }
  1697. ucl_object_insert_key (top, ucl_object_fromint (
  1698. spam), "spam_count", 0, false);
  1699. ucl_object_insert_key (top, ucl_object_fromint (
  1700. ham), "ham_count", 0, false);
  1701. ucl_object_insert_key (top,
  1702. ucl_object_fromint (stat->connections_count), "connections", 0, false);
  1703. ucl_object_insert_key (top,
  1704. ucl_object_fromint (stat->control_connections_count),
  1705. "control_connections", 0, false);
  1706. ucl_object_insert_key (top,
  1707. ucl_object_fromint (mem_st.pools_allocated), "pools_allocated", 0,
  1708. false);
  1709. ucl_object_insert_key (top,
  1710. ucl_object_fromint (mem_st.pools_freed), "pools_freed", 0, false);
  1711. ucl_object_insert_key (top,
  1712. ucl_object_fromint (mem_st.bytes_allocated), "bytes_allocated", 0,
  1713. false);
  1714. ucl_object_insert_key (top,
  1715. ucl_object_fromint (
  1716. mem_st.chunks_allocated), "chunks_allocated", 0, false);
  1717. ucl_object_insert_key (top,
  1718. ucl_object_fromint (mem_st.shared_chunks_allocated),
  1719. "shared_chunks_allocated", 0, false);
  1720. ucl_object_insert_key (top,
  1721. ucl_object_fromint (mem_st.chunks_freed), "chunks_freed", 0, false);
  1722. ucl_object_insert_key (top,
  1723. ucl_object_fromint (
  1724. mem_st.oversized_chunks), "chunks_oversized", 0, false);
  1725. if (do_reset) {
  1726. session->ctx->srv->stat->messages_scanned = 0;
  1727. session->ctx->srv->stat->messages_learned = 0;
  1728. session->ctx->srv->stat->connections_count = 0;
  1729. session->ctx->srv->stat->control_connections_count = 0;
  1730. rspamd_mempool_stat_reset ();
  1731. }
  1732. /* Now write statistics for each statfile */
  1733. rspamd_stat_statistics (task, session->ctx->cfg, &cbdata->learned,
  1734. &cbdata->stat);
  1735. session->task = task;
  1736. rspamd_session_pending (task->s);
  1737. return 0;
  1738. }
  1739. static int
  1740. rspamd_controller_handle_stat (struct rspamd_http_connection_entry *conn_ent,
  1741. struct rspamd_http_message *msg)
  1742. {
  1743. struct rspamd_controller_session *session = conn_ent->ud;
  1744. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  1745. return 0;
  1746. }
  1747. return rspamd_controller_handle_stat_common (conn_ent, msg, FALSE);
  1748. }
  1749. static int
  1750. rspamd_controller_handle_statreset (
  1751. struct rspamd_http_connection_entry *conn_ent,
  1752. struct rspamd_http_message *msg)
  1753. {
  1754. struct rspamd_controller_session *session = conn_ent->ud;
  1755. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1756. return 0;
  1757. }
  1758. msg_info_session ("<%s> reset stat",
  1759. rspamd_inet_address_to_string (session->from_addr));
  1760. return rspamd_controller_handle_stat_common (conn_ent, msg, TRUE);
  1761. }
  1762. /*
  1763. * Counters command handler:
  1764. * request: /counters
  1765. * headers: Password
  1766. * reply: json array of all counters
  1767. */
  1768. static int
  1769. rspamd_controller_handle_counters (
  1770. struct rspamd_http_connection_entry *conn_ent,
  1771. struct rspamd_http_message *msg)
  1772. {
  1773. struct rspamd_controller_session *session = conn_ent->ud;
  1774. ucl_object_t *top;
  1775. struct symbols_cache *cache;
  1776. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  1777. return 0;
  1778. }
  1779. cache = session->ctx->cfg->cache;
  1780. if (cache != NULL) {
  1781. top = rspamd_symbols_cache_counters (cache);
  1782. rspamd_controller_send_ucl (conn_ent, top);
  1783. ucl_object_unref (top);
  1784. }
  1785. else {
  1786. rspamd_controller_send_error (conn_ent, 500, "Invalid cache");
  1787. }
  1788. return 0;
  1789. }
  1790. static int
  1791. rspamd_controller_handle_custom (struct rspamd_http_connection_entry *conn_ent,
  1792. struct rspamd_http_message *msg)
  1793. {
  1794. struct rspamd_controller_session *session = conn_ent->ud;
  1795. struct rspamd_custom_controller_command *cmd;
  1796. gchar *url_str;
  1797. url_str = rspamd_fstring_cstr (msg->url);
  1798. cmd = g_hash_table_lookup (session->ctx->custom_commands, url_str);
  1799. g_free (url_str);
  1800. if (cmd == NULL || cmd->handler == NULL) {
  1801. msg_err_session ("custom command %V has not been found", msg->url);
  1802. rspamd_controller_send_error (conn_ent, 404, "No command associated");
  1803. return 0;
  1804. }
  1805. if (!rspamd_controller_check_password (conn_ent, session, msg,
  1806. cmd->privilleged)) {
  1807. return 0;
  1808. }
  1809. if (cmd->require_message && (msg->body == NULL || msg->body->len == 0)) {
  1810. msg_err_session ("got zero length body, cannot continue");
  1811. rspamd_controller_send_error (conn_ent,
  1812. 400,
  1813. "Empty body is not permitted");
  1814. return 0;
  1815. }
  1816. return cmd->handler (conn_ent, msg, cmd->ctx);
  1817. }
  1818. static void
  1819. rspamd_controller_error_handler (struct rspamd_http_connection_entry *conn_ent,
  1820. GError *err)
  1821. {
  1822. struct rspamd_controller_session *session = conn_ent->ud;
  1823. msg_err_session ("http error occurred: %s", err->message);
  1824. }
  1825. static void
  1826. rspamd_controller_finish_handler (struct rspamd_http_connection_entry *conn_ent)
  1827. {
  1828. struct rspamd_controller_session *session = conn_ent->ud;
  1829. session->ctx->worker->srv->stat->control_connections_count++;
  1830. msg_debug_session ("destroy session %p", session);
  1831. if (session->task != NULL) {
  1832. rspamd_session_destroy (session->task->s);
  1833. }
  1834. if (session->pool) {
  1835. rspamd_mempool_delete (session->pool);
  1836. }
  1837. session->wrk->nconns --;
  1838. rspamd_inet_address_destroy (session->from_addr);
  1839. REF_RELEASE (session->cfg);
  1840. g_slice_free1 (sizeof (struct rspamd_controller_session), session);
  1841. }
  1842. static void
  1843. rspamd_controller_accept_socket (gint fd, short what, void *arg)
  1844. {
  1845. struct rspamd_worker *worker = (struct rspamd_worker *) arg;
  1846. struct rspamd_controller_worker_ctx *ctx;
  1847. struct rspamd_controller_session *session;
  1848. rspamd_inet_addr_t *addr;
  1849. gint nfd;
  1850. ctx = worker->ctx;
  1851. if ((nfd =
  1852. rspamd_accept_from_socket (fd, &addr)) == -1) {
  1853. msg_warn_ctx ("accept failed: %s", strerror (errno));
  1854. return;
  1855. }
  1856. /* Check for EAGAIN */
  1857. if (nfd == 0) {
  1858. return;
  1859. }
  1860. session = g_slice_alloc0 (sizeof (struct rspamd_controller_session));
  1861. session->pool = rspamd_mempool_new (rspamd_mempool_suggest_size (),
  1862. "csession");
  1863. session->ctx = ctx;
  1864. session->cfg = ctx->cfg;
  1865. REF_RETAIN (session->cfg);
  1866. session->from_addr = addr;
  1867. session->wrk = worker;
  1868. worker->nconns ++;
  1869. rspamd_http_router_handle_socket (ctx->http, nfd, session);
  1870. }
  1871. static void
  1872. rspamd_controller_rrd_update (gint fd, short what, void *arg)
  1873. {
  1874. struct rspamd_controller_worker_ctx *ctx = arg;
  1875. struct rspamd_stat *stat;
  1876. GArray ar;
  1877. gdouble points[4];
  1878. GError *err = NULL;
  1879. guint i, j;
  1880. gdouble val;
  1881. g_assert (ctx->rrd != NULL);
  1882. stat = ctx->srv->stat;
  1883. for (i = METRIC_ACTION_REJECT, j = 0;
  1884. i <= METRIC_ACTION_NOACTION && j < G_N_ELEMENTS (points);
  1885. i++) {
  1886. switch (i) {
  1887. case METRIC_ACTION_SOFT_REJECT:
  1888. break;
  1889. case METRIC_ACTION_REWRITE_SUBJECT:
  1890. val = stat->actions_stat[i];
  1891. break;
  1892. case METRIC_ACTION_ADD_HEADER:
  1893. val += stat->actions_stat[i];
  1894. points[j++] = val;
  1895. break;
  1896. default:
  1897. val = stat->actions_stat[i];
  1898. points[j++] = val;
  1899. }
  1900. }
  1901. ar.data = (gchar *)points;
  1902. ar.len = sizeof (points);
  1903. if (!rspamd_rrd_add_record (ctx->rrd, &ar, rspamd_get_calendar_ticks (),
  1904. &err)) {
  1905. msg_err_ctx ("cannot update rrd file: %e", err);
  1906. g_error_free (err);
  1907. }
  1908. /* Plan new event */
  1909. event_del (ctx->rrd_event);
  1910. evtimer_add (ctx->rrd_event, &rrd_update_time);
  1911. }
  1912. static void
  1913. rspamd_controller_load_saved_stats (struct rspamd_controller_worker_ctx *ctx)
  1914. {
  1915. struct ucl_parser *parser;
  1916. ucl_object_t *obj;
  1917. const ucl_object_t *elt, *subelt;
  1918. struct rspamd_stat *stat, stat_copy;
  1919. gint i;
  1920. g_assert (ctx->saved_stats_path != NULL);
  1921. if (access (ctx->saved_stats_path, R_OK) == -1) {
  1922. msg_err_ctx ("cannot load controller stats from %s: %s",
  1923. ctx->saved_stats_path, strerror (errno));
  1924. return;
  1925. }
  1926. parser = ucl_parser_new (0);
  1927. if (!ucl_parser_add_file (parser, ctx->saved_stats_path)) {
  1928. msg_err_ctx ("cannot parse controller stats from %s: %s",
  1929. ctx->saved_stats_path, ucl_parser_get_error (parser));
  1930. ucl_parser_free (parser);
  1931. return;
  1932. }
  1933. obj = ucl_parser_get_object (parser);
  1934. ucl_parser_free (parser);
  1935. stat = ctx->srv->stat;
  1936. memcpy (&stat_copy, stat, sizeof (stat_copy));
  1937. elt = ucl_object_find_key (obj, "scanned");
  1938. if (elt != NULL && ucl_object_type (elt) == UCL_INT) {
  1939. stat_copy.messages_scanned = ucl_object_toint (elt);
  1940. }
  1941. elt = ucl_object_find_key (obj, "learned");
  1942. if (elt != NULL && ucl_object_type (elt) == UCL_INT) {
  1943. stat_copy.messages_learned = ucl_object_toint (elt);
  1944. }
  1945. elt = ucl_object_find_key (obj, "actions");
  1946. if (elt != NULL) {
  1947. for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
  1948. subelt = ucl_object_find_key (elt, rspamd_action_to_str (i));
  1949. if (subelt && ucl_object_type (subelt) == UCL_INT) {
  1950. stat_copy.actions_stat[i] = ucl_object_toint (subelt);
  1951. }
  1952. }
  1953. }
  1954. elt = ucl_object_find_key (obj, "connections_count");
  1955. if (elt != NULL && ucl_object_type (elt) == UCL_INT) {
  1956. stat_copy.connections_count = ucl_object_toint (elt);
  1957. }
  1958. elt = ucl_object_find_key (obj, "control_connections_count");
  1959. if (elt != NULL && ucl_object_type (elt) == UCL_INT) {
  1960. stat_copy.control_connections_count = ucl_object_toint (elt);
  1961. }
  1962. ucl_object_unref (obj);
  1963. memcpy (stat, &stat_copy, sizeof (stat_copy));
  1964. }
  1965. static void
  1966. rspamd_controller_store_saved_stats (struct rspamd_controller_worker_ctx *ctx)
  1967. {
  1968. struct rspamd_stat *stat;
  1969. ucl_object_t *top, *sub;
  1970. gint i, fd;
  1971. g_assert (ctx->saved_stats_path != NULL);
  1972. fd = open (ctx->saved_stats_path, O_WRONLY|O_CREAT|O_TRUNC, 00644);
  1973. if (fd == -1) {
  1974. msg_err_ctx ("cannot load controller stats from %s: %s",
  1975. ctx->saved_stats_path, strerror (errno));
  1976. return;
  1977. }
  1978. if (rspamd_file_lock (fd, FALSE) == -1) {
  1979. msg_err_ctx ("cannot load controller stats from %s: %s",
  1980. ctx->saved_stats_path, strerror (errno));
  1981. close (fd);
  1982. return;
  1983. }
  1984. stat = ctx->srv->stat;
  1985. top = ucl_object_typed_new (UCL_OBJECT);
  1986. ucl_object_insert_key (top, ucl_object_fromint (
  1987. stat->messages_scanned), "scanned", 0, false);
  1988. ucl_object_insert_key (top, ucl_object_fromint (
  1989. stat->messages_learned), "learned", 0, false);
  1990. if (stat->messages_scanned > 0) {
  1991. sub = ucl_object_typed_new (UCL_OBJECT);
  1992. for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
  1993. ucl_object_insert_key (sub,
  1994. ucl_object_fromint (stat->actions_stat[i]),
  1995. rspamd_action_to_str (i), 0, false);
  1996. }
  1997. ucl_object_insert_key (top, sub, "actions", 0, false);
  1998. }
  1999. ucl_object_insert_key (top,
  2000. ucl_object_fromint (stat->connections_count), "connections", 0, false);
  2001. ucl_object_insert_key (top,
  2002. ucl_object_fromint (stat->control_connections_count),
  2003. "control_connections", 0, false);
  2004. ucl_object_emit_full (top, UCL_EMIT_JSON_COMPACT,
  2005. ucl_object_emit_fd_funcs (fd), NULL);
  2006. rspamd_file_unlock (fd, FALSE);
  2007. close (fd);
  2008. }
  2009. static void
  2010. rspamd_controller_password_sane (struct rspamd_controller_worker_ctx *ctx,
  2011. const gchar *password, const gchar *type)
  2012. {
  2013. const struct rspamd_controller_pbkdf *pbkdf = &pbkdf_list[0];
  2014. if (password == NULL) {
  2015. msg_warn_ctx ("%s is not set, so you should filter controller "
  2016. "availability "
  2017. "by using of firewall or `secure_ip` option", type);
  2018. return;
  2019. }
  2020. g_assert (pbkdf != NULL);
  2021. if (!rspamd_is_encrypted_password (password, NULL)) {
  2022. /* Suggest encryption to a user */
  2023. msg_warn_ctx ("your %s is not encrypted, we strongly "
  2024. "recommend to replace it with the encrypted one", type);
  2025. }
  2026. }
  2027. gpointer
  2028. init_controller_worker (struct rspamd_config *cfg)
  2029. {
  2030. struct rspamd_controller_worker_ctx *ctx;
  2031. GQuark type;
  2032. type = g_quark_try_string ("controller");
  2033. ctx = g_malloc0 (sizeof (struct rspamd_controller_worker_ctx));
  2034. ctx->timeout = DEFAULT_WORKER_IO_TIMEOUT;
  2035. rspamd_rcl_register_worker_option (cfg,
  2036. type,
  2037. "password",
  2038. rspamd_rcl_parse_struct_string,
  2039. ctx,
  2040. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, password),
  2041. 0,
  2042. "Password for read-only commands");
  2043. rspamd_rcl_register_worker_option (cfg,
  2044. type,
  2045. "enable_password",
  2046. rspamd_rcl_parse_struct_string,
  2047. ctx,
  2048. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2049. enable_password),
  2050. 0,
  2051. "Password for read and write commands");
  2052. rspamd_rcl_register_worker_option (cfg,
  2053. type,
  2054. "ssl",
  2055. rspamd_rcl_parse_struct_boolean,
  2056. ctx,
  2057. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, use_ssl),
  2058. 0,
  2059. "Unimplemented");
  2060. rspamd_rcl_register_worker_option (cfg,
  2061. type,
  2062. "ssl_cert",
  2063. rspamd_rcl_parse_struct_string,
  2064. ctx,
  2065. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, ssl_cert),
  2066. 0,
  2067. "Unimplemented");
  2068. rspamd_rcl_register_worker_option (cfg,
  2069. type,
  2070. "ssl_key",
  2071. rspamd_rcl_parse_struct_string,
  2072. ctx,
  2073. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, ssl_key),
  2074. 0,
  2075. "Unimplemented");
  2076. rspamd_rcl_register_worker_option (cfg,
  2077. type,
  2078. "timeout",
  2079. rspamd_rcl_parse_struct_time,
  2080. ctx,
  2081. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2082. timeout),
  2083. RSPAMD_CL_FLAG_TIME_INTEGER,
  2084. "Protocol timeout");
  2085. rspamd_rcl_register_worker_option (cfg,
  2086. type,
  2087. "secure_ip",
  2088. rspamd_rcl_parse_struct_string_list,
  2089. ctx,
  2090. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, secure_ip),
  2091. 0,
  2092. "List of IP addresses that are allowed for password-less access");
  2093. rspamd_rcl_register_worker_option (cfg,
  2094. type,
  2095. "static_dir",
  2096. rspamd_rcl_parse_struct_string,
  2097. ctx,
  2098. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2099. static_files_dir),
  2100. 0,
  2101. "Directory for static files served by controller's HTTP server");
  2102. rspamd_rcl_register_worker_option (cfg,
  2103. type,
  2104. "keypair",
  2105. rspamd_rcl_parse_struct_keypair,
  2106. ctx,
  2107. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2108. key),
  2109. 0,
  2110. "Encryption keypair");
  2111. rspamd_rcl_register_worker_option (cfg,
  2112. type,
  2113. "stats_path",
  2114. rspamd_rcl_parse_struct_string,
  2115. ctx,
  2116. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2117. saved_stats_path),
  2118. 0,
  2119. "Directory where controller saves server's statistics between restarts");
  2120. return ctx;
  2121. }
  2122. /*
  2123. * Start worker process
  2124. */
  2125. void
  2126. start_controller_worker (struct rspamd_worker *worker)
  2127. {
  2128. struct rspamd_controller_worker_ctx *ctx = worker->ctx;
  2129. GList *cur;
  2130. struct module_ctx *mctx;
  2131. GHashTableIter iter;
  2132. gpointer key, value;
  2133. struct rspamd_keypair_cache *cache;
  2134. gchar *secure_ip;
  2135. gpointer m;
  2136. ctx->ev_base = rspamd_prepare_worker (worker,
  2137. "controller",
  2138. rspamd_controller_accept_socket);
  2139. msec_to_tv (ctx->timeout, &ctx->io_tv);
  2140. ctx->start_time = time (NULL);
  2141. ctx->worker = worker;
  2142. ctx->cfg = worker->srv->cfg;
  2143. ctx->srv = worker->srv;
  2144. ctx->custom_commands = g_hash_table_new (rspamd_strcase_hash,
  2145. rspamd_strcase_equal);
  2146. if (ctx->secure_ip != NULL) {
  2147. cur = ctx->secure_ip;
  2148. while (cur) {
  2149. secure_ip = cur->data;
  2150. /* Try map syntax */
  2151. if (!rspamd_map_add (worker->srv->cfg, secure_ip,
  2152. "Allow webui access from the specified IP",
  2153. rspamd_radix_read, rspamd_radix_fin, (void **)&ctx->secure_map)) {
  2154. /* Fallback to the plain IP */
  2155. if (!radix_add_generic_iplist (secure_ip,
  2156. &ctx->secure_map)) {
  2157. msg_warn_ctx ("cannot load or parse ip list from '%s'",
  2158. secure_ip);
  2159. }
  2160. }
  2161. cur = g_list_next (cur);
  2162. }
  2163. }
  2164. if (ctx->saved_stats_path == NULL) {
  2165. /* Assume default path */
  2166. ctx->saved_stats_path = rspamd_mempool_strdup (worker->srv->cfg->cfg_pool,
  2167. DEFAULT_STATS_PATH);
  2168. }
  2169. rspamd_controller_load_saved_stats (ctx);
  2170. /* RRD collector */
  2171. if (ctx->cfg->rrd_file && worker->index == 0) {
  2172. ctx->rrd = rspamd_rrd_file_default (ctx->cfg->rrd_file, NULL);
  2173. if (ctx->rrd) {
  2174. ctx->rrd_event = g_slice_alloc0 (sizeof (*ctx->rrd_event));
  2175. evtimer_set (ctx->rrd_event, rspamd_controller_rrd_update, ctx);
  2176. event_base_set (ctx->ev_base, ctx->rrd_event);
  2177. event_add (ctx->rrd_event, &rrd_update_time);
  2178. }
  2179. }
  2180. else {
  2181. ctx->rrd = NULL;
  2182. }
  2183. rspamd_controller_password_sane (ctx, ctx->password, "normal password");
  2184. rspamd_controller_password_sane (ctx, ctx->enable_password, "enable "
  2185. "password");
  2186. /* Accept event */
  2187. cache = rspamd_keypair_cache_new (256);
  2188. ctx->http = rspamd_http_router_new (rspamd_controller_error_handler,
  2189. rspamd_controller_finish_handler, &ctx->io_tv, ctx->ev_base,
  2190. ctx->static_files_dir, cache);
  2191. /* Add callbacks for different methods */
  2192. rspamd_http_router_add_path (ctx->http,
  2193. PATH_AUTH,
  2194. rspamd_controller_handle_auth);
  2195. rspamd_http_router_add_path (ctx->http,
  2196. PATH_SYMBOLS,
  2197. rspamd_controller_handle_symbols);
  2198. rspamd_http_router_add_path (ctx->http,
  2199. PATH_ACTIONS,
  2200. rspamd_controller_handle_actions);
  2201. rspamd_http_router_add_path (ctx->http,
  2202. PATH_MAPS,
  2203. rspamd_controller_handle_maps);
  2204. rspamd_http_router_add_path (ctx->http,
  2205. PATH_GET_MAP,
  2206. rspamd_controller_handle_get_map);
  2207. rspamd_http_router_add_path (ctx->http,
  2208. PATH_PIE_CHART,
  2209. rspamd_controller_handle_pie_chart);
  2210. rspamd_http_router_add_path (ctx->http,
  2211. PATH_GRAPH,
  2212. rspamd_controller_handle_graph);
  2213. rspamd_http_router_add_path (ctx->http,
  2214. PATH_HISTORY,
  2215. rspamd_controller_handle_history);
  2216. rspamd_http_router_add_path (ctx->http,
  2217. PATH_HISTORY_RESET,
  2218. rspamd_controller_handle_history_reset);
  2219. rspamd_http_router_add_path (ctx->http,
  2220. PATH_LEARN_SPAM,
  2221. rspamd_controller_handle_learnspam);
  2222. rspamd_http_router_add_path (ctx->http,
  2223. PATH_LEARN_HAM,
  2224. rspamd_controller_handle_learnham);
  2225. rspamd_http_router_add_path (ctx->http,
  2226. PATH_SAVE_ACTIONS,
  2227. rspamd_controller_handle_saveactions);
  2228. rspamd_http_router_add_path (ctx->http,
  2229. PATH_SAVE_SYMBOLS,
  2230. rspamd_controller_handle_savesymbols);
  2231. rspamd_http_router_add_path (ctx->http,
  2232. PATH_SAVE_MAP,
  2233. rspamd_controller_handle_savemap);
  2234. rspamd_http_router_add_path (ctx->http,
  2235. PATH_SCAN,
  2236. rspamd_controller_handle_scan);
  2237. rspamd_http_router_add_path (ctx->http,
  2238. PATH_CHECK,
  2239. rspamd_controller_handle_scan);
  2240. rspamd_http_router_add_path (ctx->http,
  2241. PATH_STAT,
  2242. rspamd_controller_handle_stat);
  2243. rspamd_http_router_add_path (ctx->http,
  2244. PATH_STAT_RESET,
  2245. rspamd_controller_handle_statreset);
  2246. rspamd_http_router_add_path (ctx->http,
  2247. PATH_COUNTERS,
  2248. rspamd_controller_handle_counters);
  2249. if (ctx->key) {
  2250. rspamd_http_router_set_key (ctx->http, ctx->key);
  2251. }
  2252. g_hash_table_iter_init (&iter, ctx->cfg->c_modules);
  2253. while (g_hash_table_iter_next (&iter, &key, &value)) {
  2254. mctx = value;
  2255. if (mctx->mod->module_attach_controller_func != NULL) {
  2256. mctx->mod->module_attach_controller_func (mctx,
  2257. ctx->custom_commands);
  2258. }
  2259. }
  2260. g_hash_table_iter_init (&iter, ctx->custom_commands);
  2261. while (g_hash_table_iter_next (&iter, &key, &value)) {
  2262. rspamd_http_router_add_path (ctx->http,
  2263. key,
  2264. rspamd_controller_handle_custom);
  2265. }
  2266. ctx->resolver = dns_resolver_init (worker->srv->logger,
  2267. ctx->ev_base,
  2268. worker->srv->cfg);
  2269. rspamd_upstreams_library_config (worker->srv->cfg, worker->srv->cfg->ups_ctx,
  2270. ctx->ev_base, ctx->resolver->r);
  2271. /* Maps events */
  2272. rspamd_map_watch (worker->srv->cfg, ctx->ev_base);
  2273. rspamd_symbols_cache_start_refresh (worker->srv->cfg->cache, ctx->ev_base);
  2274. rspamd_stat_init (worker->srv->cfg, ctx->ev_base);
  2275. event_base_loop (ctx->ev_base, 0);
  2276. rspamd_worker_block_signals ();
  2277. g_mime_shutdown ();
  2278. rspamd_stat_close ();
  2279. rspamd_http_router_free (ctx->http);
  2280. rspamd_log_close (worker->srv->logger);
  2281. rspamd_controller_store_saved_stats (ctx);
  2282. if (ctx->rrd) {
  2283. rspamd_rrd_close (ctx->rrd);
  2284. }
  2285. if (ctx->cached_password.len > 0) {
  2286. m = (gpointer)ctx->cached_password.begin;
  2287. munmap (m, ctx->cached_password.len);
  2288. }
  2289. if (ctx->cached_enable_password.len > 0) {
  2290. m = (gpointer) ctx->cached_enable_password.begin;
  2291. munmap (m, ctx->cached_enable_password.len);
  2292. }
  2293. exit (EXIT_SUCCESS);
  2294. }