xref: /netbsd-src/external/ibm-public/postfix/dist/src/verify/verify.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: verify.c,v 1.1.1.3 2011/05/11 09:11:28 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	verify 8
6 /* SUMMARY
7 /*	Postfix address verification server
8 /* SYNOPSIS
9 /*	\fBverify\fR [generic Postfix daemon options]
10 /* DESCRIPTION
11 /*	The \fBverify\fR(8) address verification server maintains a record
12 /*	of what recipient addresses are known to be deliverable or
13 /*	undeliverable.
14 /*
15 /*	Addresses are verified by injecting probe messages into the
16 /*	Postfix queue. Probe messages are run through all the routing
17 /*	and rewriting machinery except for final delivery, and are
18 /*	discarded rather than being deferred or bounced.
19 /*
20 /*	Address verification relies on the answer from the nearest
21 /*	MTA for the specified address, and will therefore not detect
22 /*	all undeliverable addresses.
23 /*
24 /*	The \fBverify\fR(8) server is designed to run under control
25 /*	by the Postfix
26 /*	master server. It maintains an optional persistent database.
27 /*	To avoid being interrupted by "postfix stop" in the middle
28 /*	of a database update, the process runs in a separate process
29 /*	group.
30 /*
31 /*	The \fBverify\fR(8) server implements the following requests:
32 /* .IP "\fBupdate\fI address status text\fR"
33 /*	Update the status and text of the specified address.
34 /* .IP "\fBquery\fI address\fR"
35 /*	Look up the \fIstatus\fR and \fItext\fR for the specified
36 /*	\fIaddress\fR.
37 /*	If the status is unknown, a probe is sent and an "in progress"
38 /*	status is returned.
39 /* SECURITY
40 /* .ad
41 /* .fi
42 /*	The address verification server is not security-sensitive. It does
43 /*	not talk to the network, and it does not talk to local users.
44 /*	The verify server can run chrooted at fixed low privilege.
45 /*
46 /*	The address verification server can be coerced to store
47 /*	unlimited amounts of garbage. Limiting the cache expiry
48 /*	time
49 /*	trades one problem (disk space exhaustion) for another
50 /*	one (poor response time to client requests).
51 /*
52 /*	With Postfix version 2.5 and later, the \fBverify\fR(8)
53 /*	server no longer uses root privileges when opening the
54 /*	\fBaddress_verify_map\fR cache file. The file should now
55 /*	be stored under the Postfix-owned \fBdata_directory\fR.  As
56 /*	a migration aid, an attempt to open a cache file under a
57 /*	non-Postfix directory is redirected to the Postfix-owned
58 /*	\fBdata_directory\fR, and a warning is logged.
59 /* DIAGNOSTICS
60 /*	Problems and transactions are logged to \fBsyslogd\fR(8).
61 /* BUGS
62 /*	Address verification probe messages add additional traffic
63 /*	to the mail queue.
64 /*	Recipient verification may cause an increased load on
65 /*	down-stream servers in the case of a dictionary attack or
66 /*	a flood of backscatter bounces.
67 /*	Sender address verification may cause your site to be
68 /*	blacklisted by some providers.
69 /*
70 /*	If the persistent database ever gets corrupted then the world
71 /*	comes to an end and human intervention is needed. This violates
72 /*	a basic Postfix principle.
73 /* CONFIGURATION PARAMETERS
74 /* .ad
75 /* .fi
76 /*	Changes to \fBmain.cf\fR are not picked up automatically,
77 /*	as \fBverify\fR(8)
78 /*	processes are long-lived. Use the command "\fBpostfix reload\fR" after
79 /*	a configuration change.
80 /*
81 /*	The text below provides only a parameter summary. See
82 /*	\fBpostconf\fR(5) for more details including examples.
83 /* CACHE CONTROLS
84 /* .ad
85 /* .fi
86 /* .IP "\fBaddress_verify_map (see 'postconf -d' output)\fR"
87 /*	Lookup table for persistent address verification status
88 /*	storage.
89 /* .IP "\fBaddress_verify_sender ($double_bounce_sender)\fR"
90 /*	The sender address to use in address verification probes; prior
91 /*	to Postfix 2.5 the default was "postmaster".
92 /* .IP "\fBaddress_verify_positive_expire_time (31d)\fR"
93 /*	The time after which a successful probe expires from the address
94 /*	verification cache.
95 /* .IP "\fBaddress_verify_positive_refresh_time (7d)\fR"
96 /*	The time after which a successful address verification probe needs
97 /*	to be refreshed.
98 /* .IP "\fBaddress_verify_negative_cache (yes)\fR"
99 /*	Enable caching of failed address verification probe results.
100 /* .IP "\fBaddress_verify_negative_expire_time (3d)\fR"
101 /*	The time after which a failed probe expires from the address
102 /*	verification cache.
103 /* .IP "\fBaddress_verify_negative_refresh_time (3h)\fR"
104 /*	The time after which a failed address verification probe needs to
105 /*	be refreshed.
106 /* .PP
107 /*	Available with Postfix 2.7 and later:
108 /* .IP "\fBaddress_verify_cache_cleanup_interval (12h)\fR"
109 /*	The amount of time between \fBverify\fR(8) address verification
110 /*	database cleanup runs.
111 /* PROBE MESSAGE ROUTING CONTROLS
112 /* .ad
113 /* .fi
114 /*	By default, probe messages are delivered via the same route
115 /*	as regular messages.  The following parameters can be used to
116 /*	override specific message routing mechanisms.
117 /* .IP "\fBaddress_verify_relayhost ($relayhost)\fR"
118 /*	Overrides the relayhost parameter setting for address verification
119 /*	probes.
120 /* .IP "\fBaddress_verify_transport_maps ($transport_maps)\fR"
121 /*	Overrides the transport_maps parameter setting for address verification
122 /*	probes.
123 /* .IP "\fBaddress_verify_local_transport ($local_transport)\fR"
124 /*	Overrides the local_transport parameter setting for address
125 /*	verification probes.
126 /* .IP "\fBaddress_verify_virtual_transport ($virtual_transport)\fR"
127 /*	Overrides the virtual_transport parameter setting for address
128 /*	verification probes.
129 /* .IP "\fBaddress_verify_relay_transport ($relay_transport)\fR"
130 /*	Overrides the relay_transport parameter setting for address
131 /*	verification probes.
132 /* .IP "\fBaddress_verify_default_transport ($default_transport)\fR"
133 /*	Overrides the default_transport parameter setting for address
134 /*	verification probes.
135 /* .PP
136 /*	Available in Postfix 2.3 and later:
137 /* .IP "\fBaddress_verify_sender_dependent_relayhost_maps ($sender_dependent_relayhost_maps)\fR"
138 /*	Overrides the sender_dependent_relayhost_maps parameter setting for address
139 /*	verification probes.
140 /* .PP
141 /*	Available in Postfix 2.7 and later:
142 /* .IP "\fBaddress_verify_sender_dependent_default_transport_maps ($sender_dependent_default_transport_maps)\fR"
143 /*	Overrides the sender_dependent_default_transport_maps parameter
144 /*	setting for address verification probes.
145 /* MISCELLANEOUS CONTROLS
146 /* .ad
147 /* .fi
148 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
149 /*	The default location of the Postfix main.cf and master.cf
150 /*	configuration files.
151 /* .IP "\fBdaemon_timeout (18000s)\fR"
152 /*	How much time a Postfix daemon process may take to handle a
153 /*	request before it is terminated by a built-in watchdog timer.
154 /* .IP "\fBipc_timeout (3600s)\fR"
155 /*	The time limit for sending or receiving information over an internal
156 /*	communication channel.
157 /* .IP "\fBprocess_id (read-only)\fR"
158 /*	The process ID of a Postfix command or daemon process.
159 /* .IP "\fBprocess_name (read-only)\fR"
160 /*	The process name of a Postfix command or daemon process.
161 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
162 /*	The location of the Postfix top-level queue directory.
163 /* .IP "\fBsyslog_facility (mail)\fR"
164 /*	The syslog facility of Postfix logging.
165 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
166 /*	The mail system name that is prepended to the process name in syslog
167 /*	records, so that "smtpd" becomes, for example, "postfix/smtpd".
168 /* SEE ALSO
169 /*	smtpd(8), Postfix SMTP server
170 /*	cleanup(8), enqueue Postfix message
171 /*	postconf(5), configuration parameters
172 /*	syslogd(5), system logging
173 /* README FILES
174 /* .ad
175 /* .fi
176 /*	Use "\fBpostconf readme_directory\fR" or
177 /*	"\fBpostconf html_directory\fR" to locate this information.
178 /* .na
179 /* .nf
180 /*	ADDRESS_VERIFICATION_README, address verification howto
181 /* LICENSE
182 /* .ad
183 /* .fi
184 /*	The Secure Mailer license must be distributed with this software.
185 /* HISTORY
186 /* .ad
187 /* .fi
188 /*	This service was introduced with Postfix version 2.1.
189 /* AUTHOR(S)
190 /*	Wietse Venema
191 /*	IBM T.J. Watson Research
192 /*	P.O. Box 704
193 /*	Yorktown Heights, NY 10598, USA
194 /*--*/
195 
196 /* System library. */
197 
198 #include <sys_defs.h>
199 #include <sys/stat.h>
200 #include <time.h>
201 #include <string.h>
202 #include <stdlib.h>
203 #include <unistd.h>
204 
205 /* Utility library. */
206 
207 #include <msg.h>
208 #include <mymalloc.h>
209 #include <htable.h>
210 #include <dict_ht.h>
211 #include <dict_cache.h>
212 #include <split_at.h>
213 #include <stringops.h>
214 #include <set_eugid.h>
215 #include <events.h>
216 
217 /* Global library. */
218 
219 #include <mail_conf.h>
220 #include <mail_params.h>
221 #include <mail_version.h>
222 #include <mail_proto.h>
223 #include <post_mail.h>
224 #include <data_redirect.h>
225 #include <verify_clnt.h>
226 
227 /* Server skeleton. */
228 
229 #include <mail_server.h>
230 
231 /* Application-specific. */
232 
233  /*
234   * Tunable parameters.
235   */
236 char   *var_verify_map;
237 int     var_verify_pos_exp;
238 int     var_verify_pos_try;
239 int     var_verify_neg_exp;
240 int     var_verify_neg_try;
241 int     var_verify_scan_cache;
242 char   *var_verify_sender;
243 
244  /*
245   * State.
246   */
247 static DICT_CACHE *verify_map;
248 
249  /*
250   * Silly little macros.
251   */
252 #define STR(x)			vstring_str(x)
253 #define STREQ(x,y)		(strcmp(x,y) == 0)
254 
255  /*
256   * The address verification database consists of (address, data) tuples. The
257   * format of the data field is "status:probed:updated:text". The meaning of
258   * each field is:
259   *
260   * status: one of the four recipient status codes (OK, DEFER, BOUNCE or TODO).
261   * In the case of TODO, we have no information about the address, and the
262   * address is being probed.
263   *
264   * probed: if non-zero, the time the currently outstanding address probe was
265   * sent. If zero, there is no outstanding address probe.
266   *
267   * updated: if non-zero, the time the address probe result was received. If
268   * zero, we have no information about the address, and the address is being
269   * probed.
270   *
271   * text: descriptive text from delivery agents etc.
272   */
273 
274  /*
275   * Quick test to see status without parsing the whole entry.
276   */
277 #define STATUS_FROM_RAW_ENTRY(e) atoi(e)
278 
279 /* verify_make_entry - construct table entry */
280 
281 static void verify_make_entry(VSTRING *buf, int status, long probed,
282 			              long updated, const char *text)
283 {
284     vstring_sprintf(buf, "%d:%ld:%ld:%s", status, probed, updated, text);
285 }
286 
287 /* verify_parse_entry - parse table entry */
288 
289 static int verify_parse_entry(char *buf, int *status, long *probed,
290 			              long *updated, char **text)
291 {
292     char   *probed_text;
293     char   *updated_text;
294 
295     if ((probed_text = split_at(buf, ':')) != 0
296 	&& (updated_text = split_at(probed_text, ':')) != 0
297 	&& (*text = split_at(updated_text, ':')) != 0
298 	&& alldig(buf)
299 	&& alldig(probed_text)
300 	&& alldig(updated_text)) {
301 	*probed = atol(probed_text);
302 	*updated = atol(updated_text);
303 	*status = atoi(buf);
304 
305 	/*
306 	 * Coverity 200604: the code incorrectly tested (probed || updated),
307 	 * so that the sanity check never detected all-zero time stamps. Such
308 	 * records are never written. If we read a record with all-zero time
309 	 * stamps, then something is badly broken.
310 	 */
311 	if ((*status == DEL_RCPT_STAT_OK
312 	     || *status == DEL_RCPT_STAT_DEFER
313 	     || *status == DEL_RCPT_STAT_BOUNCE
314 	     || *status == DEL_RCPT_STAT_TODO)
315 	    && (*probed || *updated))
316 	    return (0);
317     }
318     msg_warn("bad address verify table entry: %.100s", buf);
319     return (-1);
320 }
321 
322 /* verify_stat2name - status to name */
323 
324 static const char *verify_stat2name(int addr_status)
325 {
326     if (addr_status == DEL_RCPT_STAT_OK)
327 	return ("deliverable");
328     if (addr_status == DEL_RCPT_STAT_DEFER)
329 	return ("undeliverable");
330     if (addr_status == DEL_RCPT_STAT_BOUNCE)
331 	return ("undeliverable");
332     return (0);
333 }
334 
335 /* verify_update_service - update address service */
336 
337 static void verify_update_service(VSTREAM *client_stream)
338 {
339     VSTRING *buf = vstring_alloc(10);
340     VSTRING *addr = vstring_alloc(10);
341     int     addr_status;
342     VSTRING *text = vstring_alloc(10);
343     const char *status_name;
344     const char *raw_data;
345     long    probed;
346     long    updated;
347 
348     if (attr_scan(client_stream, ATTR_FLAG_STRICT,
349 		  ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
350 		  ATTR_TYPE_INT, MAIL_ATTR_ADDR_STATUS, &addr_status,
351 		  ATTR_TYPE_STR, MAIL_ATTR_WHY, text,
352 		  ATTR_TYPE_END) == 3) {
353 	/* FIX 200501 IPv6 patch did not neuter ":" in address literals. */
354 	translit(STR(addr), ":", "_");
355 	if ((status_name = verify_stat2name(addr_status)) == 0) {
356 	    msg_warn("bad recipient status %d for recipient %s",
357 		     addr_status, STR(addr));
358 	    attr_print(client_stream, ATTR_FLAG_NONE,
359 		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, VRFY_STAT_BAD,
360 		       ATTR_TYPE_END);
361 	} else {
362 
363 	    /*
364 	     * Robustness: don't allow a failed probe to clobber an OK
365 	     * address before it expires. The failed probe is ignored so that
366 	     * the address will be re-probed upon the next query. As long as
367 	     * some probes succeed the address will remain cached as OK.
368 	     */
369 	    if (addr_status == DEL_RCPT_STAT_OK
370 		|| (raw_data = dict_cache_lookup(verify_map, STR(addr))) == 0
371 		|| STATUS_FROM_RAW_ENTRY(raw_data) != DEL_RCPT_STAT_OK) {
372 		probed = 0;
373 		updated = (long) time((time_t *) 0);
374 		verify_make_entry(buf, addr_status, probed, updated, STR(text));
375 		if (msg_verbose)
376 		    msg_info("PUT %s status=%d probed=%ld updated=%ld text=%s",
377 			STR(addr), addr_status, probed, updated, STR(text));
378 		dict_cache_update(verify_map, STR(addr), STR(buf));
379 	    }
380 	    attr_print(client_stream, ATTR_FLAG_NONE,
381 		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, VRFY_STAT_OK,
382 		       ATTR_TYPE_END);
383 	}
384     }
385     vstring_free(buf);
386     vstring_free(addr);
387     vstring_free(text);
388 }
389 
390 /* verify_post_mail_action - callback */
391 
392 static void verify_post_mail_action(VSTREAM *stream, void *unused_context)
393 {
394 
395     /*
396      * Probe messages need no body content, because they are never delivered,
397      * deferred, or bounced.
398      */
399     if (stream != 0)
400 	post_mail_fclose(stream);
401 }
402 
403 /* verify_query_service - query address status */
404 
405 static void verify_query_service(VSTREAM *client_stream)
406 {
407     VSTRING *addr = vstring_alloc(10);
408     VSTRING *get_buf = 0;
409     VSTRING *put_buf = 0;
410     const char *raw_data;
411     int     addr_status;
412     long    probed;
413     long    updated;
414     char   *text;
415 
416     if (attr_scan(client_stream, ATTR_FLAG_STRICT,
417 		  ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
418 		  ATTR_TYPE_END) == 1) {
419 	long    now = (long) time((time_t *) 0);
420 
421 	/*
422 	 * Produce a default record when no usable record exists.
423 	 *
424 	 * If negative caching is disabled, purge an expired record from the
425 	 * database.
426 	 *
427 	 * XXX Assume that a probe is lost if no response is received in 1000
428 	 * seconds. If this number is too small the queue will slowly fill up
429 	 * with delayed probes.
430 	 *
431 	 * XXX Maintain a moving average for the probe turnaround time, and
432 	 * allow probe "retransmission" when a probe is outstanding for, say
433 	 * some minimal amount of time (1000 sec) plus several times the
434 	 * observed probe turnaround time. This causes probing to back off
435 	 * when the mail system becomes congested.
436 	 */
437 #define POSITIVE_ENTRY_EXPIRED(addr_status, updated) \
438     (addr_status == DEL_RCPT_STAT_OK && updated + var_verify_pos_exp < now)
439 #define NEGATIVE_ENTRY_EXPIRED(addr_status, updated) \
440     (addr_status != DEL_RCPT_STAT_OK && updated + var_verify_neg_exp < now)
441 #define PROBE_TTL	1000
442 
443 	/* FIX 200501 IPv6 patch did not neuter ":" in address literals. */
444 	translit(STR(addr), ":", "_");
445 	if ((raw_data = dict_cache_lookup(verify_map, STR(addr))) == 0	/* not found */
446 	    || ((get_buf = vstring_alloc(10)),
447 		vstring_strcpy(get_buf, raw_data),	/* malformed */
448 		verify_parse_entry(STR(get_buf), &addr_status, &probed,
449 				   &updated, &text) < 0)
450 	    || (now - probed > PROBE_TTL	/* safe to probe */
451 		&& (POSITIVE_ENTRY_EXPIRED(addr_status, updated)
452 		    || NEGATIVE_ENTRY_EXPIRED(addr_status, updated)))) {
453 	    addr_status = DEL_RCPT_STAT_TODO;
454 	    probed = 0;
455 	    updated = 0;
456 	    text = "Address verification in progress";
457 	    if (raw_data != 0 && var_verify_neg_cache == 0)
458 		dict_cache_delete(verify_map, STR(addr));
459 	}
460 	if (msg_verbose)
461 	    msg_info("GOT %s status=%d probed=%ld updated=%ld text=%s",
462 		     STR(addr), addr_status, probed, updated, text);
463 
464 	/*
465 	 * Respond to the client.
466 	 */
467 	attr_print(client_stream, ATTR_FLAG_NONE,
468 		   ATTR_TYPE_INT, MAIL_ATTR_STATUS, VRFY_STAT_OK,
469 		   ATTR_TYPE_INT, MAIL_ATTR_ADDR_STATUS, addr_status,
470 		   ATTR_TYPE_STR, MAIL_ATTR_WHY, text,
471 		   ATTR_TYPE_END);
472 
473 	/*
474 	 * Send a new probe when the information needs to be refreshed.
475 	 *
476 	 * XXX For an initial proof of concept implementation, use synchronous
477 	 * mail submission. This needs to be made async for high-volume
478 	 * sites, which makes it even more interesting to eliminate duplicate
479 	 * queries while a probe is being built.
480 	 *
481 	 * If negative caching is turned off, update the database only when
482 	 * refreshing an existing entry.
483 	 */
484 #define POSITIVE_REFRESH_NEEDED(addr_status, updated) \
485     (addr_status == DEL_RCPT_STAT_OK && updated + var_verify_pos_try < now)
486 #define NEGATIVE_REFRESH_NEEDED(addr_status, updated) \
487     (addr_status != DEL_RCPT_STAT_OK && updated + var_verify_neg_try < now)
488 
489 	if (now - probed > PROBE_TTL
490 	    && (POSITIVE_REFRESH_NEEDED(addr_status, updated)
491 		|| NEGATIVE_REFRESH_NEEDED(addr_status, updated))) {
492 	    if (msg_verbose)
493 		msg_info("PROBE %s status=%d probed=%ld updated=%ld",
494 			 STR(addr), addr_status, now, updated);
495 	    post_mail_fopen_async(strcmp(var_verify_sender, "<>") == 0 ?
496 				  "" : var_verify_sender, STR(addr),
497 				  INT_FILT_MASK_NONE,
498 				  DEL_REQ_FLAG_MTA_VRFY,
499 				  (VSTRING *) 0,
500 				  verify_post_mail_action,
501 				  (void *) 0);
502 	    if (updated != 0 || var_verify_neg_cache != 0) {
503 		put_buf = vstring_alloc(10);
504 		verify_make_entry(put_buf, addr_status, now, updated, text);
505 		if (msg_verbose)
506 		    msg_info("PUT %s status=%d probed=%ld updated=%ld text=%s",
507 			     STR(addr), addr_status, now, updated, text);
508 		dict_cache_update(verify_map, STR(addr), STR(put_buf));
509 	    }
510 	}
511     }
512     vstring_free(addr);
513     if (get_buf)
514 	vstring_free(get_buf);
515     if (put_buf)
516 	vstring_free(put_buf);
517 }
518 
519 /* verify_cache_validator - cache cleanup validator */
520 
521 static int verify_cache_validator(const char *addr, const char *raw_data,
522 			            char *context)
523 {
524     VSTRING *get_buf = (VSTRING *) context;
525     int     addr_status;
526     long    probed;
527     long    updated;
528     char   *text;
529     long    now = (long) event_time();
530 
531 #define POS_OR_NEG_ENTRY_EXPIRED(stat, stamp) \
532 	(POSITIVE_ENTRY_EXPIRED((stat), (stamp)) \
533 	    || NEGATIVE_ENTRY_EXPIRED((stat), (stamp)))
534 
535     vstring_strcpy(get_buf, raw_data);
536     return (verify_parse_entry(STR(get_buf), &addr_status,	/* syntax OK */
537 			       &probed, &updated, &text) == 0
538 	    && (now - probed < PROBE_TTL	/* probe in progress */
539 		|| !POS_OR_NEG_ENTRY_EXPIRED(addr_status, updated)));
540 }
541 
542 /* verify_service - perform service for client */
543 
544 static void verify_service(VSTREAM *client_stream, char *unused_service,
545 			           char **argv)
546 {
547     VSTRING *request = vstring_alloc(10);
548 
549     /*
550      * Sanity check. This service takes no command-line arguments.
551      */
552     if (argv[0])
553 	msg_fatal("unexpected command-line argument: %s", argv[0]);
554 
555     /*
556      * This routine runs whenever a client connects to the socket dedicated
557      * to the address verification service. All connection-management stuff
558      * is handled by the common code in multi_server.c.
559      */
560     if (attr_scan(client_stream,
561 		  ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
562 		  ATTR_TYPE_STR, MAIL_ATTR_REQ, request,
563 		  ATTR_TYPE_END) == 1) {
564 	if (STREQ(STR(request), VRFY_REQ_UPDATE)) {
565 	    verify_update_service(client_stream);
566 	} else if (STREQ(STR(request), VRFY_REQ_QUERY)) {
567 	    verify_query_service(client_stream);
568 	} else {
569 	    msg_warn("unrecognized request: \"%s\", ignored", STR(request));
570 	    attr_print(client_stream, ATTR_FLAG_NONE,
571 		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, VRFY_STAT_BAD,
572 		       ATTR_TYPE_END);
573 	}
574     }
575     vstream_fflush(client_stream);
576     vstring_free(request);
577 }
578 
579 /* verify_dump - dump some statistics */
580 
581 static void verify_dump(void)
582 {
583 
584     /*
585      * Dump preliminary cache cleanup statistics when the process commits
586      * suicide while a cache cleanup run is in progress. We can't currently
587      * distinguish between "postfix reload" (we should restart) or "maximal
588      * idle time reached" (we could finish the cache cleanup first).
589      */
590     dict_cache_close(verify_map);
591     verify_map = 0;
592 }
593 
594 /* post_jail_init - post-jail initialization */
595 
596 static void post_jail_init(char *unused_name, char **unused_argv)
597 {
598 
599     /*
600      * If the database is in volatile memory only, prevent automatic process
601      * suicide after a limited number of client requests or after a limited
602      * amount of idle time.
603      */
604     if (*var_verify_map == 0) {
605 	var_use_limit = 0;
606 	var_idle_limit = 0;
607     }
608 
609     /*
610      * Start the cache cleanup thread.
611      */
612     if (var_verify_scan_cache > 0) {
613 	int     cache_flags;
614 
615 	cache_flags = DICT_CACHE_FLAG_STATISTICS;
616 	if (msg_verbose)
617 	    cache_flags |= DICT_CACHE_FLAG_VERBOSE;
618 	dict_cache_control(verify_map,
619 			   DICT_CACHE_CTL_FLAGS, cache_flags,
620 			   DICT_CACHE_CTL_INTERVAL, var_verify_scan_cache,
621 			   DICT_CACHE_CTL_VALIDATOR, verify_cache_validator,
622 			DICT_CACHE_CTL_CONTEXT, (char *) vstring_alloc(100),
623 			   DICT_CACHE_CTL_END);
624     }
625 }
626 
627 /* pre_jail_init - pre-jail initialization */
628 
629 static void pre_jail_init(char *unused_name, char **unused_argv)
630 {
631     mode_t  saved_mask;
632     VSTRING *redirect;
633 
634     /*
635      * Never, ever, get killed by a master signal, as that would corrupt the
636      * database when we're in the middle of an update.
637      */
638     setsid();
639 
640     /*
641      * Security: don't create root-owned files that contain untrusted data.
642      * And don't create Postfix-owned files in root-owned directories,
643      * either. We want a correct relationship between (file/directory)
644      * ownership and (file/directory) content.
645      *
646      * XXX Non-root open can violate the principle of least surprise: Postfix
647      * can't open an *SQL config file for database read-write access, even
648      * though it can open that same control file for database read-only
649      * access.
650      *
651      * The solution is to query a map type and obtain its properties before
652      * opening it. A clean solution is to add a dict_info() API that is
653      * similar to dict_open() except it returns properties (dict flags) only.
654      * A pragmatic solution is to overload the existing API and have
655      * dict_open() return a dummy map when given a null map name.
656      *
657      * However, the proxymap daemon has been opening *SQL maps as non-root for
658      * years now without anyone complaining, let's not solve a problem that
659      * doesn't exist.
660      */
661     SAVE_AND_SET_EUGID(var_owner_uid, var_owner_gid);
662     redirect = vstring_alloc(100);
663 
664     /*
665      * Keep state in persistent (external) or volatile (internal) map.
666      *
667      * Start the cache cleanup thread after permanently dropping privileges.
668      */
669 #define VERIFY_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE \
670 	    | DICT_FLAG_OPEN_LOCK)
671 
672     saved_mask = umask(022);
673     verify_map =
674 	dict_cache_open(*var_verify_map ?
675 			data_redirect_map(redirect, var_verify_map) :
676 			"internal:verify",
677 			O_CREAT | O_RDWR, VERIFY_DICT_OPEN_FLAGS);
678     (void) umask(saved_mask);
679 
680     /*
681      * Clean up and restore privilege.
682      */
683     vstring_free(redirect);
684     RESTORE_SAVED_EUGID();
685 }
686 
687 MAIL_VERSION_STAMP_DECLARE;
688 
689 /* main - pass control to the multi-threaded skeleton */
690 
691 int     main(int argc, char **argv)
692 {
693     static const CONFIG_STR_TABLE str_table[] = {
694 	VAR_VERIFY_MAP, DEF_VERIFY_MAP, &var_verify_map, 0, 0,
695 	VAR_VERIFY_SENDER, DEF_VERIFY_SENDER, &var_verify_sender, 0, 0,
696 	0,
697     };
698     static const CONFIG_TIME_TABLE time_table[] = {
699 	VAR_VERIFY_POS_EXP, DEF_VERIFY_POS_EXP, &var_verify_pos_exp, 1, 0,
700 	VAR_VERIFY_POS_TRY, DEF_VERIFY_POS_TRY, &var_verify_pos_try, 1, 0,
701 	VAR_VERIFY_NEG_EXP, DEF_VERIFY_NEG_EXP, &var_verify_neg_exp, 1, 0,
702 	VAR_VERIFY_NEG_TRY, DEF_VERIFY_NEG_TRY, &var_verify_neg_try, 1, 0,
703 	VAR_VERIFY_SCAN_CACHE, DEF_VERIFY_SCAN_CACHE, &var_verify_scan_cache, 0, 0,
704 	0,
705     };
706 
707     /*
708      * Fingerprint executables and core dumps.
709      */
710     MAIL_VERSION_STAMP_ALLOCATE;
711 
712     multi_server_main(argc, argv, verify_service,
713 		      MAIL_SERVER_STR_TABLE, str_table,
714 		      MAIL_SERVER_TIME_TABLE, time_table,
715 		      MAIL_SERVER_PRE_INIT, pre_jail_init,
716 		      MAIL_SERVER_POST_INIT, post_jail_init,
717 		      MAIL_SERVER_SOLITARY,
718 		      MAIL_SERVER_EXIT, verify_dump,
719 		      0);
720 }
721