xref: /netbsd-src/external/ibm-public/postfix/dist/src/milter/milter.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
1 /*	$NetBSD: milter.c,v 1.5 2022/10/08 16:12:46 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	milter 3
6 /* SUMMARY
7 /*	generic MTA-side mail filter interface
8 /* SYNOPSIS
9 /*	#include <milter.h>
10 /*
11 /*	MILTERS	*milter_create(milter_names, conn_timeout, cmd_timeout,
12 /*					msg_timeout, protocol, def_action,
13 /*					conn_macros, helo_macros,
14 /*					mail_macros, rcpt_macros,
15 /*					data_macros, eoh_macros,
16 /*					eod_macros, unk_macros,
17 /*					macro_deflts)
18 /*	const char *milter_names;
19 /*	int	conn_timeout;
20 /*	int	cmd_timeout;
21 /*	int	msg_timeout;
22 /*	const char *protocol;
23 /*	const char *def_action;
24 /*	const char *conn_macros;
25 /*	const char *helo_macros;
26 /*	const char *mail_macros;
27 /*	const char *rcpt_macrps;
28 /*	const char *data_macros;
29 /*	const char *eoh_macros;
30 /*	const char *eod_macros;
31 /*	const char *unk_macros;
32 /*	const char *macro_deflts;
33 /*
34 /*	void	milter_free(milters)
35 /*	MILTERS	*milters;
36 /*
37 /*	void	milter_macro_callback(milters, mac_lookup, mac_context)
38 /*	const char *(*mac_lookup)(const char *name, void *context);
39 /*	void	*mac_context;
40 /*
41 /*	void	milter_edit_callback(milters, add_header, upd_header,
42 /*					ins_header, del_header, chg_from,
43 /*					add_rcpt, add_rcpt_par, del_rcpt,
44 /*					repl_body, context)
45 /*	MILTERS	*milters;
46 /*	MILTER_ADD_HEADER_FN add_header;
47 /*	MILTER_EDIT_HEADER_FN upd_header;
48 /*	MILTER_EDIT_HEADER_FN ins_header;
49 /*	MILTER_DEL_HEADER_FN del_header;
50 /*	MILTER_EDIT_FROM_FN chg_from;
51 /*	MILTER_EDIT_RCPT_FN add_rcpt;
52 /*	MILTER_EDIT_RCPT_PAR_FN add_rcpt_par;
53 /*	MILTER_EDIT_RCPT_FN del_rcpt;
54 /*	MILTER_EDIT_BODY_FN repl_body;
55 /*	void	*context;
56 /*
57 /*	const char *milter_conn_event(milters, client_name, client_addr,
58 /*					client_port, addr_family)
59 /*	MILTERS	*milters;
60 /*	const char *client_name;
61 /*	const char *client_addr;
62 /*	const char *client_port;
63 /*	int	addr_family;
64 /*
65 /*	const char *milter_disc_event(milters)
66 /*	MILTERS	*milters;
67 /*
68 /*	const char *milter_helo_event(milters, helo_name, esmtp_flag)
69 /*	MILTERS	*milters;
70 /*	const char *helo_name;
71 /*	int	esmtp_flag;
72 /*
73 /*	const char *milter_mail_event(milters, argv)
74 /*	MILTERS	*milters;
75 /*	const char **argv;
76 /*
77 /*	const char *milter_rcpt_event(milters, flags, argv)
78 /*	MILTERS	*milters;
79 /*	int	flags;
80 /*	const char **argv;
81 /*
82 /*	const char *milter_data_event(milters)
83 /*	MILTERS	*milters;
84 /*
85 /*	const char *milter_unknown_event(milters, command)
86 /*	MILTERS	*milters;
87 /*	const char *command;
88 /*
89 /*	const char *milter_other_event(milters)
90 /*	MILTERS	*milters;
91 /*
92 /*	const char *milter_message(milters, qfile, data_offset, auto_hdrs)
93 /*	MILTERS	*milters;
94 /*	VSTREAM *qfile;
95 /*	off_t	data_offset;
96 /*	ARGV	*auto_hdrs;
97 /*
98 /*	const char *milter_abort(milters)
99 /*	MILTERS	*milters;
100 /*
101 /*	int	milter_send(milters, fp)
102 /*	MILTERS	*milters;
103 /*	VSTREAM *fp;
104 /*
105 /*	MILTERS	*milter_receive(fp, count)
106 /*	VSTREAM	*fp;
107 /*	int	count;
108 /*
109 /*	int	milter_dummy(milters, fp)
110 /*	MILTERS	*milters;
111 /*	VSTREAM *fp;
112 /* DESCRIPTION
113 /*	The functions in this module manage one or more milter (mail
114 /*	filter) clients. Currently, only the Sendmail 8 filter
115 /*	protocol is supported.
116 /*
117 /*	The functions that inspect content or envelope commands
118 /*	return either an SMTP reply ([45]XX followed by enhanced
119 /*	status code and text), "D" (discard), "H" (quarantine),
120 /*	"S" (shutdown connection), or a null pointer, which means
121 /*	"no news is good news".
122 /*
123 /*	milter_create() instantiates the milter clients specified
124 /*	with the milter_names argument.  The conn_macros etc.
125 /*	arguments specify the names of macros that are sent to the
126 /*	mail filter applications upon a connect etc. event, and the
127 /*	macro_deflts argument specifies macro defaults that will be used
128 /*	only if the application's lookup call-back returns null. This
129 /*	function should be called during process initialization,
130 /*	before entering a chroot jail. The timeout parameters specify
131 /*	time limits for the completion of the specified request
132 /*	classes. The protocol parameter specifies a protocol version
133 /*	and optional extensions.  When the milter application is
134 /*	unavailable, the milter client will go into a suitable error
135 /*	state as specified with the def_action parameter (i.e.
136 /*	reject, tempfail or accept all subsequent events).
137 /*
138 /*	milter_free() disconnects from the milter instances that
139 /*	are still opened, and destroys the data structures created
140 /*	by milter_create(). This function is safe to call at any
141 /*	point after milter_create().
142 /*
143 /*	milter_macro_callback() specifies a call-back function and
144 /*	context for macro lookup. This function must be called
145 /*	before milter_conn_event().
146 /*
147 /*	milter_edit_callback() specifies call-back functions and
148 /*	context for editing the queue file after the end-of-data
149 /*	is received. This function must be called before milter_message();
150 /*
151 /*	milter_conn_event() reports an SMTP client connection event
152 /*	to the specified milter instances, after sending the macros
153 /*	specified with the milter_create() conn_macros argument.
154 /*	This function must be called before reporting any other
155 /*	events.
156 /*
157 /*	milter_disc_event() reports an SMTP client disconnection
158 /*	event to the specified milter instances. No events can
159 /*	reported after this call. To simplify usage, redundant calls
160 /*	of this function are NO-OPs and don't raise a run-time
161 /*	error.
162 /*
163 /*	milter_helo_event() reports a HELO or EHLO event to the
164 /*	specified milter instances, after sending the macros that
165 /*	were specified with the milter_create() helo_macros argument.
166 /*
167 /*	milter_mail_event() reports a MAIL FROM event to the specified
168 /*	milter instances, after sending the macros that were specified
169 /*	with the milter_create() mail_macros argument.
170 /*
171 /*	milter_rcpt_event() reports an RCPT TO event to the specified
172 /*	milter instances, after sending the macros that were specified
173 /*	with the milter_create() rcpt_macros argument. The flags
174 /*	argument supports the following:
175 /* .IP MILTER_FLAG_WANT_RCPT_REJ
176 /*	When this flag is cleared, invoke all milters.  When this
177 /*	flag is set, invoke only milters that want to receive
178 /*	rejected recipients; with Sendmail V8 Milters, {rcpt_mailer}
179 /*	is set to "error", {rcpt_host} is set to an enhanced status
180 /*	code, and {rcpt_addr} is set to descriptive text.
181 /* .PP
182 /*	milter_data_event() reports a DATA event to the specified
183 /*	milter instances, after sending the macros that were specified
184 /*	with the milter_create() data_macros argument.
185 /*
186 /*	milter_unknown_event() reports an unknown command event to
187 /*	the specified milter instances, after sending the macros
188 /*	that were specified with the milter_create() unk_macros
189 /*	argument.
190 /*
191 /*	milter_other_event() returns the current default mail filter
192 /*	reply for the current SMTP connection state; it does not
193 /*	change milter states. A null pointer result means that all
194 /*	is well. This function can be used for SMTP commands such
195 /*	as AUTH, STARTTLS that don't have their own milter event
196 /*	routine.
197 /*
198 /*	milter_message() sends the message header and body to the
199 /*	to the specified milter instances, and sends the macros
200 /*	specified with the milter_create() eoh_macros after the
201 /*	message header, and with the eod_macros argument at
202 /*	the end.  Each milter sees the result of any changes made
203 /*	by a preceding milter. This function must be called with
204 /*	as argument an open Postfix queue file.
205 /*
206 /*	milter_abort() cancels a mail transaction in progress.  To
207 /*	simplify usage, redundant calls of this function are NO-OPs
208 /*	and don't raise a run-time error.
209 /*
210 /*	milter_send() sends a list of mail filters over the specified
211 /*	stream. When given a null list pointer, a "no filter"
212 /*	indication is sent.  The result is non-zero in case of
213 /*	error.
214 /*
215 /*	milter_receive() receives the specified number of mail
216 /*	filters over the specified stream. The result is a null
217 /*	pointer when no milters were sent, or when an error happened.
218 /*
219 /*	milter_dummy() is like milter_send(), except that it sends
220 /*	a dummy, but entirely valid, mail filter list.
221 /* SEE ALSO
222 /*	milter8(3) Sendmail 8 Milter protocol
223 /* DIAGNOSTICS
224 /*	Panic: interface violation.
225 /*	Fatal errors: memory allocation problem.
226 /* LICENSE
227 /* .ad
228 /* .fi
229 /*	The Secure Mailer license must be distributed with this software.
230 /* AUTHOR(S)
231 /*	Wietse Venema
232 /*	IBM T.J. Watson Research
233 /*	P.O. Box 704
234 /*	Yorktown Heights, NY 10598, USA
235 /*
236 /*	Wietse Venema
237 /*	Google, Inc.
238 /*	111 8th Avenue
239 /*	New York, NY 10011, USA
240 /*--*/
241 
242 /* System library. */
243 
244 #include <sys_defs.h>
245 
246 /* Utility library. */
247 
248 #include <msg.h>
249 #include <mymalloc.h>
250 #include <stringops.h>
251 #include <argv.h>
252 #include <attr.h>
253 #include <htable.h>
254 
255 /* Global library. */
256 
257 #include <mail_proto.h>
258 #include <record.h>
259 #include <rec_type.h>
260 #include <mail_params.h>
261 #include <attr_override.h>
262 
263 /* Postfix Milter library. */
264 
265 #include <milter.h>
266 
267 /* Application-specific. */
268 
269  /*
270   * SLMs.
271   */
272 #define STR(x)	vstring_str(x)
273 
274 /* milter_macro_defaults_create - parse default macro entries */
275 
milter_macro_defaults_create(const char * macro_defaults)276 HTABLE *milter_macro_defaults_create(const char *macro_defaults)
277 {
278     const char myname[] = "milter_macro_defaults_create";
279     char   *saved_defaults = mystrdup(macro_defaults);
280     char   *cp = saved_defaults;
281     HTABLE *table = 0;
282     VSTRING *canon_buf = 0;
283     char   *nameval;
284 
285     while ((nameval = mystrtokq(&cp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
286 	const char *err;
287 	char   *name;
288 	char   *value;
289 
290 	/*
291 	 * Split the input into (name, value) pairs. Allow the forms
292 	 * name=value and  { name = value }, where the last form ignores
293 	 * whitespace after the opening "{", around the "=", and before the
294 	 * closing "}". A name may also be specified as {name}.
295 	 *
296 	 * Use the form {name} for table lookups, because that is the form of
297 	 * the S8_MAC_* macro names.
298 	 */
299 	if (*nameval == CHARS_BRACE[0]
300 	    && nameval[balpar(nameval, CHARS_BRACE)] != '='
301 	    && (err = extpar(&nameval, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0)
302 	    msg_fatal("malformed default macro entry: %s in \"%s\"",
303 		      err, macro_defaults);
304 	if ((err = split_nameval(nameval, &name, &value)) != 0)
305 	    msg_fatal("malformed default macro entry: %s in \"%s\"",
306 		      err, macro_defaults);
307 	if (*name != '{')			/* } */
308 	    name = STR(vstring_sprintf(canon_buf ? canon_buf :
309 			    (canon_buf = vstring_alloc(20)), "{%s}", name));
310 	if (table == 0)
311 	    table = htable_create(1);
312 	if (htable_find(table, name) != 0) {
313 	    msg_warn("ignoring multiple default macro entries for %s in \"%s\"",
314 		     name, macro_defaults);
315 	} else {
316 	    (void) htable_enter(table, name, mystrdup(value));
317 	    if (msg_verbose)
318 		msg_info("%s: add name=%s default=%s", myname, name, value);
319 	}
320     }
321     myfree(saved_defaults);
322     if (canon_buf)
323 	vstring_free(canon_buf);
324     return (table);
325 }
326 
327 /* milter_macro_lookup - look up macros */
328 
milter_macro_lookup(MILTERS * milters,const char * macro_names)329 static ARGV *milter_macro_lookup(MILTERS *milters, const char *macro_names)
330 {
331     const char *myname = "milter_macro_lookup";
332     char   *saved_names = mystrdup(macro_names);
333     char   *cp = saved_names;
334     ARGV   *argv = argv_alloc(10);
335     VSTRING *canon_buf = vstring_alloc(20);
336     const char *value;
337     const char *name;
338     const char *cname;
339 
340     while ((name = mystrtok(&cp, CHARS_COMMA_SP)) != 0) {
341 	if (msg_verbose)
342 	    msg_info("%s: \"%s\"", myname, name);
343 	if (*name != '{')			/* } */
344 	    cname = STR(vstring_sprintf(canon_buf, "{%s}", name));
345 	else
346 	    cname = name;
347 	if ((value = milters->mac_lookup(cname, milters->mac_context)) != 0) {
348 	    if (msg_verbose)
349 		msg_info("%s: result \"%s\"", myname, value);
350 	    argv_add(argv, name, value, (char *) 0);
351 	} else if (milters->macro_defaults != 0
352 	    && (value = htable_find(milters->macro_defaults, cname)) != 0) {
353 	    if (msg_verbose)
354 		msg_info("%s: using default \"%s\"", myname, value);
355 	    argv_add(argv, name, value, (char *) 0);
356 	}
357     }
358     myfree(saved_names);
359     vstring_free(canon_buf);
360     return (argv);
361 }
362 
363 /* milter_macro_callback - specify macro lookup */
364 
milter_macro_callback(MILTERS * milters,const char * (* mac_lookup)(const char *,void *),void * mac_context)365 void    milter_macro_callback(MILTERS *milters,
366 		           const char *(*mac_lookup) (const char *, void *),
367 			              void *mac_context)
368 {
369     milters->mac_lookup = mac_lookup;
370     milters->mac_context = mac_context;
371 }
372 
373 /* milter_edit_callback - specify queue file edit call-back information */
374 
milter_edit_callback(MILTERS * milters,MILTER_ADD_HEADER_FN add_header,MILTER_EDIT_HEADER_FN upd_header,MILTER_EDIT_HEADER_FN ins_header,MILTER_DEL_HEADER_FN del_header,MILTER_EDIT_FROM_FN chg_from,MILTER_EDIT_RCPT_FN add_rcpt,MILTER_EDIT_RCPT_PAR_FN add_rcpt_par,MILTER_EDIT_RCPT_FN del_rcpt,MILTER_EDIT_BODY_FN repl_body,void * chg_context)375 void    milter_edit_callback(MILTERS *milters,
376 			             MILTER_ADD_HEADER_FN add_header,
377 			             MILTER_EDIT_HEADER_FN upd_header,
378 			             MILTER_EDIT_HEADER_FN ins_header,
379 			             MILTER_DEL_HEADER_FN del_header,
380 			             MILTER_EDIT_FROM_FN chg_from,
381 			             MILTER_EDIT_RCPT_FN add_rcpt,
382 			             MILTER_EDIT_RCPT_PAR_FN add_rcpt_par,
383 			             MILTER_EDIT_RCPT_FN del_rcpt,
384 			             MILTER_EDIT_BODY_FN repl_body,
385 			             void *chg_context)
386 {
387     milters->add_header = add_header;
388     milters->upd_header = upd_header;
389     milters->ins_header = ins_header;
390     milters->del_header = del_header;
391     milters->chg_from = chg_from;
392     milters->add_rcpt = add_rcpt;
393     milters->add_rcpt_par = add_rcpt_par;
394     milters->del_rcpt = del_rcpt;
395     milters->repl_body = repl_body;
396     milters->chg_context = chg_context;
397 }
398 
399 /* milter_conn_event - report connect event */
400 
milter_conn_event(MILTERS * milters,const char * client_name,const char * client_addr,const char * client_port,unsigned addr_family)401 const char *milter_conn_event(MILTERS *milters,
402 			              const char *client_name,
403 			              const char *client_addr,
404 			              const char *client_port,
405 			              unsigned addr_family)
406 {
407     const char *resp;
408     MILTER *m;
409     ARGV   *global_macros = 0;
410     ARGV   *any_macros;
411 
412 #define MILTER_MACRO_EVAL(global_macros, m, milters, member) \
413 	((m->macros && m->macros->member[0]) ? \
414 	    milter_macro_lookup(milters, m->macros->member) : \
415 		global_macros ? global_macros : \
416 		    (global_macros = \
417 		         milter_macro_lookup(milters, milters->macros->member)))
418 
419     if (msg_verbose)
420 	msg_info("report connect to all milters");
421     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
422 	if (m->connect_on_demand != 0)
423 	    m->connect_on_demand(m);
424 	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, conn_macros);
425 	resp = m->conn_event(m, client_name, client_addr, client_port,
426 			     addr_family, any_macros);
427 	if (any_macros != global_macros)
428 	    argv_free(any_macros);
429     }
430     if (global_macros)
431 	argv_free(global_macros);
432     return (resp);
433 }
434 
435 /* milter_helo_event - report helo event */
436 
milter_helo_event(MILTERS * milters,const char * helo_name,int esmtp_flag)437 const char *milter_helo_event(MILTERS *milters, const char *helo_name,
438 			              int esmtp_flag)
439 {
440     const char *resp;
441     MILTER *m;
442     ARGV   *global_macros = 0;
443     ARGV   *any_macros;
444 
445     if (msg_verbose)
446 	msg_info("report helo to all milters");
447     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
448 	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, helo_macros);
449 	resp = m->helo_event(m, helo_name, esmtp_flag, any_macros);
450 	if (any_macros != global_macros)
451 	    argv_free(any_macros);
452     }
453     if (global_macros)
454 	argv_free(global_macros);
455     return (resp);
456 }
457 
458 /* milter_mail_event - report mail from event */
459 
milter_mail_event(MILTERS * milters,const char ** argv)460 const char *milter_mail_event(MILTERS *milters, const char **argv)
461 {
462     const char *resp;
463     MILTER *m;
464     ARGV   *global_macros = 0;
465     ARGV   *any_macros;
466 
467     if (msg_verbose)
468 	msg_info("report sender to all milters");
469     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
470 	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, mail_macros);
471 	resp = m->mail_event(m, argv, any_macros);
472 	if (any_macros != global_macros)
473 	    argv_free(any_macros);
474     }
475     if (global_macros)
476 	argv_free(global_macros);
477     return (resp);
478 }
479 
480 /* milter_rcpt_event - report rcpt to event */
481 
milter_rcpt_event(MILTERS * milters,int flags,const char ** argv)482 const char *milter_rcpt_event(MILTERS *milters, int flags, const char **argv)
483 {
484     const char *resp;
485     MILTER *m;
486     ARGV   *global_macros = 0;
487     ARGV   *any_macros;
488 
489     if (msg_verbose)
490 	msg_info("report recipient to all milters (flags=0x%x)", flags);
491     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
492 	if ((flags & MILTER_FLAG_WANT_RCPT_REJ) == 0
493 	    || (m->flags & MILTER_FLAG_WANT_RCPT_REJ) != 0) {
494 	    any_macros =
495 		MILTER_MACRO_EVAL(global_macros, m, milters, rcpt_macros);
496 	    resp = m->rcpt_event(m, argv, any_macros);
497 	    if (any_macros != global_macros)
498 		argv_free(any_macros);
499 	}
500     }
501     if (global_macros)
502 	argv_free(global_macros);
503     return (resp);
504 }
505 
506 /* milter_data_event - report data event */
507 
milter_data_event(MILTERS * milters)508 const char *milter_data_event(MILTERS *milters)
509 {
510     const char *resp;
511     MILTER *m;
512     ARGV   *global_macros = 0;
513     ARGV   *any_macros;
514 
515     if (msg_verbose)
516 	msg_info("report data to all milters");
517     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
518 	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, data_macros);
519 	resp = m->data_event(m, any_macros);
520 	if (any_macros != global_macros)
521 	    argv_free(any_macros);
522     }
523     if (global_macros)
524 	argv_free(global_macros);
525     return (resp);
526 }
527 
528 /* milter_unknown_event - report unknown command */
529 
milter_unknown_event(MILTERS * milters,const char * command)530 const char *milter_unknown_event(MILTERS *milters, const char *command)
531 {
532     const char *resp;
533     MILTER *m;
534     ARGV   *global_macros = 0;
535     ARGV   *any_macros;
536 
537     if (msg_verbose)
538 	msg_info("report unknown command to all milters");
539     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
540 	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, unk_macros);
541 	resp = m->unknown_event(m, command, any_macros);
542 	if (any_macros != global_macros)
543 	    argv_free(any_macros);
544     }
545     if (global_macros)
546 	argv_free(global_macros);
547     return (resp);
548 }
549 
550 /* milter_other_event - other SMTP event */
551 
milter_other_event(MILTERS * milters)552 const char *milter_other_event(MILTERS *milters)
553 {
554     const char *resp;
555     MILTER *m;
556 
557     if (msg_verbose)
558 	msg_info("query milter states for other event");
559     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next)
560 	resp = m->other_event(m);
561     return (resp);
562 }
563 
564 /* milter_message - inspect message content */
565 
milter_message(MILTERS * milters,VSTREAM * fp,off_t data_offset,ARGV * auto_hdrs)566 const char *milter_message(MILTERS *milters, VSTREAM *fp, off_t data_offset,
567 			           ARGV *auto_hdrs)
568 {
569     const char *resp;
570     MILTER *m;
571     ARGV   *global_eoh_macros = 0;
572     ARGV   *global_eod_macros = 0;
573     ARGV   *any_eoh_macros;
574     ARGV   *any_eod_macros;
575 
576     if (msg_verbose)
577 	msg_info("inspect content by all milters");
578     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
579 	any_eoh_macros = MILTER_MACRO_EVAL(global_eoh_macros, m, milters, eoh_macros);
580 	any_eod_macros = MILTER_MACRO_EVAL(global_eod_macros, m, milters, eod_macros);
581 	resp = m->message(m, fp, data_offset, any_eoh_macros, any_eod_macros,
582 			  auto_hdrs);
583 	if (any_eoh_macros != global_eoh_macros)
584 	    argv_free(any_eoh_macros);
585 	if (any_eod_macros != global_eod_macros)
586 	    argv_free(any_eod_macros);
587     }
588     if (global_eoh_macros)
589 	argv_free(global_eoh_macros);
590     if (global_eod_macros)
591 	argv_free(global_eod_macros);
592     return (resp);
593 }
594 
595 /* milter_abort - cancel message receiving state, all milters */
596 
milter_abort(MILTERS * milters)597 void    milter_abort(MILTERS *milters)
598 {
599     MILTER *m;
600 
601     if (msg_verbose)
602 	msg_info("abort all milters");
603     for (m = milters->milter_list; m != 0; m = m->next)
604 	m->abort(m);
605 }
606 
607 /* milter_disc_event - report client disconnect event to all milters */
608 
milter_disc_event(MILTERS * milters)609 void    milter_disc_event(MILTERS *milters)
610 {
611     MILTER *m;
612 
613     if (msg_verbose)
614 	msg_info("disconnect event to all milters");
615     for (m = milters->milter_list; m != 0; m = m->next)
616 	m->disc_event(m);
617 }
618 
619  /*
620   * Table-driven parsing of main.cf parameter overrides for specific Milters.
621   * We derive the override names from the corresponding main.cf parameter
622   * names by skipping the redundant "milter_" prefix.
623   */
624 static ATTR_OVER_TIME time_table[] = {
625     7 + (const char *) VAR_MILT_CONN_TIME, DEF_MILT_CONN_TIME, 0, 1, 0,
626     7 + (const char *) VAR_MILT_CMD_TIME, DEF_MILT_CMD_TIME, 0, 1, 0,
627     7 + (const char *) VAR_MILT_MSG_TIME, DEF_MILT_MSG_TIME, 0, 1, 0,
628     0,
629 };
630 static ATTR_OVER_STR str_table[] = {
631     7 + (const char *) VAR_MILT_PROTOCOL, 0, 1, 0,
632     7 + (const char *) VAR_MILT_DEF_ACTION, 0, 1, 0,
633     0,
634 };
635 
636 #define link_override_table_to_variable(table, var) \
637 	do { table[var##_offset].target = &var; } while (0)
638 
639 #define my_conn_timeout_offset	0
640 #define my_cmd_timeout_offset	1
641 #define my_msg_timeout_offset	2
642 
643 #define	my_protocol_offset	0
644 #define	my_def_action_offset	1
645 
646 /* milter_new - create milter list */
647 
milter_new(const char * names,int conn_timeout,int cmd_timeout,int msg_timeout,const char * protocol,const char * def_action,MILTER_MACROS * macros,HTABLE * macro_defaults)648 MILTERS *milter_new(const char *names,
649 		            int conn_timeout,
650 		            int cmd_timeout,
651 		            int msg_timeout,
652 		            const char *protocol,
653 		            const char *def_action,
654 		            MILTER_MACROS *macros,
655 		            HTABLE *macro_defaults)
656 {
657     MILTERS *milters;
658     MILTER *head = 0;
659     MILTER *tail = 0;
660     char   *name;
661     MILTER *milter;
662     const char *sep = CHARS_COMMA_SP;
663     const char *parens = CHARS_BRACE;
664     int     my_conn_timeout;
665     int     my_cmd_timeout;
666     int     my_msg_timeout;
667     const char *my_protocol;
668     const char *my_def_action;
669 
670     /*
671      * Initialize.
672      */
673     link_override_table_to_variable(time_table, my_conn_timeout);
674     link_override_table_to_variable(time_table, my_cmd_timeout);
675     link_override_table_to_variable(time_table, my_msg_timeout);
676     link_override_table_to_variable(str_table, my_protocol);
677     link_override_table_to_variable(str_table, my_def_action);
678 
679     /*
680      * Parse the milter list.
681      */
682     milters = (MILTERS *) mymalloc(sizeof(*milters));
683     if (names != 0 && *names != 0) {
684 	char   *saved_names = mystrdup(names);
685 	char   *cp = saved_names;
686 	char   *op;
687 	char   *err;
688 
689 	/*
690 	 * Instantiate Milters, allowing for per-Milter overrides.
691 	 */
692 	while ((name = mystrtokq(&cp, sep, parens)) != 0) {
693 	    my_conn_timeout = conn_timeout;
694 	    my_cmd_timeout = cmd_timeout;
695 	    my_msg_timeout = msg_timeout;
696 	    my_protocol = protocol;
697 	    my_def_action = def_action;
698 	    if (name[0] == parens[0]) {
699 		op = name;
700 		if ((err = extpar(&op, parens, EXTPAR_FLAG_NONE)) != 0)
701 		    msg_fatal("milter service syntax error: %s", err);
702 		if ((name = mystrtok(&op, sep)) == 0)
703 		    msg_fatal("empty milter definition: \"%s\"", names);
704 		attr_override(op, sep, parens,
705 			      CA_ATTR_OVER_STR_TABLE(str_table),
706 			      CA_ATTR_OVER_TIME_TABLE(time_table),
707 			      CA_ATTR_OVER_END);
708 	    }
709 	    milter = milter8_create(name, my_conn_timeout, my_cmd_timeout,
710 				    my_msg_timeout, my_protocol,
711 				    my_def_action, milters);
712 	    if (head == 0) {
713 		head = milter;
714 	    } else {
715 		tail->next = milter;
716 	    }
717 	    tail = milter;
718 	}
719 	myfree(saved_names);
720     }
721     milters->milter_list = head;
722     milters->mac_lookup = 0;
723     milters->mac_context = 0;
724     milters->macros = macros;
725     milters->macro_defaults = macro_defaults;
726     milters->add_header = 0;
727     milters->upd_header = milters->ins_header = 0;
728     milters->del_header = 0;
729     milters->add_rcpt = milters->del_rcpt = 0;
730     milters->repl_body = 0;
731     milters->chg_context = 0;
732     return (milters);
733 }
734 
735 /* milter_free - destroy all milters */
736 
milter_free(MILTERS * milters)737 void    milter_free(MILTERS *milters)
738 {
739     MILTER *m;
740     MILTER *next;
741 
742     if (msg_verbose)
743 	msg_info("free all milters");
744     for (m = milters->milter_list; m != 0; m = next)
745 	next = m->next, m->free(m);
746     if (milters->macros)
747 	milter_macros_free(milters->macros);
748     if (milters->macro_defaults)
749 	htable_free(milters->macro_defaults, myfree);
750     myfree((void *) milters);
751 }
752 
753 /* milter_dummy - send empty milter list */
754 
milter_dummy(MILTERS * milters,VSTREAM * stream)755 int     milter_dummy(MILTERS *milters, VSTREAM *stream)
756 {
757     MILTERS dummy = *milters;
758 
759     dummy.milter_list = 0;
760     return (milter_send(&dummy, stream));
761 }
762 
763 /* milter_send - send Milter instances over stream */
764 
milter_send(MILTERS * milters,VSTREAM * stream)765 int     milter_send(MILTERS *milters, VSTREAM *stream)
766 {
767     MILTER *m;
768     int     status = 0;
769     int     count = 0;
770 
771     /*
772      * XXX Optimization: send only the filters that are actually used in the
773      * remote process. No point sending a filter that looks at HELO commands
774      * to a cleanup server. For now we skip only the filters that are known
775      * to be disabled (either in global error state or in global accept
776      * state).
777      *
778      * XXX We must send *some* information, even when there are no active
779      * filters, otherwise the cleanup server would try to apply its own
780      * non_smtpd_milters settings.
781      */
782     if (milters != 0)
783 	for (m = milters->milter_list; m != 0; m = m->next)
784 	    if (m->active(m))
785 		count++;
786     (void) rec_fprintf(stream, REC_TYPE_MILT_COUNT, "%d", count);
787 
788     if (msg_verbose)
789 	msg_info("send %d milters", count);
790 
791     /*
792      * XXX Optimization: don't send or receive further information when there
793      * aren't any active filters.
794      */
795     if (count <= 0)
796 	return (0);
797 
798     /*
799      * Send the filter macro name lists.
800      */
801     (void) attr_print(stream, ATTR_FLAG_MORE,
802 		      SEND_ATTR_FUNC(milter_macros_print,
803 				     (const void *) milters->macros),
804 		      ATTR_TYPE_END);
805 
806     /*
807      * Send the filter macro defaults.
808      */
809     count = milters->macro_defaults ? milters->macro_defaults->used : 0;
810     (void) attr_print(stream, ATTR_FLAG_MORE,
811 		      SEND_ATTR_INT(MAIL_ATTR_SIZE, count),
812 		      ATTR_TYPE_END);
813     if (count > 0)
814 	(void) attr_print(stream, ATTR_FLAG_MORE,
815 			  SEND_ATTR_HASH(milters->macro_defaults),
816 			  ATTR_TYPE_END);
817 
818     /*
819      * Send the filter instances.
820      */
821     for (m = milters->milter_list; m != 0; m = m->next)
822 	if (m->active(m) && (status = m->send(m, stream)) != 0)
823 	    break;
824 
825     /*
826      * Over to you.
827      */
828     if (status != 0
829 	|| attr_scan(stream, ATTR_FLAG_STRICT,
830 		     RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
831 		     ATTR_TYPE_END) != 1
832 	|| status != 0) {
833 	msg_warn("cannot send milters to service %s", VSTREAM_PATH(stream));
834 	return (-1);
835     }
836     return (0);
837 }
838 
839 /* milter_receive - receive milters from stream */
840 
milter_receive(VSTREAM * stream,int count)841 MILTERS *milter_receive(VSTREAM *stream, int count)
842 {
843     MILTERS *milters;
844     MILTER *head = 0;
845     MILTER *tail = 0;
846     MILTER *milter = 0;
847     int     macro_default_count;
848 
849     if (msg_verbose)
850 	msg_info("receive %d milters", count);
851 
852     /*
853      * XXX We must instantiate a MILTERS structure even when the sender has
854      * no active filters, otherwise the cleanup server would try to use its
855      * own non_smtpd_milters settings.
856      */
857 #define NO_MILTERS	((char *) 0)
858 #define NO_TIMEOUTS	0, 0, 0
859 #define NO_PROTOCOL	((char *) 0)
860 #define NO_ACTION	((char *) 0)
861 #define NO_MACROS	((MILTER_MACROS *) 0)
862 #define NO_MACRO_DEFLTS	((HTABLE *) 0)
863 
864     milters = milter_new(NO_MILTERS, NO_TIMEOUTS, NO_PROTOCOL, NO_ACTION,
865 			 NO_MACROS, NO_MACRO_DEFLTS);
866 
867     /*
868      * XXX Optimization: don't send or receive further information when there
869      * aren't any active filters.
870      */
871     if (count <= 0)
872 	return (milters);
873 
874     /*
875      * Receive the global macro name lists.
876      */
877     milters->macros = milter_macros_alloc(MILTER_MACROS_ALLOC_ZERO);
878     if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
879 		  RECV_ATTR_FUNC(milter_macros_scan,
880 				 (void *) milters->macros),
881 		  ATTR_TYPE_END) != 1) {
882 	milter_free(milters);
883 	return (0);
884     }
885 
886     /*
887      * Receive the filter macro defaults.
888      */
889     if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
890 		  RECV_ATTR_INT(MAIL_ATTR_SIZE, &macro_default_count),
891 		  ATTR_TYPE_END) != 1
892 	|| (macro_default_count > 0
893 	    && attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
894 			 RECV_ATTR_HASH(milters->macro_defaults
895 					= htable_create(1)),
896 			 ATTR_TYPE_END) != macro_default_count)) {
897 	milter_free(milters);
898 	return (0);
899     }
900 
901     /*
902      * Receive the filters.
903      */
904     for (; count > 0; count--) {
905 	if ((milter = milter8_receive(stream, milters)) == 0) {
906 	    msg_warn("cannot receive milters via service %s socket",
907 		     VSTREAM_PATH(stream));
908 	    milter_free(milters);
909 	    return (0);
910 	}
911 	if (head == 0) {
912 	    /* Coverity: milter_free() depends on milters->milter_list. */
913 	    milters->milter_list = head = milter;
914 	} else {
915 	    tail->next = milter;
916 	}
917 	tail = milter;
918     }
919 
920     /*
921      * Over to you.
922      */
923     (void) attr_print(stream, ATTR_FLAG_NONE,
924 		      SEND_ATTR_INT(MAIL_ATTR_STATUS, 0),
925 		      ATTR_TYPE_END);
926     return (milters);
927 }
928 
929 #ifdef TEST
930 
931  /*
932   * Proof-of-concept test program. This can be used interactively, but is
933   * typically used for automated regression tests from a script.
934   */
935 
936 /* System library. */
937 
938 #include <sys/socket.h>
939 #include <stdlib.h>
940 #include <string.h>
941 
942 /* Utility library. */
943 
944 #include "msg_vstream.h"
945 #include "vstring_vstream.h"
946 
947 /* Global library. */
948 
949 #include <mail_params.h>
950 
951 int     var_milt_conn_time = 10;
952 int     var_milt_cmd_time = 10;
953 int     var_milt_msg_time = 100;
954 char   *var_milt_protocol = DEF_MILT_PROTOCOL;
955 char   *var_milt_def_action = DEF_MILT_DEF_ACTION;
956 
usage(void)957 static void usage(void)
958 {
959     vstream_fprintf(VSTREAM_ERR, "usage: \n"
960 		    "	create names...		create and connect\n"
961 #if 0
962 		    "	conn_macros names...	define connect macros\n"
963 		    "	helo_macros names...	define helo command macros\n"
964 		    "	mail_macros names...	define mail command macros\n"
965 		    "	rcpt_macros names...	define rcpt command macros\n"
966 		    "	data_macros names...	define data command macros\n"
967 		    "	unk_macros names...	unknown command macros\n"
968 		    "	message_macros names...	define message macros\n"
969 #endif
970 		    "	free			disconnect and destroy\n"
971 		    "	connect name addr port family\n"
972 		    "	helo hostname\n"
973 		    "	ehlo hostname\n"
974 		    "	mail from sender...\n"
975 		    "	rcpt to recipient...\n"
976 		    "	data\n"
977 		    "	disconnect\n"
978 		    "	unknown command\n");
979     vstream_fflush(VSTREAM_ERR);
980 }
981 
main(int argc,char ** argv)982 int     main(int argc, char **argv)
983 {
984     MILTERS *milters = 0;
985     char   *conn_macros, *helo_macros, *mail_macros, *rcpt_macros;
986     char   *data_macros, *eoh_macros, *eod_macros, *unk_macros;
987     char   *macro_deflts;
988     VSTRING *inbuf = vstring_alloc(100);
989     char   *bufp;
990     char   *cmd;
991     int     ch;
992     int     istty = isatty(vstream_fileno(VSTREAM_IN));
993 
994     conn_macros = helo_macros = mail_macros = rcpt_macros = data_macros
995 	= eoh_macros = eod_macros = unk_macros = macro_deflts = "";
996 
997     msg_vstream_init(argv[0], VSTREAM_ERR);
998     while ((ch = GETOPT(argc, argv, "V:v")) > 0) {
999 	switch (ch) {
1000 	default:
1001 	    msg_fatal("usage: %s [-a action] [-p protocol] [-v]", argv[0]);
1002 	case 'a':
1003 	    var_milt_def_action = optarg;
1004 	    break;
1005 	case 'p':
1006 	    var_milt_protocol = optarg;
1007 	    break;
1008 	case 'v':
1009 	    msg_verbose++;
1010 	    break;
1011 	}
1012     }
1013     optind = OPTIND;
1014 
1015     for (;;) {
1016 	const char *resp = 0;
1017 	ARGV   *argv;
1018 	char  **args;
1019 
1020 	if (istty) {
1021 	    vstream_printf("- ");
1022 	    vstream_fflush(VSTREAM_OUT);
1023 	}
1024 	if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0)
1025 	    break;
1026 	bufp = vstring_str(inbuf);
1027 	if (!istty) {
1028 	    vstream_printf("> %s\n", bufp);
1029 	    vstream_fflush(VSTREAM_OUT);
1030 	}
1031 	if (*bufp == '#')
1032 	    continue;
1033 	cmd = mystrtok(&bufp, " ");
1034 	if (cmd == 0) {
1035 	    usage();
1036 	    continue;
1037 	}
1038 	argv = argv_split(bufp, " ");
1039 	args = argv->argv;
1040 	if (strcmp(cmd, "create") == 0 && argv->argc == 1) {
1041 	    if (milters != 0) {
1042 		msg_warn("deleting existing milters");
1043 		milter_free(milters);
1044 	    }
1045 	    milters = milter_create(args[0], var_milt_conn_time,
1046 				    var_milt_cmd_time, var_milt_msg_time,
1047 				    var_milt_protocol, var_milt_def_action,
1048 				    conn_macros, helo_macros, mail_macros,
1049 				    rcpt_macros, data_macros, eoh_macros,
1050 				    eod_macros, unk_macros, macro_deflts);
1051 	} else if (strcmp(cmd, "free") == 0 && argv->argc == 0) {
1052 	    if (milters == 0) {
1053 		msg_warn("no milters");
1054 		continue;
1055 	    }
1056 	    milter_free(milters);
1057 	    milters = 0;
1058 	} else if (strcmp(cmd, "connect") == 0 && argv->argc == 4) {
1059 	    if (milters == 0) {
1060 		msg_warn("no milters");
1061 		continue;
1062 	    }
1063 	    resp = milter_conn_event(milters, args[0], args[1], args[2],
1064 				 strcmp(args[3], "AF_INET") == 0 ? AF_INET :
1065 			       strcmp(args[3], "AF_INET6") == 0 ? AF_INET6 :
1066 				 strcmp(args[3], "AF_UNIX") == 0 ? AF_UNIX :
1067 				     AF_UNSPEC);
1068 	} else if (strcmp(cmd, "helo") == 0 && argv->argc == 1) {
1069 	    if (milters == 0) {
1070 		msg_warn("no milters");
1071 		continue;
1072 	    }
1073 	    resp = milter_helo_event(milters, args[0], 0);
1074 	} else if (strcmp(cmd, "ehlo") == 0 && argv->argc == 1) {
1075 	    if (milters == 0) {
1076 		msg_warn("no milters");
1077 		continue;
1078 	    }
1079 	    resp = milter_helo_event(milters, args[0], 1);
1080 	} else if (strcmp(cmd, "mail") == 0 && argv->argc > 0) {
1081 	    if (milters == 0) {
1082 		msg_warn("no milters");
1083 		continue;
1084 	    }
1085 	    resp = milter_mail_event(milters, (const char **) args);
1086 	} else if (strcmp(cmd, "rcpt") == 0 && argv->argc > 0) {
1087 	    if (milters == 0) {
1088 		msg_warn("no milters");
1089 		continue;
1090 	    }
1091 	    resp = milter_rcpt_event(milters, 0, (const char **) args);
1092 	} else if (strcmp(cmd, "unknown") == 0 && argv->argc > 0) {
1093 	    if (milters == 0) {
1094 		msg_warn("no milters");
1095 		continue;
1096 	    }
1097 	    resp = milter_unknown_event(milters, args[0]);
1098 	} else if (strcmp(cmd, "data") == 0 && argv->argc == 0) {
1099 	    if (milters == 0) {
1100 		msg_warn("no milters");
1101 		continue;
1102 	    }
1103 	    resp = milter_data_event(milters);
1104 	} else if (strcmp(cmd, "disconnect") == 0 && argv->argc == 0) {
1105 	    if (milters == 0) {
1106 		msg_warn("no milters");
1107 		continue;
1108 	    }
1109 	    milter_disc_event(milters);
1110 	} else {
1111 	    usage();
1112 	}
1113 	if (resp != 0)
1114 	    msg_info("%s", resp);
1115 	argv_free(argv);
1116     }
1117     if (milters != 0)
1118 	milter_free(milters);
1119     vstring_free(inbuf);
1120     return (0);
1121 }
1122 
1123 #endif
1124