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