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, ¯o_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