1/*++
2/* NAME
3/*	resolve_local 3
4/* SUMMARY
5/*	determine if domain resolves to local mail system
6/* SYNOPSIS
7/*	#include <resolve_local.h>
8/*
9/*	void	resolve_local_init()
10/*
11/*	int	resolve_local(domain)
12/*	const char *domain;
13/* DESCRIPTION
14/*	resolve_local() determines if the named domain resolves to the
15/*	local mail system, either by case-insensitive exact match
16/*	against the domains, files or tables listed in $mydestination,
17/*	or by a match of an [address-literal] against of the network
18/*	addresses listed in $inet_interfaces or in $proxy_interfaces.
19/*	The result is > 0 if the domain matches the list of local
20/*	domains and IP addresses, 0 when it does not match, and < 0
21/*	in case of error.
22/*
23/*	resolve_local_init() performs initialization. If this routine is
24/*	not called explicitly ahead of time, it will be called on the fly.
25/* BUGS
26/*	Calling resolve_local_init() on the fly is an incomplete solution.
27/*	It is bound to fail with applications that enter a chroot jail.
28/* SEE ALSO
29/*	own_inet_addr(3), find out my own network interfaces
30/*	match_list(3), generic pattern matching engine
31/*	match_ops(3), generic pattern matching operators
32/* LICENSE
33/* .ad
34/* .fi
35/*	The Secure Mailer license must be distributed with this software.
36/* AUTHOR(S)
37/*	Wietse Venema
38/*	IBM T.J. Watson Research
39/*	P.O. Box 704
40/*	Yorktown Heights, NY 10598, USA
41/*--*/
42
43/* System library. */
44
45#include <sys_defs.h>
46
47/* Utility library. */
48
49#include <msg.h>
50#include <mymalloc.h>
51#include <string_list.h>
52#include <myaddrinfo.h>
53#include <valid_mailhost_addr.h>
54
55/* Global library. */
56
57#include <mail_params.h>
58#include <own_inet_addr.h>
59#include <resolve_local.h>
60
61/* Application-specific */
62
63static STRING_LIST *resolve_local_list;
64
65/* resolve_local_init - initialize lookup table */
66
67void    resolve_local_init(void)
68{
69    /* Allow on-the-fly update to make testing easier. */
70    if (resolve_local_list)
71	string_list_free(resolve_local_list);
72    resolve_local_list = string_list_init(MATCH_FLAG_RETURN, var_mydest);
73}
74
75/* resolve_local - match domain against list of local destinations */
76
77int     resolve_local(const char *addr)
78{
79    char   *saved_addr = mystrdup(addr);
80    char   *dest;
81    const char *bare_dest;
82    struct addrinfo *res0 = 0;
83    ssize_t len;
84
85    /*
86     * The optimizer will eliminate tests that always fail.
87     */
88#define RETURN(x) \
89    do { \
90	myfree(saved_addr); \
91	if (res0) \
92	    freeaddrinfo(res0); \
93	return(x); \
94    } while (0)
95
96    if (resolve_local_list == 0)
97	resolve_local_init();
98
99    /*
100     * Strip one trailing dot but not dot-dot.
101     *
102     * XXX This should not be distributed all over the code. Problem is,
103     * addresses can enter the system via multiple paths: networks, local
104     * forward/alias/include files, even as the result of address rewriting.
105     */
106    len = strlen(saved_addr);
107    if (len == 0)
108	RETURN(0);
109    if (saved_addr[len - 1] == '.')
110	saved_addr[--len] = 0;
111    if (len == 0 || saved_addr[len - 1] == '.')
112	RETURN(0);
113
114    /*
115     * Compare the destination against the list of destinations that we
116     * consider local.
117     */
118    if (string_list_match(resolve_local_list, saved_addr))
119	RETURN(1);
120    if (resolve_local_list->error != 0)
121	RETURN(resolve_local_list->error);
122
123    /*
124     * Compare the destination against the list of interface addresses that
125     * we are supposed to listen on.
126     *
127     * The destination may be an IPv6 address literal that was buried somewhere
128     * inside a deeply recursively nested address. This information comes
129     * from an untrusted source, and Wietse is not confident that everyone's
130     * getaddrinfo() etc. implementation is sufficiently robust. The syntax
131     * is complex enough with null field compression and with IPv4-in-IPv6
132     * addresses that errors are likely.
133     *
134     * The solution below is ad-hoc. We neutralize the string as soon as we
135     * realize that its contents could be harmful. We neutralize the string
136     * here, instead of neutralizing it in every resolve_local() caller.
137     * That's because resolve_local knows how the address is going to be
138     * parsed and converted into binary form.
139     *
140     * There are several more structural solutions to this.
141     *
142     * - One solution is to disallow address literals. This is not as bad as it
143     * seems: I have never seen actual legitimate use of address literals.
144     *
145     * - Another solution is to label each string with a trustworthiness label
146     * and to expect that all Postfix infrastructure will exercise additional
147     * caution when given a string with untrusted content. This is not likely
148     * to happen.
149     *
150     * FIX 200501 IPv6 patch did not require "IPv6:" prefix in numerical
151     * addresses.
152     */
153    dest = saved_addr;
154    if (*dest == '[' && dest[len - 1] == ']') {
155	dest++;
156	dest[len -= 2] = 0;
157	if ((bare_dest = valid_mailhost_addr(dest, DO_GRIPE)) != 0
158	    && hostaddr_to_sockaddr(bare_dest, (char *) 0, 0, &res0) == 0) {
159	    if (own_inet_addr(res0->ai_addr) || proxy_inet_addr(res0->ai_addr))
160		RETURN(1);
161	}
162    }
163
164    /*
165     * Must be remote, or a syntax error.
166     */
167    RETURN(0);
168}
169
170#ifdef TEST
171
172#include <vstream.h>
173#include <mail_conf.h>
174
175int     main(int argc, char **argv)
176{
177    int     rc;
178
179    if (argc != 3)
180	msg_fatal("usage: %s mydestination domain", argv[0]);
181    mail_conf_read();
182    myfree(var_mydest);
183    var_mydest = mystrdup(argv[1]);
184    vstream_printf("mydestination=%s destination=%s %s\n", argv[1], argv[2],
185		   (rc = resolve_local(argv[2])) > 0 ? "YES" :
186		   rc == 0 ? "NO" : "ERROR");
187    vstream_fflush(VSTREAM_OUT);
188    return (0);
189}
190
191#endif
192