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