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