/* $NetBSD: resolve_local.c,v 1.2 2017/02/14 01:16:45 christos Exp $ */
/*++
/* NAME
/* resolve_local 3
/* SUMMARY
/* determine if domain resolves to local mail system
/* SYNOPSIS
/* #include <resolve_local.h>
/*
/* void resolve_local_init()
/*
/* int resolve_local(domain)
/* const char *domain;
/* DESCRIPTION
/* resolve_local() determines if the named domain resolves to the
/* local mail system, either by case-insensitive exact match
/* against the domains, files or tables listed in $mydestination,
/* or by a match of an [address-literal] against of the network
/* addresses listed in $inet_interfaces or in $proxy_interfaces.
/* The result is > 0 if the domain matches the list of local
/* domains and IP addresses, 0 when it does not match, and < 0
/* in case of error.
/*
/* resolve_local_init() performs initialization. If this routine is
/* not called explicitly ahead of time, it will be called on the fly.
/* BUGS
/* Calling resolve_local_init() on the fly is an incomplete solution.
/* It is bound to fail with applications that enter a chroot jail.
/* SEE ALSO
/* own_inet_addr(3), find out my own network interfaces
/* match_list(3), generic pattern matching engine
/* match_ops(3), generic pattern matching operators
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <string_list.h>
#include <myaddrinfo.h>
#include <valid_mailhost_addr.h>
/* Global library. */
#include <mail_params.h>
#include <own_inet_addr.h>
#include <resolve_local.h>
/* Application-specific */
static STRING_LIST *resolve_local_list;
/* resolve_local_init - initialize lookup table */
void resolve_local_init(void)
{
/* Allow on-the-fly update to make testing easier. */
if (resolve_local_list)
string_list_free(resolve_local_list);
resolve_local_list = string_list_init(VAR_MYDEST, MATCH_FLAG_RETURN,
var_mydest);
}
/* resolve_local - match domain against list of local destinations */
int resolve_local(const char *addr)
{
char *saved_addr = mystrdup(addr);
char *dest;
const char *bare_dest;
struct addrinfo *res0 = 0;
ssize_t len;
/*
* The optimizer will eliminate tests that always fail.
*/
#define RETURN(x) \
do { \
myfree(saved_addr); \
if (res0) \
freeaddrinfo(res0); \
return(x); \
} while (0)
if (resolve_local_list == 0)
resolve_local_init();
/*
* Strip one trailing dot but not dot-dot.
*
* XXX This should not be distributed all over the code. Problem is,
* addresses can enter the system via multiple paths: networks, local
* forward/alias/include files, even as the result of address rewriting.
*/
len = strlen(saved_addr);
if (len == 0)
RETURN(0);
if (saved_addr[len - 1] == '.')
saved_addr[--len] = 0;
if (len == 0 || saved_addr[len - 1] == '.')
RETURN(0);
/*
* Compare the destination against the list of destinations that we
* consider local.
*/
if (string_list_match(resolve_local_list, saved_addr))
RETURN(1);
if (resolve_local_list->error != 0)
RETURN(resolve_local_list->error);
/*
* Compare the destination against the list of interface addresses that
* we are supposed to listen on.
*
* The destination may be an IPv6 address literal that was buried somewhere
* inside a deeply recursively nested address. This information comes
* from an untrusted source, and Wietse is not confident that everyone's
* getaddrinfo() etc. implementation is sufficiently robust. The syntax
* is complex enough with null field compression and with IPv4-in-IPv6
* addresses that errors are likely.
*
* The solution below is ad-hoc. We neutralize the string as soon as we
* realize that its contents could be harmful. We neutralize the string
* here, instead of neutralizing it in every resolve_local() caller.
* That's because resolve_local knows how the address is going to be
* parsed and converted into binary form.
*
* There are several more structural solutions to this.
*
* - One solution is to disallow address literals. This is not as bad as it
* seems: I have never seen actual legitimate use of address literals.
*
* - Another solution is to label each string with a trustworthiness label
* and to expect that all Postfix infrastructure will exercise additional
* caution when given a string with untrusted content. This is not likely
* to happen.
*
* FIX 200501 IPv6 patch did not require "IPv6:" prefix in numerical
* addresses.
*/
dest = saved_addr;
if (*dest == '[' && dest[len - 1] == ']') {
dest++;
dest[len -= 2] = 0;
if ((bare_dest = valid_mailhost_addr(dest, DO_GRIPE)) != 0
&& hostaddr_to_sockaddr(bare_dest, (char *) 0, 0, &res0) == 0) {
if (own_inet_addr(res0->ai_addr) || proxy_inet_addr(res0->ai_addr))
RETURN(1);
}
}
/*
* Must be remote, or a syntax error.
*/
RETURN(0);
}
#ifdef TEST
#include <vstream.h>
#include <mail_conf.h>
int main(int argc, char **argv)
{
int rc;
if (argc != 3)
msg_fatal("usage: %s mydestination domain", argv[0]);
mail_conf_read();
myfree(var_mydest);
var_mydest = mystrdup(argv[1]);
vstream_printf("mydestination=%s destination=%s %s\n", argv[1], argv[2],
(rc = resolve_local(argv[2])) > 0 ? "YES" :
rc == 0 ? "NO" : "ERROR");
vstream_fflush(VSTREAM_OUT);
return (0);
}
#endif