1/*	$NetBSD$	*/
2
3/*++
4/* NAME
5/*	smtpd_check 3
6/* SUMMARY
7/*	SMTP client request filtering
8/* SYNOPSIS
9/*	#include "smtpd.h"
10/*	#include "smtpd_check.h"
11/*
12/*	void	smtpd_check_init()
13/*
14/*	int	smtpd_check_addr(address)
15/*	const char *address;
16/*
17/*	char	*smtpd_check_rewrite(state)
18/*	SMTPD_STATE *state;
19/*
20/*	char	*smtpd_check_client(state)
21/*	SMTPD_STATE *state;
22/*
23/*	char	*smtpd_check_helo(state, helohost)
24/*	SMTPD_STATE *state;
25/*	char	*helohost;
26/*
27/*	char	*smtpd_check_mail(state, sender)
28/*	SMTPD_STATE *state;
29/*	char	*sender;
30/*
31/*	char	*smtpd_check_rcpt(state, recipient)
32/*	SMTPD_STATE *state;
33/*	char	*recipient;
34/*
35/*	char	*smtpd_check_etrn(state, destination)
36/*	SMTPD_STATE *state;
37/*	char	*destination;
38/*
39/*	char	*smtpd_check_data(state)
40/*	SMTPD_STATE *state;
41/*
42/*	char	*smtpd_check_eod(state)
43/*	SMTPD_STATE *state;
44/*
45/*	char	*smtpd_check_size(state, size)
46/*	SMTPD_STATE *state;
47/*	off_t	size;
48/*
49/*	char	*smtpd_check_queue(state)
50/*	SMTPD_STATE *state;
51/* DESCRIPTION
52/*	This module implements additional checks on SMTP client requests.
53/*	A client request is validated in the context of the session state.
54/*	The result is either an error response (including the numerical
55/*	code) or the result is a null pointer in case of success.
56/*
57/*	smtpd_check_init() initializes. This function should be called
58/*	once during the process life time.
59/*
60/*	smtpd_check_addr() sanity checks an email address and returns
61/*	non-zero in case of badness.
62/*
63/*	smtpd_check_rewrite() should be called before opening a queue
64/*	file or proxy connection, in order to establish the proper
65/*	header address rewriting context.
66/*
67/*	Each of the following routines scrutinizes the argument passed to
68/*	an SMTP command such as HELO, MAIL FROM, RCPT TO, or scrutinizes
69/*	the initial client connection request.  The administrator can
70/*	specify what restrictions apply.
71/*
72/*	Restrictions are specified via configuration parameters named
73/*	\fIsmtpd_{client,helo,sender,recipient}_restrictions.\fR Each
74/*	configuration parameter specifies a list of zero or more
75/*	restrictions that are applied in the order as specified.
76/* .PP
77/*	smtpd_check_client() validates the client host name or address.
78/*	Relevant configuration parameters:
79/* .IP smtpd_client_restrictions
80/*	Restrictions on the names or addresses of clients that may connect
81/*	to this SMTP server.
82/* .PP
83/*	smtpd_check_helo() validates the hostname provided with the
84/*	HELO/EHLO commands. Relevant configuration parameters:
85/* .IP smtpd_helo_restrictions
86/*	Restrictions on the hostname that is sent with the HELO/EHLO
87/*	command.
88/* .PP
89/*	smtpd_check_mail() validates the sender address provided with
90/*	a MAIL FROM request. Relevant configuration parameters:
91/* .IP smtpd_sender_restrictions
92/*	Restrictions on the sender address that is sent with the MAIL FROM
93/*	command.
94/* .PP
95/*	smtpd_check_rcpt() validates the recipient address provided
96/*	with an RCPT TO request. Relevant configuration parameters:
97/* .IP smtpd_recipient_restrictions
98/*	Restrictions on the recipient address that is sent with the RCPT
99/*	TO command.
100/* .IP local_recipient_maps
101/*	Tables of user names (not addresses) that exist in $mydestination.
102/*	Mail for local users not in these tables is rejected.
103/* .PP
104/*	smtpd_check_etrn() validates the domain name provided with the
105/*	ETRN command, and other client-provided information. Relevant
106/*	configuration parameters:
107/* .IP smtpd_etrn_restrictions
108/*	Restrictions on the hostname that is sent with the HELO/EHLO
109/*	command.
110/* .PP
111/*	smtpd_check_size() checks if a message with the given size can
112/*	be received (zero means that the message size is unknown).  The
113/*	message is rejected when
114/*	the message size exceeds the non-zero bound specified with the
115/*	\fImessage_size_limit\fR configuration parameter. This is a
116/*	permanent error.
117/*
118/*	smtpd_check_queue() checks the available queue file system
119/*	space.  The message is rejected when:
120/* .IP \(bu
121/*	The available queue file system space is less than the amount
122/*	specified with the \fImin_queue_free\fR configuration parameter.
123/*	This is a temporary error.
124/* .IP \(bu
125/*	The available queue file system space is less than twice the
126/*	message size limit. This is a temporary error.
127/* .PP
128/*	smtpd_check_data() enforces generic restrictions after the
129/*	client has sent the DATA command.
130/*
131/*	smtpd_check_eod() enforces generic restrictions after the
132/*	client has sent the END-OF-DATA command.
133/*
134/*	Arguments:
135/* .IP name
136/*	The client hostname, or \fIunknown\fR.
137/* .IP addr
138/*	The client address.
139/* .IP helohost
140/*	The hostname given with the HELO command.
141/* .IP sender
142/*	The sender address given with the MAIL FROM command.
143/* .IP recipient
144/*	The recipient address given with the RCPT TO or VRFY command.
145/* .IP size
146/*	The message size given with the MAIL FROM command (zero if unknown).
147/* BUGS
148/*	Policies like these should not be hard-coded in C, but should
149/*	be user-programmable instead.
150/* SEE ALSO
151/*	namadr_list(3) host access control
152/*	domain_list(3) domain access control
153/*	fsspace(3) free file system space
154/* LICENSE
155/* .ad
156/* .fi
157/*	The Secure Mailer license must be distributed with this software.
158/* AUTHOR(S)
159/*	Wietse Venema
160/*	IBM T.J. Watson Research
161/*	P.O. Box 704
162/*	Yorktown Heights, NY 10598, USA
163/*
164/*	TLS support originally by:
165/*	Lutz Jaenicke
166/*	BTU Cottbus
167/*	Allgemeine Elektrotechnik
168/*	Universitaetsplatz 3-4
169/*	D-03044 Cottbus, Germany
170/*--*/
171
172/* System library. */
173
174#include <sys_defs.h>
175#include <sys/socket.h>
176#include <netinet/in.h>
177#include <arpa/inet.h>
178#include <string.h>
179#include <ctype.h>
180#include <stdarg.h>
181#include <netdb.h>
182#include <setjmp.h>
183#include <stdlib.h>
184#include <unistd.h>
185#include <errno.h>
186
187#ifdef STRCASECMP_IN_STRINGS_H
188#include <strings.h>
189#endif
190
191/* Utility library. */
192
193#include <msg.h>
194#include <vstring.h>
195#include <split_at.h>
196#include <fsspace.h>
197#include <stringops.h>
198#include <valid_hostname.h>
199#include <argv.h>
200#include <mymalloc.h>
201#include <dict.h>
202#include <htable.h>
203#include <ctable.h>
204#include <mac_expand.h>
205#include <attr_clnt.h>
206#include <myaddrinfo.h>
207#include <inet_proto.h>
208#include <ip_match.h>
209
210/* DNS library. */
211
212#include <dns.h>
213
214/* Global library. */
215
216#include <string_list.h>
217#include <namadr_list.h>
218#include <domain_list.h>
219#include <mail_params.h>
220#include <rewrite_clnt.h>
221#include <resolve_clnt.h>
222#include <mail_error.h>
223#include <resolve_local.h>
224#include <own_inet_addr.h>
225#include <mail_conf.h>
226#include <maps.h>
227#include <mail_addr_find.h>
228#include <match_parent_style.h>
229#include <strip_addr.h>
230#include <cleanup_user.h>
231#include <record.h>
232#include <rec_type.h>
233#include <mail_proto.h>
234#include <mail_addr.h>
235#include <verify_clnt.h>
236#include <input_transp.h>
237#include <is_header.h>
238#include <rewrite_clnt.h>
239#include <valid_mailhost_addr.h>
240#include <dsn_util.h>
241#include <conv_time.h>
242#include <xtext.h>
243
244/* Application-specific. */
245
246#include "smtpd.h"
247#include "smtpd_sasl_glue.h"
248#include "smtpd_check.h"
249#include "smtpd_dsn_fix.h"
250#include "smtpd_resolve.h"
251#include "smtpd_expand.h"
252
253#define RESTRICTION_SEPARATORS ", \t\r\n"
254
255 /*
256  * Eject seat in case of parsing problems.
257  */
258static jmp_buf smtpd_check_buf;
259
260 /*
261  * Results of restrictions.
262  */
263#define SMTPD_CHECK_DUNNO	0	/* indifferent */
264#define SMTPD_CHECK_OK		1	/* explicitly permit */
265#define SMTPD_CHECK_REJECT	2	/* explicitly reject */
266
267 /*
268  * Intermediate results. These are static to avoid unnecessary stress on the
269  * memory manager routines.
270  */
271static VSTRING *error_text;
272static CTABLE *smtpd_rbl_cache;
273static CTABLE *smtpd_rbl_byte_cache;
274
275 /*
276  * Pre-opened SMTP recipient maps so we can reject mail for unknown users.
277  * XXX This does not belong here and will eventually become part of the
278  * trivial-rewrite resolver.
279  */
280static MAPS *local_rcpt_maps;
281static MAPS *rcpt_canon_maps;
282static MAPS *canonical_maps;
283static MAPS *virt_alias_maps;
284static MAPS *virt_mailbox_maps;
285static MAPS *relay_rcpt_maps;
286
287#ifdef TEST
288
289static STRING_LIST *virt_alias_doms;
290static STRING_LIST *virt_mailbox_doms;
291
292#endif
293
294 /*
295  * Response templates for various rbl domains.
296  */
297static MAPS *rbl_reply_maps;
298
299 /*
300  * Pre-opened sender to login name mapping.
301  */
302static MAPS *smtpd_sender_login_maps;
303
304 /*
305  * Pre-opened access control lists.
306  */
307static DOMAIN_LIST *relay_domains;
308static NAMADR_LIST *mynetworks;
309static NAMADR_LIST *perm_mx_networks;
310
311#ifdef USE_TLS
312static MAPS *relay_ccerts;
313
314#endif
315
316 /*
317  * How to do parent domain wildcard matching, if any.
318  */
319static int access_parent_style;
320
321 /*
322  * Pre-parsed restriction lists.
323  */
324static ARGV *client_restrctions;
325static ARGV *helo_restrctions;
326static ARGV *mail_restrctions;
327static ARGV *rcpt_restrctions;
328static ARGV *etrn_restrctions;
329static ARGV *data_restrctions;
330static ARGV *eod_restrictions;
331
332static HTABLE *smtpd_rest_classes;
333static HTABLE *policy_clnt_table;
334
335static ARGV *local_rewrite_clients;
336
337 /*
338  * The routine that recursively applies restrictions.
339  */
340static int generic_checks(SMTPD_STATE *, ARGV *, const char *, const char *, const char *);
341
342 /*
343  * Recipient table check.
344  */
345static int check_sender_rcpt_maps(SMTPD_STATE *, const char *);
346static int check_recipient_rcpt_maps(SMTPD_STATE *, const char *);
347static int check_rcpt_maps(SMTPD_STATE *, const char *, const char *);
348
349 /*
350  * Tempfail actions;
351  */
352static int unk_name_tf_act;
353static int unk_addr_tf_act;
354static int unv_rcpt_tf_act;
355static int unv_from_tf_act;
356
357 /*
358  * YASLM.
359  */
360#define STR	vstring_str
361#define CONST_STR(x)	((const char *) vstring_str(x))
362#define UPDATE_STRING(ptr,val) { if (ptr) myfree(ptr); ptr = mystrdup(val); }
363
364 /*
365  * If some decision can't be made due to a temporary error, then change
366  * other decisions into deferrals.
367  *
368  * XXX Deferrals can be postponed only with restrictions that are based on
369  * client-specified information: this restricts their use to parameters
370  * given in HELO, MAIL FROM, RCPT TO commands.
371  *
372  * XXX Deferrals must not be postponed after client hostname lookup failure.
373  * The reason is that the effect of access tables may depend on whether a
374  * client hostname is available or not. Thus, the reject_unknown_client
375  * restriction must defer immediately when lookup fails, otherwise incorrect
376  * results happen with:
377  *
378  * reject_unknown_client, hostname-based white-list, reject
379  *
380  * XXX With warn_if_reject, don't raise the defer_if_permit flag when a
381  * reject-style restriction fails. Instead, log the warning for the
382  * resulting defer message.
383  *
384  * XXX With warn_if_reject, do raise the defer_if_reject flag when a
385  * permit-style restriction fails. Otherwise, we could reject legitimate
386  * mail.
387  */
388static int PRINTFLIKE(5, 6) defer_if(SMTPD_DEFER *, int, int, const char *, const char *,...);
389static int PRINTFLIKE(5, 6) smtpd_check_reject(SMTPD_STATE *, int, int, const char *, const char *,...);
390
391#define DEFER_IF_REJECT2(state, class, code, dsn, fmt, a1, a2) \
392    defer_if(&(state)->defer_if_reject, (class), (code), (dsn), (fmt), (a1), (a2))
393#define DEFER_IF_REJECT3(state, class, code, dsn, fmt, a1, a2, a3) \
394    defer_if(&(state)->defer_if_reject, (class), (code), (dsn), (fmt), (a1), (a2), (a3))
395#define DEFER_IF_REJECT4(state, class, code, dsn, fmt, a1, a2, a3, a4) \
396    defer_if(&(state)->defer_if_reject, (class), (code), (dsn), (fmt), (a1), (a2), (a3), (a4))
397
398#define DEFER_EXPLICIT 1
399
400#define DEFER_IF_PERMIT2(type, state, class, code, dsn, fmt, a1, a2) \
401    (((state)->warn_if_reject == 0 && (type) != 0) ? \
402	defer_if(&(state)->defer_if_permit, (class), (code), (dsn), (fmt), (a1), (a2)) \
403    : \
404	smtpd_check_reject((state), (class), (code), (dsn), (fmt), (a1), (a2)))
405#define DEFER_IF_PERMIT3(type, state, class, code, dsn, fmt, a1, a2, a3) \
406    (((state)->warn_if_reject == 0 && (type) != 0) ? \
407	defer_if(&(state)->defer_if_permit, (class), (code), (dsn), (fmt), (a1), (a2), (a3)) \
408    : \
409	smtpd_check_reject((state), (class), (code), (dsn), (fmt), (a1), (a2), (a3)))
410#define DEFER_IF_PERMIT4(type, state, class, code, dsn, fmt, a1, a2, a3, a4) \
411    (((state)->warn_if_reject == 0 && (type) != 0) ? \
412	defer_if(&(state)->defer_if_permit, (class), (code), (dsn), (fmt), (a1), (a2), (a3), (a4)) \
413    : \
414	smtpd_check_reject((state), (class), (code), (dsn), (fmt), (a1), (a2), (a3), (a4)))
415
416 /*
417  * Cached RBL lookup state.
418  */
419typedef struct {
420    char   *txt;			/* TXT content or NULL */
421    DNS_RR *a;				/* A records */
422} SMTPD_RBL_STATE;
423
424static void *rbl_pagein(const char *, void *);
425static void rbl_pageout(void *, void *);
426static void *rbl_byte_pagein(const char *, void *);
427static void rbl_byte_pageout(void *, void *);
428
429 /*
430  * Context for RBL $name expansion.
431  */
432typedef struct {
433    SMTPD_STATE *state;			/* general state */
434    char *domain;			/* query domain */
435    const char *what;			/* rejected value */
436    const char *class;			/* name of rejected value */
437    const char *txt;			/* randomly selected trimmed TXT rr */
438} SMTPD_RBL_EXPAND_CONTEXT;
439
440 /*
441  * Multiplication factor for free space check. Free space must be at least
442  * smtpd_space_multf * message_size_limit.
443  */
444double  smtpd_space_multf = 1.5;
445
446/* policy_client_register - register policy service endpoint */
447
448static void policy_client_register(const char *name)
449{
450    if (policy_clnt_table == 0)
451	policy_clnt_table = htable_create(1);
452
453    if (htable_find(policy_clnt_table, name) == 0)
454	htable_enter(policy_clnt_table, name,
455		     (char *) attr_clnt_create(name,
456					       var_smtpd_policy_tmout,
457					       var_smtpd_policy_idle,
458					       var_smtpd_policy_ttl));
459}
460
461/* smtpd_check_parse - pre-parse restrictions */
462
463static ARGV *smtpd_check_parse(int flags, const char *checks)
464{
465    char   *saved_checks = mystrdup(checks);
466    ARGV   *argv = argv_alloc(1);
467    char   *bp = saved_checks;
468    char   *name;
469    char   *last = 0;
470
471    /*
472     * Pre-parse the restriction list, and open any dictionaries that we
473     * encounter. Dictionaries must be opened before entering the chroot
474     * jail.
475     */
476#define SMTPD_CHECK_PARSE_POLICY	(1<<0)
477#define SMTPD_CHECK_PARSE_MAPS		(1<<1)
478#define SMTPD_CHECK_PARSE_ALL		(~0)
479
480    while ((name = mystrtok(&bp, RESTRICTION_SEPARATORS)) != 0) {
481	argv_add(argv, name, (char *) 0);
482	if ((flags & SMTPD_CHECK_PARSE_POLICY)
483	    && last && strcasecmp(last, CHECK_POLICY_SERVICE) == 0)
484	    policy_client_register(name);
485	else if ((flags & SMTPD_CHECK_PARSE_MAPS)
486		 && strchr(name, ':') && dict_handle(name) == 0) {
487	    dict_register(name, dict_open(name, O_RDONLY, DICT_FLAG_LOCK
488					  | DICT_FLAG_FOLD_FIX));
489	}
490	last = name;
491    }
492    argv_terminate(argv);
493
494    /*
495     * Cleanup.
496     */
497    myfree(saved_checks);
498    return (argv);
499}
500
501/* has_required - make sure required restriction is present */
502
503static int has_required(ARGV *restrictions, const char **required)
504{
505    char  **rest;
506    const char **reqd;
507    ARGV   *expansion;
508
509    /*
510     * Recursively check list membership.
511     */
512    for (rest = restrictions->argv; *rest; rest++) {
513	if (strcmp(*rest, WARN_IF_REJECT) == 0 && rest[1] != 0) {
514	    rest += 1;
515	    continue;
516	}
517	for (reqd = required; *reqd; reqd++)
518	    if (strcmp(*rest, *reqd) == 0)
519		return (1);
520	if ((expansion = (ARGV *) htable_find(smtpd_rest_classes, *rest)) != 0)
521	    if (has_required(expansion, required))
522		return (1);
523    }
524    return (0);
525}
526
527/* fail_required - handle failure to use required restriction */
528
529static void fail_required(const char *name, const char **required)
530{
531    const char *myname = "fail_required";
532    const char **reqd;
533    VSTRING *example;
534
535    /*
536     * Sanity check.
537     */
538    if (required[0] == 0)
539	msg_panic("%s: null required list", myname);
540
541    /*
542     * Go bust.
543     */
544    example = vstring_alloc(10);
545    for (reqd = required; *reqd; reqd++)
546	vstring_sprintf_append(example, "%s%s", *reqd,
547			  reqd[1] == 0 ? "" : reqd[2] == 0 ? " or " : ", ");
548    msg_fatal("parameter \"%s\": specify at least one working instance of: %s",
549	      name, STR(example));
550}
551
552/* smtpd_check_init - initialize once during process lifetime */
553
554void    smtpd_check_init(void)
555{
556    char   *saved_classes;
557    const char *name;
558    const char *value;
559    char   *cp;
560    static const char *rcpt_required[] = {
561	CHECK_RELAY_DOMAINS,
562	REJECT_UNAUTH_DEST,
563	REJECT_ALL,
564	DEFER_ALL,
565	DEFER_IF_PERMIT,
566	0,
567    };
568    static NAME_CODE tempfail_actions[] = {
569	DEFER_ALL, 0,
570	DEFER_IF_PERMIT, 1,
571	0, -1,
572    };
573
574    /*
575     * Pre-open access control lists before going to jail.
576     */
577    mynetworks =
578	namadr_list_init(match_parent_style(VAR_MYNETWORKS),
579			 var_mynetworks);
580    relay_domains =
581	domain_list_init(match_parent_style(VAR_RELAY_DOMAINS),
582			 var_relay_domains);
583    perm_mx_networks =
584	namadr_list_init(match_parent_style(VAR_PERM_MX_NETWORKS),
585			 var_perm_mx_networks);
586#ifdef USE_TLS
587    relay_ccerts = maps_create(VAR_RELAY_CCERTS, var_smtpd_relay_ccerts,
588			       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
589#endif
590
591    /*
592     * Pre-parse and pre-open the recipient maps.
593     */
594    local_rcpt_maps = maps_create(VAR_LOCAL_RCPT_MAPS, var_local_rcpt_maps,
595				  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
596    rcpt_canon_maps = maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps,
597				  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
598    canonical_maps = maps_create(VAR_CANONICAL_MAPS, var_canonical_maps,
599				 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
600    virt_alias_maps = maps_create(VAR_VIRT_ALIAS_MAPS, var_virt_alias_maps,
601				  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
602    virt_mailbox_maps = maps_create(VAR_VIRT_MAILBOX_MAPS,
603				    var_virt_mailbox_maps,
604				    DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
605    relay_rcpt_maps = maps_create(VAR_RELAY_RCPT_MAPS, var_relay_rcpt_maps,
606				  DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
607
608#ifdef TEST
609    virt_alias_doms = string_list_init(MATCH_FLAG_NONE, var_virt_alias_doms);
610    virt_mailbox_doms = string_list_init(MATCH_FLAG_NONE, var_virt_mailbox_doms);
611#endif
612
613    access_parent_style = match_parent_style(SMTPD_ACCESS_MAPS);
614
615    /*
616     * Templates for RBL rejection replies.
617     */
618    rbl_reply_maps = maps_create(VAR_RBL_REPLY_MAPS, var_rbl_reply_maps,
619				 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
620
621    /*
622     * Sender to login name mapping.
623     */
624    smtpd_sender_login_maps = maps_create(VAR_SMTPD_SND_AUTH_MAPS,
625					  var_smtpd_snd_auth_maps,
626				       DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
627
628    /*
629     * error_text is used for returning error responses.
630     */
631    error_text = vstring_alloc(10);
632
633    /*
634     * Initialize the resolved address cache. Note: the cache persists across
635     * SMTP sessions so we cannot make it dependent on session state.
636     */
637    smtpd_resolve_init(100);
638
639    /*
640     * Initialize the RBL lookup cache. Note: the cache persists across SMTP
641     * sessions so we cannot make it dependent on session state.
642     */
643    smtpd_rbl_cache = ctable_create(100, rbl_pagein, rbl_pageout, (void *) 0);
644    smtpd_rbl_byte_cache = ctable_create(1000, rbl_byte_pagein,
645					 rbl_byte_pageout, (void *) 0);
646
647    /*
648     * Pre-parse the restriction lists. At the same time, pre-open tables
649     * before going to jail.
650     */
651    client_restrctions = smtpd_check_parse(SMTPD_CHECK_PARSE_ALL,
652					   var_client_checks);
653    helo_restrctions = smtpd_check_parse(SMTPD_CHECK_PARSE_ALL,
654					 var_helo_checks);
655    mail_restrctions = smtpd_check_parse(SMTPD_CHECK_PARSE_ALL,
656					 var_mail_checks);
657    rcpt_restrctions = smtpd_check_parse(SMTPD_CHECK_PARSE_ALL,
658					 var_rcpt_checks);
659    etrn_restrctions = smtpd_check_parse(SMTPD_CHECK_PARSE_ALL,
660					 var_etrn_checks);
661    data_restrctions = smtpd_check_parse(SMTPD_CHECK_PARSE_ALL,
662					 var_data_checks);
663    eod_restrictions = smtpd_check_parse(SMTPD_CHECK_PARSE_ALL,
664					 var_eod_checks);
665
666    /*
667     * Parse the pre-defined restriction classes.
668     */
669    smtpd_rest_classes = htable_create(1);
670    if (*var_rest_classes) {
671	cp = saved_classes = mystrdup(var_rest_classes);
672	while ((name = mystrtok(&cp, RESTRICTION_SEPARATORS)) != 0) {
673	    if ((value = mail_conf_lookup_eval(name)) == 0 || *value == 0)
674		msg_fatal("restriction class `%s' needs a definition", name);
675	    htable_enter(smtpd_rest_classes, name,
676			 (char *) smtpd_check_parse(SMTPD_CHECK_PARSE_ALL,
677						    value));
678	}
679	myfree(saved_classes);
680    }
681
682    /*
683     * This is the place to specify definitions for complex restrictions such
684     * as check_relay_domains in terms of more elementary restrictions.
685     */
686#if 0
687    htable_enter(smtpd_rest_classes, "check_relay_domains",
688		 smtpd_check_parse(SMTPD_CHECK_PARSE_ALL,
689			      "permit_mydomain reject_unauth_destination"));
690#endif
691    htable_enter(smtpd_rest_classes, REJECT_SENDER_LOGIN_MISMATCH,
692		 (char *) smtpd_check_parse(SMTPD_CHECK_PARSE_ALL,
693					    REJECT_AUTH_SENDER_LOGIN_MISMATCH
694				  " " REJECT_UNAUTH_SENDER_LOGIN_MISMATCH));
695
696    /*
697     * People screw up the relay restrictions too often. Require that they
698     * list at least one restriction that rejects mail by default.
699     */
700#ifndef TEST
701    if (!has_required(rcpt_restrctions, rcpt_required))
702	fail_required(VAR_RCPT_CHECKS, rcpt_required);
703#endif
704
705    /*
706     * Local rewrite policy.
707     */
708    local_rewrite_clients = smtpd_check_parse(SMTPD_CHECK_PARSE_MAPS,
709					      var_local_rwr_clients);
710
711    /*
712     * Tempfail_actions.
713     *
714     * XXX This name-to-number mapping should be encapsulated in a separate
715     * mail_conf_name_code.c module.
716     */
717    if ((unk_name_tf_act = name_code(tempfail_actions, NAME_CODE_FLAG_NONE,
718				     var_unk_name_tf_act)) < 0)
719	msg_fatal("bad configuration: %s = %s",
720		  VAR_UNK_NAME_TF_ACT, var_unk_name_tf_act);
721    if ((unk_addr_tf_act = name_code(tempfail_actions, NAME_CODE_FLAG_NONE,
722				     var_unk_addr_tf_act)) < 0)
723	msg_fatal("bad configuration: %s = %s",
724		  VAR_UNK_ADDR_TF_ACT, var_unk_addr_tf_act);
725    if ((unv_rcpt_tf_act = name_code(tempfail_actions, NAME_CODE_FLAG_NONE,
726				     var_unv_rcpt_tf_act)) < 0)
727	msg_fatal("bad configuration: %s = %s",
728		  VAR_UNV_RCPT_TF_ACT, var_unv_rcpt_tf_act);
729    if ((unv_from_tf_act = name_code(tempfail_actions, NAME_CODE_FLAG_NONE,
730				     var_unv_from_tf_act)) < 0)
731	msg_fatal("bad configuration: %s = %s",
732		  VAR_UNV_FROM_TF_ACT, var_unv_from_tf_act);
733    if (msg_verbose) {
734	msg_info("%s = %s", VAR_UNK_NAME_TF_ACT, tempfail_actions[unk_name_tf_act].name);
735	msg_info("%s = %s", VAR_UNK_ADDR_TF_ACT, tempfail_actions[unk_addr_tf_act].name);
736	msg_info("%s = %s", VAR_UNV_RCPT_TF_ACT, tempfail_actions[unv_rcpt_tf_act].name);
737	msg_info("%s = %s", VAR_UNV_FROM_TF_ACT, tempfail_actions[unv_from_tf_act].name);
738    }
739}
740
741/* log_whatsup - log as much context as we have */
742
743static void log_whatsup(SMTPD_STATE *state, const char *whatsup,
744			        const char *text)
745{
746    VSTRING *buf = vstring_alloc(100);
747
748    vstring_sprintf(buf, "%s: %s: %s from %s: %s;",
749		    state->queue_id ? state->queue_id : "NOQUEUE",
750		    whatsup, state->where, state->namaddr, text);
751    if (state->sender)
752	vstring_sprintf_append(buf, " from=<%s>", state->sender);
753    if (state->recipient)
754	vstring_sprintf_append(buf, " to=<%s>", state->recipient);
755    if (state->protocol)
756	vstring_sprintf_append(buf, " proto=%s", state->protocol);
757    if (state->helo_name)
758	vstring_sprintf_append(buf, " helo=<%s>", state->helo_name);
759    msg_info("%s", STR(buf));
760    vstring_free(buf);
761}
762
763/* smtpd_check_reject - do the boring things that must be done */
764
765static int smtpd_check_reject(SMTPD_STATE *state, int error_class,
766			              int code, const char *dsn,
767			              const char *format,...)
768{
769    va_list ap;
770    int     warn_if_reject;
771    const char *whatsup;
772
773    /*
774     * Do not reject mail if we were asked to warn only. However,
775     * configuration errors cannot be converted into warnings.
776     */
777    if (state->warn_if_reject && error_class != MAIL_ERROR_SOFTWARE) {
778	warn_if_reject = 1;
779	whatsup = "reject_warning";
780    } else {
781	warn_if_reject = 0;
782	whatsup = "reject";
783    }
784
785    /*
786     * Update the error class mask, and format the response. XXX What about
787     * multi-line responses? For now we cheat and send whitespace.
788     *
789     * Format the response before complaining about configuration errors, so
790     * that we can show the error in context.
791     */
792    state->error_mask |= error_class;
793    vstring_sprintf(error_text, "%d %s ", code, dsn);
794    va_start(ap, format);
795    vstring_vsprintf_append(error_text, format, ap);
796    va_end(ap);
797
798    /*
799     * Validate the response, that is, the response must begin with a
800     * three-digit status code, and the first digit must be 4 or 5. If the
801     * response is bad, log a warning and send a generic response instead.
802     */
803    if (code < 400 || code > 599) {
804	msg_warn("SMTP reply code configuration error: %s", STR(error_text));
805	vstring_strcpy(error_text, "450 4.7.1 Service unavailable");
806    }
807    if (!dsn_valid(STR(error_text) + 4)) {
808	msg_warn("DSN detail code configuration error: %s", STR(error_text));
809	vstring_strcpy(error_text, "450 4.7.1 Service unavailable");
810    }
811
812    /*
813     * Ensure RFC compliance. We could do this inside smtpd_chat_reply() and
814     * switch to multi-line for long replies.
815     */
816    vstring_truncate(error_text, 510);
817    printable(STR(error_text), ' ');
818
819    /*
820     * Force this rejection into deferral because of some earlier temporary
821     * error that may have prevented us from accepting mail, and report the
822     * earlier problem instead.
823     */
824    if (!warn_if_reject && state->defer_if_reject.active && STR(error_text)[0] == '5') {
825	state->warn_if_reject = state->defer_if_reject.active = 0;
826	return (smtpd_check_reject(state, state->defer_if_reject.class,
827				   state->defer_if_reject.code,
828				   STR(state->defer_if_reject.dsn),
829				 "%s", STR(state->defer_if_reject.reason)));
830    }
831
832    /*
833     * Soft bounce safety net.
834     *
835     * XXX The code below also appears in the Postfix SMTP server reply output
836     * routine. It is duplicated here in order to avoid discrepancies between
837     * the reply codes that are shown in "reject" logging and the reply codes
838     * that are actually sent to the SMTP client.
839     *
840     * Implementing the soft_bounce safety net in the SMTP server reply output
841     * routine has the advantage that it covers all 5xx replies, including
842     * SMTP protocol or syntax errors, which makes soft_bounce great for
843     * non-destructive tests (especially by people who are paranoid about
844     * losing mail).
845     *
846     * We could eliminate the code duplication and implement the soft_bounce
847     * safety net only in the code below. But then the safety net would cover
848     * the UCE restrictions only. This would be at odds with documentation
849     * which says soft_bounce changes all 5xx replies into 4xx ones.
850     */
851    if (var_soft_bounce && STR(error_text)[0] == '5')
852	STR(error_text)[0] = '4';
853
854    /*
855     * In any case, enforce consistency between the SMTP code and DSN code.
856     * SMTP has the higher precedence since it came here first.
857     */
858    STR(error_text)[4] = STR(error_text)[0];
859
860    /*
861     * Log what is happening. When the sysadmin discards policy violation
862     * postmaster notices, this may be the only trace left that service was
863     * rejected. Print the request, client name/address, and response.
864     */
865    log_whatsup(state, whatsup, STR(error_text));
866
867    return (warn_if_reject ? 0 : SMTPD_CHECK_REJECT);
868}
869
870/* defer_if - prepare to change our mind */
871
872static int defer_if(SMTPD_DEFER *defer, int error_class,
873		            int code, const char *dsn,
874		            const char *fmt,...)
875{
876    va_list ap;
877
878    /*
879     * Keep the first reason for this type of deferral, to minimize
880     * confusion.
881     */
882    if (defer->active == 0) {
883	defer->active = 1;
884	defer->class = error_class;
885	defer->code = code;
886	if (defer->dsn == 0)
887	    defer->dsn = vstring_alloc(10);
888	vstring_strcpy(defer->dsn, dsn);
889	if (defer->reason == 0)
890	    defer->reason = vstring_alloc(10);
891	va_start(ap, fmt);
892	vstring_vsprintf(defer->reason, fmt, ap);
893	va_end(ap);
894    }
895    return (SMTPD_CHECK_DUNNO);
896}
897
898/* reject_dict_retry - reject with temporary failure if dict lookup fails */
899
900static void reject_dict_retry(SMTPD_STATE *state, const char *reply_name)
901{
902    longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_RESOURCE,
903						451, "4.3.0",
904					   "<%s>: Temporary lookup failure",
905						reply_name));
906}
907
908/* check_mail_addr_find - reject with temporary failure if dict lookup fails */
909
910static const char *check_mail_addr_find(SMTPD_STATE *state,
911					        const char *reply_name,
912					        MAPS *maps, const char *key,
913					        char **ext)
914{
915    const char *result;
916
917    dict_errno = 0;
918    if ((result = mail_addr_find(maps, key, ext)) == 0
919	&& dict_errno == DICT_ERR_RETRY)
920	reject_dict_retry(state, reply_name);
921    return (result);
922}
923
924/* reject_unknown_reverse_name - fail if reverse client hostname is unknown */
925
926static int reject_unknown_reverse_name(SMTPD_STATE *state)
927{
928    const char *myname = "reject_unknown_reverse_name";
929
930    if (msg_verbose)
931	msg_info("%s: %s", myname, state->reverse_name);
932
933    if (state->reverse_name_status != SMTPD_PEER_CODE_OK)
934	return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
935			state->reverse_name_status == SMTPD_PEER_CODE_PERM ?
936				   var_unk_client_code : 450, "4.7.1",
937	    "Client host rejected: cannot find your reverse hostname, [%s]",
938				   state->addr));
939    return (SMTPD_CHECK_DUNNO);
940}
941
942/* reject_unknown_client - fail if client hostname is unknown */
943
944static int reject_unknown_client(SMTPD_STATE *state)
945{
946    const char *myname = "reject_unknown_client";
947
948    if (msg_verbose)
949	msg_info("%s: %s %s", myname, state->name, state->addr);
950
951    if (state->name_status != SMTPD_PEER_CODE_OK)
952	return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
953				state->name_status >= SMTPD_PEER_CODE_PERM ?
954				   var_unk_client_code : 450, "4.7.1",
955		    "Client host rejected: cannot find your hostname, [%s]",
956				   state->addr));
957    return (SMTPD_CHECK_DUNNO);
958}
959
960/* reject_plaintext_session - fail if session is not encrypted */
961
962static int reject_plaintext_session(SMTPD_STATE *state)
963{
964    const char *myname = "reject_plaintext_session";
965
966    if (msg_verbose)
967	msg_info("%s: %s %s", myname, state->name, state->addr);
968
969#ifdef USE_TLS
970    if (state->tls_context == 0)
971#endif
972	return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
973				   var_plaintext_code, "4.7.1",
974				   "Session encryption is required"));
975    return (SMTPD_CHECK_DUNNO);
976}
977
978/* permit_inet_interfaces - succeed if client my own address */
979
980static int permit_inet_interfaces(SMTPD_STATE *state)
981{
982    const char *myname = "permit_inet_interfaces";
983
984    if (msg_verbose)
985	msg_info("%s: %s %s", myname, state->name, state->addr);
986
987    if (own_inet_addr((struct sockaddr *) & (state->sockaddr)))
988	return (SMTPD_CHECK_OK);
989    return (SMTPD_CHECK_DUNNO);
990}
991
992/* permit_mynetworks - succeed if client is in a trusted network */
993
994static int permit_mynetworks(SMTPD_STATE *state)
995{
996    const char *myname = "permit_mynetworks";
997
998    if (msg_verbose)
999	msg_info("%s: %s %s", myname, state->name, state->addr);
1000
1001    if (namadr_list_match(mynetworks, state->name, state->addr))
1002	return (SMTPD_CHECK_OK);
1003    return (SMTPD_CHECK_DUNNO);
1004}
1005
1006/* dup_if_truncate - save hostname and truncate if it ends in dot */
1007
1008static char *dup_if_truncate(char *name)
1009{
1010    ssize_t len;
1011    char   *result;
1012
1013    /*
1014     * Truncate hostnames ending in dot but not dot-dot.
1015     *
1016     * XXX This should not be distributed all over the code. Problem is,
1017     * addresses can enter the system via multiple paths: networks, local
1018     * forward/alias/include files, even as the result of address rewriting.
1019     */
1020    if ((len = strlen(name)) > 1
1021	&& name[len - 1] == '.'
1022	&& name[len - 2] != '.') {
1023	result = mystrndup(name, len - 1);
1024    } else
1025	result = name;
1026    return (result);
1027}
1028
1029/* reject_invalid_hostaddr - fail if host address is incorrect */
1030
1031static int reject_invalid_hostaddr(SMTPD_STATE *state, char *addr,
1032				        char *reply_name, char *reply_class)
1033{
1034    const char *myname = "reject_invalid_hostaddr";
1035    ssize_t len;
1036    char   *test_addr;
1037    int     stat;
1038
1039    if (msg_verbose)
1040	msg_info("%s: %s", myname, addr);
1041
1042    if (addr[0] == '[' && (len = strlen(addr)) > 2 && addr[len - 1] == ']') {
1043	test_addr = mystrndup(addr + 1, len - 2);
1044    } else
1045	test_addr = addr;
1046
1047    /*
1048     * Validate the address.
1049     */
1050    if (!valid_mailhost_addr(test_addr, DONT_GRIPE))
1051	stat = smtpd_check_reject(state, MAIL_ERROR_POLICY,
1052				  var_bad_name_code, "5.5.2",
1053				  "<%s>: %s rejected: invalid ip address",
1054				  reply_name, reply_class);
1055    else
1056	stat = SMTPD_CHECK_DUNNO;
1057
1058    /*
1059     * Cleanup.
1060     */
1061    if (test_addr != addr)
1062	myfree(test_addr);
1063
1064    return (stat);
1065}
1066
1067/* reject_invalid_hostname - fail if host/domain syntax is incorrect */
1068
1069static int reject_invalid_hostname(SMTPD_STATE *state, char *name,
1070				        char *reply_name, char *reply_class)
1071{
1072    const char *myname = "reject_invalid_hostname";
1073    char   *test_name;
1074    int     stat;
1075
1076    if (msg_verbose)
1077	msg_info("%s: %s", myname, name);
1078
1079    /*
1080     * Truncate hostnames ending in dot but not dot-dot.
1081     */
1082    test_name = dup_if_truncate(name);
1083
1084    /*
1085     * Validate the hostname.
1086     */
1087    if (!valid_hostname(test_name, DONT_GRIPE)
1088	&& !valid_hostaddr(test_name, DONT_GRIPE))	/* XXX back compat */
1089	stat = smtpd_check_reject(state, MAIL_ERROR_POLICY,
1090				  var_bad_name_code, "5.5.2",
1091				  "<%s>: %s rejected: Invalid name",
1092				  reply_name, reply_class);
1093    else
1094	stat = SMTPD_CHECK_DUNNO;
1095
1096    /*
1097     * Cleanup.
1098     */
1099    if (test_name != name)
1100	myfree(test_name);
1101
1102    return (stat);
1103}
1104
1105/* reject_non_fqdn_hostname - fail if host name is not in fqdn form */
1106
1107static int reject_non_fqdn_hostname(SMTPD_STATE *state, char *name,
1108				        char *reply_name, char *reply_class)
1109{
1110    const char *myname = "reject_non_fqdn_hostname";
1111    char   *test_name;
1112    int     stat;
1113
1114    if (msg_verbose)
1115	msg_info("%s: %s", myname, name);
1116
1117    /*
1118     * Truncate hostnames ending in dot but not dot-dot.
1119     */
1120    test_name = dup_if_truncate(name);
1121
1122    /*
1123     * Validate the hostname.
1124     */
1125    if (!valid_hostname(test_name, DONT_GRIPE) || !strchr(test_name, '.'))
1126	stat = smtpd_check_reject(state, MAIL_ERROR_POLICY,
1127				  var_non_fqdn_code, "5.5.2",
1128			 "<%s>: %s rejected: need fully-qualified hostname",
1129				  reply_name, reply_class);
1130    else
1131	stat = SMTPD_CHECK_DUNNO;
1132
1133    /*
1134     * Cleanup.
1135     */
1136    if (test_name != name)
1137	myfree(test_name);
1138
1139    return (stat);
1140}
1141
1142/* reject_unknown_hostname - fail if name has no A, AAAA or MX record */
1143
1144static int reject_unknown_hostname(SMTPD_STATE *state, char *name,
1145				        char *reply_name, char *reply_class)
1146{
1147    const char *myname = "reject_unknown_hostname";
1148    int     dns_status;
1149    DNS_RR *dummy;
1150
1151    if (msg_verbose)
1152	msg_info("%s: %s", myname, name);
1153
1154#ifdef T_AAAA
1155#define RR_ADDR_TYPES	T_A, T_AAAA
1156#else
1157#define RR_ADDR_TYPES	T_A
1158#endif
1159
1160    dns_status = dns_lookup_l(name, 0, &dummy, (VSTRING *) 0,
1161			      (VSTRING *) 0, DNS_REQ_FLAG_STOP_OK,
1162			      RR_ADDR_TYPES, T_MX, 0);
1163    if (dummy)
1164	dns_rr_free(dummy);
1165    if (dns_status != DNS_OK) {			/* incl. DNS_INVAL */
1166	if (dns_status != DNS_RETRY)
1167	    return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
1168				       var_unk_name_code, "4.7.1",
1169				       "<%s>: %s rejected: %s",
1170				       reply_name, reply_class,
1171				       dns_status == DNS_INVAL ?
1172				       "Malformed DNS server reply" :
1173				       "Host not found"));
1174	else
1175	    return (DEFER_IF_PERMIT2(unk_name_tf_act, state, MAIL_ERROR_POLICY,
1176				     450, "4.7.1",
1177				     "<%s>: %s rejected: Host not found",
1178				     reply_name, reply_class));
1179    }
1180    return (SMTPD_CHECK_DUNNO);
1181}
1182
1183/* reject_unknown_mailhost - fail if name has no A, AAAA or MX record */
1184
1185static int reject_unknown_mailhost(SMTPD_STATE *state, const char *name,
1186		            const char *reply_name, const char *reply_class)
1187{
1188    const char *myname = "reject_unknown_mailhost";
1189    int     dns_status;
1190    DNS_RR *dummy;
1191
1192    if (msg_verbose)
1193	msg_info("%s: %s", myname, name);
1194
1195#define MAILHOST_LOOKUP_FLAGS	(DNS_REQ_FLAG_STOP_OK | DNS_REQ_FLAG_STOP_INVAL)
1196
1197    dns_status = dns_lookup_l(name, 0, &dummy, (VSTRING *) 0,
1198			      (VSTRING *) 0, MAILHOST_LOOKUP_FLAGS,
1199			      T_MX, RR_ADDR_TYPES, 0);
1200    if (dummy)
1201	dns_rr_free(dummy);
1202    if (dns_status != DNS_OK) {			/* incl. DNS_INVAL */
1203	if (dns_status != DNS_RETRY)
1204	    return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
1205				       var_unk_addr_code,
1206			       strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
1207				       "4.1.8" : "4.1.2",
1208				       "<%s>: %s rejected: %s",
1209				       reply_name, reply_class,
1210				       dns_status == DNS_INVAL ?
1211				       "Malformed DNS server reply" :
1212				       "Domain not found"));
1213	else
1214	    return (DEFER_IF_PERMIT2(unk_addr_tf_act, state, MAIL_ERROR_POLICY,
1215			  450, strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
1216				     "4.1.8" : "4.1.2",
1217				     "<%s>: %s rejected: Domain not found",
1218				     reply_name, reply_class));
1219    }
1220    return (SMTPD_CHECK_DUNNO);
1221}
1222
1223static int permit_auth_destination(SMTPD_STATE *state, char *recipient);
1224
1225/* permit_tls_clientcerts - OK/DUNNO for message relaying */
1226
1227static int permit_tls_clientcerts(SMTPD_STATE *state, int permit_all_certs)
1228{
1229#ifdef USE_TLS
1230    const char *found;
1231
1232    if (!state->tls_context)
1233	return SMTPD_CHECK_DUNNO;
1234
1235    if (TLS_CERT_IS_TRUSTED(state->tls_context) && permit_all_certs) {
1236	if (msg_verbose)
1237	    msg_info("Relaying allowed for all verified client certificates");
1238	return (SMTPD_CHECK_OK);
1239    }
1240
1241    /*
1242     * When directly checking the fingerprint, it is OK if the issuing CA is
1243     * not trusted.
1244     */
1245    if (TLS_CERT_IS_PRESENT(state->tls_context)) {
1246	found = maps_find(relay_ccerts, state->tls_context->peer_fingerprint,
1247			  DICT_FLAG_NONE);
1248	if (found) {
1249	    if (msg_verbose)
1250		msg_info("Relaying allowed for certified client: %s", found);
1251	    return (SMTPD_CHECK_OK);
1252	} else if (msg_verbose)
1253	    msg_info("relay_clientcerts: No match for fingerprint '%s'",
1254		     state->tls_context->peer_fingerprint);
1255    }
1256#endif
1257    return (SMTPD_CHECK_DUNNO);
1258}
1259
1260/* check_relay_domains - OK/FAIL for message relaying */
1261
1262static int check_relay_domains(SMTPD_STATE *state, char *recipient,
1263			               char *reply_name, char *reply_class)
1264{
1265    const char *myname = "check_relay_domains";
1266
1267#if 1
1268    static int once;
1269
1270    if (once == 0) {
1271	once = 1;
1272	msg_warn("support for restriction \"%s\" will be removed from %s; "
1273		 "use \"%s\" instead",
1274		 CHECK_RELAY_DOMAINS, var_mail_name, REJECT_UNAUTH_DEST);
1275    }
1276#endif
1277
1278    if (msg_verbose)
1279	msg_info("%s: %s", myname, recipient);
1280
1281    /*
1282     * Permit if the client matches the relay_domains list.
1283     */
1284    if (domain_list_match(relay_domains, state->name))
1285	return (SMTPD_CHECK_OK);
1286
1287    /*
1288     * Permit authorized destinations.
1289     */
1290    if (permit_auth_destination(state, recipient) == SMTPD_CHECK_OK)
1291	return (SMTPD_CHECK_OK);
1292
1293    /*
1294     * Deny relaying between sites that both are not in relay_domains.
1295     */
1296    return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
1297			       var_relay_code, "5.7.1",
1298			       "<%s>: %s rejected: Relay access denied",
1299			       reply_name, reply_class));
1300}
1301
1302/* permit_auth_destination - OK for message relaying */
1303
1304static int permit_auth_destination(SMTPD_STATE *state, char *recipient)
1305{
1306    const char *myname = "permit_auth_destination";
1307    const RESOLVE_REPLY *reply;
1308
1309    if (msg_verbose)
1310	msg_info("%s: %s", myname, recipient);
1311
1312    /*
1313     * Resolve the address.
1314     */
1315    reply = smtpd_resolve_addr(recipient);
1316    if (reply->flags & RESOLVE_FLAG_FAIL)
1317	reject_dict_retry(state, recipient);
1318
1319    /*
1320     * Handle special case that is not supposed to happen.
1321     */
1322    if (strrchr(CONST_STR(reply->recipient), '@') == 0)
1323	return (SMTPD_CHECK_OK);
1324
1325    /*
1326     * Skip source-routed non-local or virtual mail (uncertain destination).
1327     */
1328    if (var_allow_untrust_route == 0 && (reply->flags & RESOLVE_FLAG_ROUTED))
1329	return (SMTPD_CHECK_DUNNO);
1330
1331    /*
1332     * Permit final delivery: the destination matches mydestination,
1333     * virtual_alias_domains, or virtual_mailbox_domains.
1334     */
1335    if (reply->flags & RESOLVE_CLASS_FINAL)
1336	return (SMTPD_CHECK_OK);
1337
1338    /*
1339     * Permit if the destination matches the relay_domains list.
1340     */
1341    if (reply->flags & RESOLVE_CLASS_RELAY)
1342	return (SMTPD_CHECK_OK);
1343
1344    /*
1345     * Skip when not matched
1346     */
1347    return (SMTPD_CHECK_DUNNO);
1348}
1349
1350/* reject_unauth_destination - FAIL for message relaying */
1351
1352static int reject_unauth_destination(SMTPD_STATE *state, char *recipient)
1353{
1354    const char *myname = "reject_unauth_destination";
1355
1356    if (msg_verbose)
1357	msg_info("%s: %s", myname, recipient);
1358
1359    /*
1360     * Skip authorized destination.
1361     */
1362    if (permit_auth_destination(state, recipient) == SMTPD_CHECK_OK)
1363	return (SMTPD_CHECK_DUNNO);
1364
1365    /*
1366     * Reject relaying to sites that are not listed in relay_domains.
1367     */
1368    return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
1369			       var_relay_code, "5.7.1",
1370			       "<%s>: Relay access denied",
1371			       recipient));
1372}
1373
1374/* reject_unauth_pipelining - reject improper use of SMTP command pipelining */
1375
1376static int reject_unauth_pipelining(SMTPD_STATE *state,
1377		            const char *reply_name, const char *reply_class)
1378{
1379    const char *myname = "reject_unauth_pipelining";
1380
1381    if (msg_verbose)
1382	msg_info("%s: %s", myname, state->where);
1383
1384    if (state->flags & SMTPD_FLAG_ILL_PIPELINING)
1385	return (smtpd_check_reject(state, MAIL_ERROR_PROTOCOL,
1386				   503, "5.5.0",
1387	       "<%s>: %s rejected: Improper use of SMTP command pipelining",
1388				   reply_name, reply_class));
1389
1390    return (SMTPD_CHECK_DUNNO);
1391}
1392
1393/* all_auth_mx_addr - match host addresses against permit_mx_backup_networks */
1394
1395static int all_auth_mx_addr(SMTPD_STATE *state, char *host,
1396		            const char *reply_name, const char *reply_class)
1397{
1398    const char *myname = "all_auth_mx_addr";
1399    MAI_HOSTADDR_STR hostaddr;
1400    DNS_RR *rr;
1401    DNS_RR *addr_list;
1402    int     dns_status;
1403
1404    if (msg_verbose)
1405	msg_info("%s: host %s", myname, host);
1406
1407    /*
1408     * If we can't lookup the host, defer.
1409     */
1410#define NOPE           0
1411#define YUP            1
1412
1413    /*
1414     * Verify that all host addresses are within permit_mx_backup_networks.
1415     */
1416    dns_status = dns_lookup_v(host, 0, &addr_list, (VSTRING *) 0, (VSTRING *) 0,
1417		      DNS_REQ_FLAG_NONE, inet_proto_info()->dns_atype_list);
1418    if (dns_status != DNS_OK) {			/* incl. DNS_INVAL */
1419	DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY,
1420			 450, "4.4.4",
1421	   "<%s>: %s rejected: Unable to look up host %s as mail exchanger",
1422			 reply_name, reply_class, host);
1423	return (NOPE);
1424    }
1425    for (rr = addr_list; rr != 0; rr = rr->next) {
1426	if (dns_rr_to_pa(rr, &hostaddr) == 0) {
1427	    msg_warn("%s: skipping record type %s for host %s: %m",
1428		     myname, dns_strtype(rr->type), host);
1429	    continue;
1430	}
1431	if (msg_verbose)
1432	    msg_info("%s: checking: %s", myname, hostaddr.buf);
1433
1434	if (!namadr_list_match(perm_mx_networks, host, hostaddr.buf)) {
1435
1436	    /*
1437	     * Reject: at least one IP address is not listed in
1438	     * permit_mx_backup_networks.
1439	     */
1440	    if (msg_verbose)
1441		msg_info("%s: address %s for %s does not match %s",
1442			 myname, hostaddr.buf, host, VAR_PERM_MX_NETWORKS);
1443	    dns_rr_free(addr_list);
1444	    return (NOPE);
1445	}
1446    }
1447    dns_rr_free(addr_list);
1448    return (YUP);
1449}
1450
1451/* has_my_addr - see if this host name lists one of my network addresses */
1452
1453static int has_my_addr(SMTPD_STATE *state, const char *host,
1454		            const char *reply_name, const char *reply_class)
1455{
1456    const char *myname = "has_my_addr";
1457    struct addrinfo *res;
1458    struct addrinfo *res0;
1459    int     aierr;
1460    MAI_HOSTADDR_STR hostaddr;
1461    INET_PROTO_INFO *proto_info = inet_proto_info();
1462
1463    if (msg_verbose)
1464	msg_info("%s: host %s", myname, host);
1465
1466    /*
1467     * If we can't lookup the host, defer rather than reject.
1468     */
1469#define YUP	1
1470#define NOPE	0
1471
1472    aierr = hostname_to_sockaddr(host, (char *) 0, 0, &res0);
1473    if (aierr) {
1474	DEFER_IF_REJECT4(state, MAIL_ERROR_POLICY,
1475			 450, "4.4.4",
1476	  "<%s>: %s rejected: Unable to look up mail exchanger host %s: %s",
1477			 reply_name, reply_class, host, MAI_STRERROR(aierr));
1478	return (NOPE);
1479    }
1480#define HAS_MY_ADDR_RETURN(x) { freeaddrinfo(res0); return (x); }
1481
1482    for (res = res0; res != 0; res = res->ai_next) {
1483	if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
1484	    if (msg_verbose)
1485		msg_info("skipping address family %d for host %s",
1486			 res->ai_family, host);
1487	    continue;
1488	}
1489	if (msg_verbose) {
1490	    SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
1491				 &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
1492	    msg_info("%s: addr %s", myname, hostaddr.buf);
1493	}
1494	if (own_inet_addr(res->ai_addr))
1495	    HAS_MY_ADDR_RETURN(YUP);
1496	if (proxy_inet_addr(res->ai_addr))
1497	    HAS_MY_ADDR_RETURN(YUP);
1498    }
1499    if (msg_verbose)
1500	msg_info("%s: host %s: no match", myname, host);
1501
1502    HAS_MY_ADDR_RETURN(NOPE);
1503}
1504
1505/* i_am_mx - is this machine listed as MX relay */
1506
1507static int i_am_mx(SMTPD_STATE *state, DNS_RR *mx_list,
1508		           const char *reply_name, const char *reply_class)
1509{
1510    const char *myname = "i_am_mx";
1511    DNS_RR *mx;
1512
1513    /*
1514     * Compare hostnames first. Only if no name match is found, go through
1515     * the trouble of host address lookups.
1516     */
1517    for (mx = mx_list; mx != 0; mx = mx->next) {
1518	if (msg_verbose)
1519	    msg_info("%s: resolve hostname: %s", myname, (char *) mx->data);
1520	if (resolve_local((char *) mx->data))
1521	    return (YUP);
1522    }
1523
1524    /*
1525     * Argh. Do further DNS lookups and match interface addresses.
1526     */
1527    for (mx = mx_list; mx != 0; mx = mx->next) {
1528	if (msg_verbose)
1529	    msg_info("%s: address lookup: %s", myname, (char *) mx->data);
1530	if (has_my_addr(state, (char *) mx->data, reply_name, reply_class))
1531	    return (YUP);
1532    }
1533
1534    /*
1535     * This machine is not listed as MX relay.
1536     */
1537    if (msg_verbose)
1538	msg_info("%s: I am not listed as MX relay", myname);
1539    return (NOPE);
1540}
1541
1542/* permit_mx_primary - authorize primary MX relays */
1543
1544static int permit_mx_primary(SMTPD_STATE *state, DNS_RR *mx_list,
1545		            const char *reply_name, const char *reply_class)
1546{
1547    const char *myname = "permit_mx_primary";
1548    DNS_RR *mx;
1549
1550    if (msg_verbose)
1551	msg_info("%s", myname);
1552
1553    /*
1554     * See if each best MX host has all IP addresses in
1555     * permit_mx_backup_networks.
1556     */
1557    for (mx = mx_list; mx != 0; mx = mx->next) {
1558	if (!all_auth_mx_addr(state, (char *) mx->data, reply_name, reply_class))
1559	    return (NOPE);
1560    }
1561
1562    /*
1563     * All IP addresses of the best MX hosts are within
1564     * permit_mx_backup_networks.
1565     */
1566    return (YUP);
1567}
1568
1569/* permit_mx_backup - permit use of me as MX backup for recipient domain */
1570
1571static int permit_mx_backup(SMTPD_STATE *state, const char *recipient,
1572		            const char *reply_name, const char *reply_class)
1573{
1574    const char *myname = "permit_mx_backup";
1575    const RESOLVE_REPLY *reply;
1576    const char *domain;
1577    DNS_RR *mx_list;
1578    DNS_RR *middle;
1579    DNS_RR *rest;
1580    int     dns_status;
1581
1582    if (msg_verbose)
1583	msg_info("%s: %s", myname, recipient);
1584
1585    /*
1586     * Resolve the address.
1587     */
1588    reply = smtpd_resolve_addr(recipient);
1589    if (reply->flags & RESOLVE_FLAG_FAIL)
1590	reject_dict_retry(state, recipient);
1591
1592    /*
1593     * For backwards compatibility, emulate permit_auth_destination. However,
1594     * old permit_mx_backup implementations allow source routing with local
1595     * address class.
1596     */
1597    if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0)
1598	return (SMTPD_CHECK_OK);
1599    domain += 1;
1600#if 0
1601    if (reply->flags & RESOLVE_CLASS_LOCAL)
1602	return (SMTPD_CHECK_OK);
1603#endif
1604    if (var_allow_untrust_route == 0 && (reply->flags & RESOLVE_FLAG_ROUTED))
1605	return (SMTPD_CHECK_DUNNO);
1606    if (reply->flags & RESOLVE_CLASS_FINAL)
1607	return (SMTPD_CHECK_OK);
1608    if (reply->flags & RESOLVE_CLASS_RELAY)
1609	return (SMTPD_CHECK_OK);
1610
1611    if (msg_verbose)
1612	msg_info("%s: not local: %s", myname, recipient);
1613
1614    /*
1615     * Skip numerical forms that didn't match the local system.
1616     */
1617    if (domain[0] == '[' && domain[strlen(domain) - 1] == ']')
1618	return (SMTPD_CHECK_DUNNO);
1619
1620    /*
1621     * Look up the list of MX host names for this domain. If no MX host is
1622     * found, perhaps it is a CNAME for the local machine. Clients aren't
1623     * supposed to send CNAMEs in SMTP commands, but it happens anyway. If we
1624     * can't look up the destination, play safe and turn reject into defer.
1625     */
1626    dns_status = dns_lookup(domain, T_MX, 0, &mx_list,
1627			    (VSTRING *) 0, (VSTRING *) 0);
1628#if 0
1629    if (dns_status == DNS_NOTFOUND)
1630	return (has_my_addr(state, domain, reply_name, reply_class) ?
1631		SMTPD_CHECK_OK : SMTPD_CHECK_DUNNO);
1632#endif
1633    if (dns_status != DNS_OK) {			/* incl. DNS_INVAL */
1634	if (dns_status == DNS_RETRY)
1635	    DEFER_IF_REJECT2(state, MAIL_ERROR_POLICY,
1636			     450, "4.4.4",
1637			     "<%s>: %s rejected: Unable to look up mail exchanger information",
1638			     reply_name, reply_class);
1639	return (SMTPD_CHECK_DUNNO);
1640    }
1641
1642    /*
1643     * Separate MX list into primaries and backups.
1644     */
1645    mx_list = dns_rr_sort(mx_list, dns_rr_compare_pref_any);
1646    for (middle = mx_list; /* see below */ ; middle = rest) {
1647	rest = middle->next;
1648	if (rest == 0)
1649	    break;
1650	if (rest->pref != mx_list->pref) {
1651	    middle->next = 0;
1652	    break;
1653	}
1654    }
1655    /* postcondition: middle->next = 0, rest may be 0. */
1656
1657#define PERMIT_MX_BACKUP_RETURN(x) do { \
1658	middle->next = rest; \
1659	dns_rr_free(mx_list); \
1660	return (x); \
1661   } while (0)
1662
1663    /*
1664     * First, see if we match any of the primary MX servers.
1665     */
1666    if (i_am_mx(state, mx_list, reply_name, reply_class))
1667	PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_DUNNO);
1668
1669    /*
1670     * Then, see if we match any of the backup MX servers.
1671     */
1672    if (rest == 0 || !i_am_mx(state, rest, reply_name, reply_class))
1673	PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_DUNNO);
1674
1675    /*
1676     * Optionally, see if the primary MX hosts are in a restricted list of
1677     * networks.
1678     */
1679    if (*var_perm_mx_networks
1680	&& !permit_mx_primary(state, mx_list, reply_name, reply_class))
1681	PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_DUNNO);
1682
1683    /*
1684     * The destination passed all requirements.
1685     */
1686    PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_OK);
1687}
1688
1689/* reject_non_fqdn_address - fail if address is not in fqdn form */
1690
1691static int reject_non_fqdn_address(SMTPD_STATE *state, char *addr,
1692				        char *reply_name, char *reply_class)
1693{
1694    const char *myname = "reject_non_fqdn_address";
1695    char   *domain;
1696    char   *test_dom;
1697    int     stat;
1698
1699    if (msg_verbose)
1700	msg_info("%s: %s", myname, addr);
1701
1702    /*
1703     * Locate the domain information.
1704     */
1705    if ((domain = strrchr(addr, '@')) != 0)
1706	domain++;
1707    else
1708	domain = "";
1709
1710    /*
1711     * Skip forms that we can't handle yet.
1712     */
1713    if (domain[0] == '[' && domain[strlen(domain) - 1] == ']')
1714	return (SMTPD_CHECK_DUNNO);
1715
1716    /*
1717     * Truncate names ending in dot but not dot-dot.
1718     */
1719    test_dom = dup_if_truncate(domain);
1720
1721    /*
1722     * Validate the domain.
1723     */
1724    if (!*test_dom || !valid_hostname(test_dom, DONT_GRIPE) || !strchr(test_dom, '.'))
1725	stat = smtpd_check_reject(state, MAIL_ERROR_POLICY,
1726				  var_non_fqdn_code, "4.5.2",
1727			  "<%s>: %s rejected: need fully-qualified address",
1728				  reply_name, reply_class);
1729    else
1730	stat = SMTPD_CHECK_DUNNO;
1731
1732    /*
1733     * Cleanup.
1734     */
1735    if (test_dom != domain)
1736	myfree(test_dom);
1737
1738    return (stat);
1739}
1740
1741/* reject_unknown_address - fail if address does not resolve */
1742
1743static int reject_unknown_address(SMTPD_STATE *state, const char *addr,
1744		            const char *reply_name, const char *reply_class)
1745{
1746    const char *myname = "reject_unknown_address";
1747    const RESOLVE_REPLY *reply;
1748    const char *domain;
1749
1750    if (msg_verbose)
1751	msg_info("%s: %s", myname, addr);
1752
1753    /*
1754     * Resolve the address.
1755     */
1756    reply = smtpd_resolve_addr(addr);
1757    if (reply->flags & RESOLVE_FLAG_FAIL)
1758	reject_dict_retry(state, addr);
1759
1760    /*
1761     * Skip local destinations and non-DNS forms.
1762     */
1763    if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0)
1764	return (SMTPD_CHECK_DUNNO);
1765    domain += 1;
1766    if (reply->flags & RESOLVE_CLASS_FINAL)
1767	return (SMTPD_CHECK_DUNNO);
1768    if (domain[0] == '[' && domain[strlen(domain) - 1] == ']')
1769	return (SMTPD_CHECK_DUNNO);
1770
1771    /*
1772     * Look up the name in the DNS.
1773     */
1774    return (reject_unknown_mailhost(state, domain, reply_name, reply_class));
1775}
1776
1777/* reject_unverified_address - fail if address bounces */
1778
1779static int reject_unverified_address(SMTPD_STATE *state, const char *addr,
1780		            const char *reply_name, const char *reply_class,
1781			             int unv_addr_dcode, int unv_addr_rcode,
1782				             int unv_addr_tf_act,
1783				             const char *alt_reply)
1784{
1785    const char *myname = "reject_unverified_address";
1786    VSTRING *why = vstring_alloc(10);
1787    int     rqst_status = SMTPD_CHECK_DUNNO;
1788    int     rcpt_status;
1789    int     verify_status;
1790    int     count;
1791    int     reject_code = 0;
1792
1793    if (msg_verbose)
1794	msg_info("%s: %s", myname, addr);
1795
1796    /*
1797     * Verify the address. Don't waste too much of their or our time.
1798     */
1799    for (count = 0; /* see below */ ; /* see below */ ) {
1800	verify_status = verify_clnt_query(addr, &rcpt_status, why);
1801	if (verify_status != VRFY_STAT_OK || rcpt_status != DEL_RCPT_STAT_TODO)
1802	    break;
1803	if (++count >= var_verify_poll_count)
1804	    break;
1805	sleep(var_verify_poll_delay);
1806    }
1807    if (verify_status != VRFY_STAT_OK) {
1808	msg_warn("%s service failure", var_verify_service);
1809	rqst_status =
1810	    DEFER_IF_PERMIT2(unv_addr_tf_act, state, MAIL_ERROR_POLICY,
1811			  450, strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
1812			     SND_DSN : "4.1.1",
1813			  "<%s>: %s rejected: address verification problem",
1814			     reply_name, reply_class);
1815    } else {
1816	switch (rcpt_status) {
1817	default:
1818	    msg_warn("unknown address verification status %d", rcpt_status);
1819	    break;
1820	case DEL_RCPT_STAT_TODO:
1821	case DEL_RCPT_STAT_DEFER:
1822	    reject_code = unv_addr_dcode;
1823	    break;
1824	case DEL_RCPT_STAT_OK:
1825	    break;
1826	case DEL_RCPT_STAT_BOUNCE:
1827	    reject_code = unv_addr_rcode;
1828	    break;
1829	}
1830	if (reject_code >= 400 && *alt_reply)
1831	    vstring_strcpy(why, alt_reply);
1832	switch (reject_code / 100) {
1833	case 2:
1834	    break;
1835	case 4:
1836	    rqst_status =
1837		DEFER_IF_PERMIT3(unv_addr_tf_act, state, MAIL_ERROR_POLICY,
1838			  450, strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
1839				 SND_DSN : "4.1.1",
1840			    "<%s>: %s rejected: unverified address: %.250s",
1841				 reply_name, reply_class, STR(why));
1842	    break;
1843	default:
1844	    if (reject_code != 0)
1845		rqst_status =
1846		    smtpd_check_reject(state, MAIL_ERROR_POLICY,
1847				       reject_code,
1848			       strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
1849				       SND_DSN : "4.1.1",
1850			     "<%s>: %s rejected: undeliverable address: %s",
1851				       reply_name, reply_class, STR(why));
1852	    break;
1853	}
1854    }
1855    vstring_free(why);
1856    return (rqst_status);
1857}
1858
1859/* can_delegate_action - can we delegate this to the cleanup server */
1860
1861#ifndef TEST
1862
1863static int not_in_client_helo(SMTPD_STATE *, const char *, const char *, const char *);
1864
1865static int can_delegate_action(SMTPD_STATE *state, const char *table,
1866			        const char *action, const char *reply_class)
1867{
1868
1869    /*
1870     * If we're not using the cleanup server, then there is no way that we
1871     * can support actions such as FILTER or HOLD that are delegated to the
1872     * cleanup server.
1873     */
1874    if (USE_SMTPD_PROXY(state)) {
1875	msg_warn("access table %s: with %s specified, action %s is unavailable",
1876		 table, VAR_SMTPD_PROXY_FILT, action);
1877	return (0);
1878    }
1879
1880    /*
1881     * ETRN does not receive mail so we can't store queue file records.
1882     */
1883    if (strcmp(state->where, SMTPD_CMD_ETRN) == 0) {
1884	msg_warn("access table %s: action %s is unavailable in %s",
1885		 table, action, VAR_ETRN_CHECKS);
1886	return (0);
1887    }
1888    return (not_in_client_helo(state, table, action, reply_class));
1889}
1890
1891/* not_in_client_helo - not in client or helo restriction context */
1892
1893static int not_in_client_helo(SMTPD_STATE *state, const char *table,
1894			              const char *action,
1895			              const char *unused_reply_class)
1896{
1897
1898    /*
1899     * If delay_reject=no, then client and helo restrictions take effect
1900     * immediately, outside any particular mail transaction context. For
1901     * example, rejecting HELO does not affect subsequent mail deliveries.
1902     * Thus, if delay_reject=no, client and helo actions such as FILTER or
1903     * HOLD also should not affect subsequent mail deliveries. Hmm...
1904     *
1905     * XXX If the MAIL FROM command is rejected then we have to reset access map
1906     * side effects such as FILTER.
1907     */
1908    if (state->sender == 0) {
1909	msg_warn("access table %s: with %s=%s, "
1910		 "action %s is always skipped in %s or %s restrictions",
1911		 table, VAR_SMTPD_DELAY_REJECT, CONFIG_BOOL_NO,
1912		 action, SMTPD_NAME_CLIENT, SMTPD_NAME_HELO);
1913	/* XXX What about ETRN? */
1914	return (0);
1915    }
1916    return (1);
1917}
1918
1919#endif
1920
1921/* check_table_result - translate table lookup result into pass/reject */
1922
1923static int check_table_result(SMTPD_STATE *state, const char *table,
1924			              const char *value, const char *datum,
1925			              const char *reply_name,
1926			              const char *reply_class,
1927			              const char *def_acl)
1928{
1929    const char *myname = "check_table_result";
1930    int     code;
1931    ARGV   *restrictions;
1932    jmp_buf savebuf;
1933    int     status;
1934    const char *cmd_text;
1935    int     cmd_len;
1936    static char def_dsn[] = "5.7.1";
1937    DSN_SPLIT dp;
1938
1939#ifdef DELAY_ACTION
1940    int     defer_delay;
1941
1942#endif
1943
1944    /*
1945     * Parse into command and text. Do not change the input.
1946     */
1947    cmd_text = value + strcspn(value, " \t");
1948    cmd_len = cmd_text - value;
1949    while (*cmd_text && ISSPACE(*cmd_text))
1950	cmd_text++;
1951
1952    if (msg_verbose)
1953	msg_info("%s: %s %s %s", myname, table, value, datum);
1954
1955#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
1956
1957    /*
1958     * DUNNO means skip this table. Silently ignore optional text.
1959     */
1960    if (STREQUAL(value, "DUNNO", cmd_len))
1961	return (SMTPD_CHECK_DUNNO);
1962
1963    /*
1964     * REJECT means NO. Use optional text or generate a generic error
1965     * response.
1966     */
1967    if (STREQUAL(value, "REJECT", cmd_len)) {
1968	dsn_split(&dp, "5.7.1", cmd_text);
1969	return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
1970				   var_map_reject_code,
1971				   smtpd_dsn_fix(DSN_STATUS(dp.dsn),
1972						 reply_class),
1973				   "<%s>: %s rejected: %s",
1974				   reply_name, reply_class,
1975				   *dp.text ? dp.text : "Access denied"));
1976    }
1977
1978    /*
1979     * DEFER means "try again". Use optional text or generate a generic error
1980     * response.
1981     */
1982    if (STREQUAL(value, "DEFER", cmd_len)) {
1983	dsn_split(&dp, "4.7.1", cmd_text);
1984	return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
1985				   var_map_defer_code,
1986				   smtpd_dsn_fix(DSN_STATUS(dp.dsn),
1987						 reply_class),
1988				   "<%s>: %s rejected: %s",
1989				   reply_name, reply_class,
1990				   *dp.text ? dp.text : "Access denied"));
1991    }
1992
1993    /*
1994     * WARN. Text is optional.
1995     */
1996    if (STREQUAL(value, "WARN", cmd_len)) {
1997	log_whatsup(state, "warn", cmd_text);
1998	return (SMTPD_CHECK_DUNNO);
1999    }
2000
2001    /*
2002     * FILTER means deliver to content filter. But we may still change our
2003     * mind, and reject/discard the message for other reasons.
2004     */
2005    if (STREQUAL(value, "FILTER", cmd_len)) {
2006#ifndef TEST
2007	if (can_delegate_action(state, table, "FILTER", reply_class) == 0)
2008	    return (SMTPD_CHECK_DUNNO);
2009#endif
2010	if (*cmd_text == 0) {
2011	    msg_warn("access table %s entry \"%s\" has FILTER entry without value",
2012		     table, datum);
2013	    return (SMTPD_CHECK_DUNNO);
2014	} else if (strchr(cmd_text, ':') == 0) {
2015	    msg_warn("access table %s entry \"%s\" requires transport:destination",
2016		     table, datum);
2017	    return (SMTPD_CHECK_DUNNO);
2018	} else {
2019	    vstring_sprintf(error_text, "<%s>: %s triggers FILTER %s",
2020			    reply_name, reply_class, cmd_text);
2021	    log_whatsup(state, "filter", STR(error_text));
2022#ifndef TEST
2023	    UPDATE_STRING(state->saved_filter, cmd_text);
2024#endif
2025	    return (SMTPD_CHECK_DUNNO);
2026	}
2027    }
2028
2029    /*
2030     * HOLD means deliver later. But we may still change our mind, and
2031     * reject/discard the message for other reasons.
2032     */
2033    if (STREQUAL(value, "HOLD", cmd_len)) {
2034#ifndef TEST
2035	if (can_delegate_action(state, table, "HOLD", reply_class) == 0
2036	    || (state->saved_flags & CLEANUP_FLAG_HOLD))
2037	    return (SMTPD_CHECK_DUNNO);
2038#endif
2039	vstring_sprintf(error_text, "<%s>: %s %s", reply_name, reply_class,
2040			*cmd_text ? cmd_text : "triggers HOLD action");
2041	log_whatsup(state, "hold", STR(error_text));
2042#ifndef TEST
2043	state->saved_flags |= CLEANUP_FLAG_HOLD;
2044#endif
2045	return (SMTPD_CHECK_DUNNO);
2046    }
2047
2048    /*
2049     * DELAY means deliver later. But we may still change our mind, and
2050     * reject/discard the message for other reasons.
2051     *
2052     * This feature is deleted because it has too many problems. 1) It does not
2053     * work on some remote file systems; 2) mail will be delivered anyway
2054     * with "sendmail -q" etc.; 3) while the mail is queued it bogs down the
2055     * deferred queue scan with huge amounts of useless disk I/O operations.
2056     */
2057#ifdef DELAY_ACTION
2058    if (STREQUAL(value, "DELAY", cmd_len)) {
2059#ifndef TEST
2060	if (can_delegate_action(state, table, "DELAY", reply_class) == 0)
2061	    return (SMTPD_CHECK_DUNNO);
2062#endif
2063	if (*cmd_text == 0) {
2064	    msg_warn("access table %s entry \"%s\" has DELAY entry without value",
2065		     table, datum);
2066	    return (SMTPD_CHECK_DUNNO);
2067	}
2068	if (conv_time(cmd_text, &defer_delay, 's') == 0) {
2069	    msg_warn("access table %s entry \"%s\" has invalid DELAY argument \"%s\"",
2070		     table, datum, cmd_text);
2071	    return (SMTPD_CHECK_DUNNO);
2072	}
2073	vstring_sprintf(error_text, "<%s>: %s %s", reply_name, reply_class,
2074			*cmd_text ? cmd_text : "triggers DELAY action");
2075	log_whatsup(state, "delay", STR(error_text));
2076#ifndef TEST
2077	state->saved_delay = defer_delay;
2078#endif
2079	return (SMTPD_CHECK_DUNNO);
2080    }
2081#endif
2082
2083    /*
2084     * DISCARD means silently discard and claim successful delivery.
2085     */
2086    if (STREQUAL(value, "DISCARD", cmd_len)) {
2087#ifndef TEST
2088	if (can_delegate_action(state, table, "DISCARD", reply_class) == 0)
2089	    return (SMTPD_CHECK_DUNNO);
2090#endif
2091	vstring_sprintf(error_text, "<%s>: %s %s", reply_name, reply_class,
2092			*cmd_text ? cmd_text : "triggers DISCARD action");
2093	log_whatsup(state, "discard", STR(error_text));
2094#ifndef TEST
2095	state->saved_flags |= CLEANUP_FLAG_DISCARD;
2096	state->discard = 1;
2097#endif
2098	return (SMTPD_CHECK_OK);
2099    }
2100
2101    /*
2102     * REDIRECT means deliver to designated recipient. But we may still
2103     * change our mind, and reject/discard the message for other reasons.
2104     */
2105    if (STREQUAL(value, "REDIRECT", cmd_len)) {
2106#ifndef TEST
2107	if (can_delegate_action(state, table, "REDIRECT", reply_class) == 0)
2108	    return (SMTPD_CHECK_DUNNO);
2109#endif
2110	if (strchr(cmd_text, '@') == 0) {
2111	    msg_warn("access table %s entry \"%s\" requires user@domain target",
2112		     table, datum);
2113	    return (SMTPD_CHECK_DUNNO);
2114	} else {
2115	    vstring_sprintf(error_text, "<%s>: %s triggers REDIRECT %s",
2116			    reply_name, reply_class, cmd_text);
2117	    log_whatsup(state, "redirect", STR(error_text));
2118#ifndef TEST
2119	    UPDATE_STRING(state->saved_redirect, cmd_text);
2120#endif
2121	    return (SMTPD_CHECK_DUNNO);
2122	}
2123    }
2124
2125    /*
2126     * BCC means deliver to designated recipient. But we may still change our
2127     * mind, and reject/discard the message for other reasons.
2128     */
2129#ifdef SNAPSHOT
2130    if (STREQUAL(value, "BCC", cmd_len)) {
2131#ifndef TEST
2132	if (can_delegate_action(state, table, "BCC", reply_class) == 0)
2133	    return (SMTPD_CHECK_DUNNO);
2134#endif
2135	if (strchr(cmd_text, '@') == 0) {
2136	    msg_warn("access table %s entry \"%s\" requires user@domain target",
2137		     table, datum);
2138	    return (SMTPD_CHECK_DUNNO);
2139	} else {
2140	    vstring_sprintf(error_text, "<%s>: %s triggers BCC %s",
2141			    reply_name, reply_class, cmd_text);
2142	    log_whatsup(state, "bcc", STR(error_text));
2143#ifndef TEST
2144	    UPDATE_STRING(state->saved_bcc, cmd_text);
2145#endif
2146	    return (SMTPD_CHECK_DUNNO);
2147	}
2148    }
2149#endif
2150
2151    /*
2152     * DEFER_IF_PERMIT changes "permit" into "maybe". Use optional text or
2153     * generate a generic error response.
2154     */
2155    if (STREQUAL(value, DEFER_IF_PERMIT, cmd_len)) {
2156	dsn_split(&dp, "4.7.1", cmd_text);
2157	return (DEFER_IF_PERMIT3(DEFER_EXPLICIT, state, MAIL_ERROR_POLICY,
2158				 var_map_defer_code,
2159			     smtpd_dsn_fix(DSN_STATUS(dp.dsn), reply_class),
2160				 "<%s>: %s rejected: %s",
2161				 reply_name, reply_class,
2162			       *dp.text ? dp.text : "Service unavailable"));
2163    }
2164
2165    /*
2166     * DEFER_IF_REJECT changes "reject" into "maybe". Use optional text or
2167     * generate a generic error response.
2168     */
2169    if (STREQUAL(value, DEFER_IF_REJECT, cmd_len)) {
2170	dsn_split(&dp, "4.7.1", cmd_text);
2171	DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY,
2172			 var_map_defer_code,
2173			 smtpd_dsn_fix(DSN_STATUS(dp.dsn), reply_class),
2174			 "<%s>: %s rejected: %s",
2175			 reply_name, reply_class,
2176			 *dp.text ? dp.text : "Service unavailable");
2177	return (SMTPD_CHECK_DUNNO);
2178    }
2179
2180    /*
2181     * PREPEND prepends the specified message header text.
2182     */
2183    if (STREQUAL(value, "PREPEND", cmd_len)) {
2184#ifndef TEST
2185	/* XXX what about ETRN. */
2186	if (not_in_client_helo(state, table, "PREPEND", reply_class) == 0)
2187	    return (SMTPD_CHECK_DUNNO);
2188#endif
2189	if (strcmp(state->where, SMTPD_AFTER_DOT) == 0) {
2190	    msg_warn("access table %s: action PREPEND must be used before %s",
2191		     table, VAR_EOD_CHECKS);
2192	    return (SMTPD_CHECK_DUNNO);
2193	}
2194	if (*cmd_text == 0 || is_header(cmd_text) == 0) {
2195	    msg_warn("access table %s entry \"%s\" requires header: text",
2196		     table, datum);
2197	    return (SMTPD_CHECK_DUNNO);
2198	} else {
2199	    if (state->prepend == 0)
2200		state->prepend = argv_alloc(1);
2201	    argv_add(state->prepend, cmd_text, (char *) 0);
2202	    return (SMTPD_CHECK_DUNNO);
2203	}
2204    }
2205
2206    /*
2207     * All-numeric result probably means OK - some out-of-band authentication
2208     * mechanism uses this as time stamp.
2209     */
2210    if (alldig(value))
2211	return (SMTPD_CHECK_OK);
2212
2213    /*
2214     * 4xx or 5xx means NO as well. smtpd_check_reject() will validate the
2215     * response status code.
2216     *
2217     * If the caller specifies an RFC 3463 enhanced status code, put it
2218     * immediately after the SMTP status code as described in RFC 2034.
2219     */
2220    if (cmd_len == 3 && *cmd_text
2221	&& (value[0] == '4' || value[0] == '5')
2222	&& ISDIGIT(value[1]) && ISDIGIT(value[2])) {
2223	code = atoi(value);
2224	def_dsn[0] = value[0];
2225	dsn_split(&dp, def_dsn, cmd_text);
2226	return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
2227				   code,
2228				   smtpd_dsn_fix(DSN_STATUS(dp.dsn),
2229						 reply_class),
2230				   "<%s>: %s rejected: %s",
2231				   reply_name, reply_class,
2232				   *dp.text ? dp.text : "Access denied"));
2233    }
2234
2235    /*
2236     * OK or RELAY means YES. Ignore trailing text.
2237     */
2238    if (STREQUAL(value, "OK", cmd_len) || STREQUAL(value, "RELAY", cmd_len))
2239	return (SMTPD_CHECK_OK);
2240
2241    /*
2242     * Unfortunately, maps must be declared ahead of time so they can be
2243     * opened before we go to jail. We could insist that the RHS can only
2244     * contain a pre-defined restriction class name, but that would be too
2245     * restrictive. Instead we warn if an access table references any map.
2246     *
2247     * XXX Don't use passwd files or address rewriting maps as access tables.
2248     */
2249    if (strchr(value, ':') != 0) {
2250	msg_warn("access table %s has entry with lookup table: %s",
2251		 table, value);
2252	msg_warn("do not specify lookup tables inside SMTPD access maps");
2253	msg_warn("define a restriction class and specify its name instead.");
2254	longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
2255						    451, "4.3.5",
2256					     "Server configuration error"));
2257    }
2258
2259    /*
2260     * Don't get carried away with recursion.
2261     */
2262    if (state->recursion > 100) {
2263	msg_warn("access table %s entry %s causes unreasonable recursion",
2264		 table, value);
2265	longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
2266						    451, "4.3.5",
2267					     "Server configuration error"));
2268    }
2269
2270    /*
2271     * Recursively evaluate the restrictions given in the right-hand side. In
2272     * the dark ages, an empty right-hand side meant OK. Make some
2273     * discouraging comments.
2274     *
2275     * XXX Jump some hoops to avoid a minute memory leak in case of a file
2276     * configuration error.
2277     */
2278#define ADDROF(x) ((char *) &(x))
2279
2280    restrictions = argv_split(value, RESTRICTION_SEPARATORS);
2281    memcpy(ADDROF(savebuf), ADDROF(smtpd_check_buf), sizeof(savebuf));
2282    status = setjmp(smtpd_check_buf);
2283    if (status != 0) {
2284	argv_free(restrictions);
2285	memcpy(ADDROF(smtpd_check_buf), ADDROF(savebuf),
2286	       sizeof(smtpd_check_buf));
2287	longjmp(smtpd_check_buf, status);
2288    }
2289    if (restrictions->argc == 0) {
2290	msg_warn("access table %s entry %s has empty value",
2291		 table, value);
2292	status = SMTPD_CHECK_OK;
2293    } else {
2294	status = generic_checks(state, restrictions, reply_name,
2295				reply_class, def_acl);
2296    }
2297    argv_free(restrictions);
2298    memcpy(ADDROF(smtpd_check_buf), ADDROF(savebuf), sizeof(smtpd_check_buf));
2299    return (status);
2300}
2301
2302/* check_access - table lookup without substring magic */
2303
2304static int check_access(SMTPD_STATE *state, const char *table, const char *name,
2305		              int flags, int *found, const char *reply_name,
2306			        const char *reply_class, const char *def_acl)
2307{
2308    const char *myname = "check_access";
2309    const char *value;
2310    DICT   *dict;
2311
2312#define CHK_ACCESS_RETURN(x,y) \
2313	{ *found = y; return(x); }
2314#define FULL	0
2315#define PARTIAL	DICT_FLAG_FIXED
2316#define FOUND	1
2317#define MISSED	0
2318
2319    if (msg_verbose)
2320	msg_info("%s: %s", myname, name);
2321
2322    if ((dict = dict_handle(table)) == 0) {
2323	msg_warn("%s: unexpected dictionary: %s", myname, table);
2324	value = "451 4.3.5 Server configuration error";
2325	CHK_ACCESS_RETURN(check_table_result(state, table, value, name,
2326					     reply_name, reply_class,
2327					     def_acl), FOUND);
2328    }
2329    if (flags == 0 || (flags & dict->flags) != 0) {
2330	if ((value = dict_get(dict, name)) != 0)
2331	    CHK_ACCESS_RETURN(check_table_result(state, table, value, name,
2332						 reply_name, reply_class,
2333						 def_acl), FOUND);
2334	if (dict_errno != 0) {
2335	    msg_warn("%s: table lookup problem", table);
2336	    value = "451 4.3.5 Server configuration error";
2337	    CHK_ACCESS_RETURN(check_table_result(state, table, value, name,
2338						 reply_name, reply_class,
2339						 def_acl), FOUND);
2340	}
2341    }
2342    CHK_ACCESS_RETURN(SMTPD_CHECK_DUNNO, MISSED);
2343}
2344
2345/* check_domain_access - domainname-based table lookup */
2346
2347static int check_domain_access(SMTPD_STATE *state, const char *table,
2348			               const char *domain, int flags,
2349			               int *found, const char *reply_name,
2350			               const char *reply_class,
2351			               const char *def_acl)
2352{
2353    const char *myname = "check_domain_access";
2354    const char *name;
2355    const char *next;
2356    const char *value;
2357    DICT   *dict;
2358    int     maybe_numerical = 1;
2359
2360    if (msg_verbose)
2361	msg_info("%s: %s", myname, domain);
2362
2363    /*
2364     * Try the name and its parent domains. Including top-level domains.
2365     *
2366     * Helo names can end in ".". The test below avoids lookups of the empty
2367     * key, because Berkeley DB cannot deal with it. [Victor Duchovni, Morgan
2368     * Stanley].
2369     */
2370#define CHK_DOMAIN_RETURN(x,y) { *found = y; return(x); }
2371
2372    if ((dict = dict_handle(table)) == 0) {
2373	msg_warn("%s: unexpected dictionary: %s", myname, table);
2374	value = "451 4.3.5 Server configuration error";
2375	CHK_DOMAIN_RETURN(check_table_result(state, table, value,
2376					     domain, reply_name, reply_class,
2377					     def_acl), FOUND);
2378    }
2379    for (name = domain; *name != 0; name = next) {
2380	if (flags == 0 || (flags & dict->flags) != 0) {
2381	    if ((value = dict_get(dict, name)) != 0)
2382		CHK_DOMAIN_RETURN(check_table_result(state, table, value,
2383					    domain, reply_name, reply_class,
2384						     def_acl), FOUND);
2385	    if (dict_errno != 0) {
2386		msg_warn("%s: table lookup problem", table);
2387		value = "451 4.3.5 Server configuration error";
2388		CHK_DOMAIN_RETURN(check_table_result(state, table, value,
2389					    domain, reply_name, reply_class,
2390						     def_acl), FOUND);
2391	    }
2392	}
2393	/* Don't apply subdomain magic to numerical hostnames. */
2394	if (maybe_numerical
2395	    && (maybe_numerical = valid_hostaddr(domain, DONT_GRIPE)) != 0)
2396	    break;
2397	if ((next = strchr(name + 1, '.')) == 0)
2398	    break;
2399	if (access_parent_style == MATCH_FLAG_PARENT)
2400	    next += 1;
2401	flags = PARTIAL;
2402    }
2403    CHK_DOMAIN_RETURN(SMTPD_CHECK_DUNNO, MISSED);
2404}
2405
2406/* check_addr_access - address-based table lookup */
2407
2408static int check_addr_access(SMTPD_STATE *state, const char *table,
2409			             const char *address, int flags,
2410			             int *found, const char *reply_name,
2411			             const char *reply_class,
2412			             const char *def_acl)
2413{
2414    const char *myname = "check_addr_access";
2415    char   *addr;
2416    const char *value;
2417    DICT   *dict;
2418    int     delim;
2419
2420    if (msg_verbose)
2421	msg_info("%s: %s", myname, address);
2422
2423    /*
2424     * Try the address and its parent networks.
2425     */
2426#define CHK_ADDR_RETURN(x,y) { *found = y; return(x); }
2427
2428    addr = STR(vstring_strcpy(error_text, address));
2429#ifdef HAS_IPV6
2430    if (strchr(addr, ':') != 0)
2431	delim = ':';
2432    else
2433#endif
2434	delim = '.';
2435
2436    if ((dict = dict_handle(table)) == 0) {
2437	msg_warn("%s: unexpected dictionary: %s", myname, table);
2438	value = "451 4.3.5 Server configuration error";
2439	CHK_ADDR_RETURN(check_table_result(state, table, value, address,
2440					   reply_name, reply_class,
2441					   def_acl), FOUND);
2442    }
2443    do {
2444	if (flags == 0 || (flags & dict->flags) != 0) {
2445	    if ((value = dict_get(dict, addr)) != 0)
2446		CHK_ADDR_RETURN(check_table_result(state, table, value, address,
2447						   reply_name, reply_class,
2448						   def_acl), FOUND);
2449	    if (dict_errno != 0) {
2450		msg_warn("%s: table lookup problem", table);
2451		value = "451 4.3.5 Server configuration error";
2452		CHK_ADDR_RETURN(check_table_result(state, table, value, address,
2453						   reply_name, reply_class,
2454						   def_acl), FOUND);
2455	    }
2456	}
2457	flags = PARTIAL;
2458    } while (split_at_right(addr, delim));
2459
2460    CHK_ADDR_RETURN(SMTPD_CHECK_DUNNO, MISSED);
2461}
2462
2463/* check_namadr_access - OK/FAIL based on host name/address lookup */
2464
2465static int check_namadr_access(SMTPD_STATE *state, const char *table,
2466			               const char *name, const char *addr,
2467			               int flags, int *found,
2468			               const char *reply_name,
2469			               const char *reply_class,
2470			               const char *def_acl)
2471{
2472    const char *myname = "check_namadr_access";
2473    int     status;
2474
2475    if (msg_verbose)
2476	msg_info("%s: name %s addr %s", myname, name, addr);
2477
2478    /*
2479     * Look up the host name, or parent domains thereof. XXX A domain
2480     * wildcard may pre-empt a more specific address table entry.
2481     */
2482    if ((status = check_domain_access(state, table, name, flags,
2483				      found, reply_name, reply_class,
2484				      def_acl)) != 0 || *found)
2485	return (status);
2486
2487    /*
2488     * Look up the network address, or parent networks thereof.
2489     */
2490    if ((status = check_addr_access(state, table, addr, flags,
2491				    found, reply_name, reply_class,
2492				    def_acl)) != 0 || *found)
2493	return (status);
2494
2495    /*
2496     * Undecided when the host was not found.
2497     */
2498    return (SMTPD_CHECK_DUNNO);
2499}
2500
2501/* check_server_access - access control by server host name or address */
2502
2503static int check_server_access(SMTPD_STATE *state, const char *table,
2504			               const char *name,
2505			               int type,
2506			               const char *reply_name,
2507			               const char *reply_class,
2508			               const char *def_acl)
2509{
2510    const char *myname = "check_server_access";
2511    const char *domain;
2512    int     dns_status;
2513    DNS_RR *server_list;
2514    DNS_RR *server;
2515    int     found = 0;
2516    MAI_HOSTADDR_STR addr_string;
2517    int     aierr;
2518    struct addrinfo *res0;
2519    struct addrinfo *res;
2520    int     status;
2521    INET_PROTO_INFO *proto_info;
2522
2523    /*
2524     * Sanity check.
2525     */
2526    if (type != T_MX && type != T_NS)
2527	msg_panic("%s: unexpected resource type \"%s\" in request",
2528		  myname, dns_strtype(type));
2529
2530    if (msg_verbose)
2531	msg_info("%s: %s %s", myname, dns_strtype(type), name);
2532
2533    /*
2534     * Skip over local-part.
2535     */
2536    if ((domain = strrchr(name, '@')) != 0)
2537	domain += 1;
2538    else
2539	domain = name;
2540
2541    /*
2542     * Treat an address literal as its own MX server, just like we treat a
2543     * name without MX record as its own MX server. There is, however, no
2544     * applicable NS server equivalent.
2545     */
2546    if (*domain == '[') {
2547	char   *saved_addr;
2548	const char *bare_addr;
2549	ssize_t len;
2550
2551	if (type != T_MX)
2552	    return (SMTPD_CHECK_DUNNO);
2553	len = strlen(domain);
2554	if (domain[len - 1] != ']')
2555	    return (SMTPD_CHECK_DUNNO);
2556	/* Memory leak alert: no early returns after this point. */
2557	saved_addr = mystrndup(domain + 1, len - 2);
2558	if ((bare_addr = valid_mailhost_addr(saved_addr, DONT_GRIPE)) == 0)
2559	    status = SMTPD_CHECK_DUNNO;
2560	else
2561	    status = check_addr_access(state, table, bare_addr, FULL,
2562				       &found, reply_name, reply_class,
2563				       def_acl);
2564	myfree(saved_addr);
2565	return (status);
2566    }
2567
2568    /*
2569     * If the domain name does not exist then we apply no restriction.
2570     *
2571     * If the domain name exists but no MX record exists, fabricate an MX record
2572     * that points to the domain name itself.
2573     *
2574     * If the domain name exists but no NS record exists, look up parent domain
2575     * NS records.
2576     */
2577    dns_status = dns_lookup(domain, type, 0, &server_list,
2578			    (VSTRING *) 0, (VSTRING *) 0);
2579    if (dns_status == DNS_NOTFOUND /* Not: h_errno == NO_DATA */ ) {
2580	if (type == T_MX) {
2581	    server_list = dns_rr_create(domain, domain, type, C_IN, 0, 0,
2582					domain, strlen(domain) + 1);
2583	    dns_status = DNS_OK;
2584	} else if (type == T_NS && h_errno == NO_DATA) {
2585	    while ((domain = strchr(domain, '.')) != 0 && domain[1]) {
2586		domain += 1;
2587		dns_status = dns_lookup(domain, type, 0, &server_list,
2588					(VSTRING *) 0, (VSTRING *) 0);
2589		if (dns_status != DNS_NOTFOUND || h_errno != NO_DATA)
2590		    break;
2591	    }
2592	}
2593    }
2594    if (dns_status != DNS_OK) {
2595	msg_warn("Unable to look up %s host for %s: %s", dns_strtype(type),
2596		 domain && domain[1] ? domain : name, dns_strerror(h_errno));
2597	return (SMTPD_CHECK_DUNNO);
2598    }
2599
2600    /*
2601     * No bare returns after this point or we have a memory leak.
2602     */
2603#define CHECK_SERVER_RETURN(x) { dns_rr_free(server_list); return(x); }
2604
2605    /*
2606     * Check the hostnames first, then the addresses.
2607     */
2608    proto_info = inet_proto_info();
2609    for (server = server_list; server != 0; server = server->next) {
2610	if (msg_verbose)
2611	    msg_info("%s: %s hostname check: %s",
2612		     myname, dns_strtype(type), (char *) server->data);
2613	if (valid_hostaddr((char *) server->data, DONT_GRIPE)) {
2614	    if ((status = check_addr_access(state, table, (char *) server->data,
2615				      FULL, &found, reply_name, reply_class,
2616					    def_acl)) != 0 || found)
2617		CHECK_SERVER_RETURN(status);
2618	    continue;
2619	}
2620	if ((status = check_domain_access(state, table, (char *) server->data,
2621				      FULL, &found, reply_name, reply_class,
2622					  def_acl)) != 0 || found)
2623	    CHECK_SERVER_RETURN(status);
2624	if ((aierr = hostname_to_sockaddr((char *) server->data,
2625					  (char *) 0, 0, &res0)) != 0) {
2626	    msg_warn("Unable to look up %s host %s for %s %s: %s",
2627		     dns_strtype(type), (char *) server->data,
2628		     reply_class, reply_name, MAI_STRERROR(aierr));
2629	    continue;
2630	}
2631	/* Now we must also free the addrinfo result. */
2632	if (msg_verbose)
2633	    msg_info("%s: %s host address check: %s",
2634		     myname, dns_strtype(type), (char *) server->data);
2635	for (res = res0; res != 0; res = res->ai_next) {
2636	    if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
2637		if (msg_verbose)
2638		    msg_info("skipping address family %d for host %s",
2639			     res->ai_family, server->data);
2640		continue;
2641	    }
2642	    SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
2643				 &addr_string, (MAI_SERVPORT_STR *) 0, 0);
2644	    status = check_addr_access(state, table, addr_string.buf, FULL,
2645				       &found, reply_name, reply_class,
2646				       def_acl);
2647	    if (status != 0 || found) {
2648		freeaddrinfo(res0);		/* 200412 */
2649		CHECK_SERVER_RETURN(status);
2650	    }
2651	}
2652	freeaddrinfo(res0);			/* 200412 */
2653    }
2654    CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO);
2655}
2656
2657/* check_ccert_access - access for TLS clients by certificate fingerprint */
2658
2659
2660static int check_ccert_access(SMTPD_STATE *state, const char *table,
2661			              const char *def_acl)
2662{
2663#ifdef USE_TLS
2664    const char *myname = "check_ccert_access";
2665    int     found;
2666
2667    /*
2668     * When directly checking the fingerprint, it is OK if the issuing CA is
2669     * not trusted.
2670     */
2671    if (TLS_CERT_IS_PRESENT(state->tls_context)) {
2672	if (msg_verbose)
2673	    msg_info("%s: %s", myname, state->tls_context->peer_fingerprint);
2674
2675	/*
2676	 * Regexp tables don't make sense for certificate fingerprints. That
2677	 * may be so, but we can't ignore the entire check_ccert_access
2678	 * request without logging a warning.
2679	 *
2680	 * Log the peer CommonName when access is denied. Non-printable
2681	 * characters will be neutered by smtpd_check_reject(). The SMTP
2682	 * client name and address are always syslogged as part of a "reject"
2683	 * event.
2684	 */
2685	return (check_access(state, table,
2686			     state->tls_context->peer_fingerprint,
2687			     DICT_FLAG_NONE, &found,
2688			     state->tls_context->peer_CN,
2689			     SMTPD_NAME_CCERT, def_acl));
2690    }
2691#endif
2692    return (SMTPD_CHECK_DUNNO);
2693}
2694
2695/* check_mail_access - OK/FAIL based on mail address lookup */
2696
2697static int check_mail_access(SMTPD_STATE *state, const char *table,
2698			             const char *addr, int *found,
2699			             const char *reply_name,
2700			             const char *reply_class,
2701			             const char *def_acl)
2702{
2703    const char *myname = "check_mail_access";
2704    const RESOLVE_REPLY *reply;
2705    const char *domain;
2706    int     status;
2707    char   *local_at;
2708    char   *bare_addr;
2709    char   *bare_at;
2710
2711    if (msg_verbose)
2712	msg_info("%s: %s", myname, addr);
2713
2714    /*
2715     * Resolve the address.
2716     */
2717    reply = smtpd_resolve_addr(addr);
2718    if (reply->flags & RESOLVE_FLAG_FAIL)
2719	reject_dict_retry(state, addr);
2720
2721    /*
2722     * Garbage in, garbage out. Every address from rewrite_clnt_internal()
2723     * and from resolve_clnt_query() must be fully qualified.
2724     */
2725    if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0) {
2726	msg_warn("%s: no @domain in address: %s", myname,
2727		 CONST_STR(reply->recipient));
2728	return (0);
2729    }
2730    domain += 1;
2731
2732    /*
2733     * In case of address extensions.
2734     */
2735    if (*var_rcpt_delim == 0) {
2736	bare_addr = 0;
2737    } else {
2738	bare_addr = strip_addr(addr, (char **) 0, *var_rcpt_delim);
2739    }
2740
2741#define CHECK_MAIL_ACCESS_RETURN(x) \
2742	{ if (bare_addr) myfree(bare_addr); return(x); }
2743
2744    /*
2745     * Source-routed (non-local or virtual) recipient addresses are too
2746     * suspicious for returning an "OK" result. The complicated expression
2747     * below was brought to you by the keyboard of Victor Duchovni, Morgan
2748     * Stanley and hacked up a bit by Wietse.
2749     */
2750#define SUSPICIOUS(reply, reply_class) \
2751	(var_allow_untrust_route == 0 \
2752	&& (reply->flags & RESOLVE_FLAG_ROUTED) \
2753	&& strcmp(reply_class, SMTPD_NAME_RECIPIENT) == 0)
2754
2755    /*
2756     * Look up user+foo@domain if the address has an extension, user@domain
2757     * otherwise.
2758     */
2759    if ((status = check_access(state, table, CONST_STR(reply->recipient), FULL,
2760			       found, reply_name, reply_class, def_acl)) != 0
2761	|| *found)
2762	CHECK_MAIL_ACCESS_RETURN(status == SMTPD_CHECK_OK
2763				 && SUSPICIOUS(reply, reply_class) ?
2764				 SMTPD_CHECK_DUNNO : status);
2765
2766    /*
2767     * Try user@domain if the address has an extension.
2768     */
2769    if (bare_addr)
2770	if ((status = check_access(state, table, bare_addr, PARTIAL,
2771			      found, reply_name, reply_class, def_acl)) != 0
2772	    || *found)
2773	    CHECK_MAIL_ACCESS_RETURN(status == SMTPD_CHECK_OK
2774				     && SUSPICIOUS(reply, reply_class) ?
2775				     SMTPD_CHECK_DUNNO : status);
2776
2777    /*
2778     * Look up the domain name, or parent domains thereof.
2779     */
2780    if ((status = check_domain_access(state, table, domain, PARTIAL,
2781			      found, reply_name, reply_class, def_acl)) != 0
2782	|| *found)
2783	CHECK_MAIL_ACCESS_RETURN(status == SMTPD_CHECK_OK
2784				 && SUSPICIOUS(reply, reply_class) ?
2785				 SMTPD_CHECK_DUNNO : status);
2786
2787    /*
2788     * Look up user+foo@ if the address has an extension, user@ otherwise.
2789     * XXX This leaks a little memory if map lookup is aborted.
2790     */
2791    local_at = mystrndup(CONST_STR(reply->recipient),
2792			 domain - CONST_STR(reply->recipient));
2793    status = check_access(state, table, local_at, PARTIAL, found,
2794			  reply_name, reply_class, def_acl);
2795    myfree(local_at);
2796    if (status != 0 || *found)
2797	CHECK_MAIL_ACCESS_RETURN(status == SMTPD_CHECK_OK
2798				 && SUSPICIOUS(reply, reply_class) ?
2799				 SMTPD_CHECK_DUNNO : status);
2800
2801    /*
2802     * Look up user@ if the address has an extension. XXX Same problem here.
2803     */
2804    if (bare_addr) {
2805	bare_at = strrchr(bare_addr, '@');
2806	local_at = (bare_at ? mystrndup(bare_addr, bare_at + 1 - bare_addr) :
2807		    mystrdup(bare_addr));
2808	status = check_access(state, table, local_at, PARTIAL, found,
2809			      reply_name, reply_class, def_acl);
2810	myfree(local_at);
2811	if (status != 0 || *found)
2812	    CHECK_MAIL_ACCESS_RETURN(status == SMTPD_CHECK_OK
2813				     && SUSPICIOUS(reply, reply_class) ?
2814				     SMTPD_CHECK_DUNNO : status);
2815    }
2816
2817    /*
2818     * Undecided when no match found.
2819     */
2820    CHECK_MAIL_ACCESS_RETURN(SMTPD_CHECK_DUNNO);
2821}
2822
2823/* Support for different DNSXL lookup results. */
2824
2825static SMTPD_RBL_STATE dnsxl_stat_soft[1];
2826
2827#define SMTPD_DNSXL_STAT_SOFT(dnsxl_res) ((dnsxl_res) == dnsxl_stat_soft)
2828#define SMTPD_DNXSL_STAT_HARD(dnsxl_res) ((dnsxl_res) == 0)
2829#define SMTPD_DNSXL_STAT_OK(dnsxl_res) \
2830	!(SMTPD_DNXSL_STAT_HARD(dnsxl_res) || SMTPD_DNSXL_STAT_SOFT(dnsxl_res))
2831
2832/* rbl_pagein - look up an RBL lookup result */
2833
2834static void *rbl_pagein(const char *query, void *unused_context)
2835{
2836    DNS_RR *txt_list;
2837    VSTRING *why;
2838    int     dns_status;
2839    SMTPD_RBL_STATE *rbl = 0;
2840    DNS_RR *addr_list;
2841    DNS_RR *rr;
2842    DNS_RR *next;
2843    VSTRING *buf;
2844    int     space_left;
2845
2846    /*
2847     * Do the query. If the DNS lookup produces no definitive reply, give the
2848     * requestor the benefit of the doubt. We can't block all email simply
2849     * because an RBL server is unavailable.
2850     *
2851     * Don't do this for AAAA records. Yet.
2852     */
2853    why = vstring_alloc(10);
2854    dns_status = dns_lookup(query, T_A, 0, &addr_list, (VSTRING *) 0, why);
2855    if (dns_status != DNS_OK && dns_status != DNS_NOTFOUND) {
2856	msg_warn("%s: RBL lookup error: %s", query, STR(why));
2857	rbl = dnsxl_stat_soft;
2858    }
2859    vstring_free(why);
2860    if (dns_status != DNS_OK)
2861	return ((void *) rbl);
2862
2863    /*
2864     * Save the result. Yes, we cache negative results as well as positive
2865     * results. Concatenate multiple TXT records, up to some limit.
2866     */
2867#define RBL_TXT_LIMIT	500
2868
2869    rbl = (SMTPD_RBL_STATE *) mymalloc(sizeof(*rbl));
2870    if (dns_lookup(query, T_TXT, 0, &txt_list,
2871		   (VSTRING *) 0, (VSTRING *) 0) == DNS_OK) {
2872	buf = vstring_alloc(1);
2873	space_left = RBL_TXT_LIMIT;
2874	for (rr = txt_list; rr != 0 && space_left > 0; rr = next) {
2875	    vstring_strncat(buf, rr->data, (int) rr->data_len > space_left ?
2876			    space_left : rr->data_len);
2877	    space_left = RBL_TXT_LIMIT - VSTRING_LEN(buf);
2878	    next = rr->next;
2879	    if (next && space_left > 3) {
2880		vstring_strcat(buf, " / ");
2881		space_left -= 3;
2882	    }
2883	}
2884	rbl->txt = vstring_export(buf);
2885	dns_rr_free(txt_list);
2886    } else
2887	rbl->txt = 0;
2888    rbl->a = addr_list;
2889    return ((void *) rbl);
2890}
2891
2892/* rbl_pageout - discard an RBL lookup result */
2893
2894static void rbl_pageout(void *data, void *unused_context)
2895{
2896    SMTPD_RBL_STATE *rbl = (SMTPD_RBL_STATE *) data;
2897
2898    if (SMTPD_DNSXL_STAT_OK(rbl)) {
2899	if (rbl->txt)
2900	    myfree(rbl->txt);
2901	if (rbl->a)
2902	    dns_rr_free(rbl->a);
2903	myfree((char *) rbl);
2904    }
2905}
2906
2907/* rbl_byte_pagein - parse RBL reply pattern, save byte codes */
2908
2909static void *rbl_byte_pagein(const char *query, void *unused_context)
2910{
2911    VSTRING *byte_codes = vstring_alloc(100);
2912    char   *saved_query = mystrdup(query);
2913    char   *saved_byte_codes;
2914    char   *err;
2915
2916    if ((err = ip_match_parse(byte_codes, saved_query)) != 0)
2917	msg_fatal("RBL reply error: %s", err);
2918    saved_byte_codes = ip_match_save(byte_codes);
2919    myfree(saved_query);
2920    vstring_free(byte_codes);
2921    return (saved_byte_codes);
2922}
2923
2924/* rbl_byte_pageout - discard parsed RBL reply byte codes */
2925
2926static void rbl_byte_pageout(void *data, void *unused_context)
2927{
2928    myfree(data);
2929}
2930
2931/* rbl_expand_lookup - RBL specific $name expansion */
2932
2933static const char *rbl_expand_lookup(const char *name, int mode,
2934				             char *context)
2935{
2936    SMTPD_RBL_EXPAND_CONTEXT *rbl_exp = (SMTPD_RBL_EXPAND_CONTEXT *) context;
2937    SMTPD_STATE *state = rbl_exp->state;
2938
2939#define STREQ(x,y) (*(x) == *(y) && strcmp((x), (y)) == 0)
2940
2941    if (state->expand_buf == 0)
2942	state->expand_buf = vstring_alloc(10);
2943
2944    if (msg_verbose > 1)
2945	msg_info("rbl_expand_lookup: ${%s}", name);
2946
2947    /*
2948     * Be sure to return NULL only for non-existent names.
2949     */
2950    if (STREQ(name, MAIL_ATTR_RBL_CODE)) {
2951	vstring_sprintf(state->expand_buf, "%d", var_maps_rbl_code);
2952	return (STR(state->expand_buf));
2953    } else if (STREQ(name, MAIL_ATTR_RBL_DOMAIN)) {
2954	return (rbl_exp->domain);
2955    } else if (STREQ(name, MAIL_ATTR_RBL_REASON)) {
2956	return (rbl_exp->txt);
2957    } else if (STREQ(name, MAIL_ATTR_RBL_TXT)) {/* LaMont compat */
2958	return (rbl_exp->txt);
2959    } else if (STREQ(name, MAIL_ATTR_RBL_WHAT)) {
2960	return (rbl_exp->what);
2961    } else if (STREQ(name, MAIL_ATTR_RBL_CLASS)) {
2962	return (rbl_exp->class);
2963    } else {
2964	return (smtpd_expand_lookup(name, mode, (char *) state));
2965    }
2966}
2967
2968/* rbl_reject_reply - format reply after RBL reject */
2969
2970static int rbl_reject_reply(SMTPD_STATE *state, const SMTPD_RBL_STATE *rbl,
2971			            const char *rbl_domain,
2972			            const char *what,
2973			            const char *reply_class)
2974{
2975    const char *myname = "rbl_reject_reply";
2976    VSTRING *why = 0;
2977    const char *template = 0;
2978    SMTPD_RBL_EXPAND_CONTEXT rbl_exp;
2979    int     result;
2980    DSN_SPLIT dp;
2981    int     code;
2982
2983    /*
2984     * Use the server-specific reply template or use the default one.
2985     */
2986    if (*var_rbl_reply_maps) {
2987	template = maps_find(rbl_reply_maps, rbl_domain, DICT_FLAG_NONE);
2988    }
2989    why = vstring_alloc(100);
2990    rbl_exp.state = state;
2991    rbl_exp.domain = mystrdup(rbl_domain);
2992    (void) split_at(rbl_exp.domain, '=');
2993    rbl_exp.what = what;
2994    rbl_exp.class = reply_class;
2995    rbl_exp.txt = (rbl->txt == 0 ? "" : rbl->txt);
2996
2997    for (;;) {
2998	if (template == 0)
2999	    template = var_def_rbl_reply;
3000	if (mac_expand(why, template, MAC_EXP_FLAG_NONE,
3001		       STR(smtpd_expand_filter), rbl_expand_lookup,
3002		       (char *) &rbl_exp) == 0)
3003	    break;
3004	if (template == var_def_rbl_reply)
3005	    msg_fatal("%s: bad default rbl reply template: %s",
3006		      myname, var_def_rbl_reply);
3007	msg_warn("%s: bad rbl reply template for domain %s: %s",
3008		 myname, rbl_domain, template);
3009	template = 0;				/* pretend not found */
3010    }
3011
3012    /*
3013     * XXX Impedance mis-match.
3014     *
3015     * Validate the response, that is, the response must begin with a
3016     * three-digit status code, and the first digit must be 4 or 5. If the
3017     * response is bad, log a warning and send a generic response instead.
3018     */
3019    if ((STR(why)[0] != '4' && STR(why)[0] != '5')
3020	|| !ISDIGIT(STR(why)[1]) || !ISDIGIT(STR(why)[2])
3021	|| STR(why)[3] != ' ') {
3022	msg_warn("rbl response code configuration error: %s", STR(why));
3023	result = smtpd_check_reject(state, MAIL_ERROR_POLICY,
3024				    450, "4.7.1", "Service unavailable");
3025    } else {
3026	code = atoi(STR(why));
3027	dsn_split(&dp, "4.7.1", STR(why) + 4);
3028	result = smtpd_check_reject(state, MAIL_ERROR_POLICY,
3029				    code,
3030				    smtpd_dsn_fix(DSN_STATUS(dp.dsn),
3031						  reply_class),
3032				    "%s", *dp.text ?
3033				    dp.text : "Service unavailable");
3034    }
3035
3036    /*
3037     * Clean up.
3038     */
3039    myfree(rbl_exp.domain);
3040    vstring_free(why);
3041
3042    return (result);
3043}
3044
3045/* rbl_match_addr - match address list */
3046
3047static int rbl_match_addr(SMTPD_RBL_STATE *rbl, const char *byte_codes)
3048{
3049    const char *myname = "rbl_match_addr";
3050    DNS_RR *rr;
3051
3052    for (rr = rbl->a; rr != 0; rr = rr->next) {
3053	if (rr->type == T_A) {
3054	    if (ip_match_execute(byte_codes, rr->data))
3055		return (1);
3056	} else {
3057	    msg_warn("%s: skipping record type %s for query %s",
3058		     myname, dns_strtype(rr->type), rr->qname);
3059	}
3060    }
3061    return (0);
3062}
3063
3064/* find_dnsxl_addr - look up address in DNSXL */
3065
3066static const SMTPD_RBL_STATE *find_dnsxl_addr(SMTPD_STATE *state,
3067					              const char *rbl_domain,
3068					              const char *addr)
3069{
3070    const char *myname = "find_dnsxl_addr";
3071    ARGV   *octets;
3072    VSTRING *query;
3073    int     i;
3074    SMTPD_RBL_STATE *rbl;
3075    const char *reply_addr;
3076    const char *byte_codes;
3077    struct addrinfo *res;
3078    unsigned char *ipv6_addr;
3079
3080    query = vstring_alloc(100);
3081
3082    /*
3083     * Reverse the client IPV6 address, represented as 32 hexadecimal
3084     * nibbles. We use the binary address to avoid tricky code. Asking for an
3085     * AAAA record makes no sense here. Just like with IPv4 we use the lookup
3086     * result as a bit mask, not as an IP address.
3087     */
3088#ifdef HAS_IPV6
3089    if (valid_ipv6_hostaddr(addr, DONT_GRIPE)) {
3090	if (hostaddr_to_sockaddr(addr, (char *) 0, 0, &res) != 0
3091	    || res->ai_family != PF_INET6)
3092	    msg_fatal("%s: unable to convert address %s", myname, addr);
3093	ipv6_addr = (unsigned char *) &SOCK_ADDR_IN6_ADDR(res->ai_addr);
3094	for (i = sizeof(SOCK_ADDR_IN6_ADDR(res->ai_addr)) - 1; i >= 0; i--)
3095	    vstring_sprintf_append(query, "%x.%x.",
3096				   ipv6_addr[i] & 0xf, ipv6_addr[i] >> 4);
3097	freeaddrinfo(res);
3098    } else
3099#endif
3100
3101	/*
3102	 * Reverse the client IPV4 address, represented as four decimal octet
3103	 * values. We use the textual address for convenience.
3104	 */
3105    {
3106	octets = argv_split(addr, ".");
3107	for (i = octets->argc - 1; i >= 0; i--) {
3108	    vstring_strcat(query, octets->argv[i]);
3109	    vstring_strcat(query, ".");
3110	}
3111	argv_free(octets);
3112    }
3113
3114    /*
3115     * Tack on the RBL domain name and query the DNS for an A record.
3116     */
3117    vstring_strcat(query, rbl_domain);
3118    reply_addr = split_at(STR(query), '=');
3119    rbl = (SMTPD_RBL_STATE *) ctable_locate(smtpd_rbl_cache, STR(query));
3120    if (reply_addr != 0)
3121	byte_codes = ctable_locate(smtpd_rbl_byte_cache, reply_addr);
3122
3123    /*
3124     * If the record exists, match the result address.
3125     */
3126    if (SMTPD_DNSXL_STAT_OK(rbl) && reply_addr != 0
3127	&& !rbl_match_addr(rbl, byte_codes))
3128	rbl = 0;
3129    vstring_free(query);
3130    return (rbl);
3131}
3132
3133/* reject_rbl_addr - reject address in real-time blackhole list */
3134
3135static int reject_rbl_addr(SMTPD_STATE *state, const char *rbl_domain,
3136			           const char *addr, const char *reply_class)
3137{
3138    const char *myname = "reject_rbl_addr";
3139    const SMTPD_RBL_STATE *rbl;
3140
3141    if (msg_verbose)
3142	msg_info("%s: %s %s", myname, reply_class, addr);
3143
3144    rbl = find_dnsxl_addr(state, rbl_domain, addr);
3145    if (!SMTPD_DNSXL_STAT_OK(rbl)) {
3146	return (SMTPD_CHECK_DUNNO);
3147    } else {
3148	return (rbl_reject_reply(state, rbl, rbl_domain, addr, reply_class));
3149    }
3150}
3151
3152/* permit_dnswl_addr - permit address in DNSWL */
3153
3154static int permit_dnswl_addr(SMTPD_STATE *state, const char *dnswl_domain,
3155			          const char *addr, const char *reply_class)
3156{
3157    const char *myname = "permit_dnswl_addr";
3158    const SMTPD_RBL_STATE *dnswl_result;
3159
3160    if (msg_verbose)
3161	msg_info("%s: %s", myname, addr);
3162
3163    /* Safety: don't whitelist unauthorized recipients. */
3164    if (strcmp(state->where, SMTPD_CMD_RCPT) == 0 && state->recipient != 0
3165      && permit_auth_destination(state, state->recipient) != SMTPD_CHECK_OK)
3166	return (SMTPD_CHECK_DUNNO);
3167
3168    dnswl_result = find_dnsxl_addr(state, dnswl_domain, addr);
3169    if (SMTPD_DNXSL_STAT_HARD(dnswl_result)) {
3170	return (SMTPD_CHECK_DUNNO);
3171    } else if (SMTPD_DNSXL_STAT_SOFT(dnswl_result)) {
3172	/* XXX: Make configurable as dnswl_tempfail_action. */
3173	DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY,
3174			 450, "4.7.1",
3175			 "<%s>: %s rejected: %s",
3176			 addr, reply_class,
3177			 "Service unavailable");
3178	return (SMTPD_CHECK_DUNNO);
3179    } else if (SMTPD_DNSXL_STAT_OK(dnswl_result)) {
3180	return (SMTPD_CHECK_OK);
3181    } else {
3182	/* Future proofing, in case find_dnsxl_addr() result is changed. */
3183	msg_panic("%s: find_dnsxl_addr API failure", myname);
3184    }
3185}
3186
3187/* find_dnsxl_domain - reject if domain in real-time blackhole list */
3188
3189static const SMTPD_RBL_STATE *find_dnsxl_domain(SMTPD_STATE *state,
3190			           const char *rbl_domain, const char *what)
3191{
3192    VSTRING *query;
3193    SMTPD_RBL_STATE *rbl;
3194    const char *domain;
3195    const char *reply_addr;
3196    const char *byte_codes;
3197    const char *suffix;
3198
3199    /*
3200     * Extract the domain, tack on the RBL domain name and query the DNS for
3201     * an A record.
3202     */
3203    if ((domain = strrchr(what, '@')) != 0) {
3204	domain += 1;
3205	if (domain[0] == '[')
3206	    return (SMTPD_CHECK_DUNNO);
3207    } else
3208	domain = what;
3209
3210    /*
3211     * XXX Some Spamhaus RHSBL rejects lookups with "No IP queries" even if
3212     * the name has an alphanumerical prefix. We play safe, and skip both
3213     * RHSBL and RHSWL queries for names ending in a numerical suffix.
3214     */
3215    if (domain[0] == 0 || valid_hostname(domain, DONT_GRIPE) == 0)
3216	return (SMTPD_CHECK_DUNNO);
3217    suffix = strrchr(domain, '.');
3218    if (alldig(suffix == 0 ? domain : suffix + 1))
3219	return (SMTPD_CHECK_DUNNO);
3220
3221    query = vstring_alloc(100);
3222    vstring_sprintf(query, "%s.%s", domain, rbl_domain);
3223    reply_addr = split_at(STR(query), '=');
3224    rbl = (SMTPD_RBL_STATE *) ctable_locate(smtpd_rbl_cache, STR(query));
3225    if (reply_addr != 0)
3226	byte_codes = ctable_locate(smtpd_rbl_byte_cache, reply_addr);
3227
3228    /*
3229     * If the record exists, match the result address.
3230     */
3231    if (SMTPD_DNSXL_STAT_OK(rbl) && reply_addr != 0
3232	&& !rbl_match_addr(rbl, byte_codes))
3233	rbl = 0;
3234    vstring_free(query);
3235    return (rbl);
3236}
3237
3238/* reject_rbl_domain - reject if domain in real-time blackhole list */
3239
3240static int reject_rbl_domain(SMTPD_STATE *state, const char *rbl_domain,
3241			          const char *what, const char *reply_class)
3242{
3243    const char *myname = "reject_rbl_domain";
3244    const SMTPD_RBL_STATE *rbl;
3245
3246    if (msg_verbose)
3247	msg_info("%s: %s %s", myname, rbl_domain, what);
3248
3249    rbl = find_dnsxl_domain(state, rbl_domain, what);
3250    if (!SMTPD_DNSXL_STAT_OK(rbl)) {
3251	return (SMTPD_CHECK_DUNNO);
3252    } else {
3253	return (rbl_reject_reply(state, rbl, rbl_domain, what, reply_class));
3254    }
3255}
3256
3257/* permit_dnswl_domain - permit domain in DNSWL */
3258
3259static int permit_dnswl_domain(SMTPD_STATE *state, const char *dnswl_domain,
3260			          const char *what, const char *reply_class)
3261{
3262    const char *myname = "permit_dnswl_domain";
3263    const SMTPD_RBL_STATE *dnswl_result;
3264
3265    if (msg_verbose)
3266	msg_info("%s: %s", myname, what);
3267
3268    /* Safety: don't whitelist unauthorized recipients. */
3269    if (strcmp(state->where, SMTPD_CMD_RCPT) == 0 && state->recipient != 0
3270      && permit_auth_destination(state, state->recipient) != SMTPD_CHECK_OK)
3271	return (SMTPD_CHECK_DUNNO);
3272
3273    dnswl_result = find_dnsxl_domain(state, dnswl_domain, what);
3274    if (SMTPD_DNXSL_STAT_HARD(dnswl_result)) {
3275	return (SMTPD_CHECK_DUNNO);
3276    } else if (SMTPD_DNSXL_STAT_SOFT(dnswl_result)) {
3277	/* XXX: Make configurable as rhswl_tempfail_action. */
3278	DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY,
3279			 450, "4.7.1",
3280			 "<%s>: %s rejected: %s",
3281			 what, reply_class,
3282			 "Service unavailable");
3283	return (SMTPD_CHECK_DUNNO);
3284    } else if (SMTPD_DNSXL_STAT_OK(dnswl_result)) {
3285	return (SMTPD_CHECK_OK);
3286    } else {
3287	/* Future proofing, in case find_dnsxl_addr() result is changed. */
3288	msg_panic("%s: find_dnsxl_addr API failure", myname);
3289    }
3290}
3291
3292/* reject_maps_rbl - reject if client address in real-time blackhole list */
3293
3294static int reject_maps_rbl(SMTPD_STATE *state)
3295{
3296    const char *myname = "reject_maps_rbl";
3297    char   *saved_domains = mystrdup(var_maps_rbl_domains);
3298    char   *bp = saved_domains;
3299    char   *rbl_domain;
3300    int     result = SMTPD_CHECK_DUNNO;
3301    static int warned;
3302
3303    if (msg_verbose)
3304	msg_info("%s: %s", myname, state->addr);
3305
3306    if (warned == 0) {
3307	warned++;
3308	msg_warn("support for restriction \"%s\" will be removed from %s; "
3309		 "use \"%s domain-name\" instead",
3310		 REJECT_MAPS_RBL, var_mail_name, REJECT_RBL_CLIENT);
3311    }
3312    while ((rbl_domain = mystrtok(&bp, RESTRICTION_SEPARATORS)) != 0) {
3313	result = reject_rbl_addr(state, rbl_domain, state->addr,
3314				 SMTPD_NAME_CLIENT);
3315	if (result != SMTPD_CHECK_DUNNO)
3316	    break;
3317    }
3318
3319    /*
3320     * Clean up.
3321     */
3322    myfree(saved_domains);
3323
3324    return (result);
3325}
3326
3327#ifdef USE_SASL_AUTH
3328
3329/* reject_auth_sender_login_mismatch - logged in client must own sender address */
3330
3331static int reject_auth_sender_login_mismatch(SMTPD_STATE *state, const char *sender)
3332{
3333    const RESOLVE_REPLY *reply;
3334    const char *owners;
3335    char   *saved_owners;
3336    char   *cp;
3337    char   *name;
3338    int     found = 0;
3339
3340    /*
3341     * Replace obscure code by self-evident code.
3342     */
3343#define SMTPD_SASL_AUTHENTICATED(state) \
3344	(smtpd_sasl_is_active(state) && state->sasl_username != 0)
3345
3346    /*
3347     * Reject if the client is logged in and does not own the sender address.
3348     */
3349    if (var_smtpd_sasl_enable && SMTPD_SASL_AUTHENTICATED(state)) {
3350	reply = smtpd_resolve_addr(sender);
3351	if (reply->flags & RESOLVE_FLAG_FAIL)
3352	    reject_dict_retry(state, sender);
3353	if ((owners = check_mail_addr_find(state, sender, smtpd_sender_login_maps,
3354				STR(reply->recipient), (char **) 0)) != 0) {
3355	    cp = saved_owners = mystrdup(owners);
3356	    while ((name = mystrtok(&cp, RESTRICTION_SEPARATORS)) != 0) {
3357		if (strcasecmp(state->sasl_username, name) == 0) {
3358		    found = 1;
3359		    break;
3360		}
3361	    }
3362	    myfree(saved_owners);
3363	}
3364	if (!found)
3365	    return (smtpd_check_reject(state, MAIL_ERROR_POLICY, 553, "5.7.1",
3366		      "<%s>: Sender address rejected: not owned by user %s",
3367				       sender, state->sasl_username));
3368    }
3369    return (SMTPD_CHECK_DUNNO);
3370}
3371
3372/* reject_unauth_sender_login_mismatch - sender requires client is logged in */
3373
3374static int reject_unauth_sender_login_mismatch(SMTPD_STATE *state, const char *sender)
3375{
3376    const RESOLVE_REPLY *reply;
3377
3378    /*
3379     * Reject if the client is not logged in and the sender address has an
3380     * owner.
3381     */
3382    if (var_smtpd_sasl_enable && !SMTPD_SASL_AUTHENTICATED(state)) {
3383	reply = smtpd_resolve_addr(sender);
3384	if (reply->flags & RESOLVE_FLAG_FAIL)
3385	    reject_dict_retry(state, sender);
3386	if (check_mail_addr_find(state, sender, smtpd_sender_login_maps,
3387				 STR(reply->recipient), (char **) 0) != 0)
3388	    return (smtpd_check_reject(state, MAIL_ERROR_POLICY, 553, "5.7.1",
3389		   "<%s>: Sender address rejected: not logged in", sender));
3390    }
3391    return (SMTPD_CHECK_DUNNO);
3392}
3393
3394#endif
3395
3396/* check_policy_service - check delegated policy service */
3397
3398static int check_policy_service(SMTPD_STATE *state, const char *server,
3399		            const char *reply_name, const char *reply_class,
3400				        const char *def_acl)
3401{
3402    static VSTRING *action = 0;
3403    ATTR_CLNT *policy_clnt;
3404
3405#ifdef USE_TLS
3406    VSTRING *subject_buf;
3407    VSTRING *issuer_buf;
3408    const char *subject;
3409    const char *issuer;
3410
3411#endif
3412    int     ret;
3413
3414    /*
3415     * Sanity check.
3416     */
3417    if (!policy_clnt_table
3418	|| (policy_clnt = (ATTR_CLNT *) htable_find(policy_clnt_table, server)) == 0)
3419	msg_panic("check_policy_service: no client endpoint for server %s",
3420		  server);
3421
3422    /*
3423     * Initialize.
3424     */
3425    if (action == 0)
3426	action = vstring_alloc(10);
3427
3428#ifdef USE_TLS
3429#define ENCODE_CN(coded_CN, coded_CN_buf, CN) do { \
3430	if (!TLS_CERT_IS_TRUSTED(state->tls_context) || *(CN) == 0) { \
3431	    coded_CN_buf = 0; \
3432	    coded_CN = ""; \
3433	} else { \
3434	    coded_CN_buf = vstring_alloc(strlen(CN) + 1); \
3435	    xtext_quote(coded_CN_buf, CN, ""); \
3436	    coded_CN = STR(coded_CN_buf); \
3437	} \
3438    } while (0);
3439
3440    ENCODE_CN(subject, subject_buf, state->tls_context->peer_CN);
3441    ENCODE_CN(issuer, issuer_buf, state->tls_context->issuer_CN);
3442#endif
3443
3444    if (attr_clnt_request(policy_clnt,
3445			  ATTR_FLAG_NONE,	/* Query attributes. */
3446			ATTR_TYPE_STR, MAIL_ATTR_REQ, "smtpd_access_policy",
3447			  ATTR_TYPE_STR, MAIL_ATTR_PROTO_STATE, state->where,
3448		   ATTR_TYPE_STR, MAIL_ATTR_ACT_PROTO_NAME, state->protocol,
3449		      ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_ADDR, state->addr,
3450		      ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_NAME, state->name,
3451			  ATTR_TYPE_STR, MAIL_ATTR_ACT_REVERSE_CLIENT_NAME,
3452			  state->reverse_name,
3453			  ATTR_TYPE_STR, MAIL_ATTR_ACT_HELO_NAME,
3454			  state->helo_name ? state->helo_name : "",
3455			  ATTR_TYPE_STR, MAIL_ATTR_SENDER,
3456			  state->sender ? state->sender : "",
3457			  ATTR_TYPE_STR, MAIL_ATTR_RECIP,
3458			  state->recipient ? state->recipient : "",
3459			  ATTR_TYPE_INT, MAIL_ATTR_RCPT_COUNT,
3460			  ((strcasecmp(state->where, SMTPD_CMD_DATA) == 0) ||
3461			 (strcasecmp(state->where, SMTPD_AFTER_DOT) == 0)) ?
3462			  state->rcpt_count : 0,
3463			  ATTR_TYPE_STR, MAIL_ATTR_QUEUEID,
3464			  state->queue_id ? state->queue_id : "",
3465			  ATTR_TYPE_STR, MAIL_ATTR_INSTANCE,
3466			  STR(state->instance),
3467			  ATTR_TYPE_LONG, MAIL_ATTR_SIZE,
3468			  (unsigned long) (state->act_size > 0 ?
3469					 state->act_size : state->msg_size),
3470			  ATTR_TYPE_STR, MAIL_ATTR_ETRN_DOMAIN,
3471			  state->etrn_name ? state->etrn_name : "",
3472			  ATTR_TYPE_STR, MAIL_ATTR_STRESS, var_stress,
3473#ifdef USE_SASL_AUTH
3474			  ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD,
3475			  smtpd_sasl_is_active(state) && state->sasl_method ?
3476			  state->sasl_method : "",
3477			  ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME,
3478		       smtpd_sasl_is_active(state) && state->sasl_username ?
3479			  state->sasl_username : "",
3480			  ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER,
3481			  smtpd_sasl_is_active(state) && state->sasl_sender ?
3482			  state->sasl_sender : "",
3483#endif
3484#ifdef USE_TLS
3485#define IF_ENCRYPTED(x, y) ((state->tls_context && ((x) != 0)) ? (x) : (y))
3486			  ATTR_TYPE_STR, MAIL_ATTR_CCERT_SUBJECT, subject,
3487			  ATTR_TYPE_STR, MAIL_ATTR_CCERT_ISSUER, issuer,
3488
3489    /*
3490     * When directly checking the fingerprint, it is OK if the issuing CA is
3491     * not trusted.
3492     */
3493			  ATTR_TYPE_STR, MAIL_ATTR_CCERT_FINGERPRINT,
3494		     IF_ENCRYPTED(state->tls_context->peer_fingerprint, ""),
3495			  ATTR_TYPE_STR, MAIL_ATTR_CRYPTO_PROTOCOL,
3496			  IF_ENCRYPTED(state->tls_context->protocol, ""),
3497			  ATTR_TYPE_STR, MAIL_ATTR_CRYPTO_CIPHER,
3498			  IF_ENCRYPTED(state->tls_context->cipher_name, ""),
3499			  ATTR_TYPE_INT, MAIL_ATTR_CRYPTO_KEYSIZE,
3500			IF_ENCRYPTED(state->tls_context->cipher_usebits, 0),
3501#endif
3502			  ATTR_TYPE_END,
3503			  ATTR_FLAG_MISSING,	/* Reply attributes. */
3504			  ATTR_TYPE_STR, MAIL_ATTR_ACTION, action,
3505			  ATTR_TYPE_END) != 1) {
3506	ret = smtpd_check_reject(state, MAIL_ERROR_POLICY,
3507				 451, "4.3.5",
3508				 "Server configuration problem");
3509    } else {
3510
3511	/*
3512	 * XXX This produces bogus error messages when the reply is
3513	 * malformed.
3514	 */
3515	ret = check_table_result(state, server, STR(action),
3516				 "policy query", reply_name,
3517				 reply_class, def_acl);
3518    }
3519#ifdef USE_TLS
3520    if (subject_buf)
3521	vstring_free(subject_buf);
3522    if (issuer_buf)
3523	vstring_free(issuer_buf);
3524#endif
3525    return (ret);
3526}
3527
3528/* is_map_command - restriction has form: check_xxx_access type:name */
3529
3530static int is_map_command(SMTPD_STATE *state, const char *name,
3531			          const char *command, char ***argp)
3532{
3533
3534    /*
3535     * This is a three-valued function: (a) this is not a check_xxx_access
3536     * command, (b) this is a malformed check_xxx_access command, (c) this is
3537     * a well-formed check_xxx_access command. That's too clumsy for function
3538     * result values, so we use regular returns for (a) and (c), and use long
3539     * jumps for the error case (b).
3540     */
3541    if (strcasecmp(name, command) != 0) {
3542	return (0);
3543    } else if (*(*argp + 1) == 0 || strchr(*(*argp += 1), ':') == 0) {
3544	msg_warn("restriction %s: bad argument \"%s\": need maptype:mapname",
3545		 command, **argp);
3546	longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
3547						    451, "4.3.5",
3548					     "Server configuration error"));
3549    } else {
3550	return (1);
3551    }
3552}
3553
3554/* forbid_whitelist - disallow whitelisting */
3555
3556static void forbid_whitelist(SMTPD_STATE *state, const char *name,
3557			             int status, const char *target)
3558{
3559    if (status == SMTPD_CHECK_OK) {
3560	msg_warn("restriction %s returns OK for %s", name, target);
3561	msg_warn("this is not allowed for security reasons");
3562	msg_warn("use DUNNO instead of OK if you want to make an exception");
3563	longjmp(smtpd_check_buf, smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
3564						    451, "4.3.5",
3565					     "Server configuration error"));
3566    }
3567}
3568
3569/* generic_checks - generic restrictions */
3570
3571static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
3572			          const char *reply_name,
3573			          const char *reply_class,
3574			          const char *def_acl)
3575{
3576    const char *myname = "generic_checks";
3577    char  **cpp;
3578    const char *name;
3579    int     status = 0;
3580    ARGV   *list;
3581    int     found;
3582    int     saved_recursion = state->recursion++;
3583
3584    if (msg_verbose)
3585	msg_info(">>> START %s RESTRICTIONS <<<", reply_class);
3586
3587    for (cpp = restrictions->argv; (name = *cpp) != 0; cpp++) {
3588
3589	if (state->discard != 0)
3590	    break;
3591
3592	if (msg_verbose)
3593	    msg_info("%s: name=%s", myname, name);
3594
3595	/*
3596	 * Pseudo restrictions.
3597	 */
3598	if (strcasecmp(name, WARN_IF_REJECT) == 0) {
3599	    if (state->warn_if_reject == 0)
3600		state->warn_if_reject = state->recursion;
3601	    continue;
3602	}
3603
3604	/*
3605	 * Spoof the is_map_command() routine, so that we do not have to make
3606	 * special cases for the implicit short-hand access map notation.
3607	 */
3608#define NO_DEF_ACL	0
3609
3610	if (strchr(name, ':') != 0) {
3611	    if (def_acl == NO_DEF_ACL) {
3612		msg_warn("specify one of (%s, %s, %s, %s, %s, %s) before %s restriction \"%s\"",
3613			 CHECK_CLIENT_ACL, CHECK_REVERSE_CLIENT_ACL, CHECK_HELO_ACL, CHECK_SENDER_ACL,
3614			 CHECK_RECIP_ACL, CHECK_ETRN_ACL, reply_class, name);
3615		longjmp(smtpd_check_buf,
3616			smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
3617					   451, "4.3.5",
3618					   "Server configuration error"));
3619	    }
3620	    name = def_acl;
3621	    cpp -= 1;
3622	}
3623
3624	/*
3625	 * Generic restrictions.
3626	 */
3627	if (strcasecmp(name, PERMIT_ALL) == 0) {
3628	    status = SMTPD_CHECK_OK;
3629	    if (cpp[1] != 0 && state->warn_if_reject == 0)
3630		msg_warn("restriction `%s' after `%s' is ignored",
3631			 cpp[1], PERMIT_ALL);
3632	} else if (strcasecmp(name, DEFER_ALL) == 0) {
3633	    status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
3634					var_defer_code, "4.3.2",
3635					"<%s>: %s rejected: Try again later",
3636					reply_name, reply_class);
3637	    if (cpp[1] != 0 && state->warn_if_reject == 0)
3638		msg_warn("restriction `%s' after `%s' is ignored",
3639			 cpp[1], DEFER_ALL);
3640	} else if (strcasecmp(name, REJECT_ALL) == 0) {
3641	    status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
3642					var_reject_code, "5.7.1",
3643					"<%s>: %s rejected: Access denied",
3644					reply_name, reply_class);
3645	    if (cpp[1] != 0 && state->warn_if_reject == 0)
3646		msg_warn("restriction `%s' after `%s' is ignored",
3647			 cpp[1], REJECT_ALL);
3648	} else if (strcasecmp(name, REJECT_UNAUTH_PIPE) == 0) {
3649	    status = reject_unauth_pipelining(state, reply_name, reply_class);
3650	} else if (strcasecmp(name, CHECK_POLICY_SERVICE) == 0) {
3651	    if (cpp[1] == 0 || strchr(cpp[1], ':') == 0) {
3652		msg_warn("restriction %s must be followed by transport:server",
3653			 CHECK_POLICY_SERVICE);
3654		longjmp(smtpd_check_buf,
3655			smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
3656					   451, "4.3.5",
3657					   "Server configuration error"));
3658	    } else
3659		status = check_policy_service(state, *++cpp, reply_name,
3660					      reply_class, def_acl);
3661	} else if (strcasecmp(name, DEFER_IF_PERMIT) == 0) {
3662	    status = DEFER_IF_PERMIT2(DEFER_EXPLICIT, state, MAIL_ERROR_POLICY,
3663				      450, "4.7.0",
3664			     "<%s>: %s rejected: defer_if_permit requested",
3665				      reply_name, reply_class);
3666	} else if (strcasecmp(name, DEFER_IF_REJECT) == 0) {
3667	    DEFER_IF_REJECT2(state, MAIL_ERROR_POLICY,
3668			     450, "4.7.0",
3669			     "<%s>: %s rejected: defer_if_reject requested",
3670			     reply_name, reply_class);
3671	} else if (strcasecmp(name, SLEEP) == 0) {
3672	    if (cpp[1] == 0 || alldig(cpp[1]) == 0) {
3673		msg_warn("restriction %s must be followed by number", SLEEP);
3674		longjmp(smtpd_check_buf,
3675			smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
3676					   451, "4.3.5",
3677					   "Server configuration error"));
3678	    } else
3679		sleep(atoi(*++cpp));
3680	} else if (strcasecmp(name, REJECT_PLAINTEXT_SESSION) == 0) {
3681	    status = reject_plaintext_session(state);
3682	}
3683
3684	/*
3685	 * Client name/address restrictions.
3686	 */
3687	else if (strcasecmp(name, REJECT_UNKNOWN_CLIENT_HOSTNAME) == 0
3688		 || strcasecmp(name, REJECT_UNKNOWN_CLIENT) == 0) {
3689	    status = reject_unknown_client(state);
3690	} else if (strcasecmp(name, REJECT_UNKNOWN_REVERSE_HOSTNAME) == 0) {
3691	    status = reject_unknown_reverse_name(state);
3692	} else if (strcasecmp(name, PERMIT_INET_INTERFACES) == 0) {
3693	    status = permit_inet_interfaces(state);
3694	} else if (strcasecmp(name, PERMIT_MYNETWORKS) == 0) {
3695	    status = permit_mynetworks(state);
3696	} else if (is_map_command(state, name, CHECK_CLIENT_ACL, &cpp)) {
3697	    status = check_namadr_access(state, *cpp, state->name, state->addr,
3698					 FULL, &found, state->namaddr,
3699					 SMTPD_NAME_CLIENT, def_acl);
3700	} else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_ACL, &cpp)) {
3701	    status = check_namadr_access(state, *cpp, state->reverse_name, state->addr,
3702					 FULL, &found, state->namaddr,
3703					 SMTPD_NAME_REV_CLIENT, def_acl);
3704	    forbid_whitelist(state, name, status, state->reverse_name);
3705	} else if (strcasecmp(name, REJECT_MAPS_RBL) == 0) {
3706	    status = reject_maps_rbl(state);
3707	} else if (strcasecmp(name, REJECT_RBL_CLIENT) == 0
3708		   || strcasecmp(name, REJECT_RBL) == 0) {
3709	    if (cpp[1] == 0)
3710		msg_warn("restriction %s requires domain name argument", name);
3711	    else
3712		status = reject_rbl_addr(state, *(cpp += 1), state->addr,
3713					 SMTPD_NAME_CLIENT);
3714	} else if (strcasecmp(name, PERMIT_DNSWL_CLIENT) == 0) {
3715	    if (cpp[1] == 0)
3716		msg_warn("restriction %s requires domain name argument", name);
3717	    else
3718		status = permit_dnswl_addr(state, *(cpp += 1), state->addr,
3719					   SMTPD_NAME_CLIENT);
3720	} else if (strcasecmp(name, REJECT_RHSBL_CLIENT) == 0) {
3721	    if (cpp[1] == 0)
3722		msg_warn("restriction %s requires domain name argument",
3723			 name);
3724	    else {
3725		cpp += 1;
3726		if (strcasecmp(state->name, "unknown") != 0)
3727		    status = reject_rbl_domain(state, *cpp, state->name,
3728					       SMTPD_NAME_CLIENT);
3729	    }
3730	} else if (strcasecmp(name, PERMIT_RHSWL_CLIENT) == 0) {
3731	    if (cpp[1] == 0)
3732		msg_warn("restriction %s requires domain name argument",
3733			 name);
3734	    else {
3735		cpp += 1;
3736		if (strcasecmp(state->name, "unknown") != 0)
3737		    status = permit_dnswl_domain(state, *cpp, state->name,
3738						 SMTPD_NAME_CLIENT);
3739	    }
3740	} else if (strcasecmp(name, REJECT_RHSBL_REVERSE_CLIENT) == 0) {
3741	    if (cpp[1] == 0)
3742		msg_warn("restriction %s requires domain name argument",
3743			 name);
3744	    else {
3745		cpp += 1;
3746		if (strcasecmp(state->reverse_name, "unknown") != 0)
3747		    status = reject_rbl_domain(state, *cpp, state->reverse_name,
3748					       SMTPD_NAME_REV_CLIENT);
3749	    }
3750	} else if (is_map_command(state, name, CHECK_CCERT_ACL, &cpp)) {
3751	    status = check_ccert_access(state, *cpp, def_acl);
3752	} else if (is_map_command(state, name, CHECK_CLIENT_NS_ACL, &cpp)) {
3753	    if (strcasecmp(state->name, "unknown") != 0) {
3754		status = check_server_access(state, *cpp, state->name,
3755					     T_NS, state->namaddr,
3756					     SMTPD_NAME_CLIENT, def_acl);
3757		forbid_whitelist(state, name, status, state->name);
3758	    }
3759	} else if (is_map_command(state, name, CHECK_CLIENT_MX_ACL, &cpp)) {
3760	    if (strcasecmp(state->name, "unknown") != 0) {
3761		status = check_server_access(state, *cpp, state->name,
3762					     T_MX, state->namaddr,
3763					     SMTPD_NAME_CLIENT, def_acl);
3764		forbid_whitelist(state, name, status, state->name);
3765	    }
3766	} else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_NS_ACL, &cpp)) {
3767	    if (strcasecmp(state->reverse_name, "unknown") != 0) {
3768		status = check_server_access(state, *cpp, state->reverse_name,
3769					     T_NS, state->namaddr,
3770					     SMTPD_NAME_REV_CLIENT, def_acl);
3771		forbid_whitelist(state, name, status, state->reverse_name);
3772	    }
3773	} else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_MX_ACL, &cpp)) {
3774	    if (strcasecmp(state->reverse_name, "unknown") != 0) {
3775		status = check_server_access(state, *cpp, state->reverse_name,
3776					     T_MX, state->namaddr,
3777					     SMTPD_NAME_REV_CLIENT, def_acl);
3778		forbid_whitelist(state, name, status, state->reverse_name);
3779	    }
3780	}
3781
3782	/*
3783	 * HELO/EHLO parameter restrictions.
3784	 */
3785	else if (is_map_command(state, name, CHECK_HELO_ACL, &cpp)) {
3786	    if (state->helo_name)
3787		status = check_domain_access(state, *cpp, state->helo_name,
3788					     FULL, &found, state->helo_name,
3789					     SMTPD_NAME_HELO, def_acl);
3790	} else if (strcasecmp(name, REJECT_INVALID_HELO_HOSTNAME) == 0
3791		   || strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) {
3792	    if (state->helo_name) {
3793		if (*state->helo_name != '[')
3794		    status = reject_invalid_hostname(state, state->helo_name,
3795					 state->helo_name, SMTPD_NAME_HELO);
3796		else
3797		    status = reject_invalid_hostaddr(state, state->helo_name,
3798					 state->helo_name, SMTPD_NAME_HELO);
3799	    }
3800	} else if (strcasecmp(name, REJECT_UNKNOWN_HELO_HOSTNAME) == 0
3801		   || strcasecmp(name, REJECT_UNKNOWN_HOSTNAME) == 0) {
3802	    if (state->helo_name) {
3803		if (*state->helo_name != '[')
3804		    status = reject_unknown_hostname(state, state->helo_name,
3805					 state->helo_name, SMTPD_NAME_HELO);
3806		else
3807		    status = reject_invalid_hostaddr(state, state->helo_name,
3808					 state->helo_name, SMTPD_NAME_HELO);
3809	    }
3810	} else if (strcasecmp(name, PERMIT_NAKED_IP_ADDR) == 0) {
3811	    msg_warn("restriction %s is deprecated. Use %s instead",
3812		     PERMIT_NAKED_IP_ADDR, PERMIT_MYNETWORKS);
3813	    if (state->helo_name) {
3814		if (state->helo_name[strspn(state->helo_name, "0123456789.:")] == 0
3815		&& (status = reject_invalid_hostaddr(state, state->helo_name,
3816				   state->helo_name, SMTPD_NAME_HELO)) == 0)
3817		    status = SMTPD_CHECK_OK;
3818	    }
3819	} else if (is_map_command(state, name, CHECK_HELO_NS_ACL, &cpp)) {
3820	    if (state->helo_name) {
3821		status = check_server_access(state, *cpp, state->helo_name,
3822					     T_NS, state->helo_name,
3823					     SMTPD_NAME_HELO, def_acl);
3824		forbid_whitelist(state, name, status, state->helo_name);
3825	    }
3826	} else if (is_map_command(state, name, CHECK_HELO_MX_ACL, &cpp)) {
3827	    if (state->helo_name) {
3828		status = check_server_access(state, *cpp, state->helo_name,
3829					     T_MX, state->helo_name,
3830					     SMTPD_NAME_HELO, def_acl);
3831		forbid_whitelist(state, name, status, state->helo_name);
3832	    }
3833	} else if (strcasecmp(name, REJECT_NON_FQDN_HELO_HOSTNAME) == 0
3834		   || strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
3835	    if (state->helo_name) {
3836		if (*state->helo_name != '[')
3837		    status = reject_non_fqdn_hostname(state, state->helo_name,
3838					 state->helo_name, SMTPD_NAME_HELO);
3839		else
3840		    status = reject_invalid_hostaddr(state, state->helo_name,
3841					 state->helo_name, SMTPD_NAME_HELO);
3842	    }
3843	} else if (strcasecmp(name, REJECT_RHSBL_HELO) == 0) {
3844	    if (cpp[1] == 0)
3845		msg_warn("restriction %s requires domain name argument",
3846			 name);
3847	    else {
3848		cpp += 1;
3849		if (state->helo_name)
3850		    status = reject_rbl_domain(state, *cpp, state->helo_name,
3851					       SMTPD_NAME_HELO);
3852	    }
3853	}
3854
3855	/*
3856	 * Sender mail address restrictions.
3857	 */
3858	else if (is_map_command(state, name, CHECK_SENDER_ACL, &cpp)) {
3859	    if (state->sender && *state->sender)
3860		status = check_mail_access(state, *cpp, state->sender,
3861					   &found, state->sender,
3862					   SMTPD_NAME_SENDER, def_acl);
3863	    if (state->sender && !*state->sender)
3864		status = check_access(state, *cpp, var_smtpd_null_key, FULL,
3865				      &found, state->sender,
3866				      SMTPD_NAME_SENDER, def_acl);
3867	} else if (strcasecmp(name, REJECT_UNKNOWN_ADDRESS) == 0) {
3868	    if (state->sender && *state->sender)
3869		status = reject_unknown_address(state, state->sender,
3870					  state->sender, SMTPD_NAME_SENDER);
3871	} else if (strcasecmp(name, REJECT_UNKNOWN_SENDDOM) == 0) {
3872	    if (state->sender && *state->sender)
3873		status = reject_unknown_address(state, state->sender,
3874					  state->sender, SMTPD_NAME_SENDER);
3875	} else if (strcasecmp(name, REJECT_UNVERIFIED_SENDER) == 0) {
3876	    if (state->sender && *state->sender)
3877		status = reject_unverified_address(state, state->sender,
3878					   state->sender, SMTPD_NAME_SENDER,
3879				     var_unv_from_dcode, var_unv_from_rcode,
3880						   unv_from_tf_act,
3881						   var_unv_from_why);
3882	} else if (strcasecmp(name, REJECT_NON_FQDN_SENDER) == 0) {
3883	    if (state->sender && *state->sender)
3884		status = reject_non_fqdn_address(state, state->sender,
3885					  state->sender, SMTPD_NAME_SENDER);
3886	} else if (strcasecmp(name, REJECT_AUTH_SENDER_LOGIN_MISMATCH) == 0) {
3887#ifdef USE_SASL_AUTH
3888	    if (var_smtpd_sasl_enable) {
3889		if (state->sender && *state->sender)
3890		    status = reject_auth_sender_login_mismatch(state, state->sender);
3891	    } else
3892#endif
3893		msg_warn("restriction `%s' ignored: no SASL support", name);
3894	} else if (strcasecmp(name, REJECT_UNAUTH_SENDER_LOGIN_MISMATCH) == 0) {
3895#ifdef USE_SASL_AUTH
3896	    if (var_smtpd_sasl_enable) {
3897		if (state->sender && *state->sender)
3898		    status = reject_unauth_sender_login_mismatch(state, state->sender);
3899	    } else
3900#endif
3901		msg_warn("restriction `%s' ignored: no SASL support", name);
3902	} else if (is_map_command(state, name, CHECK_SENDER_NS_ACL, &cpp)) {
3903	    if (state->sender && *state->sender) {
3904		status = check_server_access(state, *cpp, state->sender,
3905					     T_NS, state->sender,
3906					     SMTPD_NAME_SENDER, def_acl);
3907		forbid_whitelist(state, name, status, state->sender);
3908	    }
3909	} else if (is_map_command(state, name, CHECK_SENDER_MX_ACL, &cpp)) {
3910	    if (state->sender && *state->sender) {
3911		status = check_server_access(state, *cpp, state->sender,
3912					     T_MX, state->sender,
3913					     SMTPD_NAME_SENDER, def_acl);
3914		forbid_whitelist(state, name, status, state->sender);
3915	    }
3916	} else if (strcasecmp(name, REJECT_RHSBL_SENDER) == 0) {
3917	    if (cpp[1] == 0)
3918		msg_warn("restriction %s requires domain name argument", name);
3919	    else {
3920		cpp += 1;
3921		if (state->sender && *state->sender)
3922		    status = reject_rbl_domain(state, *cpp, state->sender,
3923					       SMTPD_NAME_SENDER);
3924	    }
3925	} else if (strcasecmp(name, REJECT_UNLISTED_SENDER) == 0) {
3926	    if (state->sender && *state->sender)
3927		status = check_sender_rcpt_maps(state, state->sender);
3928	}
3929
3930	/*
3931	 * Recipient mail address restrictions.
3932	 */
3933	else if (is_map_command(state, name, CHECK_RECIP_ACL, &cpp)) {
3934	    if (state->recipient)
3935		status = check_mail_access(state, *cpp, state->recipient,
3936					   &found, state->recipient,
3937					   SMTPD_NAME_RECIPIENT, def_acl);
3938	} else if (strcasecmp(name, PERMIT_MX_BACKUP) == 0) {
3939	    if (state->recipient)
3940		status = permit_mx_backup(state, state->recipient,
3941				    state->recipient, SMTPD_NAME_RECIPIENT);
3942	} else if (strcasecmp(name, PERMIT_AUTH_DEST) == 0) {
3943	    if (state->recipient)
3944		status = permit_auth_destination(state, state->recipient);
3945	} else if (strcasecmp(name, REJECT_UNAUTH_DEST) == 0) {
3946	    if (state->recipient)
3947		status = reject_unauth_destination(state, state->recipient);
3948	} else if (strcasecmp(name, CHECK_RELAY_DOMAINS) == 0) {
3949	    if (state->recipient)
3950		status = check_relay_domains(state, state->recipient,
3951				    state->recipient, SMTPD_NAME_RECIPIENT);
3952	    if (cpp[1] != 0 && state->warn_if_reject == 0)
3953		msg_warn("restriction `%s' after `%s' is ignored",
3954			 cpp[1], CHECK_RELAY_DOMAINS);
3955	} else if (strcasecmp(name, PERMIT_SASL_AUTH) == 0) {
3956#ifdef USE_SASL_AUTH
3957	    if (smtpd_sasl_is_active(state))
3958		status = permit_sasl_auth(state,
3959					  SMTPD_CHECK_OK, SMTPD_CHECK_DUNNO);
3960#endif
3961	} else if (strcasecmp(name, PERMIT_TLS_ALL_CLIENTCERTS) == 0) {
3962	    status = permit_tls_clientcerts(state, 1);
3963	} else if (strcasecmp(name, PERMIT_TLS_CLIENTCERTS) == 0) {
3964	    status = permit_tls_clientcerts(state, 0);
3965	} else if (strcasecmp(name, REJECT_UNKNOWN_RCPTDOM) == 0) {
3966	    if (state->recipient)
3967		status = reject_unknown_address(state, state->recipient,
3968				    state->recipient, SMTPD_NAME_RECIPIENT);
3969	} else if (strcasecmp(name, REJECT_NON_FQDN_RCPT) == 0) {
3970	    if (state->recipient)
3971		status = reject_non_fqdn_address(state, state->recipient,
3972				    state->recipient, SMTPD_NAME_RECIPIENT);
3973	} else if (is_map_command(state, name, CHECK_RECIP_NS_ACL, &cpp)) {
3974	    if (state->recipient && *state->recipient) {
3975		status = check_server_access(state, *cpp, state->recipient,
3976					     T_NS, state->recipient,
3977					     SMTPD_NAME_RECIPIENT, def_acl);
3978		forbid_whitelist(state, name, status, state->recipient);
3979	    }
3980	} else if (is_map_command(state, name, CHECK_RECIP_MX_ACL, &cpp)) {
3981	    if (state->recipient && *state->recipient) {
3982		status = check_server_access(state, *cpp, state->recipient,
3983					     T_MX, state->recipient,
3984					     SMTPD_NAME_RECIPIENT, def_acl);
3985		forbid_whitelist(state, name, status, state->recipient);
3986	    }
3987	} else if (strcasecmp(name, REJECT_RHSBL_RECIPIENT) == 0) {
3988	    if (cpp[1] == 0)
3989		msg_warn("restriction %s requires domain name argument", name);
3990	    else {
3991		cpp += 1;
3992		if (state->recipient)
3993		    status = reject_rbl_domain(state, *cpp, state->recipient,
3994					       SMTPD_NAME_RECIPIENT);
3995	    }
3996	} else if (strcasecmp(name, CHECK_RCPT_MAPS) == 0
3997		   || strcasecmp(name, REJECT_UNLISTED_RCPT) == 0) {
3998	    if (state->recipient && *state->recipient)
3999		status = check_recipient_rcpt_maps(state, state->recipient);
4000	} else if (strcasecmp(name, REJECT_MUL_RCPT_BOUNCE) == 0) {
4001	    if (state->sender && *state->sender == 0 && state->rcpt_count
4002		> (strcmp(state->where, SMTPD_CMD_DATA) ? 0 : 1))
4003		status = smtpd_check_reject(state, MAIL_ERROR_POLICY,
4004					    var_mul_rcpt_code, "5.5.3",
4005				"<%s>: %s rejected: Multi-recipient bounce",
4006					    reply_name, reply_class);
4007	} else if (strcasecmp(name, REJECT_UNVERIFIED_RECIP) == 0) {
4008	    if (state->recipient && *state->recipient)
4009		status = reject_unverified_address(state, state->recipient,
4010				     state->recipient, SMTPD_NAME_RECIPIENT,
4011				     var_unv_rcpt_dcode, var_unv_rcpt_rcode,
4012						   unv_rcpt_tf_act,
4013						   var_unv_rcpt_why);
4014	}
4015
4016	/*
4017	 * ETRN domain name restrictions.
4018	 */
4019	else if (is_map_command(state, name, CHECK_ETRN_ACL, &cpp)) {
4020	    if (state->etrn_name)
4021		status = check_domain_access(state, *cpp, state->etrn_name,
4022					     FULL, &found, state->etrn_name,
4023					     SMTPD_NAME_ETRN, def_acl);
4024	}
4025
4026	/*
4027	 * User-defined restriction class.
4028	 */
4029	else if ((list = (ARGV *) htable_find(smtpd_rest_classes, name)) != 0) {
4030	    status = generic_checks(state, list, reply_name,
4031				    reply_class, def_acl);
4032	}
4033
4034	/*
4035	 * Error: undefined restriction name.
4036	 */
4037	else {
4038	    msg_warn("unknown smtpd restriction: \"%s\"", name);
4039	    longjmp(smtpd_check_buf,
4040		    smtpd_check_reject(state, MAIL_ERROR_SOFTWARE,
4041				       451, "4.3.5",
4042				       "Server configuration error"));
4043	}
4044	if (msg_verbose)
4045	    msg_info("%s: name=%s status=%d", myname, name, status);
4046
4047	if (state->warn_if_reject >= state->recursion)
4048	    state->warn_if_reject = 0;
4049
4050	if (status != 0)
4051	    break;
4052
4053	if (state->defer_if_permit.active && state->defer_if_reject.active)
4054	    break;
4055    }
4056    if (msg_verbose && name == 0)
4057	msg_info(">>> END %s RESTRICTIONS <<<", reply_class);
4058
4059    state->recursion = saved_recursion;
4060
4061    return (status);
4062}
4063
4064/* smtpd_check_addr - address sanity check */
4065
4066int     smtpd_check_addr(const char *addr)
4067{
4068    const RESOLVE_REPLY *resolve_reply;
4069    const char *myname = "smtpd_check_addr";
4070
4071    if (msg_verbose)
4072	msg_info("%s: addr=%s", myname, addr);
4073
4074    /*
4075     * Catch syntax errors early on if we can, but be prepared to re-compute
4076     * the result later when the cache fills up with lots of recipients, at
4077     * which time errors can still happen.
4078     */
4079    if (addr == 0 || *addr == 0)
4080	return (0);
4081    resolve_reply = smtpd_resolve_addr(addr);
4082    if (resolve_reply->flags & RESOLVE_FLAG_ERROR)
4083	return (-1);
4084    return (0);
4085}
4086
4087/* smtpd_check_rewrite - choose address qualification context */
4088
4089void    smtpd_check_rewrite(SMTPD_STATE *state)
4090{
4091    const char *myname = "smtpd_check_rewrite";
4092    int     status;
4093    char  **cpp;
4094    DICT   *dict;
4095    char   *name;
4096
4097    /*
4098     * We don't use generic_checks() because it produces results that aren't
4099     * applicable such as DEFER or REJECT.
4100     */
4101    for (cpp = local_rewrite_clients->argv; *cpp != 0; cpp++) {
4102	if (msg_verbose)
4103	    msg_info("%s: trying: %s", myname, *cpp);
4104	status = SMTPD_CHECK_DUNNO;
4105	if (strchr(name = *cpp, ':') != 0) {
4106	    name = CHECK_ADDR_MAP;
4107	    cpp -= 1;
4108	}
4109	if (strcasecmp(name, PERMIT_INET_INTERFACES) == 0) {
4110	    status = permit_inet_interfaces(state);
4111	} else if (strcasecmp(name, PERMIT_MYNETWORKS) == 0) {
4112	    status = permit_mynetworks(state);
4113	} else if (is_map_command(state, name, CHECK_ADDR_MAP, &cpp)) {
4114	    if ((dict = dict_handle(*cpp)) == 0)
4115		msg_panic("%s: dictionary not found: %s", myname, *cpp);
4116	    if (dict_get(dict, state->addr) != 0)
4117		status = SMTPD_CHECK_OK;
4118	} else if (strcasecmp(name, PERMIT_SASL_AUTH) == 0) {
4119#ifdef USE_SASL_AUTH
4120	    if (smtpd_sasl_is_active(state))
4121		status = permit_sasl_auth(state, SMTPD_CHECK_OK,
4122					  SMTPD_CHECK_DUNNO);
4123#endif
4124	} else if (strcasecmp(name, PERMIT_TLS_ALL_CLIENTCERTS) == 0) {
4125	    status = permit_tls_clientcerts(state, 1);
4126	} else if (strcasecmp(name, PERMIT_TLS_CLIENTCERTS) == 0) {
4127	    status = permit_tls_clientcerts(state, 0);
4128	} else {
4129	    msg_warn("parameter %s: invalid request: %s",
4130		     VAR_LOC_RWR_CLIENTS, name);
4131	    continue;
4132	}
4133	if (status == SMTPD_CHECK_OK) {
4134	    state->rewrite_context = MAIL_ATTR_RWR_LOCAL;
4135	    return;
4136	}
4137    }
4138    state->rewrite_context = MAIL_ATTR_RWR_REMOTE;
4139}
4140
4141/* smtpd_check_client - validate client name or address */
4142
4143char   *smtpd_check_client(SMTPD_STATE *state)
4144{
4145    int     status;
4146
4147    /*
4148     * Initialize.
4149     */
4150    if (state->name == 0 || state->addr == 0)
4151	return (0);
4152
4153#define SMTPD_CHECK_RESET() { \
4154	state->recursion = 0; \
4155	state->warn_if_reject = 0; \
4156	state->defer_if_reject.active = 0; \
4157    }
4158
4159    /*
4160     * Reset the defer_if_permit flag.
4161     */
4162    state->defer_if_permit.active = 0;
4163
4164    /*
4165     * Apply restrictions in the order as specified.
4166     */
4167    SMTPD_CHECK_RESET();
4168    status = setjmp(smtpd_check_buf);
4169    if (status == 0 && client_restrctions->argc)
4170	status = generic_checks(state, client_restrctions, state->namaddr,
4171				SMTPD_NAME_CLIENT, CHECK_CLIENT_ACL);
4172    state->defer_if_permit_client = state->defer_if_permit.active;
4173
4174    return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
4175}
4176
4177/* smtpd_check_helo - validate HELO hostname */
4178
4179char   *smtpd_check_helo(SMTPD_STATE *state, char *helohost)
4180{
4181    int     status;
4182    char   *saved_helo;
4183
4184    /*
4185     * Initialize.
4186     */
4187    if (helohost == 0)
4188	return (0);
4189
4190    /*
4191     * Minor kluge so that we can delegate work to the generic routine and so
4192     * that we can syslog the recipient with the reject messages.
4193     */
4194#define SMTPD_CHECK_PUSH(backup, current, new) { \
4195	backup = current; \
4196	current = (new ? mystrdup(new) : 0); \
4197    }
4198
4199#define SMTPD_CHECK_POP(current, backup) { \
4200	if (current) myfree(current); \
4201	current = backup; \
4202    }
4203
4204    SMTPD_CHECK_PUSH(saved_helo, state->helo_name, helohost);
4205
4206#define SMTPD_CHECK_HELO_RETURN(x) { \
4207	SMTPD_CHECK_POP(state->helo_name, saved_helo); \
4208	return (x); \
4209    }
4210
4211    /*
4212     * Restore the defer_if_permit flag to its value before HELO/EHLO, and do
4213     * not set the flag when it was already raised by a previous protocol
4214     * stage.
4215     */
4216    state->defer_if_permit.active = state->defer_if_permit_client;
4217
4218    /*
4219     * Apply restrictions in the order as specified.
4220     */
4221    SMTPD_CHECK_RESET();
4222    status = setjmp(smtpd_check_buf);
4223    if (status == 0 && helo_restrctions->argc)
4224	status = generic_checks(state, helo_restrctions, state->helo_name,
4225				SMTPD_NAME_HELO, CHECK_HELO_ACL);
4226    state->defer_if_permit_helo = state->defer_if_permit.active;
4227
4228    SMTPD_CHECK_HELO_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
4229}
4230
4231/* smtpd_check_mail - validate sender address, driver */
4232
4233char   *smtpd_check_mail(SMTPD_STATE *state, char *sender)
4234{
4235    int     status;
4236    char   *saved_sender;
4237
4238    /*
4239     * Initialize.
4240     */
4241    if (sender == 0)
4242	return (0);
4243
4244    /*
4245     * Minor kluge so that we can delegate work to the generic routine and so
4246     * that we can syslog the recipient with the reject messages.
4247     */
4248    SMTPD_CHECK_PUSH(saved_sender, state->sender, sender);
4249
4250#define SMTPD_CHECK_MAIL_RETURN(x) { \
4251	SMTPD_CHECK_POP(state->sender, saved_sender); \
4252	return (x); \
4253    }
4254
4255    /*
4256     * Restore the defer_if_permit flag to its value before MAIL FROM, and do
4257     * not set the flag when it was already raised by a previous protocol
4258     * stage. The client may skip the helo/ehlo.
4259     */
4260    state->defer_if_permit.active = state->defer_if_permit_client
4261	| state->defer_if_permit_helo;
4262    state->sender_rcptmap_checked = 0;
4263
4264    /*
4265     * Apply restrictions in the order as specified.
4266     */
4267    SMTPD_CHECK_RESET();
4268    status = setjmp(smtpd_check_buf);
4269    if (status == 0 && mail_restrctions->argc)
4270	status = generic_checks(state, mail_restrctions, sender,
4271				SMTPD_NAME_SENDER, CHECK_SENDER_ACL);
4272    state->defer_if_permit_sender = state->defer_if_permit.active;
4273
4274    /*
4275     * If the "reject_unlisted_sender" restriction still needs to be applied,
4276     * validate the sender here.
4277     */
4278    if (var_smtpd_rej_unl_from
4279	&& status != SMTPD_CHECK_REJECT && state->sender_rcptmap_checked == 0
4280	&& state->discard == 0 && *sender)
4281	status = check_sender_rcpt_maps(state, sender);
4282
4283    SMTPD_CHECK_MAIL_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
4284}
4285
4286/* smtpd_check_rcpt - validate recipient address, driver */
4287
4288char   *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient)
4289{
4290    int     status;
4291    char   *saved_recipient;
4292    char   *err;
4293    static VSTRING *canon_verify_sender;
4294
4295    /*
4296     * Initialize.
4297     */
4298    if (recipient == 0)
4299	return (0);
4300
4301    /*
4302     * XXX 2821: Section 3.6 requires that "postmaster" be accepted even when
4303     * specified without a fully qualified domain name.
4304     */
4305    if (strcasecmp(recipient, "postmaster") == 0)
4306	return (0);
4307
4308    /*
4309     * XXX Always say OK when we're probed with our own address verification
4310     * sender address. Otherwise, some timeout or some UCE block may result
4311     * in mutual negative caching, making it painful to get the mail through.
4312     */
4313#ifndef TEST
4314    if (*recipient) {
4315	if (canon_verify_sender == 0) {
4316	    canon_verify_sender = vstring_alloc(10);
4317	    rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL,
4318				  var_verify_sender,
4319				  canon_verify_sender);
4320	}
4321	if (strcasecmp(STR(canon_verify_sender), recipient) == 0)
4322	    return (0);
4323    }
4324#endif
4325
4326    /*
4327     * Minor kluge so that we can delegate work to the generic routine and so
4328     * that we can syslog the recipient with the reject messages.
4329     */
4330    SMTPD_CHECK_PUSH(saved_recipient, state->recipient, recipient);
4331
4332#define SMTPD_CHECK_RCPT_RETURN(x) { \
4333	SMTPD_CHECK_POP(state->recipient, saved_recipient); \
4334	return (x); \
4335    }
4336
4337    /*
4338     * The "check_recipient_maps" restriction is relevant only when
4339     * responding to RCPT TO or VRFY.
4340     */
4341    state->recipient_rcptmap_checked = 0;
4342
4343    /*
4344     * Apply delayed restrictions.
4345     */
4346    if (var_smtpd_delay_reject)
4347	if ((err = smtpd_check_client(state)) != 0
4348	    || (err = smtpd_check_helo(state, state->helo_name)) != 0
4349	    || (err = smtpd_check_mail(state, state->sender)) != 0)
4350	    SMTPD_CHECK_RCPT_RETURN(err);
4351
4352    /*
4353     * Restore the defer_if_permit flag to its value before RCPT TO, and do
4354     * not set the flag when it was already raised by a previous protocol
4355     * stage.
4356     */
4357    state->defer_if_permit.active = state->defer_if_permit_sender;
4358
4359    /*
4360     * Apply restrictions in the order as specified.
4361     */
4362    SMTPD_CHECK_RESET();
4363    status = setjmp(smtpd_check_buf);
4364    if (status == 0 && rcpt_restrctions->argc)
4365	status = generic_checks(state, rcpt_restrctions,
4366			  recipient, SMTPD_NAME_RECIPIENT, CHECK_RECIP_ACL);
4367
4368    /*
4369     * Force permission into deferral when some earlier temporary error may
4370     * have prevented us from rejecting mail, and report the earlier problem.
4371     */
4372    if (status != SMTPD_CHECK_REJECT && state->defer_if_permit.active)
4373	status = smtpd_check_reject(state, state->defer_if_permit.class,
4374				    state->defer_if_permit.code,
4375				    STR(state->defer_if_permit.dsn),
4376				  "%s", STR(state->defer_if_permit.reason));
4377
4378    /*
4379     * If the "reject_unlisted_recipient" restriction still needs to be
4380     * applied, validate the recipient here.
4381     */
4382    if (var_smtpd_rej_unl_rcpt
4383	&& status != SMTPD_CHECK_REJECT
4384	&& state->recipient_rcptmap_checked == 0
4385	&& state->discard == 0)
4386	status = check_recipient_rcpt_maps(state, recipient);
4387
4388    SMTPD_CHECK_RCPT_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
4389}
4390
4391/* smtpd_check_etrn - validate ETRN request */
4392
4393char   *smtpd_check_etrn(SMTPD_STATE *state, char *domain)
4394{
4395    int     status;
4396    char   *saved_etrn_name;
4397    char   *err;
4398
4399    /*
4400     * Initialize.
4401     */
4402    if (domain == 0)
4403	return (0);
4404
4405    /*
4406     * Minor kluge so that we can delegate work to the generic routine and so
4407     * that we can syslog the recipient with the reject messages.
4408     */
4409    SMTPD_CHECK_PUSH(saved_etrn_name, state->etrn_name, domain);
4410
4411#define SMTPD_CHECK_ETRN_RETURN(x) { \
4412	SMTPD_CHECK_POP(state->etrn_name, saved_etrn_name); \
4413	return (x); \
4414    }
4415
4416    /*
4417     * Apply delayed restrictions.
4418     */
4419    if (var_smtpd_delay_reject)
4420	if ((err = smtpd_check_client(state)) != 0
4421	    || (err = smtpd_check_helo(state, state->helo_name)) != 0)
4422	    SMTPD_CHECK_ETRN_RETURN(err);
4423
4424    /*
4425     * Restore the defer_if_permit flag to its value before ETRN, and do not
4426     * set the flag when it was already raised by a previous protocol stage.
4427     * The client may skip the helo/ehlo.
4428     */
4429    state->defer_if_permit.active = state->defer_if_permit_client
4430	| state->defer_if_permit_helo;
4431
4432    /*
4433     * Apply restrictions in the order as specified.
4434     */
4435    SMTPD_CHECK_RESET();
4436    status = setjmp(smtpd_check_buf);
4437    if (status == 0 && etrn_restrctions->argc)
4438	status = generic_checks(state, etrn_restrctions, domain,
4439				SMTPD_NAME_ETRN, CHECK_ETRN_ACL);
4440
4441    /*
4442     * Force permission into deferral when some earlier temporary error may
4443     * have prevented us from rejecting mail, and report the earlier problem.
4444     */
4445    if (status != SMTPD_CHECK_REJECT && state->defer_if_permit.active)
4446	status = smtpd_check_reject(state, state->defer_if_permit.class,
4447				    state->defer_if_permit.code,
4448				    STR(state->defer_if_permit.dsn),
4449				  "%s", STR(state->defer_if_permit.reason));
4450
4451    SMTPD_CHECK_ETRN_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
4452}
4453
4454/* check_recipient_rcpt_maps - generic_checks() recipient table check */
4455
4456static int check_recipient_rcpt_maps(SMTPD_STATE *state, const char *recipient)
4457{
4458
4459    /*
4460     * Duplicate suppression. There's an implicit check_recipient_maps
4461     * restriction at the end of all recipient restrictions.
4462     */
4463    if (smtpd_input_transp_mask & INPUT_TRANSP_UNKNOWN_RCPT)
4464	return (0);
4465    if (state->recipient_rcptmap_checked == 1)
4466	return (0);
4467    if (state->warn_if_reject == 0)
4468	/* We really validate the recipient address. */
4469	state->recipient_rcptmap_checked = 1;
4470    return (check_rcpt_maps(state, recipient, SMTPD_NAME_RECIPIENT));
4471}
4472
4473/* check_sender_rcpt_maps - generic_checks() sender table check */
4474
4475static int check_sender_rcpt_maps(SMTPD_STATE *state, const char *sender)
4476{
4477
4478    /*
4479     * Duplicate suppression. There's an implicit check_sender_maps
4480     * restriction at the end of all sender restrictions.
4481     */
4482    if (smtpd_input_transp_mask & INPUT_TRANSP_UNKNOWN_RCPT)
4483	return (0);
4484    if (state->sender_rcptmap_checked == 1)
4485	return (0);
4486    if (state->warn_if_reject == 0)
4487	/* We really validate the sender address. */
4488	state->sender_rcptmap_checked = 1;
4489    return (check_rcpt_maps(state, sender, SMTPD_NAME_SENDER));
4490}
4491
4492/* check_rcpt_maps - generic_checks() interface for recipient table check */
4493
4494static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient,
4495			           const char *reply_class)
4496{
4497    const RESOLVE_REPLY *reply;
4498    DSN_SPLIT dp;
4499
4500    if (msg_verbose)
4501	msg_info(">>> CHECKING RECIPIENT MAPS <<<");
4502
4503    /*
4504     * Resolve the address.
4505     */
4506    reply = smtpd_resolve_addr(recipient);
4507    if (reply->flags & RESOLVE_FLAG_FAIL)
4508	reject_dict_retry(state, recipient);
4509
4510    /*
4511     * Make complex expressions more readable?
4512     */
4513#define MATCH(map, rcpt) \
4514    check_mail_addr_find(state, recipient, map, rcpt, (char **) 0)
4515
4516#define NOMATCH(map, rcpt) (MATCH(map, rcpt) == 0)
4517
4518    /*
4519     * XXX We assume the recipient address is OK if it matches a canonical
4520     * map or virtual alias map. Eventually, the address resolver should give
4521     * us the final resolved recipient address, and the SMTP server should
4522     * write the final resolved recipient address to the output record
4523     * stream. See also the next comment block on recipients in virtual alias
4524     * domains.
4525     */
4526    if (MATCH(rcpt_canon_maps, CONST_STR(reply->recipient))
4527	|| MATCH(canonical_maps, CONST_STR(reply->recipient))
4528	|| MATCH(virt_alias_maps, CONST_STR(reply->recipient)))
4529	return (0);
4530
4531    /*
4532     * At this point, anything that resolves to the error mailer is known to
4533     * be undeliverable.
4534     *
4535     * XXX Until the address resolver does final address resolution, known and
4536     * unknown recipients in virtual alias domains will both resolve to
4537     * "error:user unknown".
4538     */
4539    if (strcmp(STR(reply->transport), MAIL_SERVICE_ERROR) == 0) {
4540	dsn_split(&dp, strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
4541		  "5.1.0" : "5.1.1", STR(reply->nexthop));
4542	return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
4543				   (reply->flags & RESOLVE_CLASS_ALIAS) ?
4544				   var_virt_alias_code : 550,
4545				   smtpd_dsn_fix(DSN_STATUS(dp.dsn),
4546						 reply_class),
4547				   "<%s>: %s rejected: %s",
4548				   recipient, reply_class,
4549				   dp.text));
4550    }
4551
4552    /*
4553     * Search the recipient lookup tables of the respective address class.
4554     *
4555     * XXX Use the less expensive maps_find() (built-in case folding) instead of
4556     * the baroque mail_addr_find(). But then we have to strip the domain and
4557     * deal with address extensions ourselves.
4558     *
4559     * XXX But that would break sites that use the virtual delivery agent for
4560     * local delivery, because the virtual delivery agent requires
4561     * user@domain style addresses in its user database.
4562     */
4563#define MATCH_LEFT(l, r, n) (strncasecmp((l), (r), (n)) == 0 && (r)[n] == '@')
4564
4565    switch (reply->flags & RESOLVE_CLASS_MASK) {
4566
4567	/*
4568	 * Reject mail to unknown addresses in local domains (domains that
4569	 * match $mydestination or ${proxy,inet}_interfaces).
4570	 */
4571    case RESOLVE_CLASS_LOCAL:
4572	if (*var_local_rcpt_maps
4573	/* Generated by bounce, absorbed by qmgr. */
4574	&& !MATCH_LEFT(var_double_bounce_sender, CONST_STR(reply->recipient),
4575		       strlen(var_double_bounce_sender))
4576	/* Absorbed by qmgr. */
4577	    && !MATCH_LEFT(MAIL_ADDR_POSTMASTER, CONST_STR(reply->recipient),
4578			   strlen(MAIL_ADDR_POSTMASTER))
4579	/* Generated by bounce. */
4580	  && !MATCH_LEFT(MAIL_ADDR_MAIL_DAEMON, CONST_STR(reply->recipient),
4581			 strlen(MAIL_ADDR_MAIL_DAEMON))
4582	    && NOMATCH(local_rcpt_maps, CONST_STR(reply->recipient)))
4583	    return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
4584				       var_local_rcpt_code,
4585			       strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
4586				       "5.1.0" : "5.1.1",
4587				       "<%s>: %s rejected: User unknown%s",
4588				       recipient, reply_class,
4589				       var_show_unk_rcpt_table ?
4590				       " in local recipient table" : ""));
4591	break;
4592
4593	/*
4594	 * Reject mail to unknown addresses in virtual mailbox domains.
4595	 */
4596    case RESOLVE_CLASS_VIRTUAL:
4597	if (*var_virt_mailbox_maps
4598	    && NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient)))
4599	    return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
4600				       var_virt_mailbox_code,
4601			       strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
4602				       "5.1.0" : "5.1.1",
4603				       "<%s>: %s rejected: User unknown%s",
4604				       recipient, reply_class,
4605				       var_show_unk_rcpt_table ?
4606				       " in virtual mailbox table" : ""));
4607	break;
4608
4609	/*
4610	 * Reject mail to unknown addresses in relay domains.
4611	 */
4612    case RESOLVE_CLASS_RELAY:
4613	if (*var_relay_rcpt_maps
4614	    && NOMATCH(relay_rcpt_maps, CONST_STR(reply->recipient)))
4615	    return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
4616				       var_relay_rcpt_code,
4617			       strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
4618				       "5.1.0" : "5.1.1",
4619				       "<%s>: %s rejected: User unknown%s",
4620				       recipient, reply_class,
4621				       var_show_unk_rcpt_table ?
4622				       " in relay recipient table" : ""));
4623	break;
4624    }
4625
4626    /*
4627     * Accept all other addresses - including addresses that passed the above
4628     * tests because of some table lookup problem.
4629     */
4630    return (0);
4631}
4632
4633/* smtpd_check_size - check optional SIZE parameter value */
4634
4635char   *smtpd_check_size(SMTPD_STATE *state, off_t size)
4636{
4637    int     status;
4638
4639    /*
4640     * Return here in case of serious trouble.
4641     */
4642    SMTPD_CHECK_RESET();
4643    if ((status = setjmp(smtpd_check_buf)) != 0)
4644	return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
4645
4646    /*
4647     * Check against file size limit.
4648     */
4649    if (var_message_limit > 0 && size > var_message_limit) {
4650	(void) smtpd_check_reject(state, MAIL_ERROR_POLICY,
4651				  552, "5.3.4",
4652				  "Message size exceeds fixed limit");
4653	return (STR(error_text));
4654    }
4655    return (0);
4656}
4657
4658/* smtpd_check_queue - check queue space */
4659
4660char   *smtpd_check_queue(SMTPD_STATE *state)
4661{
4662    const char *myname = "smtpd_check_queue";
4663    struct fsspace fsbuf;
4664    int     status;
4665
4666    /*
4667     * Return here in case of serious trouble.
4668     */
4669    SMTPD_CHECK_RESET();
4670    if ((status = setjmp(smtpd_check_buf)) != 0)
4671	return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
4672
4673    /*
4674     * Avoid overflow/underflow when comparing message size against available
4675     * space.
4676     */
4677#define BLOCKS(x)	((x) / fsbuf.block_size)
4678
4679    fsspace(".", &fsbuf);
4680    if (msg_verbose)
4681	msg_info("%s: blocks %lu avail %lu min_free %lu msg_size_limit %lu",
4682		 myname,
4683		 (unsigned long) fsbuf.block_size,
4684		 (unsigned long) fsbuf.block_free,
4685		 (unsigned long) var_queue_minfree,
4686		 (unsigned long) var_message_limit);
4687    if (BLOCKS(var_queue_minfree) >= fsbuf.block_free
4688     || BLOCKS(var_message_limit) >= fsbuf.block_free / smtpd_space_multf) {
4689	(void) smtpd_check_reject(state, MAIL_ERROR_RESOURCE,
4690				  452, "4.3.1",
4691				  "Insufficient system storage");
4692	msg_warn("not enough free space in mail queue: %lu bytes < "
4693		 "%g*message size limit",
4694		 (unsigned long) fsbuf.block_free * fsbuf.block_size,
4695		 smtpd_space_multf);
4696	return (STR(error_text));
4697    }
4698    return (0);
4699}
4700
4701/* smtpd_check_data - check DATA command */
4702
4703char   *smtpd_check_data(SMTPD_STATE *state)
4704{
4705    int     status;
4706    char   *NOCLOBBER saved_recipient;
4707
4708    /*
4709     * Minor kluge so that we can delegate work to the generic routine. We
4710     * provide no recipient information in the case of multiple recipients,
4711     * This restriction applies to all recipients alike, and logging only one
4712     * of them would be misleading.
4713     */
4714    if (state->rcpt_count > 1) {
4715	saved_recipient = state->recipient;
4716	state->recipient = 0;
4717    }
4718
4719    /*
4720     * Reset the defer_if_permit flag. This is necessary when some recipients
4721     * were accepted but the last one was rejected.
4722     */
4723    state->defer_if_permit.active = 0;
4724
4725    /*
4726     * Apply restrictions in the order as specified.
4727     *
4728     * XXX We cannot specify a default target for a bare access map.
4729     */
4730    SMTPD_CHECK_RESET();
4731    status = setjmp(smtpd_check_buf);
4732    if (status == 0 && data_restrctions->argc)
4733	status = generic_checks(state, data_restrctions,
4734				SMTPD_CMD_DATA, SMTPD_NAME_DATA, NO_DEF_ACL);
4735
4736    /*
4737     * Force permission into deferral when some earlier temporary error may
4738     * have prevented us from rejecting mail, and report the earlier problem.
4739     */
4740    if (status != SMTPD_CHECK_REJECT && state->defer_if_permit.active)
4741	status = smtpd_check_reject(state, state->defer_if_permit.class,
4742				    state->defer_if_permit.code,
4743				    STR(state->defer_if_permit.dsn),
4744				  "%s", STR(state->defer_if_permit.reason));
4745
4746    if (state->rcpt_count > 1)
4747	state->recipient = saved_recipient;
4748
4749    return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
4750}
4751
4752/* smtpd_check_eod - check end-of-data command */
4753
4754char   *smtpd_check_eod(SMTPD_STATE *state)
4755{
4756    int     status;
4757    char   *NOCLOBBER saved_recipient;
4758
4759    /*
4760     * Minor kluge so that we can delegate work to the generic routine. We
4761     * provide no recipient information in the case of multiple recipients,
4762     * This restriction applies to all recipients alike, and logging only one
4763     * of them would be misleading.
4764     */
4765    if (state->rcpt_count > 1) {
4766	saved_recipient = state->recipient;
4767	state->recipient = 0;
4768    }
4769
4770    /*
4771     * Reset the defer_if_permit flag. This is necessary when some recipients
4772     * were accepted but the last one was rejected.
4773     */
4774    state->defer_if_permit.active = 0;
4775
4776    /*
4777     * Apply restrictions in the order as specified.
4778     *
4779     * XXX We cannot specify a default target for a bare access map.
4780     */
4781    SMTPD_CHECK_RESET();
4782    status = setjmp(smtpd_check_buf);
4783    if (status == 0 && eod_restrictions->argc)
4784	status = generic_checks(state, eod_restrictions,
4785				SMTPD_CMD_EOD, SMTPD_NAME_EOD, NO_DEF_ACL);
4786
4787    /*
4788     * Force permission into deferral when some earlier temporary error may
4789     * have prevented us from rejecting mail, and report the earlier problem.
4790     */
4791    if (status != SMTPD_CHECK_REJECT && state->defer_if_permit.active)
4792	status = smtpd_check_reject(state, state->defer_if_permit.class,
4793				    state->defer_if_permit.code,
4794				    STR(state->defer_if_permit.dsn),
4795				  "%s", STR(state->defer_if_permit.reason));
4796
4797    if (state->rcpt_count > 1)
4798	state->recipient = saved_recipient;
4799
4800    return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
4801}
4802
4803#ifdef TEST
4804
4805 /*
4806  * Test program to try out all these restrictions without having to go live.
4807  * This is not entirely stand-alone, as it requires access to the Postfix
4808  * rewrite/resolve service. This is just for testing code, not for debugging
4809  * configuration files.
4810  */
4811#include <stdlib.h>
4812
4813#include <msg_vstream.h>
4814#include <vstring_vstream.h>
4815
4816#include <mail_conf.h>
4817
4818#include <smtpd_chat.h>
4819
4820int     smtpd_input_transp_mask;
4821
4822 /*
4823  * Dummies. These are never set.
4824  */
4825char   *var_client_checks = "";
4826char   *var_helo_checks = "";
4827char   *var_mail_checks = "";
4828char   *var_rcpt_checks = "";
4829char   *var_etrn_checks = "";
4830char   *var_data_checks = "";
4831char   *var_eod_checks = "";
4832char   *var_relay_domains = "";
4833
4834#ifdef USE_TLS
4835char   *var_relay_ccerts = "";
4836
4837#endif
4838char   *var_mynetworks = "";
4839char   *var_notify_classes = "";
4840
4841 /*
4842  * String-valued configuration parameters.
4843  */
4844char   *var_maps_rbl_domains;
4845char   *var_myorigin;
4846char   *var_mydest;
4847char   *var_inet_interfaces;
4848char   *var_proxy_interfaces;
4849char   *var_rcpt_delim;
4850char   *var_rest_classes;
4851char   *var_alias_maps;
4852char   *var_rcpt_canon_maps;
4853char   *var_canonical_maps;
4854char   *var_virt_alias_maps;
4855char   *var_virt_alias_doms;
4856char   *var_virt_mailbox_maps;
4857char   *var_virt_mailbox_doms;
4858char   *var_local_rcpt_maps;
4859char   *var_perm_mx_networks;
4860char   *var_par_dom_match;
4861char   *var_smtpd_null_key;
4862char   *var_smtpd_snd_auth_maps;
4863char   *var_double_bounce_sender;
4864char   *var_rbl_reply_maps;
4865char   *var_smtpd_exp_filter;
4866char   *var_def_rbl_reply;
4867char   *var_relay_rcpt_maps;
4868char   *var_verify_sender;
4869char   *var_smtpd_sasl_opts;
4870char   *var_local_rwr_clients;
4871char   *var_smtpd_relay_ccerts;
4872char   *var_unv_from_why;
4873char   *var_unv_rcpt_why;
4874char   *var_stress;
4875char   *var_unk_name_tf_act;
4876char   *var_unk_addr_tf_act;
4877char   *var_unv_rcpt_tf_act;
4878char   *var_unv_from_tf_act;
4879
4880typedef struct {
4881    char   *name;
4882    char   *defval;
4883    char  **target;
4884} STRING_TABLE;
4885
4886#undef DEF_VIRT_ALIAS_MAPS
4887#define DEF_VIRT_ALIAS_MAPS	""
4888
4889#undef DEF_LOCAL_RCPT_MAPS
4890#define DEF_LOCAL_RCPT_MAPS	""
4891
4892static const STRING_TABLE string_table[] = {
4893    VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains,
4894    VAR_MYORIGIN, DEF_MYORIGIN, &var_myorigin,
4895    VAR_MYDEST, DEF_MYDEST, &var_mydest,
4896    VAR_INET_INTERFACES, DEF_INET_INTERFACES, &var_inet_interfaces,
4897    VAR_PROXY_INTERFACES, DEF_PROXY_INTERFACES, &var_proxy_interfaces,
4898    VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim,
4899    VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes,
4900    VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps,
4901    VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps,
4902    VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps,
4903    VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps,
4904    VAR_VIRT_ALIAS_DOMS, DEF_VIRT_ALIAS_DOMS, &var_virt_alias_doms,
4905    VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps,
4906    VAR_VIRT_MAILBOX_DOMS, DEF_VIRT_MAILBOX_DOMS, &var_virt_mailbox_doms,
4907    VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps,
4908    VAR_PERM_MX_NETWORKS, DEF_PERM_MX_NETWORKS, &var_perm_mx_networks,
4909    VAR_PAR_DOM_MATCH, DEF_PAR_DOM_MATCH, &var_par_dom_match,
4910    VAR_SMTPD_SND_AUTH_MAPS, DEF_SMTPD_SND_AUTH_MAPS, &var_smtpd_snd_auth_maps,
4911    VAR_SMTPD_NULL_KEY, DEF_SMTPD_NULL_KEY, &var_smtpd_null_key,
4912    VAR_DOUBLE_BOUNCE, DEF_DOUBLE_BOUNCE, &var_double_bounce_sender,
4913    VAR_RBL_REPLY_MAPS, DEF_RBL_REPLY_MAPS, &var_rbl_reply_maps,
4914    VAR_SMTPD_EXP_FILTER, DEF_SMTPD_EXP_FILTER, &var_smtpd_exp_filter,
4915    VAR_DEF_RBL_REPLY, DEF_DEF_RBL_REPLY, &var_def_rbl_reply,
4916    VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps,
4917    VAR_VERIFY_SENDER, DEF_VERIFY_SENDER, &var_verify_sender,
4918    VAR_MAIL_NAME, DEF_MAIL_NAME, &var_mail_name,
4919    VAR_SMTPD_SASL_OPTS, DEF_SMTPD_SASL_OPTS, &var_smtpd_sasl_opts,
4920    VAR_LOC_RWR_CLIENTS, DEF_LOC_RWR_CLIENTS, &var_local_rwr_clients,
4921    VAR_RELAY_CCERTS, DEF_RELAY_CCERTS, &var_smtpd_relay_ccerts,
4922    VAR_UNV_FROM_WHY, DEF_UNV_FROM_WHY, &var_unv_from_why,
4923    VAR_UNV_RCPT_WHY, DEF_UNV_RCPT_WHY, &var_unv_rcpt_why,
4924    VAR_STRESS, DEF_STRESS, &var_stress,
4925    /* XXX Can't use ``$name'' type default values below. */
4926    VAR_UNK_NAME_TF_ACT, DEF_REJECT_TMPF_ACT, &var_unk_name_tf_act,
4927    VAR_UNK_ADDR_TF_ACT, DEF_REJECT_TMPF_ACT, &var_unk_addr_tf_act,
4928    VAR_UNV_RCPT_TF_ACT, DEF_REJECT_TMPF_ACT, &var_unv_rcpt_tf_act,
4929    VAR_UNV_FROM_TF_ACT, DEF_REJECT_TMPF_ACT, &var_unv_from_tf_act,
4930    0,
4931};
4932
4933/* string_init - initialize string parameters */
4934
4935static void string_init(void)
4936{
4937    const STRING_TABLE *sp;
4938
4939    for (sp = string_table; sp->name; sp++)
4940	sp->target[0] = mystrdup(sp->defval);
4941}
4942
4943/* string_update - update string parameter */
4944
4945static int string_update(char **argv)
4946{
4947    const STRING_TABLE *sp;
4948
4949    for (sp = string_table; sp->name; sp++) {
4950	if (strcasecmp(argv[0], sp->name) == 0) {
4951	    myfree(sp->target[0]);
4952	    sp->target[0] = mystrdup(argv[1]);
4953	    return (1);
4954	}
4955    }
4956    return (0);
4957}
4958
4959 /*
4960  * Integer parameters.
4961  */
4962int     var_queue_minfree;		/* XXX use off_t */
4963typedef struct {
4964    char   *name;
4965    int     defval;
4966    int    *target;
4967} INT_TABLE;
4968
4969int     var_unk_client_code;
4970int     var_bad_name_code;
4971int     var_unk_name_code;
4972int     var_unk_addr_code;
4973int     var_relay_code;
4974int     var_maps_rbl_code;
4975int     var_map_reject_code;
4976int     var_map_defer_code;
4977int     var_reject_code;
4978int     var_defer_code;
4979int     var_non_fqdn_code;
4980int     var_smtpd_delay_reject;
4981int     var_allow_untrust_route;
4982int     var_mul_rcpt_code;
4983int     var_unv_from_rcode;
4984int     var_unv_from_dcode;
4985int     var_unv_rcpt_rcode;
4986int     var_unv_rcpt_dcode;
4987int     var_local_rcpt_code;
4988int     var_relay_rcpt_code;
4989int     var_virt_mailbox_code;
4990int     var_virt_alias_code;
4991int     var_show_unk_rcpt_table;
4992int     var_verify_poll_count;
4993int     var_verify_poll_delay;
4994int     var_smtpd_policy_tmout;
4995int     var_smtpd_policy_idle;
4996int     var_smtpd_policy_ttl;
4997int     var_smtpd_rej_unl_from;
4998int     var_smtpd_rej_unl_rcpt;
4999int     var_plaintext_code;
5000bool    var_smtpd_peername_lookup;
5001bool    var_smtpd_client_port_log;
5002
5003static const INT_TABLE int_table[] = {
5004    "msg_verbose", 0, &msg_verbose,
5005    VAR_UNK_CLIENT_CODE, DEF_UNK_CLIENT_CODE, &var_unk_client_code,
5006    VAR_BAD_NAME_CODE, DEF_BAD_NAME_CODE, &var_bad_name_code,
5007    VAR_UNK_NAME_CODE, DEF_UNK_NAME_CODE, &var_unk_name_code,
5008    VAR_UNK_ADDR_CODE, DEF_UNK_ADDR_CODE, &var_unk_addr_code,
5009    VAR_RELAY_CODE, DEF_RELAY_CODE, &var_relay_code,
5010    VAR_MAPS_RBL_CODE, DEF_MAPS_RBL_CODE, &var_maps_rbl_code,
5011    VAR_MAP_REJECT_CODE, DEF_MAP_REJECT_CODE, &var_map_reject_code,
5012    VAR_MAP_DEFER_CODE, DEF_MAP_DEFER_CODE, &var_map_defer_code,
5013    VAR_REJECT_CODE, DEF_REJECT_CODE, &var_reject_code,
5014    VAR_DEFER_CODE, DEF_DEFER_CODE, &var_defer_code,
5015    VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code,
5016    VAR_SMTPD_DELAY_REJECT, DEF_SMTPD_DELAY_REJECT, &var_smtpd_delay_reject,
5017    VAR_ALLOW_UNTRUST_ROUTE, DEF_ALLOW_UNTRUST_ROUTE, &var_allow_untrust_route,
5018    VAR_MUL_RCPT_CODE, DEF_MUL_RCPT_CODE, &var_mul_rcpt_code,
5019    VAR_UNV_FROM_RCODE, DEF_UNV_FROM_RCODE, &var_unv_from_rcode,
5020    VAR_UNV_FROM_DCODE, DEF_UNV_FROM_DCODE, &var_unv_from_dcode,
5021    VAR_UNV_RCPT_RCODE, DEF_UNV_RCPT_RCODE, &var_unv_rcpt_rcode,
5022    VAR_UNV_RCPT_DCODE, DEF_UNV_RCPT_DCODE, &var_unv_rcpt_dcode,
5023    VAR_LOCAL_RCPT_CODE, DEF_LOCAL_RCPT_CODE, &var_local_rcpt_code,
5024    VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code,
5025    VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code,
5026    VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code,
5027    VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table,
5028    VAR_VERIFY_POLL_COUNT, 3, &var_verify_poll_count,
5029    VAR_SMTPD_REJ_UNL_FROM, DEF_SMTPD_REJ_UNL_FROM, &var_smtpd_rej_unl_from,
5030    VAR_SMTPD_REJ_UNL_RCPT, DEF_SMTPD_REJ_UNL_RCPT, &var_smtpd_rej_unl_rcpt,
5031    VAR_PLAINTEXT_CODE, DEF_PLAINTEXT_CODE, &var_plaintext_code,
5032    VAR_SMTPD_PEERNAME_LOOKUP, DEF_SMTPD_PEERNAME_LOOKUP, &var_smtpd_peername_lookup,
5033    VAR_SMTPD_CLIENT_PORT_LOG, DEF_SMTPD_CLIENT_PORT_LOG, &var_smtpd_client_port_log,
5034    0,
5035};
5036
5037/* int_init - initialize int parameters */
5038
5039static void int_init(void)
5040{
5041    const INT_TABLE *sp;
5042
5043    for (sp = int_table; sp->name; sp++)
5044	sp->target[0] = sp->defval;
5045}
5046
5047/* int_update - update int parameter */
5048
5049static int int_update(char **argv)
5050{
5051    const INT_TABLE *ip;
5052
5053    for (ip = int_table; ip->name; ip++) {
5054	if (strcasecmp(argv[0], ip->name) == 0) {
5055	    if (!ISDIGIT(*argv[1]))
5056		msg_fatal("bad number: %s %s", ip->name, argv[1]);
5057	    ip->target[0] = atoi(argv[1]);
5058	    return (1);
5059	}
5060    }
5061    return (0);
5062}
5063
5064 /*
5065  * Restrictions.
5066  */
5067typedef struct {
5068    char   *name;
5069    ARGV  **target;
5070} REST_TABLE;
5071
5072static const REST_TABLE rest_table[] = {
5073    "client_restrictions", &client_restrctions,
5074    "helo_restrictions", &helo_restrctions,
5075    "sender_restrictions", &mail_restrctions,
5076    "recipient_restrictions", &rcpt_restrctions,
5077    "etrn_restrictions", &etrn_restrctions,
5078    0,
5079};
5080
5081/* rest_update - update restriction */
5082
5083static int rest_update(char **argv)
5084{
5085    const REST_TABLE *rp;
5086
5087    for (rp = rest_table; rp->name; rp++) {
5088	if (strcasecmp(rp->name, argv[0]) == 0) {
5089	    argv_free(rp->target[0]);
5090	    rp->target[0] = smtpd_check_parse(SMTPD_CHECK_PARSE_ALL, argv[1]);
5091	    return (1);
5092	}
5093    }
5094    return (0);
5095}
5096
5097/* rest_class - (re)define a restriction class */
5098
5099static void rest_class(char *class)
5100{
5101    char   *cp = class;
5102    char   *name;
5103    HTABLE_INFO *entry;
5104
5105    if (smtpd_rest_classes == 0)
5106	smtpd_rest_classes = htable_create(1);
5107
5108    if ((name = mystrtok(&cp, RESTRICTION_SEPARATORS)) == 0)
5109	msg_panic("rest_class: null class name");
5110    if ((entry = htable_locate(smtpd_rest_classes, name)) != 0)
5111	argv_free((ARGV *) entry->value);
5112    else
5113	entry = htable_enter(smtpd_rest_classes, name, (char *) 0);
5114    entry->value = (char *) smtpd_check_parse(SMTPD_CHECK_PARSE_ALL, cp);
5115}
5116
5117/* resolve_clnt_init - initialize reply */
5118
5119void    resolve_clnt_init(RESOLVE_REPLY *reply)
5120{
5121    reply->flags = 0;
5122    reply->transport = vstring_alloc(100);
5123    reply->nexthop = vstring_alloc(100);
5124    reply->recipient = vstring_alloc(100);
5125}
5126
5127void    resolve_clnt_free(RESOLVE_REPLY *reply)
5128{
5129    vstring_free(reply->transport);
5130    vstring_free(reply->nexthop);
5131    vstring_free(reply->recipient);
5132}
5133
5134bool    var_smtpd_sasl_enable = 0;
5135
5136#ifdef USE_SASL_AUTH
5137
5138/* smtpd_sasl_activate - stub */
5139
5140void    smtpd_sasl_activate(SMTPD_STATE *state, const char *opts_name,
5141			            const char *opts_var)
5142{
5143    msg_panic("smtpd_sasl_activate was called");
5144}
5145
5146/* smtpd_sasl_deactivate - stub */
5147
5148void    smtpd_sasl_deactivate(SMTPD_STATE *state)
5149{
5150    msg_panic("smtpd_sasl_deactivate was called");
5151}
5152
5153/* permit_sasl_auth - stub */
5154
5155int     permit_sasl_auth(SMTPD_STATE *state, int ifyes, int ifnot)
5156{
5157    return (ifnot);
5158}
5159
5160#endif
5161
5162/* verify_clnt_query - stub */
5163
5164int     verify_clnt_query(const char *addr, int *addr_status, VSTRING *why)
5165{
5166    *addr_status = DEL_RCPT_STAT_OK;
5167    return (VRFY_STAT_OK);
5168}
5169
5170/* rewrite_clnt_internal - stub */
5171
5172VSTRING *rewrite_clnt_internal(const char *context, const char *addr,
5173			               VSTRING *result)
5174{
5175    if (addr == STR(result))
5176	msg_panic("rewrite_clnt_internal: result clobbers input");
5177    if (*addr && strchr(addr, '@') == 0)
5178	msg_fatal("%s: address rewriting is disabled", addr);
5179    vstring_strcpy(result, addr);
5180    return (result);
5181}
5182
5183/* resolve_clnt_query - stub */
5184
5185void    resolve_clnt(const char *class, const char *unused_sender, const char *addr,
5186		             RESOLVE_REPLY *reply)
5187{
5188    const char *domain;
5189
5190    if (addr == CONST_STR(reply->recipient))
5191	msg_panic("resolve_clnt_query: result clobbers input");
5192    if (strchr(addr, '%'))
5193	msg_fatal("%s: address rewriting is disabled", addr);
5194    if ((domain = strrchr(addr, '@')) == 0)
5195	msg_fatal("%s: unqualified address", addr);
5196    domain += 1;
5197    if (resolve_local(domain)) {
5198	reply->flags = RESOLVE_CLASS_LOCAL;
5199	vstring_strcpy(reply->transport, MAIL_SERVICE_LOCAL);
5200	vstring_strcpy(reply->nexthop, domain);
5201    } else if (string_list_match(virt_alias_doms, domain)) {
5202	reply->flags = RESOLVE_CLASS_ALIAS;
5203	vstring_strcpy(reply->transport, MAIL_SERVICE_ERROR);
5204	vstring_strcpy(reply->nexthop, "user unknown");
5205    } else if (string_list_match(virt_mailbox_doms, domain)) {
5206	reply->flags = RESOLVE_CLASS_VIRTUAL;
5207	vstring_strcpy(reply->transport, MAIL_SERVICE_VIRTUAL);
5208	vstring_strcpy(reply->nexthop, domain);
5209    } else if (domain_list_match(relay_domains, domain)) {
5210	reply->flags = RESOLVE_CLASS_RELAY;
5211	vstring_strcpy(reply->transport, MAIL_SERVICE_RELAY);
5212	vstring_strcpy(reply->nexthop, domain);
5213    } else {
5214	reply->flags = RESOLVE_CLASS_DEFAULT;
5215	vstring_strcpy(reply->transport, MAIL_SERVICE_SMTP);
5216	vstring_strcpy(reply->nexthop, domain);
5217    }
5218    vstring_strcpy(reply->recipient, addr);
5219}
5220
5221/* smtpd_chat_reset - stub */
5222
5223void    smtpd_chat_reset(SMTPD_STATE *unused_state)
5224{
5225}
5226
5227/* usage - scream and terminate */
5228
5229static NORETURN usage(char *myname)
5230{
5231    msg_fatal("usage: %s", myname);
5232}
5233
5234int     main(int argc, char **argv)
5235{
5236    VSTRING *buf = vstring_alloc(100);
5237    SMTPD_STATE state;
5238    ARGV   *args;
5239    char   *bp;
5240    char   *resp;
5241    char   *addr;
5242    INET_PROTO_INFO *proto_info;
5243
5244    /*
5245     * Initialization. Use dummies for client information.
5246     */
5247    msg_vstream_init(argv[0], VSTREAM_ERR);
5248    if (argc != 1)
5249	usage(argv[0]);
5250    string_init();
5251    int_init();
5252    smtpd_check_init();
5253    smtpd_expand_init();
5254    smtpd_state_init(&state, VSTREAM_IN, "smtpd");
5255    state.queue_id = "<queue id>";
5256
5257    proto_info = inet_proto_init(argv[0], INET_PROTO_NAME_ALL);
5258
5259    /*
5260     * Main loop: update config parameters or test the client, helo, sender
5261     * and recipient restrictions.
5262     */
5263    while (vstring_fgets_nonl(buf, VSTREAM_IN) != 0) {
5264
5265	/*
5266	 * Tokenize the command. Note, the comma is not a separator, so that
5267	 * restriction lists can be entered as comma-separated lists.
5268	 */
5269	bp = STR(buf);
5270	if (!isatty(0)) {
5271	    vstream_printf(">>> %s\n", bp);
5272	    vstream_fflush(VSTREAM_OUT);
5273	}
5274	if (*bp == '#')
5275	    continue;
5276	if (*bp == '!') {
5277	    vstream_printf("exit %d\n", system(bp + 1));
5278	    continue;
5279	}
5280	args = argv_split(bp, " \t\r\n");
5281
5282	/*
5283	 * Recognize the command.
5284	 */
5285	resp = "bad command";
5286	switch (args->argc) {
5287
5288	    /*
5289	     * Emtpy line.
5290	     */
5291	case 0:
5292	    continue;
5293
5294	    /*
5295	     * Special case: client identity.
5296	     */
5297	case 4:
5298	case 3:
5299	    if (strcasecmp(args->argv[0], "client") == 0) {
5300		state.where = SMTPD_AFTER_CONNECT;
5301		UPDATE_STRING(state.name, args->argv[1]);
5302		UPDATE_STRING(state.reverse_name, args->argv[1]);
5303		UPDATE_STRING(state.addr, args->argv[2]);
5304		if (args->argc == 4)
5305		    state.name_status =
5306			state.reverse_name_status =
5307			atoi(args->argv[3]);
5308		else if (strcmp(state.name, "unknown") == 0)
5309		    state.name_status =
5310			state.reverse_name_status =
5311			SMTPD_PEER_CODE_TEMP;
5312		else
5313		    state.name_status =
5314			state.reverse_name_status =
5315			SMTPD_PEER_CODE_OK;
5316		if (state.namaddr)
5317		    myfree(state.namaddr);
5318		state.namaddr = concatenate(state.name, "[", state.addr,
5319					    "]", (char *) 0);
5320		resp = smtpd_check_client(&state);
5321	    }
5322	    break;
5323
5324	    /*
5325	     * Try config settings.
5326	     */
5327#define UPDATE_MAPS(ptr, var, val, lock) \
5328	{ if (ptr) maps_free(ptr); ptr = maps_create(var, val, lock); }
5329
5330#define UPDATE_LIST(ptr, val) \
5331	{ if (ptr) string_list_free(ptr); \
5332	  ptr = string_list_init(MATCH_FLAG_NONE, val); }
5333
5334	case 2:
5335	    if (strcasecmp(args->argv[0], VAR_VIRT_ALIAS_MAPS) == 0) {
5336		UPDATE_STRING(var_virt_alias_maps, args->argv[1]);
5337		UPDATE_MAPS(virt_alias_maps, VAR_VIRT_ALIAS_MAPS,
5338			    var_virt_alias_maps, DICT_FLAG_LOCK
5339			    | DICT_FLAG_FOLD_FIX);
5340		resp = 0;
5341		break;
5342	    }
5343	    if (strcasecmp(args->argv[0], VAR_VIRT_ALIAS_DOMS) == 0) {
5344		UPDATE_STRING(var_virt_alias_doms, args->argv[1]);
5345		UPDATE_LIST(virt_alias_doms, var_virt_alias_doms);
5346		resp = 0;
5347		break;
5348	    }
5349	    if (strcasecmp(args->argv[0], VAR_VIRT_MAILBOX_MAPS) == 0) {
5350		UPDATE_STRING(var_virt_mailbox_maps, args->argv[1]);
5351		UPDATE_MAPS(virt_mailbox_maps, VAR_VIRT_MAILBOX_MAPS,
5352			    var_virt_mailbox_maps, DICT_FLAG_LOCK
5353			    | DICT_FLAG_FOLD_FIX);
5354		resp = 0;
5355		break;
5356	    }
5357	    if (strcasecmp(args->argv[0], VAR_VIRT_MAILBOX_DOMS) == 0) {
5358		UPDATE_STRING(var_virt_mailbox_doms, args->argv[1]);
5359		UPDATE_LIST(virt_mailbox_doms, var_virt_mailbox_doms);
5360		resp = 0;
5361		break;
5362	    }
5363	    if (strcasecmp(args->argv[0], VAR_LOCAL_RCPT_MAPS) == 0) {
5364		UPDATE_STRING(var_local_rcpt_maps, args->argv[1]);
5365		UPDATE_MAPS(local_rcpt_maps, VAR_LOCAL_RCPT_MAPS,
5366			    var_local_rcpt_maps, DICT_FLAG_LOCK
5367			    | DICT_FLAG_FOLD_FIX);
5368		resp = 0;
5369		break;
5370	    }
5371	    if (strcasecmp(args->argv[0], VAR_RELAY_RCPT_MAPS) == 0) {
5372		UPDATE_STRING(var_relay_rcpt_maps, args->argv[1]);
5373		UPDATE_MAPS(relay_rcpt_maps, VAR_RELAY_RCPT_MAPS,
5374			    var_relay_rcpt_maps, DICT_FLAG_LOCK
5375			    | DICT_FLAG_FOLD_FIX);
5376		resp = 0;
5377		break;
5378	    }
5379	    if (strcasecmp(args->argv[0], VAR_CANONICAL_MAPS) == 0) {
5380		UPDATE_STRING(var_canonical_maps, args->argv[1]);
5381		UPDATE_MAPS(canonical_maps, VAR_CANONICAL_MAPS,
5382			    var_canonical_maps, DICT_FLAG_LOCK
5383			    | DICT_FLAG_FOLD_FIX);
5384		resp = 0;
5385		break;
5386	    }
5387	    if (strcasecmp(args->argv[0], VAR_RBL_REPLY_MAPS) == 0) {
5388		UPDATE_STRING(var_rbl_reply_maps, args->argv[1]);
5389		UPDATE_MAPS(rbl_reply_maps, VAR_RBL_REPLY_MAPS,
5390			    var_rbl_reply_maps, DICT_FLAG_LOCK
5391			    | DICT_FLAG_FOLD_FIX);
5392		resp = 0;
5393		break;
5394	    }
5395	    if (strcasecmp(args->argv[0], VAR_MYNETWORKS) == 0) {
5396		/* NOT: UPDATE_STRING */
5397		namadr_list_free(mynetworks);
5398		mynetworks =
5399		    namadr_list_init(match_parent_style(VAR_MYNETWORKS),
5400				     args->argv[1]);
5401		resp = 0;
5402		break;
5403	    }
5404	    if (strcasecmp(args->argv[0], VAR_RELAY_DOMAINS) == 0) {
5405		/* NOT: UPDATE_STRING */
5406		domain_list_free(relay_domains);
5407		relay_domains =
5408		    domain_list_init(match_parent_style(VAR_RELAY_DOMAINS),
5409				     args->argv[1]);
5410		resp = 0;
5411		break;
5412	    }
5413	    if (strcasecmp(args->argv[0], VAR_PERM_MX_NETWORKS) == 0) {
5414		UPDATE_STRING(var_perm_mx_networks, args->argv[1]);
5415		domain_list_free(perm_mx_networks);
5416		perm_mx_networks =
5417		    namadr_list_init(match_parent_style(VAR_PERM_MX_NETWORKS),
5418				     args->argv[1]);
5419		resp = 0;
5420		break;
5421	    }
5422	    if (strcasecmp(args->argv[0], "restriction_class") == 0) {
5423		rest_class(args->argv[1]);
5424		resp = 0;
5425		break;
5426	    }
5427	    if (int_update(args->argv)
5428		|| string_update(args->argv)
5429		|| rest_update(args->argv)) {
5430		resp = 0;
5431		break;
5432	    }
5433
5434	    /*
5435	     * Try restrictions.
5436	     */
5437#define TRIM_ADDR(src, res) { \
5438	    if (*(res = src) == '<') { \
5439		res += strlen(res) - 1; \
5440		if (*res == '>') \
5441		    *res = 0; \
5442		res = src + 1; \
5443	    } \
5444	}
5445
5446	    if (strcasecmp(args->argv[0], "helo") == 0) {
5447		state.where = "HELO";
5448		resp = smtpd_check_helo(&state, args->argv[1]);
5449		UPDATE_STRING(state.helo_name, args->argv[1]);
5450	    } else if (strcasecmp(args->argv[0], "mail") == 0) {
5451		state.where = "MAIL";
5452		TRIM_ADDR(args->argv[1], addr);
5453		UPDATE_STRING(state.sender, addr);
5454		resp = smtpd_check_mail(&state, addr);
5455	    } else if (strcasecmp(args->argv[0], "rcpt") == 0) {
5456		state.where = "RCPT";
5457		TRIM_ADDR(args->argv[1], addr);
5458		resp = smtpd_check_rcpt(&state, addr);
5459	    }
5460	    break;
5461
5462	    /*
5463	     * Show commands.
5464	     */
5465	default:
5466	    if (strcasecmp(args->argv[0], "check_rewrite") == 0) {
5467		smtpd_check_rewrite(&state);
5468		resp = state.rewrite_context;
5469		break;
5470	    }
5471	    resp = "Commands...\n\
5472		client <name> <address> [<code>]\n\
5473		helo <hostname>\n\
5474		sender <address>\n\
5475		recipient <address>\n\
5476		check_rewrite\n\
5477		msg_verbose <level>\n\
5478		client_restrictions <restrictions>\n\
5479		helo_restrictions <restrictions>\n\
5480		sender_restrictions <restrictions>\n\
5481		recipient_restrictions <restrictions>\n\
5482		restriction_class name,<restrictions>\n\
5483		\n\
5484		Note: no address rewriting \n";
5485	    break;
5486	}
5487	vstream_printf("%s\n", resp ? resp : "OK");
5488	vstream_fflush(VSTREAM_OUT);
5489	argv_free(args);
5490    }
5491    vstring_free(buf);
5492    smtpd_state_reset(&state);
5493#define FREE_STRING(s) { if (s) myfree(s); }
5494    FREE_STRING(state.helo_name);
5495    FREE_STRING(state.sender);
5496    exit(0);
5497}
5498
5499#endif
5500