1 /*-
2 * Copyright 2017 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
17 #include "config.h"
18 #include "milter.h"
19 #include "milter_internal.h"
20 #include "email_addr.h"
21 #include "addr.h"
22 #include "unix-std.h"
23 #include "logger.h"
24 #include "ottery.h"
25 #include "libserver/http/http_connection.h"
26 #include "libserver/http/http_private.h"
27 #include "libserver/protocol_internal.h"
28 #include "libserver/cfg_file_private.h"
29 #include "libmime/scan_result.h"
30 #include "libserver/worker_util.h"
31 #include "utlist.h"
32
33 #define msg_err_milter(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
34 "milter", priv->pool->tag.uid, \
35 G_STRFUNC, \
36 __VA_ARGS__)
37 #define msg_warn_milter(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
38 "milter", priv->pool->tag.uid, \
39 G_STRFUNC, \
40 __VA_ARGS__)
41 #define msg_info_milter(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \
42 "milter", priv->pool->tag.uid, \
43 G_STRFUNC, \
44 __VA_ARGS__)
45 #define msg_debug_milter(...) rspamd_conditional_debug_fast (NULL, NULL, \
46 rspamd_milter_log_id, "milter", priv->pool->tag.uid, \
47 G_STRFUNC, \
48 __VA_ARGS__)
49
50 INIT_LOG_MODULE(milter)
51
52 static const struct rspamd_milter_context *milter_ctx = NULL;
53
54 static gboolean rspamd_milter_handle_session (
55 struct rspamd_milter_session *session,
56 struct rspamd_milter_private *priv);
57 static inline void rspamd_milter_plan_io (struct rspamd_milter_session *session,
58 struct rspamd_milter_private *priv, gshort what);
59
60 static GQuark
rspamd_milter_quark(void)61 rspamd_milter_quark (void)
62 {
63 return g_quark_from_static_string ("milter");
64 }
65
66 static void
rspamd_milter_obuf_free(struct rspamd_milter_outbuf * obuf)67 rspamd_milter_obuf_free (struct rspamd_milter_outbuf *obuf)
68 {
69 if (obuf) {
70 if (obuf->buf) {
71 rspamd_fstring_free (obuf->buf);
72 }
73
74 g_free (obuf);
75 }
76 }
77
78 #define RSPAMD_MILTER_RESET_COMMON (1 << 0)
79 #define RSPAMD_MILTER_RESET_IO (1 << 1)
80 #define RSPAMD_MILTER_RESET_ADDR (1 << 2)
81 #define RSPAMD_MILTER_RESET_MACRO (1 << 3)
82 #define RSPAMD_MILTER_RESET_ALL (RSPAMD_MILTER_RESET_COMMON | \
83 RSPAMD_MILTER_RESET_IO | \
84 RSPAMD_MILTER_RESET_ADDR | \
85 RSPAMD_MILTER_RESET_MACRO)
86 #define RSPAMD_MILTER_RESET_QUIT_NC (RSPAMD_MILTER_RESET_COMMON | \
87 RSPAMD_MILTER_RESET_ADDR | \
88 RSPAMD_MILTER_RESET_MACRO)
89 #define RSPAMD_MILTER_RESET_ABORT (RSPAMD_MILTER_RESET_COMMON)
90
91 static void
rspamd_milter_session_reset(struct rspamd_milter_session * session,guint how)92 rspamd_milter_session_reset (struct rspamd_milter_session *session,
93 guint how)
94 {
95 struct rspamd_milter_outbuf *obuf, *obuf_tmp;
96 struct rspamd_milter_private *priv = session->priv;
97 struct rspamd_email_address *cur;
98 guint i;
99
100 if (how & RSPAMD_MILTER_RESET_IO) {
101 msg_debug_milter ("cleanup IO on abort");
102
103 DL_FOREACH_SAFE (priv->out_chain, obuf, obuf_tmp) {
104 rspamd_milter_obuf_free (obuf);
105 }
106
107 priv->out_chain = NULL;
108
109 if (priv->parser.buf) {
110 priv->parser.buf->len = 0;
111 }
112 }
113
114 if (how & RSPAMD_MILTER_RESET_COMMON) {
115 msg_debug_milter ("cleanup common data on abort");
116
117 if (session->message) {
118 session->message->len = 0;
119 msg_debug_milter ("cleanup message on abort");
120 }
121
122 if (session->rcpts) {
123 PTR_ARRAY_FOREACH (session->rcpts, i, cur) {
124 rspamd_email_address_free (cur);
125 }
126
127 msg_debug_milter ("cleanup %d recipients on abort",
128 (gint)session->rcpts->len);
129
130 g_ptr_array_free (session->rcpts, TRUE);
131 session->rcpts = NULL;
132 }
133
134 if (session->from) {
135 msg_debug_milter ("cleanup from");
136 rspamd_email_address_free (session->from);
137 session->from = NULL;
138 }
139
140 if (priv->headers) {
141 msg_debug_milter ("cleanup headers");
142 gchar *k;
143 GArray *ar;
144
145 kh_foreach (priv->headers, k, ar, {
146 g_free (k);
147 g_array_free (ar, TRUE);
148 });
149
150 kh_clear (milter_headers_hash_t, priv->headers);
151 }
152
153 priv->cur_hdr = 0;
154 }
155
156 if (how & RSPAMD_MILTER_RESET_ADDR) {
157 if (session->addr) {
158 msg_debug_milter ("cleanup addr");
159 rspamd_inet_address_free (session->addr);
160 session->addr = NULL;
161 }
162 if (session->hostname) {
163 msg_debug_milter ("cleanup hostname");
164 session->hostname->len = 0;
165 }
166 }
167
168 if (how & RSPAMD_MILTER_RESET_MACRO) {
169 if (session->macros) {
170 msg_debug_milter ("cleanup macros");
171 g_hash_table_unref (session->macros);
172 session->macros = NULL;
173 }
174 }
175 }
176
177 static void
rspamd_milter_session_dtor(struct rspamd_milter_session * session)178 rspamd_milter_session_dtor (struct rspamd_milter_session *session)
179 {
180 struct rspamd_milter_private *priv;
181
182 if (session) {
183 priv = session->priv;
184 msg_debug_milter ("destroying milter session");
185
186 rspamd_ev_watcher_stop (priv->event_loop, &priv->ev);
187 rspamd_milter_session_reset (session, RSPAMD_MILTER_RESET_ALL);
188 close (priv->fd);
189
190 if (priv->parser.buf) {
191 rspamd_fstring_free (priv->parser.buf);
192 }
193
194 if (session->message) {
195 rspamd_fstring_free (session->message);
196 }
197
198 if (session->helo) {
199 rspamd_fstring_free (session->helo);
200 }
201
202 if (session->hostname) {
203 rspamd_fstring_free (session->hostname);
204 }
205
206 if (priv->headers) {
207 gchar *k;
208 GArray *ar;
209
210 kh_foreach (priv->headers, k, ar, {
211 g_free (k);
212 g_array_free (ar, TRUE);
213 });
214
215 kh_destroy (milter_headers_hash_t, priv->headers);
216 }
217
218 if (milter_ctx->sessions_cache) {
219 rspamd_worker_session_cache_remove (milter_ctx->sessions_cache,
220 session);
221 }
222
223 rspamd_mempool_delete (priv->pool);
224 g_free (priv);
225 g_free (session);
226 }
227 }
228
229 static void
rspamd_milter_on_protocol_error(struct rspamd_milter_session * session,struct rspamd_milter_private * priv,GError * err)230 rspamd_milter_on_protocol_error (struct rspamd_milter_session *session,
231 struct rspamd_milter_private *priv, GError *err)
232 {
233 msg_debug_milter ("protocol error: %e", err);
234 priv->state = RSPAMD_MILTER_WANNA_DIE;
235 REF_RETAIN (session);
236 priv->err_cb (priv->fd, session, priv->ud, err);
237 REF_RELEASE (session);
238 g_error_free (err);
239
240 rspamd_milter_plan_io (session, priv, EV_WRITE);
241 }
242
243 static void
rspamd_milter_on_protocol_ping(struct rspamd_milter_session * session,struct rspamd_milter_private * priv)244 rspamd_milter_on_protocol_ping (struct rspamd_milter_session *session,
245 struct rspamd_milter_private *priv)
246 {
247 GError *err = NULL;
248 static const gchar reply[] = "HTTP/1.1 200 OK\r\n"
249 "Connection: close\r\n"
250 "Server: rspamd/2.7 (milter mode)\r\n"
251 "Content-Length: 6\r\n"
252 "Content-Type: text/plain\r\n"
253 "\r\n"
254 "pong\r\n";
255
256 if (write (priv->fd, reply, sizeof (reply)) == -1) {
257 gint serrno = errno;
258 msg_err_milter ("cannot write pong reply: %s", strerror (serrno));
259 g_set_error (&err, rspamd_milter_quark (), serrno, "ping command IO error: %s",
260 strerror (serrno));
261 priv->state = RSPAMD_MILTER_WANNA_DIE;
262 REF_RETAIN (session);
263 priv->err_cb (priv->fd, session, priv->ud, err);
264 REF_RELEASE (session);
265 g_error_free (err);
266 }
267 else {
268 priv->state = RSPAMD_MILTER_PONG_AND_DIE;
269 rspamd_milter_plan_io (session, priv, EV_WRITE);
270 }
271 }
272
273 static gint
rspamd_milter_http_on_url(http_parser * parser,const gchar * at,size_t length)274 rspamd_milter_http_on_url (http_parser * parser, const gchar *at, size_t length)
275 {
276 GString *url = (GString *)parser->data;
277
278 g_string_append_len (url, at, length);
279
280 return 0;
281 }
282
283 static void
rspamd_milter_io_handler(gint fd,gshort what,void * ud)284 rspamd_milter_io_handler (gint fd, gshort what, void *ud)
285 {
286 struct rspamd_milter_session *session = ud;
287 struct rspamd_milter_private *priv;
288 GError *err;
289
290 priv = session->priv;
291
292 if (what == EV_TIMEOUT) {
293 msg_debug_milter ("connection timed out");
294 err = g_error_new (rspamd_milter_quark (), ETIMEDOUT, "connection "
295 "timed out");
296 rspamd_milter_on_protocol_error (session, priv, err);
297 }
298 else {
299 rspamd_milter_handle_session (session, priv);
300 }
301 }
302
303 static inline void
rspamd_milter_plan_io(struct rspamd_milter_session * session,struct rspamd_milter_private * priv,gshort what)304 rspamd_milter_plan_io (struct rspamd_milter_session *session,
305 struct rspamd_milter_private *priv, gshort what)
306 {
307 rspamd_ev_watcher_reschedule (priv->event_loop, &priv->ev, what);
308 }
309
310
311 #define READ_INT_32(pos, var) do { \
312 memcpy (&(var), (pos), sizeof (var)); \
313 (pos) += sizeof (var); \
314 (var) = ntohl (var); \
315 } while (0)
316 #define READ_INT_16(pos, var) do { \
317 memcpy (&(var), (pos), sizeof (var)); \
318 (pos) += sizeof (var); \
319 (var) = ntohs (var); \
320 } while (0)
321
322 static gboolean
rspamd_milter_process_command(struct rspamd_milter_session * session,struct rspamd_milter_private * priv)323 rspamd_milter_process_command (struct rspamd_milter_session *session,
324 struct rspamd_milter_private *priv)
325 {
326 GError *err;
327 rspamd_fstring_t *buf;
328 const guchar *pos, *end, *zero;
329 guint cmdlen;
330 guint32 version, actions, protocol;
331
332 buf = priv->parser.buf;
333 pos = buf->str + priv->parser.cmd_start;
334 cmdlen = priv->parser.datalen;
335 end = pos + cmdlen;
336
337 switch (priv->parser.cur_cmd) {
338 case RSPAMD_MILTER_CMD_ABORT:
339 msg_debug_milter ("got abort command");
340 rspamd_milter_session_reset (session, RSPAMD_MILTER_RESET_ABORT);
341 break;
342 case RSPAMD_MILTER_CMD_BODY:
343 if (!session->message) {
344 session->message = rspamd_fstring_sized_new (
345 RSPAMD_MILTER_MESSAGE_CHUNK);
346 }
347
348 msg_debug_milter ("got body chunk: %d bytes", (int)cmdlen);
349 session->message = rspamd_fstring_append (session->message,
350 pos, cmdlen);
351 break;
352 case RSPAMD_MILTER_CMD_CONNECT:
353 msg_debug_milter ("got connect command");
354
355 /*
356 * char hostname[]: Hostname, NUL terminated
357 * char family: Protocol family
358 * uint16 port: Port number (SMFIA_INET or SMFIA_INET6 only)
359 * char address[]: IP address (ASCII) or unix socket path, NUL terminated
360 */
361 zero = memchr (pos, '\0', cmdlen);
362
363 if (zero == NULL || zero > (end - sizeof (guint16) + 1)) {
364 err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
365 "connect command (no name)");
366 rspamd_milter_on_protocol_error (session, priv, err);
367
368 return FALSE;
369 }
370 else {
371 guchar proto;
372 guint16 port;
373 gchar ip6_str[INET6_ADDRSTRLEN + 3];
374 gsize r;
375
376 /*
377 * Important notice: Postfix do NOT use this command to pass
378 * client's info (e.g. hostname is not really here)
379 * Sendmail will pass it here
380 */
381 if (session->hostname == NULL) {
382 session->hostname = rspamd_fstring_new_init (pos, zero - pos);
383 msg_debug_milter ("got hostname on connect phase: %V",
384 session->hostname);
385 }
386 else {
387 session->hostname = rspamd_fstring_assign (session->hostname,
388 pos, zero - pos);
389 msg_debug_milter ("rewrote hostname on connect phase: %V",
390 session->hostname);
391 }
392
393 pos = zero + 1;
394 proto = *pos ++;
395
396 if (proto == RSPAMD_MILTER_CONN_UNKNOWN) {
397 /* We have no information about host */
398 msg_debug_milter ("unknown connect address");
399 }
400 else {
401 READ_INT_16 (pos, port);
402
403 if (pos >= end) {
404 /* No IP somehow */
405 msg_debug_milter ("unknown connect IP/socket");
406 }
407 else {
408 zero = memchr (pos, '\0', end - pos);
409
410 if (zero == NULL) {
411 err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
412 "connect command (no zero terminated IP)");
413 rspamd_milter_on_protocol_error (session, priv, err);
414
415 return FALSE;
416 }
417
418 switch (proto) {
419 case RSPAMD_MILTER_CONN_UNIX:
420 session->addr = rspamd_inet_address_new (AF_UNIX,
421 pos);
422 break;
423
424 case RSPAMD_MILTER_CONN_INET:
425 session->addr = rspamd_inet_address_new (AF_INET, NULL);
426
427 if (!rspamd_parse_inet_address_ip (pos, zero - pos,
428 session->addr)) {
429 err = g_error_new (rspamd_milter_quark (), EINVAL,
430 "invalid connect command (bad IPv4)");
431 rspamd_milter_on_protocol_error (session, priv,
432 err);
433
434 return FALSE;
435 }
436
437 rspamd_inet_address_set_port (session->addr, port);
438 break;
439
440 case RSPAMD_MILTER_CONN_INET6:
441 session->addr = rspamd_inet_address_new (AF_INET6, NULL);
442
443 if (zero - pos > sizeof ("IPv6:") &&
444 rspamd_lc_cmp (pos, "IPv6:",
445 sizeof ("IPv6:") - 1) == 0) {
446 /* Kill sendmail please */
447 pos += sizeof ("IPv6:") - 1;
448
449 if (*pos != '[') {
450 /* Add explicit braces */
451 r = rspamd_snprintf (ip6_str, sizeof (ip6_str),
452 "[%*s]", (int)(zero - pos), pos);
453 }
454 else {
455 r = rspamd_strlcpy (ip6_str, pos, sizeof (ip6_str));
456 }
457 }
458 else {
459 r = rspamd_strlcpy (ip6_str, pos, sizeof (ip6_str));
460 }
461
462 if (!rspamd_parse_inet_address_ip (ip6_str, r,
463 session->addr)) {
464 err = g_error_new (rspamd_milter_quark (), EINVAL,
465 "invalid connect command (bad IPv6)");
466 rspamd_milter_on_protocol_error (session, priv,
467 err);
468
469 return FALSE;
470 }
471
472 rspamd_inet_address_set_port (session->addr, port);
473 break;
474
475 default:
476 err = g_error_new (rspamd_milter_quark (), EINVAL,
477 "invalid connect command (bad protocol: %c)",
478 proto);
479 rspamd_milter_on_protocol_error (session, priv,
480 err);
481
482 return FALSE;
483 }
484 }
485 }
486
487 msg_info_milter ("got connection from %s",
488 rspamd_inet_address_to_string_pretty (session->addr));
489 }
490 break;
491 case RSPAMD_MILTER_CMD_MACRO:
492 msg_debug_milter ("got macro command");
493 /*
494 * Format is
495 * 1 byte - command associated (we don't care about it)
496 * 0-terminated name
497 * 0-terminated value
498 * ...
499 */
500 if (session->macros == NULL) {
501 session->macros = g_hash_table_new_full (rspamd_ftok_icase_hash,
502 rspamd_ftok_icase_equal,
503 rspamd_fstring_mapped_ftok_free,
504 rspamd_fstring_mapped_ftok_free);
505 }
506
507 /* Ignore one byte */
508 pos ++;
509
510 while (pos < end) {
511 zero = memchr (pos, '\0', cmdlen);
512
513 if (zero == NULL || zero >= end) {
514 err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
515 "macro command (no name)");
516 rspamd_milter_on_protocol_error (session, priv, err);
517
518 return FALSE;
519 }
520 else {
521 rspamd_fstring_t *name, *value;
522 rspamd_ftok_t *name_tok, *value_tok;
523 const guchar *zero_val;
524
525 zero_val = memchr (zero + 1, '\0', end - zero - 1);
526
527 if (zero_val != NULL && end > zero_val) {
528 name = rspamd_fstring_new_init (pos, zero - pos);
529 value = rspamd_fstring_new_init (zero + 1,
530 zero_val - zero - 1);
531 name_tok = rspamd_ftok_map (name);
532 value_tok = rspamd_ftok_map (value);
533
534 g_hash_table_replace (session->macros, name_tok, value_tok);
535 msg_debug_milter ("got macro: %T -> %T",
536 name_tok, value_tok);
537
538 cmdlen -= zero_val - pos;
539 pos = zero_val + 1;
540 }
541 else {
542 err = g_error_new (rspamd_milter_quark (), EINVAL,
543 "invalid macro command (bad value)");
544 rspamd_milter_on_protocol_error (session, priv, err);
545
546 return FALSE;
547 }
548 }
549 }
550 break;
551 case RSPAMD_MILTER_CMD_BODYEOB:
552 msg_debug_milter ("got eob command");
553 REF_RETAIN (session);
554 priv->fin_cb (priv->fd, session, priv->ud);
555 REF_RELEASE (session);
556 break;
557 case RSPAMD_MILTER_CMD_HELO:
558 msg_debug_milter ("got helo command");
559
560 if (end > pos && *(end - 1) == '\0') {
561 if (session->helo == NULL) {
562 session->helo = rspamd_fstring_new_init (pos, cmdlen - 1);
563 }
564 else {
565 session->helo = rspamd_fstring_assign (session->helo,
566 pos, cmdlen - 1);
567 }
568 }
569 else if (end > pos) {
570 /* Should not happen */
571 if (session->helo == NULL) {
572 session->helo = rspamd_fstring_new_init (pos, cmdlen);
573 }
574 else {
575 session->helo = rspamd_fstring_assign (session->helo,
576 pos, cmdlen);
577 }
578 }
579
580 msg_debug_milter ("got helo value: %V", session->helo);
581
582 break;
583 case RSPAMD_MILTER_CMD_QUIT_NC:
584 /* We need to reset session and start over */
585 msg_debug_milter ("got quit_nc command");
586 rspamd_milter_session_reset (session, RSPAMD_MILTER_RESET_QUIT_NC);
587 break;
588 case RSPAMD_MILTER_CMD_HEADER:
589 msg_debug_milter ("got header command");
590 if (!session->message) {
591 session->message = rspamd_fstring_sized_new (
592 RSPAMD_MILTER_MESSAGE_CHUNK);
593 }
594 zero = memchr (pos, '\0', cmdlen);
595
596 if (zero == NULL) {
597 err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
598 "header command (no name)");
599 rspamd_milter_on_protocol_error (session, priv, err);
600
601 return FALSE;
602 }
603 else {
604 if (end > zero && *(end - 1) == '\0') {
605 khiter_t k;
606 gint res;
607
608 k = kh_get (milter_headers_hash_t, priv->headers, (gchar *)pos);
609
610 if (k == kh_end (priv->headers)) {
611 GArray *ar;
612
613 k = kh_put (milter_headers_hash_t, priv->headers,
614 g_strdup (pos), &res);
615 ar = g_array_new (FALSE, FALSE, sizeof (gint));
616 g_array_append_val (ar, priv->cur_hdr);
617 kh_value (priv->headers, k) = ar;
618 }
619 else {
620 g_array_append_val (kh_value (priv->headers, k),
621 priv->cur_hdr);
622 }
623
624 rspamd_printf_fstring (&session->message, "%*s: %*s\r\n",
625 (int)(zero - pos), pos,
626 (int)(end - zero - 2), zero + 1);
627 priv->cur_hdr ++;
628 }
629 else {
630 err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
631 "header command (bad value)");
632 rspamd_milter_on_protocol_error (session, priv, err);
633
634 return FALSE;
635 }
636 }
637 break;
638 case RSPAMD_MILTER_CMD_MAIL:
639 msg_debug_milter ("mail command");
640
641 while (pos < end) {
642 struct rspamd_email_address *addr;
643 gchar *cpy;
644
645 zero = memchr (pos, '\0', end - pos);
646
647 if (zero && zero > pos) {
648 cpy = rspamd_mempool_alloc (priv->pool, zero - pos);
649 memcpy (cpy, pos, zero - pos);
650 msg_debug_milter ("got mail: %*s", (int)(zero - pos), cpy);
651 addr = rspamd_email_address_from_smtp (cpy, zero - pos);
652
653 if (addr) {
654 session->from = addr;
655 }
656
657 /* TODO: parse esmtp arguments */
658 break;
659 }
660 else {
661 msg_debug_milter ("got weird from: %*s", (int)(end - pos),
662 pos);
663 /* That actually should not happen */
664 cpy = rspamd_mempool_alloc (priv->pool, end - pos);
665 memcpy (cpy, pos, end - pos);
666 addr = rspamd_email_address_from_smtp (cpy, end - pos);
667
668 if (addr) {
669 session->from = addr;
670 }
671
672 break;
673 }
674 }
675 break;
676 case RSPAMD_MILTER_CMD_EOH:
677 msg_debug_milter ("got eoh command");
678
679 if (!session->message) {
680 session->message = rspamd_fstring_sized_new (
681 RSPAMD_MILTER_MESSAGE_CHUNK);
682 }
683
684 session->message = rspamd_fstring_append (session->message,
685 "\r\n", 2);
686 break;
687 case RSPAMD_MILTER_CMD_OPTNEG:
688 if (cmdlen != sizeof (guint32) * 3) {
689 err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
690 "optneg command");
691 rspamd_milter_on_protocol_error (session, priv, err);
692
693 return FALSE;
694 }
695
696 READ_INT_32 (pos, version);
697 READ_INT_32 (pos, actions);
698 READ_INT_32 (pos, protocol);
699
700 msg_debug_milter ("optneg: version: %d, actions: %d, protocol: %d",
701 version, actions, protocol);
702
703 if (version < RSPAMD_MILTER_PROTO_VER) {
704 msg_warn_milter ("MTA specifies too old protocol: %d, "
705 "aborting connection", version);
706
707 err = g_error_new (rspamd_milter_quark (), EINVAL, "invalid "
708 "protocol version: %d", version);
709 rspamd_milter_on_protocol_error (session, priv, err);
710
711 return FALSE;
712 }
713
714 version = RSPAMD_MILTER_PROTO_VER;
715 actions |= RSPAMD_MILTER_ACTIONS_MASK;
716 protocol = RSPAMD_MILTER_FLAG_NOREPLY_MASK;
717
718 return rspamd_milter_send_action (session, RSPAMD_MILTER_OPTNEG,
719 version, actions, protocol);
720 break;
721 case RSPAMD_MILTER_CMD_QUIT:
722 if (priv->out_chain) {
723 msg_debug_milter ("quit command, refcount: %d, "
724 "some output buffers left - draining",
725 session->ref.refcount);
726
727 priv->state = RSPAMD_MILTER_WRITE_AND_DIE;
728 }
729 else {
730 msg_debug_milter ("quit command, refcount: %d",
731 session->ref.refcount);
732
733 priv->state = RSPAMD_MILTER_WANNA_DIE;
734 REF_RETAIN (session);
735 priv->fin_cb (priv->fd, session, priv->ud);
736 REF_RELEASE (session);
737 return FALSE;
738 }
739 break;
740 case RSPAMD_MILTER_CMD_RCPT:
741 msg_debug_milter ("rcpt command");
742
743 while (pos < end) {
744 struct rspamd_email_address *addr;
745 gchar *cpy;
746
747 zero = memchr (pos, '\0', end - pos);
748
749 if (zero && zero > pos) {
750 cpy = rspamd_mempool_alloc (priv->pool, end - pos);
751 memcpy (cpy, pos, end - pos);
752
753 msg_debug_milter ("got rcpt: %*s", (int)(zero - pos), cpy);
754 addr = rspamd_email_address_from_smtp (cpy, zero - pos);
755
756 if (addr) {
757 if (!session->rcpts) {
758 session->rcpts = g_ptr_array_sized_new (1);
759 }
760
761 g_ptr_array_add (session->rcpts, addr);
762 }
763
764 pos = zero + 1;
765 }
766 else {
767 cpy = rspamd_mempool_alloc (priv->pool, end - pos);
768 memcpy (cpy, pos, end - pos);
769
770 msg_debug_milter ("got weird rcpt: %*s", (int)(end - pos),
771 pos);
772 /* That actually should not happen */
773 addr = rspamd_email_address_from_smtp (cpy, end - pos);
774
775 if (addr) {
776 if (!session->rcpts) {
777 session->rcpts = g_ptr_array_sized_new (1);
778 }
779
780 g_ptr_array_add (session->rcpts, addr);
781 }
782
783 break;
784 }
785 }
786 break;
787 case RSPAMD_MILTER_CMD_DATA:
788 if (!session->message) {
789 session->message = rspamd_fstring_sized_new (
790 RSPAMD_MILTER_MESSAGE_CHUNK);
791 }
792 msg_debug_milter ("got data command");
793 /* We do not need reply as specified */
794 break;
795 default:
796 msg_debug_milter ("got bad command: %c", priv->parser.cur_cmd);
797 break;
798 }
799
800 return TRUE;
801 }
802
803 static gboolean
rspamd_milter_is_valid_cmd(guchar c)804 rspamd_milter_is_valid_cmd (guchar c)
805 {
806 switch (c) {
807 case RSPAMD_MILTER_CMD_ABORT:
808 case RSPAMD_MILTER_CMD_BODY:
809 case RSPAMD_MILTER_CMD_CONNECT:
810 case RSPAMD_MILTER_CMD_MACRO:
811 case RSPAMD_MILTER_CMD_BODYEOB:
812 case RSPAMD_MILTER_CMD_HELO:
813 case RSPAMD_MILTER_CMD_QUIT_NC:
814 case RSPAMD_MILTER_CMD_HEADER:
815 case RSPAMD_MILTER_CMD_MAIL:
816 case RSPAMD_MILTER_CMD_EOH:
817 case RSPAMD_MILTER_CMD_OPTNEG:
818 case RSPAMD_MILTER_CMD_QUIT:
819 case RSPAMD_MILTER_CMD_RCPT:
820 case RSPAMD_MILTER_CMD_DATA:
821 case RSPAMD_MILTER_CMD_UNKNOWN:
822 return TRUE;
823 default:
824 break;
825 }
826
827 return FALSE;
828 }
829
830 static gboolean
rspamd_milter_consume_input(struct rspamd_milter_session * session,struct rspamd_milter_private * priv)831 rspamd_milter_consume_input (struct rspamd_milter_session *session,
832 struct rspamd_milter_private *priv)
833 {
834 const guchar *p, *end;
835 GError *err;
836
837 p = priv->parser.buf->str + priv->parser.pos;
838 end = priv->parser.buf->str + priv->parser.buf->len;
839
840 while (p < end) {
841 msg_debug_milter("offset: %d, state: %d",
842 (gint)(p - (const guchar *)priv->parser.buf->str),
843 priv->parser.state);
844
845 switch (priv->parser.state) {
846 case st_len_1:
847 /* The first length byte in big endian order */
848 priv->parser.datalen = 0;
849 priv->parser.datalen |= ((gsize)*p) << 24;
850 priv->parser.state = st_len_2;
851 p++;
852 break;
853 case st_len_2:
854 /* The second length byte in big endian order */
855 priv->parser.datalen |= ((gsize)*p) << 16;
856 priv->parser.state = st_len_3;
857 p++;
858 break;
859 case st_len_3:
860 /* The third length byte in big endian order */
861 priv->parser.datalen |= ((gsize)*p) << 8;
862 priv->parser.state = st_len_4;
863 p++;
864 break;
865 case st_len_4:
866 /* The fourth length byte in big endian order */
867 priv->parser.datalen |= ((gsize)*p);
868 priv->parser.state = st_read_cmd;
869 p++;
870 break;
871 case st_read_cmd:
872 priv->parser.cur_cmd = *p;
873 priv->parser.state = st_read_data;
874
875 if (priv->parser.datalen < 1) {
876 err = g_error_new (rspamd_milter_quark (), EINVAL,
877 "Command length is too short");
878 rspamd_milter_on_protocol_error (session, priv, err);
879
880 return FALSE;
881 }
882 else {
883 /* Eat command itself */
884 priv->parser.datalen --;
885 }
886
887 p++;
888 priv->parser.cmd_start = p - (const guchar *)priv->parser.buf->str;
889 break;
890 case st_read_data:
891 /* We might need some more data in buffer for further steps */
892 if (priv->parser.datalen >
893 RSPAMD_MILTER_MESSAGE_CHUNK * 2) {
894 /* Check if we have HTTP input instead of milter */
895 if (priv->parser.buf->len > sizeof ("GET") &&
896 memcmp (priv->parser.buf->str, "GET", 3) == 0) {
897 struct http_parser http_parser;
898 struct http_parser_settings http_callbacks;
899 GString *url = g_string_new (NULL);
900
901 /* Hack, hack, hack */
902 /*
903 * This code is assumed to read `/ping` command and
904 * handle it to monitor port's availability since
905 * milter protocol is stupid and does not allow to do that
906 * This code also assumes that HTTP request can be read
907 * as as single data chunk which is not true in some cases
908 * In general, don't use it for anything but ping checks
909 */
910 memset (&http_callbacks, 0, sizeof (http_callbacks));
911 http_parser.data = url;
912 http_parser_init (&http_parser, HTTP_REQUEST);
913 http_callbacks.on_url = rspamd_milter_http_on_url;
914 http_parser_execute (&http_parser, &http_callbacks,
915 priv->parser.buf->str, priv->parser.buf->len);
916
917 if (url->len == sizeof ("/ping") - 1 &&
918 rspamd_lc_cmp (url->str, "/ping", url->len) == 0) {
919 rspamd_milter_on_protocol_ping (session, priv);
920 g_string_free (url, TRUE);
921
922 return TRUE;
923 }
924 else {
925 err = g_error_new (rspamd_milter_quark (), EINVAL,
926 "HTTP GET request is not supported in milter mode, url: %s",
927 url->str);
928 }
929
930 g_string_free (url, TRUE);
931 }
932 else if (priv->parser.buf->len > sizeof ("POST") &&
933 memcmp (priv->parser.buf->str, "POST", 4) == 0) {
934 err = g_error_new (rspamd_milter_quark (), EINVAL,
935 "HTTP POST request is not supported in milter mode");
936 }
937 else {
938 err = g_error_new (rspamd_milter_quark (), E2BIG,
939 "Command length is too big: %zd",
940 priv->parser.datalen);
941 }
942
943 rspamd_milter_on_protocol_error (session, priv, err);
944
945 return FALSE;
946 }
947 if (!rspamd_milter_is_valid_cmd (priv->parser.cur_cmd)) {
948 err = g_error_new (rspamd_milter_quark (), E2BIG,
949 "Unvalid command: %c",
950 priv->parser.cur_cmd);
951 rspamd_milter_on_protocol_error (session, priv, err);
952
953 return FALSE;
954 }
955 if (priv->parser.buf->allocated < priv->parser.datalen) {
956 priv->parser.pos = p - (const guchar *)priv->parser.buf->str;
957 priv->parser.buf = rspamd_fstring_grow (priv->parser.buf,
958 priv->parser.buf->len + priv->parser.datalen);
959 /* This can realloc buffer */
960 rspamd_milter_plan_io (session, priv, EV_READ);
961 goto end;
962 }
963 else {
964 /* We may have the full command available */
965 if (p + priv->parser.datalen <= end) {
966 /* We can process command */
967 if (!rspamd_milter_process_command (session, priv)) {
968 return FALSE;
969 }
970
971 p += priv->parser.datalen;
972 priv->parser.state = st_len_1;
973 priv->parser.cur_cmd = '\0';
974 priv->parser.cmd_start = 0;
975 }
976 else {
977 /* Need to read more */
978 priv->parser.pos = p - (const guchar *)priv->parser.buf->str;
979 rspamd_milter_plan_io (session, priv, EV_READ);
980 goto end;
981 }
982 }
983 break;
984 }
985 }
986
987 /* Leftover */
988 switch (priv->parser.state) {
989 case st_read_data:
990 if (p + priv->parser.datalen <= end) {
991 if (!rspamd_milter_process_command (session, priv)) {
992 return FALSE;
993 }
994
995 priv->parser.state = st_len_1;
996 priv->parser.cur_cmd = '\0';
997 priv->parser.cmd_start = 0;
998 }
999 break;
1000 default:
1001 /* No need to do anything */
1002 break;
1003 }
1004
1005 if (p == end) {
1006 priv->parser.buf->len = 0;
1007 priv->parser.pos = 0;
1008 priv->parser.cmd_start = 0;
1009 }
1010
1011 if (priv->out_chain) {
1012 rspamd_milter_plan_io (session, priv, EV_READ|EV_WRITE);
1013 }
1014 else {
1015 rspamd_milter_plan_io (session, priv, EV_READ);
1016 }
1017 end:
1018
1019 return TRUE;
1020 }
1021
1022 static gboolean
rspamd_milter_handle_session(struct rspamd_milter_session * session,struct rspamd_milter_private * priv)1023 rspamd_milter_handle_session (struct rspamd_milter_session *session,
1024 struct rspamd_milter_private *priv)
1025 {
1026 struct rspamd_milter_outbuf *obuf, *obuf_tmp;
1027 gssize r, to_write;
1028 GError *err;
1029
1030 g_assert (session != NULL);
1031
1032 switch (priv->state) {
1033 case RSPAMD_MILTER_READ_MORE:
1034 if (priv->parser.buf->len >= priv->parser.buf->allocated) {
1035 priv->parser.buf = rspamd_fstring_grow (priv->parser.buf,
1036 priv->parser.buf->len * 2);
1037 }
1038
1039 r = read (priv->fd, priv->parser.buf->str + priv->parser.buf->len,
1040 priv->parser.buf->allocated - priv->parser.buf->len);
1041
1042 msg_debug_milter ("read %z bytes, %z remain, %z allocated",
1043 r, priv->parser.buf->len, priv->parser.buf->allocated);
1044
1045 if (r == -1) {
1046 if (errno == EAGAIN || errno == EINTR) {
1047 rspamd_milter_plan_io (session, priv, EV_READ);
1048
1049 return TRUE;
1050 }
1051 else {
1052 /* Fatal IO error */
1053 err = g_error_new (rspamd_milter_quark (), errno,
1054 "IO read error: %s", strerror (errno));
1055 REF_RETAIN (session);
1056 priv->err_cb (priv->fd, session, priv->ud, err);
1057 REF_RELEASE (session);
1058 g_error_free (err);
1059
1060 REF_RELEASE (session);
1061
1062 return FALSE;
1063 }
1064 }
1065 else if (r == 0) {
1066 err = g_error_new (rspamd_milter_quark (), ECONNRESET,
1067 "Unexpected EOF");
1068 REF_RETAIN (session);
1069 priv->err_cb (priv->fd, session, priv->ud, err);
1070 REF_RELEASE (session);
1071 g_error_free (err);
1072
1073 REF_RELEASE (session);
1074
1075 return FALSE;
1076 }
1077 else {
1078 priv->parser.buf->len += r;
1079
1080 return rspamd_milter_consume_input (session, priv);
1081 }
1082
1083 break;
1084 case RSPAMD_MILTER_WRITE_REPLY:
1085 case RSPAMD_MILTER_WRITE_AND_DIE:
1086 if (priv->out_chain == NULL) {
1087 if (priv->state == RSPAMD_MILTER_WRITE_AND_DIE) {
1088 /* Finished writing, let's die finally */
1089 msg_debug_milter ("output drained, terminating, refcount: %d",
1090 session->ref.refcount);
1091
1092 /* Session should be destroyed by fin_cb... */
1093 REF_RETAIN (session);
1094 priv->fin_cb (priv->fd, session, priv->ud);
1095 REF_RELEASE (session);
1096
1097 return FALSE;
1098 }
1099 else {
1100 /* We have written everything, so we can read something */
1101 priv->state = RSPAMD_MILTER_READ_MORE;
1102 rspamd_milter_plan_io (session, priv, EV_READ);
1103 }
1104 }
1105 else {
1106 DL_FOREACH_SAFE (priv->out_chain, obuf, obuf_tmp) {
1107 to_write = obuf->buf->len - obuf->pos;
1108
1109 g_assert (to_write > 0);
1110
1111 r = write (priv->fd, obuf->buf->str + obuf->pos, to_write);
1112
1113 if (r == -1) {
1114 if (errno == EAGAIN || errno == EINTR) {
1115 rspamd_milter_plan_io (session, priv, EV_WRITE);
1116 }
1117 else {
1118 /* Fatal IO error */
1119 err = g_error_new (rspamd_milter_quark (), errno,
1120 "IO write error: %s", strerror (errno));
1121 REF_RETAIN (session);
1122 priv->err_cb (priv->fd, session, priv->ud, err);
1123 REF_RELEASE (session);
1124 g_error_free (err);
1125
1126 REF_RELEASE (session);
1127
1128 return FALSE;
1129 }
1130 }
1131 else if (r == 0) {
1132 err = g_error_new (rspamd_milter_quark (), ECONNRESET,
1133 "Unexpected EOF");
1134 REF_RETAIN (session);
1135 priv->err_cb (priv->fd, session, priv->ud, err);
1136 REF_RELEASE (session);
1137 g_error_free (err);
1138
1139 REF_RELEASE (session);
1140
1141 return FALSE;
1142 }
1143 else {
1144 if (r == to_write) {
1145 /* We have done with this buf */
1146 DL_DELETE (priv->out_chain, obuf);
1147 rspamd_milter_obuf_free (obuf);
1148 }
1149 else {
1150 /* We need to plan another write */
1151 obuf->pos += r;
1152 rspamd_milter_plan_io (session, priv, EV_WRITE);
1153
1154 return TRUE;
1155 }
1156 }
1157 }
1158
1159 /* Here we have written everything, so we can plan reading */
1160 priv->state = RSPAMD_MILTER_READ_MORE;
1161 rspamd_milter_plan_io (session, priv, EV_READ);
1162 }
1163 break;
1164 case RSPAMD_MILTER_WANNA_DIE:
1165 /* We are here after processing everything, so release session */
1166 REF_RELEASE (session);
1167 return FALSE;
1168 break;
1169 case RSPAMD_MILTER_PONG_AND_DIE:
1170 err = g_error_new (rspamd_milter_quark (), 0,
1171 "ping command");
1172 REF_RETAIN (session);
1173 priv->err_cb (priv->fd, session, priv->ud, err);
1174 REF_RELEASE (session);
1175 g_error_free (err);
1176 REF_RELEASE (session);
1177 return FALSE;
1178 break;
1179 }
1180
1181 return TRUE;
1182 }
1183
1184
1185 gboolean
rspamd_milter_handle_socket(gint fd,ev_tstamp timeout,rspamd_mempool_t * pool,struct ev_loop * ev_base,rspamd_milter_finish finish_cb,rspamd_milter_error error_cb,void * ud)1186 rspamd_milter_handle_socket (gint fd, ev_tstamp timeout,
1187 rspamd_mempool_t *pool,
1188 struct ev_loop *ev_base, rspamd_milter_finish finish_cb,
1189 rspamd_milter_error error_cb, void *ud)
1190 {
1191 struct rspamd_milter_session *session;
1192 struct rspamd_milter_private *priv;
1193 gint nfd = dup (fd);
1194
1195 if (nfd == -1) {
1196 GError *err = g_error_new (rspamd_milter_quark (), errno,
1197 "dup failed: %s", strerror (errno));
1198 error_cb (fd, NULL, ud, err);
1199
1200 return FALSE;
1201 }
1202
1203 g_assert (finish_cb != NULL);
1204 g_assert (error_cb != NULL);
1205 g_assert (milter_ctx != NULL);
1206
1207 session = g_malloc0 (sizeof (*session));
1208 priv = g_malloc0 (sizeof (*priv));
1209 priv->fd = nfd;
1210 priv->ud = ud;
1211 priv->fin_cb = finish_cb;
1212 priv->err_cb = error_cb;
1213 priv->parser.state = st_len_1;
1214 priv->parser.buf = rspamd_fstring_sized_new (RSPAMD_MILTER_MESSAGE_CHUNK + 5);
1215 priv->event_loop = ev_base;
1216 priv->state = RSPAMD_MILTER_READ_MORE;
1217 priv->pool = rspamd_mempool_new (rspamd_mempool_suggest_size (), "milter", 0);
1218 priv->discard_on_reject = milter_ctx->discard_on_reject;
1219 priv->quarantine_on_reject = milter_ctx->quarantine_on_reject;
1220 priv->ev.timeout = timeout;
1221
1222 rspamd_ev_watcher_init (&priv->ev, priv->fd, EV_READ|EV_WRITE,
1223 rspamd_milter_io_handler, session);
1224
1225 if (pool) {
1226 /* Copy tag */
1227 memcpy (priv->pool->tag.uid, pool->tag.uid, sizeof (pool->tag.uid));
1228 }
1229
1230 priv->headers = kh_init (milter_headers_hash_t);
1231 kh_resize (milter_headers_hash_t, priv->headers, 32);
1232
1233 session->priv = priv;
1234 REF_INIT_RETAIN (session, rspamd_milter_session_dtor);
1235
1236 if (milter_ctx->sessions_cache) {
1237 rspamd_worker_session_cache_add (milter_ctx->sessions_cache,
1238 priv->pool->tag.uid, &session->ref.refcount, session);
1239 }
1240
1241 return rspamd_milter_handle_session (session, priv);
1242 }
1243
1244 gboolean
rspamd_milter_set_reply(struct rspamd_milter_session * session,rspamd_fstring_t * rcode,rspamd_fstring_t * xcode,rspamd_fstring_t * reply)1245 rspamd_milter_set_reply (struct rspamd_milter_session *session,
1246 rspamd_fstring_t *rcode,
1247 rspamd_fstring_t *xcode,
1248 rspamd_fstring_t *reply)
1249 {
1250 GString *buf;
1251 gboolean ret;
1252
1253 buf = g_string_sized_new (xcode->len + rcode->len + reply->len + 2);
1254 rspamd_printf_gstring (buf, "%V %V %V", rcode, xcode, reply);
1255 ret = rspamd_milter_send_action (session, RSPAMD_MILTER_REPLYCODE,
1256 buf);
1257 g_string_free (buf, TRUE);
1258
1259 return ret;
1260 }
1261
1262 #define SET_COMMAND(cmd, sz, reply, pos) do { \
1263 guint32 _len; \
1264 _len = (sz) + 1; \
1265 (reply) = rspamd_fstring_sized_new (sizeof (_len) + _len); \
1266 (reply)->len = sizeof (_len) + _len; \
1267 _len = htonl (_len); \
1268 memcpy ((reply)->str, &_len, sizeof (_len)); \
1269 (reply)->str[sizeof(_len)] = (cmd); \
1270 (pos) = (guchar *)(reply)->str + sizeof (_len) + 1; \
1271 } while (0)
1272
1273 gboolean
rspamd_milter_send_action(struct rspamd_milter_session * session,enum rspamd_milter_reply act,...)1274 rspamd_milter_send_action (struct rspamd_milter_session *session,
1275 enum rspamd_milter_reply act, ...)
1276 {
1277 guint32 ver, actions, protocol, idx;
1278 va_list ap;
1279 guchar cmd, *pos;
1280 rspamd_fstring_t *reply = NULL;
1281 gsize len;
1282 GString *name, *value;
1283 const char *reason, *body_str;
1284 struct rspamd_milter_outbuf *obuf;
1285 struct rspamd_milter_private *priv = session->priv;
1286
1287 va_start (ap, act);
1288 cmd = act;
1289
1290 switch (act) {
1291 case RSPAMD_MILTER_ACCEPT:
1292 case RSPAMD_MILTER_CONTINUE:
1293 case RSPAMD_MILTER_DISCARD:
1294 case RSPAMD_MILTER_PROGRESS:
1295 case RSPAMD_MILTER_REJECT:
1296 case RSPAMD_MILTER_TEMPFAIL:
1297 /* No additional arguments */
1298 msg_debug_milter ("send %c command", cmd);
1299 SET_COMMAND (cmd, 0, reply, pos);
1300 break;
1301 case RSPAMD_MILTER_QUARANTINE:
1302 reason = va_arg (ap, const char *);
1303
1304 if (reason == NULL) {
1305 reason = "";
1306 }
1307
1308 len = strlen (reason);
1309 msg_debug_milter ("send quarantine action %s", reason);
1310 SET_COMMAND (cmd, len + 1, reply, pos);
1311 memcpy (pos, reason, len + 1);
1312 break;
1313 case RSPAMD_MILTER_ADDHEADER:
1314 name = va_arg (ap, GString *);
1315 value = va_arg (ap, GString *);
1316
1317 /* Name and value must be zero terminated */
1318 msg_debug_milter ("add header command - \"%v\"=\"%v\"", name, value);
1319 SET_COMMAND (cmd, name->len + value->len + 2, reply, pos);
1320 memcpy (pos, name->str, name->len + 1);
1321 pos += name->len + 1;
1322 memcpy (pos, value->str, value->len + 1);
1323 break;
1324 case RSPAMD_MILTER_CHGHEADER:
1325 case RSPAMD_MILTER_INSHEADER:
1326 idx = va_arg (ap, guint32);
1327 name = va_arg (ap, GString *);
1328 value = va_arg (ap, GString *);
1329
1330 msg_debug_milter ("change/insert header command pos = %d- \"%v\"=\"%v\"",
1331 idx, name, value);
1332 /* Name and value must be zero terminated */
1333 SET_COMMAND (cmd, name->len + value->len + 2 + sizeof (guint32),
1334 reply, pos);
1335 idx = htonl (idx);
1336 memcpy (pos, &idx, sizeof (idx));
1337 pos += sizeof (idx);
1338 memcpy (pos, name->str, name->len + 1);
1339 pos += name->len + 1;
1340 memcpy (pos, value->str, value->len + 1);
1341 break;
1342 case RSPAMD_MILTER_REPLBODY:
1343 len = va_arg (ap, gsize);
1344 body_str = va_arg (ap, const char *);
1345 msg_debug_milter ("want to change body; size = %uz",
1346 len);
1347 SET_COMMAND (cmd, len, reply, pos);
1348 memcpy (pos, body_str, len);
1349 break;
1350 case RSPAMD_MILTER_REPLYCODE:
1351 case RSPAMD_MILTER_ADDRCPT:
1352 case RSPAMD_MILTER_DELRCPT:
1353 case RSPAMD_MILTER_CHGFROM:
1354 /* Single GString * argument */
1355 value = va_arg (ap, GString *);
1356 msg_debug_milter ("command %c; value=%v", cmd, value);
1357 SET_COMMAND (cmd, value->len + 1, reply, pos);
1358 memcpy (pos, value->str, value->len + 1);
1359 break;
1360 case RSPAMD_MILTER_OPTNEG:
1361 ver = va_arg (ap, guint32);
1362 actions = va_arg (ap, guint32);
1363 protocol = va_arg (ap, guint32);
1364
1365 msg_debug_milter ("optneg reply: ver=%d, actions=%d, protocol=%d",
1366 ver, actions, protocol);
1367 ver = htonl (ver);
1368 actions = htonl (actions);
1369 protocol = htonl (protocol);
1370 SET_COMMAND (cmd, sizeof (guint32) * 3, reply, pos);
1371 memcpy (pos, &ver, sizeof (ver));
1372 pos += sizeof (ver);
1373 memcpy (pos, &actions, sizeof (actions));
1374 pos += sizeof (actions);
1375 memcpy (pos, &protocol, sizeof (protocol));
1376 break;
1377 default:
1378 msg_err_milter ("invalid command: %c", cmd);
1379 break;
1380 }
1381
1382 va_end (ap);
1383
1384 if (reply) {
1385 obuf = g_malloc (sizeof (*obuf));
1386 obuf->buf = reply;
1387 obuf->pos = 0;
1388 DL_APPEND (priv->out_chain, obuf);
1389 priv->state = RSPAMD_MILTER_WRITE_REPLY;
1390 rspamd_milter_plan_io (session, priv, EV_WRITE);
1391
1392 return TRUE;
1393 }
1394
1395 return FALSE;
1396 }
1397
1398 gboolean
rspamd_milter_add_header(struct rspamd_milter_session * session,GString * name,GString * value)1399 rspamd_milter_add_header (struct rspamd_milter_session *session,
1400 GString *name, GString *value)
1401 {
1402 return rspamd_milter_send_action (session, RSPAMD_MILTER_ADDHEADER,
1403 name, value);
1404 }
1405
1406 gboolean
rspamd_milter_del_header(struct rspamd_milter_session * session,GString * name)1407 rspamd_milter_del_header (struct rspamd_milter_session *session,
1408 GString *name)
1409 {
1410 GString value;
1411 guint32 idx = 1;
1412
1413 value.str = (gchar *)"";
1414 value.len = 0;
1415
1416 return rspamd_milter_send_action (session, RSPAMD_MILTER_CHGHEADER,
1417 idx, name, &value);
1418 }
1419
1420 void
rspamd_milter_session_unref(struct rspamd_milter_session * session)1421 rspamd_milter_session_unref (struct rspamd_milter_session *session)
1422 {
1423 REF_RELEASE (session);
1424 }
1425
1426 struct rspamd_milter_session *
rspamd_milter_session_ref(struct rspamd_milter_session * session)1427 rspamd_milter_session_ref (struct rspamd_milter_session *session)
1428 {
1429 REF_RETAIN (session);
1430
1431 return session;
1432 }
1433
1434 #define IF_MACRO(lit) RSPAMD_FTOK_ASSIGN (&srch, (lit)); \
1435 found = g_hash_table_lookup (session->macros, &srch); \
1436 if (found)
1437
1438 static void
rspamd_milter_macro_http(struct rspamd_milter_session * session,struct rspamd_http_message * msg)1439 rspamd_milter_macro_http (struct rspamd_milter_session *session,
1440 struct rspamd_http_message *msg)
1441 {
1442 rspamd_ftok_t *found, srch;
1443 struct rspamd_milter_private *priv = session->priv;
1444
1445 /*
1446 * We assume postfix macros here, sendmail ones might be slightly
1447 * different
1448 */
1449
1450 if (!session->macros) {
1451 return;
1452 }
1453
1454 IF_MACRO("{i}") {
1455 rspamd_http_message_add_header_len (msg, QUEUE_ID_HEADER,
1456 found->begin, found->len);
1457 }
1458 else {
1459 IF_MACRO("i") {
1460 rspamd_http_message_add_header_len (msg, QUEUE_ID_HEADER,
1461 found->begin, found->len);
1462 }
1463 }
1464
1465 IF_MACRO("{v}") {
1466 rspamd_http_message_add_header_len (msg, USER_AGENT_HEADER,
1467 found->begin, found->len);
1468 }
1469 else {
1470 IF_MACRO("v") {
1471 rspamd_http_message_add_header_len (msg, USER_AGENT_HEADER,
1472 found->begin, found->len);
1473 }
1474 }
1475
1476 IF_MACRO("{cipher}") {
1477 rspamd_http_message_add_header_len (msg, TLS_CIPHER_HEADER,
1478 found->begin, found->len);
1479 }
1480
1481 IF_MACRO("{tls_version}") {
1482 rspamd_http_message_add_header_len (msg, TLS_VERSION_HEADER,
1483 found->begin, found->len);
1484 }
1485
1486 IF_MACRO("{auth_authen}") {
1487 rspamd_http_message_add_header_len (msg, USER_HEADER,
1488 found->begin, found->len);
1489 }
1490
1491 IF_MACRO("{rcpt_mailer}") {
1492 rspamd_http_message_add_header_len (msg, MAILER_HEADER,
1493 found->begin, found->len);
1494 }
1495
1496 if (milter_ctx->client_ca_name) {
1497 IF_MACRO ("{cert_issuer}") {
1498 rspamd_http_message_add_header_len (msg, CERT_ISSUER_HEADER,
1499 found->begin, found->len);
1500
1501 if (found->len == strlen (milter_ctx->client_ca_name) &&
1502 rspamd_cryptobox_memcmp (found->begin,
1503 milter_ctx->client_ca_name, found->len) == 0) {
1504 msg_debug_milter ("process certificate issued by %T", found);
1505 IF_MACRO("{cert_subject}") {
1506 rspamd_http_message_add_header_len (msg, USER_HEADER,
1507 found->begin, found->len);
1508 }
1509 }
1510 else {
1511 msg_debug_milter ("skip certificate issued by %T", found);
1512 }
1513
1514
1515 }
1516 }
1517 else {
1518 IF_MACRO ("{cert_issuer}") {
1519 rspamd_http_message_add_header_len (msg, CERT_ISSUER_HEADER,
1520 found->begin, found->len);
1521 }
1522 }
1523
1524 if (!session->hostname || session->hostname->len == 0) {
1525 IF_MACRO("{client_name}") {
1526 if (!(found->len == sizeof ("unknown") - 1 &&
1527 memcmp (found->begin, "unknown",
1528 sizeof ("unknown") - 1) == 0)) {
1529 rspamd_http_message_add_header_len (msg, HOSTNAME_HEADER,
1530 found->begin, found->len);
1531 }
1532 else {
1533 msg_debug_milter ("skip unknown hostname from being added");
1534 }
1535 }
1536 }
1537
1538 IF_MACRO("{daemon_name}") {
1539 /* Postfix style */
1540 rspamd_http_message_add_header_len (msg, MTA_NAME_HEADER,
1541 found->begin, found->len);
1542 }
1543 else {
1544 /* Sendmail style */
1545 IF_MACRO("{j}") {
1546 rspamd_http_message_add_header_len (msg, MTA_NAME_HEADER,
1547 found->begin, found->len);
1548 }
1549 else {
1550 IF_MACRO("j") {
1551 rspamd_http_message_add_header_len (msg, MTA_NAME_HEADER,
1552 found->begin, found->len);
1553 }
1554 }
1555 }
1556 }
1557
1558 struct rspamd_http_message *
rspamd_milter_to_http(struct rspamd_milter_session * session)1559 rspamd_milter_to_http (struct rspamd_milter_session *session)
1560 {
1561 struct rspamd_http_message *msg;
1562 guint i;
1563 struct rspamd_email_address *rcpt;
1564 struct rspamd_milter_private *priv = session->priv;
1565
1566 g_assert (session != NULL);
1567
1568 msg = rspamd_http_new_message (HTTP_REQUEST);
1569
1570 msg->url = rspamd_fstring_assign (msg->url, "/" MSG_CMD_CHECK_V2,
1571 sizeof ("/" MSG_CMD_CHECK_V2) - 1);
1572
1573 if (session->message) {
1574 rspamd_http_message_set_body_from_fstring_steal (msg, session->message);
1575 session->message = NULL;
1576 }
1577
1578 if (session->hostname && RSPAMD_FSTRING_LEN (session->hostname) > 0) {
1579 if (!(session->hostname->len == sizeof ("unknown") - 1 &&
1580 memcmp (RSPAMD_FSTRING_DATA (session->hostname), "unknown",
1581 sizeof ("unknown") - 1) == 0)) {
1582 rspamd_http_message_add_header_fstr (msg, HOSTNAME_HEADER,
1583 session->hostname);
1584 }
1585 else {
1586 msg_debug_milter ("skip unknown hostname from being added");
1587 }
1588 }
1589
1590 if (session->helo && session->helo->len > 0) {
1591 rspamd_http_message_add_header_fstr (msg, HELO_HEADER,
1592 session->helo);
1593 }
1594
1595 if (session->from) {
1596 rspamd_http_message_add_header_len (msg, FROM_HEADER,
1597 session->from->raw, session->from->raw_len);
1598 }
1599
1600 if (session->rcpts) {
1601 PTR_ARRAY_FOREACH (session->rcpts, i, rcpt) {
1602 rspamd_http_message_add_header_len (msg, RCPT_HEADER,
1603 rcpt->raw, rcpt->raw_len);
1604 }
1605 }
1606
1607 if (session->addr) {
1608 if (rspamd_inet_address_get_af (session->addr) != AF_UNIX) {
1609 rspamd_http_message_add_header (msg, IP_ADDR_HEADER,
1610 rspamd_inet_address_to_string_pretty (session->addr));
1611 }
1612 else {
1613 rspamd_http_message_add_header (msg, IP_ADDR_HEADER,
1614 rspamd_inet_address_to_string (session->addr));
1615 }
1616 }
1617
1618 rspamd_milter_macro_http (session, msg);
1619 rspamd_http_message_add_header (msg, FLAGS_HEADER, "milter,body_block");
1620
1621 return msg;
1622 }
1623
1624 void *
rspamd_milter_update_userdata(struct rspamd_milter_session * session,void * ud)1625 rspamd_milter_update_userdata (struct rspamd_milter_session *session,
1626 void *ud)
1627 {
1628 struct rspamd_milter_private *priv = session->priv;
1629 void *prev_ud;
1630
1631 prev_ud = priv->ud;
1632 priv->ud = ud;
1633
1634 return prev_ud;
1635 }
1636
1637 static void
rspamd_milter_remove_header_safe(struct rspamd_milter_session * session,const gchar * key,gint nhdr)1638 rspamd_milter_remove_header_safe (struct rspamd_milter_session *session,
1639 const gchar *key, gint nhdr)
1640 {
1641 gint i;
1642 GString *hname, *hvalue;
1643 struct rspamd_milter_private *priv = session->priv;
1644 khiter_t k;
1645 GArray *ar;
1646
1647 k = kh_get (milter_headers_hash_t, priv->headers, (char *)key);
1648
1649 if (k != kh_end (priv->headers)) {
1650 ar = kh_val (priv->headers, k);
1651
1652 hname = g_string_new (key);
1653 hvalue = g_string_new ("");
1654
1655 if (nhdr >= 1) {
1656 rspamd_milter_send_action (session,
1657 RSPAMD_MILTER_CHGHEADER,
1658 nhdr, hname, hvalue);
1659 }
1660 else if (nhdr == 0 && ar->len > 0) {
1661 /* We need to clear all headers */
1662 for (i = ar->len; i > 0; i --) {
1663 rspamd_milter_send_action (session,
1664 RSPAMD_MILTER_CHGHEADER,
1665 i, hname, hvalue);
1666 }
1667 }
1668 else {
1669 /* Remove from the end */
1670 if (nhdr >= -(ar->len)) {
1671 rspamd_milter_send_action (session,
1672 RSPAMD_MILTER_CHGHEADER,
1673 ar->len + nhdr + 1, hname, hvalue);
1674 }
1675 }
1676
1677 g_string_free (hname, TRUE);
1678 g_string_free (hvalue, TRUE);
1679 }
1680 }
1681
1682 static void
rspamd_milter_extract_single_header(struct rspamd_milter_session * session,const gchar * hdr,const ucl_object_t * obj)1683 rspamd_milter_extract_single_header (struct rspamd_milter_session *session,
1684 const gchar *hdr, const ucl_object_t *obj)
1685 {
1686 GString *hname, *hvalue;
1687 struct rspamd_milter_private *priv = session->priv;
1688 gint idx = -1;
1689 const ucl_object_t *val;
1690
1691 val = ucl_object_lookup (obj, "value");
1692
1693 if (val && ucl_object_type (val) == UCL_STRING) {
1694 const ucl_object_t *idx_obj;
1695 gboolean has_idx = FALSE;
1696
1697 idx_obj = ucl_object_lookup_any (obj, "order",
1698 "index", NULL);
1699
1700 if (idx_obj) {
1701 idx = ucl_object_toint (idx_obj);
1702 has_idx = TRUE;
1703 }
1704
1705 hname = g_string_new (hdr);
1706 hvalue = g_string_new (ucl_object_tostring (val));
1707
1708 if (has_idx) {
1709 if (idx >= 0) {
1710 rspamd_milter_send_action (session,
1711 RSPAMD_MILTER_INSHEADER,
1712 idx,
1713 hname, hvalue);
1714 }
1715 else {
1716 /* Calculate negative offset */
1717
1718 if (-idx <= priv->cur_hdr) {
1719 rspamd_milter_send_action (session,
1720 RSPAMD_MILTER_INSHEADER,
1721 priv->cur_hdr + idx + 1,
1722 hname, hvalue);
1723 }
1724 else {
1725 rspamd_milter_send_action (session,
1726 RSPAMD_MILTER_INSHEADER,
1727 0,
1728 hname, hvalue);
1729 }
1730 }
1731 }
1732 else {
1733 rspamd_milter_send_action (session,
1734 RSPAMD_MILTER_ADDHEADER,
1735 hname, hvalue);
1736 }
1737
1738 g_string_free (hname, TRUE);
1739 g_string_free (hvalue, TRUE);
1740 }
1741 }
1742
1743 /*
1744 * Returns `TRUE` if action has been processed internally by this function
1745 */
1746 static gboolean
rspamd_milter_process_milter_block(struct rspamd_milter_session * session,const ucl_object_t * obj,struct rspamd_action * action)1747 rspamd_milter_process_milter_block (struct rspamd_milter_session *session,
1748 const ucl_object_t *obj, struct rspamd_action *action)
1749 {
1750 const ucl_object_t *elt, *cur, *cur_elt;
1751 ucl_object_iter_t it;
1752 struct rspamd_milter_private *priv = session->priv;
1753 GString *hname, *hvalue;
1754
1755 if (obj && ucl_object_type (obj) == UCL_OBJECT) {
1756 elt = ucl_object_lookup (obj, "remove_headers");
1757 /*
1758 * remove_headers: {"name": 1, ... }
1759 * where number is the header's position starting from '1'
1760 */
1761 if (elt && ucl_object_type (elt) == UCL_OBJECT) {
1762 it = NULL;
1763
1764 while ((cur = ucl_object_iterate (elt, &it, true)) != NULL) {
1765 if (ucl_object_type (cur) == UCL_INT) {
1766 rspamd_milter_remove_header_safe (session,
1767 ucl_object_key (cur),
1768 ucl_object_toint (cur));
1769 }
1770 }
1771 }
1772
1773 elt = ucl_object_lookup (obj, "add_headers");
1774 /*
1775 * add_headers: {"name": "value", ... }
1776 * name could have multiple values
1777 * -or- (since 1.7)
1778 * {"name": {"value": "val", "order": 0}, ... }
1779 */
1780 if (elt && ucl_object_type (elt) == UCL_OBJECT) {
1781 it = NULL;
1782
1783 while ((cur = ucl_object_iterate (elt, &it, true)) != NULL) {
1784 ucl_object_iter_t *elt_it;
1785
1786 elt_it = ucl_object_iterate_new (cur);
1787
1788 while ((cur_elt = ucl_object_iterate_safe (elt_it, true)) != NULL) {
1789 if (ucl_object_type (cur_elt) == UCL_STRING) {
1790 hname = g_string_new (ucl_object_key (cur));
1791 hvalue = g_string_new (ucl_object_tostring (cur_elt));
1792
1793 rspamd_milter_send_action (session,
1794 RSPAMD_MILTER_ADDHEADER,
1795 hname, hvalue);
1796 g_string_free (hname, TRUE);
1797 g_string_free (hvalue, TRUE);
1798 }
1799 else if (ucl_object_type (cur_elt) == UCL_OBJECT) {
1800 rspamd_milter_extract_single_header (session,
1801 ucl_object_key (cur), cur_elt);
1802 }
1803 else if (ucl_object_type (cur_elt) == UCL_ARRAY) {
1804 /* Multiple values for the same key */
1805 ucl_object_iter_t *array_it;
1806 const ucl_object_t *array_elt;
1807
1808 array_it = ucl_object_iterate_new (cur_elt);
1809
1810 while ((array_elt = ucl_object_iterate_safe (array_it,
1811 true)) != NULL) {
1812 rspamd_milter_extract_single_header (session,
1813 ucl_object_key (cur), array_elt);
1814 }
1815
1816 ucl_object_iterate_free (array_it);
1817 }
1818 }
1819
1820 ucl_object_iterate_free (elt_it);
1821 }
1822 }
1823
1824 elt = ucl_object_lookup (obj, "change_from");
1825
1826 if (elt && ucl_object_type (elt) == UCL_STRING) {
1827 hvalue = g_string_new (ucl_object_tostring (elt));
1828 rspamd_milter_send_action (session,
1829 RSPAMD_MILTER_CHGFROM,
1830 hvalue);
1831 g_string_free (hvalue, TRUE);
1832 }
1833
1834 elt = ucl_object_lookup (obj, "add_rcpt");
1835
1836 if (elt && ucl_object_type (elt) == UCL_ARRAY) {
1837 it = NULL;
1838
1839 while ((cur = ucl_object_iterate (elt, &it, true)) != NULL) {
1840 hvalue = g_string_new (ucl_object_tostring (cur));
1841 rspamd_milter_send_action (session,
1842 RSPAMD_MILTER_ADDRCPT,
1843 hvalue);
1844 g_string_free (hvalue, TRUE);
1845 }
1846 }
1847
1848 elt = ucl_object_lookup (obj, "del_rcpt");
1849
1850 if (elt && ucl_object_type (elt) == UCL_ARRAY) {
1851 it = NULL;
1852
1853 while ((cur = ucl_object_iterate (elt, &it, true)) != NULL) {
1854 hvalue = g_string_new (ucl_object_tostring (cur));
1855 rspamd_milter_send_action (session,
1856 RSPAMD_MILTER_DELRCPT,
1857 hvalue);
1858 g_string_free (hvalue, TRUE);
1859 }
1860 }
1861
1862 elt = ucl_object_lookup (obj, "reject");
1863
1864 if (elt && ucl_object_type (elt) == UCL_STRING) {
1865 if (strcmp (ucl_object_tostring (elt), "discard") == 0) {
1866 priv->discard_on_reject = TRUE;
1867 msg_info_milter ("discard message instead of rejection");
1868 }
1869 else if (strcmp (ucl_object_tostring (elt), "quarantine") == 0) {
1870 priv->quarantine_on_reject = TRUE;
1871 msg_info_milter ("quarantine message instead of rejection");
1872 }
1873 else {
1874 priv->discard_on_reject = FALSE;
1875 priv->quarantine_on_reject = FALSE;
1876 }
1877 }
1878
1879 elt = ucl_object_lookup (obj, "no_action");
1880
1881 if (elt && ucl_object_type (elt) == UCL_BOOLEAN) {
1882 priv->no_action = ucl_object_toboolean (elt);
1883 }
1884 }
1885
1886 if (action->action_type == METRIC_ACTION_ADD_HEADER) {
1887 elt = ucl_object_lookup (obj, "spam_header");
1888
1889 if (elt) {
1890 if (ucl_object_type (elt) == UCL_STRING) {
1891 rspamd_milter_remove_header_safe (session,
1892 milter_ctx->spam_header,
1893 0);
1894
1895 hname = g_string_new (milter_ctx->spam_header);
1896 hvalue = g_string_new (ucl_object_tostring (elt));
1897 rspamd_milter_send_action (session, RSPAMD_MILTER_CHGHEADER,
1898 (guint32)1, hname, hvalue);
1899 g_string_free (hname, TRUE);
1900 g_string_free (hvalue, TRUE);
1901 rspamd_milter_send_action (session, RSPAMD_MILTER_ACCEPT);
1902
1903 return TRUE;
1904 }
1905 else if (ucl_object_type (elt) == UCL_OBJECT) {
1906 it = NULL;
1907
1908 while ((cur = ucl_object_iterate (elt, &it, true)) != NULL) {
1909 rspamd_milter_remove_header_safe (session,
1910 ucl_object_key (cur),
1911 0);
1912
1913 hname = g_string_new (ucl_object_key (cur));
1914 hvalue = g_string_new (ucl_object_tostring (cur));
1915 rspamd_milter_send_action (session, RSPAMD_MILTER_CHGHEADER,
1916 (guint32) 1, hname, hvalue);
1917 g_string_free (hname, TRUE);
1918 g_string_free (hvalue, TRUE);
1919 }
1920
1921 rspamd_milter_send_action (session, RSPAMD_MILTER_ACCEPT);
1922
1923 return TRUE;
1924 }
1925 }
1926 }
1927
1928 return FALSE;
1929 }
1930
1931 void
rspamd_milter_send_task_results(struct rspamd_milter_session * session,const ucl_object_t * results,const gchar * new_body,gsize bodylen)1932 rspamd_milter_send_task_results (struct rspamd_milter_session *session,
1933 const ucl_object_t *results,
1934 const gchar *new_body,
1935 gsize bodylen)
1936 {
1937 const ucl_object_t *elt;
1938 struct rspamd_milter_private *priv = session->priv;
1939 const gchar *str_action;
1940 struct rspamd_action *action;
1941 rspamd_fstring_t *xcode = NULL, *rcode = NULL, *reply = NULL;
1942 GString *hname, *hvalue;
1943 gboolean processed = FALSE;
1944
1945 if (results == NULL) {
1946 msg_err_milter ("cannot find scan results, tempfail");
1947 rspamd_milter_send_action (session, RSPAMD_MILTER_TEMPFAIL);
1948
1949 goto cleanup;
1950 }
1951
1952 elt = ucl_object_lookup (results, "action");
1953
1954 if (!elt) {
1955 msg_err_milter ("cannot find action in results, tempfail");
1956 rspamd_milter_send_action (session, RSPAMD_MILTER_TEMPFAIL);
1957
1958 goto cleanup;
1959 }
1960
1961 str_action = ucl_object_tostring (elt);
1962 action = rspamd_config_get_action (milter_ctx->cfg, str_action);
1963
1964 if (action == NULL) {
1965 msg_err_milter ("action %s has not been registered", str_action);
1966 rspamd_milter_send_action (session, RSPAMD_MILTER_TEMPFAIL);
1967
1968 goto cleanup;
1969 }
1970
1971 elt = ucl_object_lookup (results, "messages");
1972 if (elt) {
1973 const ucl_object_t *smtp_res;
1974 const gchar *msg;
1975 gsize len = 0;
1976
1977 smtp_res = ucl_object_lookup (elt, "smtp_message");
1978
1979 if (smtp_res) {
1980 msg = ucl_object_tolstring (smtp_res, &len);
1981 reply = rspamd_fstring_new_init (msg, len);
1982 }
1983 }
1984
1985 /* Deal with milter headers */
1986 elt = ucl_object_lookup (results, "milter");
1987
1988 if (elt) {
1989 processed = rspamd_milter_process_milter_block (session, elt, action);
1990 }
1991
1992 /* DKIM-Signature */
1993 elt = ucl_object_lookup (results, "dkim-signature");
1994
1995 if (elt) {
1996 hname = g_string_new (RSPAMD_MILTER_DKIM_HEADER);
1997
1998 if (ucl_object_type (elt) == UCL_STRING) {
1999 hvalue = g_string_new (ucl_object_tostring (elt));
2000
2001 rspamd_milter_send_action (session, RSPAMD_MILTER_INSHEADER,
2002 1, hname, hvalue);
2003
2004 g_string_free (hvalue, TRUE);
2005 }
2006 else {
2007 ucl_object_iter_t it;
2008 const ucl_object_t *cur;
2009 int i = 1;
2010
2011 it = ucl_object_iterate_new (elt);
2012
2013 while ((cur = ucl_object_iterate_safe (it, true)) != NULL) {
2014 hvalue = g_string_new (ucl_object_tostring (cur));
2015
2016 rspamd_milter_send_action (session, RSPAMD_MILTER_INSHEADER,
2017 i++, hname, hvalue);
2018
2019 g_string_free (hvalue, TRUE);
2020 }
2021
2022 ucl_object_iterate_free (it);
2023 }
2024
2025 g_string_free (hname, TRUE);
2026 }
2027
2028 if (processed) {
2029 goto cleanup;
2030 }
2031
2032 if (new_body) {
2033 rspamd_milter_send_action (session, RSPAMD_MILTER_REPLBODY,
2034 bodylen, new_body);
2035 }
2036
2037 if (priv->no_action) {
2038 msg_info_milter ("do not apply action %s, no_action is set",
2039 str_action);
2040 hname = g_string_new (RSPAMD_MILTER_ACTION_HEADER);
2041 hvalue = g_string_new (str_action);
2042
2043 rspamd_milter_send_action (session, RSPAMD_MILTER_ADDHEADER,
2044 hname, hvalue);
2045 g_string_free (hname, TRUE);
2046 g_string_free (hvalue, TRUE);
2047 rspamd_milter_send_action (session, RSPAMD_MILTER_ACCEPT);
2048
2049 goto cleanup;
2050 }
2051
2052 switch (action->action_type) {
2053 case METRIC_ACTION_REJECT:
2054 if (priv->discard_on_reject) {
2055 rspamd_milter_send_action (session, RSPAMD_MILTER_DISCARD);
2056 }
2057 else if (priv->quarantine_on_reject) {
2058 /* TODO: be more flexible about SMTP messages */
2059 rspamd_milter_send_action (session, RSPAMD_MILTER_QUARANTINE,
2060 RSPAMD_MILTER_QUARANTINE_MESSAGE);
2061
2062 /* Quarantine also requires accept action, all hail Sendmail */
2063 rspamd_milter_send_action (session, RSPAMD_MILTER_ACCEPT);
2064 }
2065 else {
2066 rcode = rspamd_fstring_new_init (RSPAMD_MILTER_RCODE_REJECT,
2067 sizeof (RSPAMD_MILTER_RCODE_REJECT) - 1);
2068 xcode = rspamd_fstring_new_init (RSPAMD_MILTER_XCODE_REJECT,
2069 sizeof (RSPAMD_MILTER_XCODE_REJECT) - 1);
2070
2071 if (!reply) {
2072 if (milter_ctx->reject_message == NULL) {
2073 reply = rspamd_fstring_new_init (
2074 RSPAMD_MILTER_REJECT_MESSAGE,
2075 sizeof (RSPAMD_MILTER_REJECT_MESSAGE) - 1);
2076 }
2077 else {
2078 reply = rspamd_fstring_new_init (milter_ctx->reject_message,
2079 strlen (milter_ctx->reject_message));
2080 }
2081 }
2082
2083 rspamd_milter_set_reply (session, rcode, xcode, reply);
2084 }
2085 break;
2086 case METRIC_ACTION_SOFT_REJECT:
2087 rcode = rspamd_fstring_new_init (RSPAMD_MILTER_RCODE_TEMPFAIL,
2088 sizeof (RSPAMD_MILTER_RCODE_TEMPFAIL) - 1);
2089 xcode = rspamd_fstring_new_init (RSPAMD_MILTER_XCODE_TEMPFAIL,
2090 sizeof (RSPAMD_MILTER_XCODE_TEMPFAIL) - 1);
2091
2092 if (!reply) {
2093 reply = rspamd_fstring_new_init (RSPAMD_MILTER_TEMPFAIL_MESSAGE,
2094 sizeof (RSPAMD_MILTER_TEMPFAIL_MESSAGE) - 1);
2095 }
2096
2097 rspamd_milter_set_reply (session, rcode, xcode, reply);
2098 break;
2099
2100 case METRIC_ACTION_REWRITE_SUBJECT:
2101 elt = ucl_object_lookup (results, "subject");
2102
2103 if (elt) {
2104 hname = g_string_new ("Subject");
2105 hvalue = g_string_new (ucl_object_tostring (elt));
2106
2107 rspamd_milter_send_action (session, RSPAMD_MILTER_CHGHEADER,
2108 (guint32)1, hname, hvalue);
2109 g_string_free (hname, TRUE);
2110 g_string_free (hvalue, TRUE);
2111 }
2112
2113 rspamd_milter_send_action (session, RSPAMD_MILTER_ACCEPT);
2114 break;
2115
2116 case METRIC_ACTION_ADD_HEADER:
2117 /* Remove existing headers */
2118 rspamd_milter_remove_header_safe (session,
2119 milter_ctx->spam_header,
2120 0);
2121
2122 hname = g_string_new (milter_ctx->spam_header);
2123 hvalue = g_string_new ("Yes");
2124 rspamd_milter_send_action (session, RSPAMD_MILTER_CHGHEADER,
2125 (guint32)1, hname, hvalue);
2126 g_string_free (hname, TRUE);
2127 g_string_free (hvalue, TRUE);
2128 rspamd_milter_send_action (session, RSPAMD_MILTER_ACCEPT);
2129 break;
2130
2131 case METRIC_ACTION_QUARANTINE:
2132 /* TODO: be more flexible about SMTP messages */
2133 rspamd_milter_send_action (session, RSPAMD_MILTER_QUARANTINE,
2134 RSPAMD_MILTER_QUARANTINE_MESSAGE);
2135
2136 /* Quarantine also requires accept action, all hail Sendmail */
2137 rspamd_milter_send_action (session, RSPAMD_MILTER_ACCEPT);
2138 break;
2139 case METRIC_ACTION_DISCARD:
2140 rspamd_milter_send_action (session, RSPAMD_MILTER_DISCARD);
2141 break;
2142 case METRIC_ACTION_GREYLIST:
2143 case METRIC_ACTION_NOACTION:
2144 default:
2145 rspamd_milter_send_action (session, RSPAMD_MILTER_ACCEPT);
2146 break;
2147 }
2148
2149 cleanup:
2150 rspamd_fstring_free (rcode);
2151 rspamd_fstring_free (xcode);
2152 rspamd_fstring_free (reply);
2153
2154 rspamd_milter_session_reset (session, RSPAMD_MILTER_RESET_ABORT);
2155 }
2156
2157 void
rspamd_milter_init_library(const struct rspamd_milter_context * ctx)2158 rspamd_milter_init_library (const struct rspamd_milter_context *ctx)
2159 {
2160 milter_ctx = ctx;
2161 }
2162
2163 rspamd_mempool_t *
rspamd_milter_get_session_pool(struct rspamd_milter_session * session)2164 rspamd_milter_get_session_pool (struct rspamd_milter_session *session)
2165 {
2166 struct rspamd_milter_private *priv = session->priv;
2167
2168 return priv->pool;
2169 }
2170