Home | History | Annotate | Line # | Download | only in milter
      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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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