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