1 /* $NetBSD: match_ops.c,v 1.1.1.1 2009/06/23 10:09:00 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* match_ops 3 6 /* SUMMARY 7 /* simple string or host pattern matching 8 /* SYNOPSIS 9 /* #include <match_ops.h> 10 /* 11 /* int match_string(flags, string, pattern) 12 /* int flags; 13 /* const char *string; 14 /* const char *pattern; 15 /* 16 /* int match_hostname(flags, name, pattern) 17 /* int flags; 18 /* const char *name; 19 /* const char *pattern; 20 /* 21 /* int match_hostaddr(flags, addr, pattern) 22 /* int flags; 23 /* const char *addr; 24 /* const char *pattern; 25 /* DESCRIPTION 26 /* This module implements simple string and host name or address 27 /* matching. The matching process is case insensitive. If a pattern 28 /* has the form type:name, table lookup is used instead of string 29 /* or address comparison. 30 /* 31 /* match_string() matches the string against the pattern, requiring 32 /* an exact (case-insensitive) match. The flags argument is not used. 33 /* 34 /* match_hostname() matches the host name when the hostname matches 35 /* the pattern exactly, or when the pattern matches a parent domain 36 /* of the named host. The flags argument specifies the bit-wise OR 37 /* of zero or more of the following: 38 /* .IP MATCH_FLAG_PARENT 39 /* The hostname pattern foo.com matches itself and any name below 40 /* the domain foo.com. If this flag is cleared, foo.com matches itself 41 /* only, and .foo.com matches any name below the domain foo.com. 42 /* .RE 43 /* Specify MATCH_FLAG_NONE to request none of the above. 44 /* 45 /* match_hostaddr() matches a host address when the pattern is 46 /* identical to the host address, or when the pattern is a net/mask 47 /* that contains the address. The mask specifies the number of 48 /* bits in the network part of the pattern. The flags argument is 49 /* not used. 50 /* LICENSE 51 /* .ad 52 /* .fi 53 /* The Secure Mailer license must be distributed with this software. 54 /* AUTHOR(S) 55 /* Wietse Venema 56 /* IBM T.J. Watson Research 57 /* P.O. Box 704 58 /* Yorktown Heights, NY 10598, USA 59 /*--*/ 60 61 /* System library. */ 62 63 #include <sys_defs.h> 64 #include <netinet/in.h> 65 #include <arpa/inet.h> 66 #include <string.h> 67 #include <stdlib.h> 68 69 #ifdef STRCASECMP_IN_STRINGS_H 70 #include <strings.h> 71 #endif 72 73 /* Utility library. */ 74 75 #include <msg.h> 76 #include <mymalloc.h> 77 #include <split_at.h> 78 #include <dict.h> 79 #include <match_ops.h> 80 #include <stringops.h> 81 #include <cidr_match.h> 82 83 #define MATCH_DICTIONARY(pattern) \ 84 ((pattern)[0] != '[' && strchr((pattern), ':') != 0) 85 86 /* match_string - match a string literal */ 87 88 int match_string(int unused_flags, const char *string, const char *pattern) 89 { 90 const char *myname = "match_string"; 91 int match; 92 93 if (msg_verbose) 94 msg_info("%s: %s ~? %s", myname, string, pattern); 95 96 /* 97 * Try dictionary lookup: exact match. 98 */ 99 if (MATCH_DICTIONARY(pattern)) { 100 match = (dict_lookup(pattern, string) != 0); 101 if (match != 0) 102 return (1); 103 if (dict_errno != 0) 104 msg_fatal("%s: table lookup problem", pattern); 105 return (0); 106 } 107 108 /* 109 * Try an exact string match. 110 */ 111 if (strcasecmp(string, pattern) == 0) { 112 return (1); 113 } 114 115 /* 116 * No match found. 117 */ 118 return (0); 119 } 120 121 /* match_hostname - match a host by name */ 122 123 int match_hostname(int flags, const char *name, const char *pattern) 124 { 125 const char *myname = "match_hostname"; 126 const char *pd; 127 const char *entry; 128 const char *next; 129 int match; 130 DICT *dict; 131 132 if (msg_verbose) 133 msg_info("%s: %s ~? %s", myname, name, pattern); 134 135 /* 136 * Try dictionary lookup: exact match and parent domains. 137 * 138 * Don't look up parent domain substrings with regexp maps etc. 139 */ 140 if (MATCH_DICTIONARY(pattern)) { 141 if ((dict = dict_handle(pattern)) == 0) 142 msg_panic("%s: unknown dictionary: %s", myname, pattern); 143 match = 0; 144 for (entry = name; *entry != 0; entry = next) { 145 if (entry == name || (dict->flags & DICT_FLAG_FIXED)) { 146 match = (dict_get(dict, entry) != 0); 147 if (msg_verbose > 1) 148 msg_info("%s: lookup %s:%s %s: %s", 149 myname, dict->type, dict->name, entry, 150 match ? "found" : "notfound"); 151 if (match != 0) 152 break; 153 if (dict_errno != 0) 154 msg_fatal("%s: table lookup problem", pattern); 155 } 156 if ((next = strchr(entry + 1, '.')) == 0) 157 break; 158 if (flags & MATCH_FLAG_PARENT) 159 next += 1; 160 } 161 return (match); 162 } 163 164 /* 165 * Try an exact match with the host name. 166 */ 167 if (strcasecmp(name, pattern) == 0) { 168 return (1); 169 } 170 171 /* 172 * See if the pattern is a parent domain of the hostname. 173 */ 174 else { 175 if (flags & MATCH_FLAG_PARENT) { 176 pd = name + strlen(name) - strlen(pattern); 177 if (pd > name && pd[-1] == '.' && strcasecmp(pd, pattern) == 0) 178 return (1); 179 } else if (pattern[0] == '.') { 180 pd = name + strlen(name) - strlen(pattern); 181 if (pd > name && strcasecmp(pd, pattern) == 0) 182 return (1); 183 } 184 } 185 return (0); 186 } 187 188 /* match_hostaddr - match host by address */ 189 190 int match_hostaddr(int unused_flags, const char *addr, const char *pattern) 191 { 192 const char *myname = "match_hostaddr"; 193 char *saved_patt; 194 CIDR_MATCH match_info; 195 VSTRING *err; 196 197 if (msg_verbose) 198 msg_info("%s: %s ~? %s", myname, addr, pattern); 199 200 #define V4_ADDR_STRING_CHARS "01234567890." 201 #define V6_ADDR_STRING_CHARS V4_ADDR_STRING_CHARS "abcdefABCDEF:" 202 203 if (addr[strspn(addr, V6_ADDR_STRING_CHARS)] != 0) 204 return (0); 205 206 /* 207 * Try dictionary lookup. This can be case insensitive. 208 */ 209 if (MATCH_DICTIONARY(pattern)) { 210 if (dict_lookup(pattern, addr) != 0) 211 return (1); 212 if (dict_errno != 0) 213 msg_fatal("%s: table lookup problem", pattern); 214 return (0); 215 } 216 217 /* 218 * Try an exact match with the host address. 219 */ 220 if (pattern[0] != '[') { 221 if (strcasecmp(addr, pattern) == 0) 222 return (1); 223 } else { 224 size_t addr_len = strlen(addr); 225 226 if (strncasecmp(addr, pattern + 1, addr_len) == 0 227 && strcmp(pattern + 1 + addr_len, "]") == 0) 228 return (1); 229 } 230 231 /* 232 * Light-weight tests before we get into expensive operations. 233 * 234 * - Don't bother matching IPv4 against IPv6. Postfix transforms 235 * IPv4-in-IPv6 to native IPv4 form when IPv4 support is enabled in 236 * Postfix; if not, then Postfix has no business dealing with IPv4 237 * addresses anyway. 238 * 239 * - Don't bother unless the pattern is either an IPv6 address or net/mask. 240 * 241 * We can safely skip IPv4 address patterns because their form is 242 * unambiguous and they did not match in the strcasecmp() calls above. 243 * 244 * XXX We MUST skip (parent) domain names, which may appear in NAMADR_LIST 245 * input, to avoid triggering false cidr_match_parse() errors. 246 * 247 * The last two conditions below are for backwards compatibility with 248 * earlier Postfix versions: don't abort with fatal errors on junk that 249 * was silently ignored (principle of least astonishment). 250 */ 251 if (!strchr(addr, ':') != !strchr(pattern, ':') 252 || pattern[strcspn(pattern, ":/")] == 0 253 || pattern[strspn(pattern, V4_ADDR_STRING_CHARS)] == 0 254 || pattern[strspn(pattern, V6_ADDR_STRING_CHARS "[]/")] != 0) 255 return (0); 256 257 /* 258 * No escape from expensive operations: either we have a net/mask 259 * pattern, or we have an address that can have multiple valid 260 * representations (e.g., 0:0:0:0:0:0:0:1 versus ::1, etc.). The only way 261 * to find out if the address matches the pattern is to transform 262 * everything into to binary form, and to do the comparison there. 263 */ 264 saved_patt = mystrdup(pattern); 265 if ((err = cidr_match_parse(&match_info, saved_patt, (VSTRING *) 0)) != 0) 266 msg_fatal("%s", vstring_str(err)); 267 myfree(saved_patt); 268 return (cidr_match_execute(&match_info, addr) != 0); 269 } 270