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