1 /* $NetBSD: server_acl.c,v 1.1.1.1 2013/01/02 18:59:00 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* server_acl 3 6 /* SUMMARY 7 /* server access list 8 /* SYNOPSIS 9 /* #include <server_acl.h> 10 /* 11 /* void server_acl_pre_jail_init(mynetworks, param_name) 12 /* const char *mynetworks; 13 /* const char *param_name; 14 /* 15 /* SERVER_ACL *server_acl_parse(extern_acl, param_name) 16 /* const char *extern_acl; 17 /* const char *param_name; 18 /* 19 /* int server_acl_eval(client_addr, intern_acl, param_name) 20 /* const char *client_addr; 21 /* SERVER_ACL *intern_acl; 22 /* const char *param_name; 23 /* DESCRIPTION 24 /* This module implements a permanent black/whitelist that 25 /* is meant to be evaluated immediately after a client connects 26 /* to a server. 27 /* 28 /* server_acl_pre_jail_init() does before-chroot initialization 29 /* for the permit_mynetworks setting. 30 /* 31 /* server_acl_parse() converts an access list from raw string 32 /* form to binary form. It should also be called as part of 33 /* before-chroot initialization. 34 /* 35 /* server_acl_eval() evaluates an access list for the specified 36 /* client address. The result is SERVER_ACL_ACT_PERMIT (permit), 37 /* SERVER_ACL_ACT_REJECT (reject), SERVER_ACL_ACT_DUNNO (no 38 /* decision), or SERVER_ACL_ACT_ERROR (error, unknown command 39 /* or database access error). 40 /* 41 /* Arguments: 42 /* .IP mynetworks 43 /* Network addresses that match "permit_mynetworks". 44 /* .IP param_name 45 /* The configuration parameter name for the access list from 46 /* main.cf. The information is used for error reporting (nested 47 /* table, unknown keyword) and to select the appropriate 48 /* behavior from parent_domain_matches_subdomains. 49 /* .IP extern_acl 50 /* External access list representation. 51 /* .IP intern_acl 52 /* Internal access list representation. 53 /* .IP client_addr 54 /* The client IP address as printable string (without []). 55 /* LICENSE 56 /* .ad 57 /* .fi 58 /* The Secure Mailer license must be distributed with this software. 59 /* AUTHOR(S) 60 /* Wietse Venema 61 /* IBM T.J. Watson Research 62 /* P.O. Box 704 63 /* Yorktown Heights, NY 10598, USA 64 /*--*/ 65 66 /* System library. */ 67 68 #include <sys_defs.h> 69 #include <string.h> 70 71 #ifdef STRCASECMP_IN_STRINGS_H 72 #include <strings.h> 73 #endif 74 75 /* Utility library. */ 76 77 #include <msg.h> 78 #include <mymalloc.h> 79 #include <stringops.h> 80 #include <dict.h> 81 82 /* Global library. */ 83 84 #include <mail_params.h> 85 #include <addr_match_list.h> 86 #include <match_parent_style.h> 87 #include <server_acl.h> 88 89 /* Application-specific. */ 90 91 #define SERVER_ACL_SEPARATORS ", \t\r\n" 92 93 static ADDR_MATCH_LIST *server_acl_mynetworks; 94 95 #define STR vstring_str 96 97 /* server_acl_pre_jail_init - initialize */ 98 99 void server_acl_pre_jail_init(const char *mynetworks, const char *origin) 100 { 101 if (server_acl_mynetworks) 102 addr_match_list_free(server_acl_mynetworks); 103 server_acl_mynetworks = 104 addr_match_list_init(MATCH_FLAG_RETURN | match_parent_style(origin), 105 mynetworks); 106 } 107 108 /* server_acl_parse - parse access list */ 109 110 SERVER_ACL *server_acl_parse(const char *extern_acl, const char *origin) 111 { 112 char *saved_acl = mystrdup(extern_acl); 113 SERVER_ACL *intern_acl = argv_alloc(1); 114 char *bp = saved_acl; 115 char *acl; 116 117 #define STREQ(x,y) (strcasecmp((x), (y)) == 0) 118 #define STRNE(x,y) (strcasecmp((x), (y)) != 0) 119 120 /* 121 * Nested tables are not allowed. Tables are opened before entering the 122 * chroot jail, while access lists are evaluated after entering the 123 * chroot jail. 124 */ 125 while ((acl = mystrtok(&bp, SERVER_ACL_SEPARATORS)) != 0) { 126 if (strchr(acl, ':') != 0) { 127 if (strchr(origin, ':') != 0) { 128 msg_warn("table %s: lookup result \"%s\" is not allowed" 129 " -- ignoring remainder of access list", 130 origin, acl); 131 argv_add(intern_acl, SERVER_ACL_NAME_DUNNO, (char *) 0); 132 break; 133 } else { 134 if (dict_handle(acl) == 0) 135 dict_register(acl, dict_open(acl, O_RDONLY, DICT_FLAG_LOCK 136 | DICT_FLAG_FOLD_FIX)); 137 } 138 } 139 argv_add(intern_acl, acl, (char *) 0); 140 } 141 argv_terminate(intern_acl); 142 143 /* 144 * Cleanup. 145 */ 146 myfree(saved_acl); 147 return (intern_acl); 148 } 149 150 /* server_acl_eval - evaluate access list */ 151 152 int server_acl_eval(const char *client_addr, SERVER_ACL * intern_acl, 153 const char *origin) 154 { 155 const char *myname = "server_acl_eval"; 156 char **cpp; 157 DICT *dict; 158 SERVER_ACL *argv; 159 const char *acl; 160 const char *dict_val; 161 int ret; 162 163 for (cpp = intern_acl->argv; (acl = *cpp) != 0; cpp++) { 164 if (msg_verbose) 165 msg_info("source=%s address=%s acl=%s", 166 origin, client_addr, acl); 167 if (STREQ(acl, SERVER_ACL_NAME_REJECT)) { 168 return (SERVER_ACL_ACT_REJECT); 169 } else if (STREQ(acl, SERVER_ACL_NAME_PERMIT)) { 170 return (SERVER_ACL_ACT_PERMIT); 171 } else if (STREQ(acl, SERVER_ACL_NAME_WL_MYNETWORKS)) { 172 if (addr_match_list_match(server_acl_mynetworks, client_addr)) 173 return (SERVER_ACL_ACT_PERMIT); 174 if (server_acl_mynetworks->error != 0) { 175 msg_warn("%s: %s: mynetworks lookup error -- ignoring the " 176 "remainder of this access list", origin, acl); 177 return (SERVER_ACL_ACT_ERROR); 178 } 179 } else if (strchr(acl, ':') != 0) { 180 if ((dict = dict_handle(acl)) == 0) 181 msg_panic("%s: unexpected dictionary: %s", myname, acl); 182 if ((dict_val = dict_get(dict, client_addr)) != 0) { 183 /* Fake up an ARGV to avoid lots of mallocs and frees. */ 184 if (dict_val[strcspn(dict_val, ":" SERVER_ACL_SEPARATORS)] == 0) { 185 ARGV_FAKE_BEGIN(fake_argv, dict_val); 186 ret = server_acl_eval(client_addr, &fake_argv, acl); 187 ARGV_FAKE_END; 188 } else { 189 argv = server_acl_parse(dict_val, acl); 190 ret = server_acl_eval(client_addr, argv, acl); 191 argv_free(argv); 192 } 193 if (ret != SERVER_ACL_ACT_DUNNO) 194 return (ret); 195 } else if (dict->error != 0) { 196 msg_warn("%s: %s: table lookup error -- ignoring the remainder " 197 "of this access list", origin, acl); 198 return (SERVER_ACL_ACT_ERROR); 199 } 200 } else if (STREQ(acl, SERVER_ACL_NAME_DUNNO)) { 201 return (SERVER_ACL_ACT_DUNNO); 202 } else { 203 msg_warn("%s: unknown command: %s -- ignoring the remainder " 204 "of this access list", origin, acl); 205 return (SERVER_ACL_ACT_ERROR); 206 } 207 } 208 if (msg_verbose) 209 msg_info("source=%s address=%s - no match", 210 origin, client_addr); 211 return (SERVER_ACL_ACT_DUNNO); 212 } 213 214 /* 215 * Access lists need testing. Not only with good inputs; error cases must 216 * also be handled appropriately. 217 */ 218 #ifdef TEST 219 #include <unistd.h> 220 #include <stdlib.h> 221 #include <vstring_vstream.h> 222 #include <name_code.h> 223 #include <split_at.h> 224 225 char *var_par_dom_match = DEF_PAR_DOM_MATCH; 226 char *var_mynetworks = ""; 227 char *var_server_acl = ""; 228 229 #define UPDATE_VAR(s,v) do { if (*(s)) myfree(s); (s) = mystrdup(v); } while (0) 230 231 int main(void) 232 { 233 VSTRING *buf = vstring_alloc(100); 234 SERVER_ACL *argv; 235 int ret; 236 int have_tty = isatty(0); 237 char *bufp; 238 char *cmd; 239 char *value; 240 const NAME_CODE acl_map[] = { 241 SERVER_ACL_NAME_ERROR, SERVER_ACL_ACT_ERROR, 242 SERVER_ACL_NAME_PERMIT, SERVER_ACL_ACT_PERMIT, 243 SERVER_ACL_NAME_REJECT, SERVER_ACL_ACT_REJECT, 244 SERVER_ACL_NAME_DUNNO, SERVER_ACL_ACT_DUNNO, 245 0, 246 }; 247 248 #define VAR_SERVER_ACL "server_acl" 249 250 while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { 251 bufp = STR(buf); 252 if (have_tty == 0) { 253 vstream_printf("> %s\n", bufp); 254 vstream_fflush(VSTREAM_OUT); 255 } 256 if (*bufp == '#') 257 continue; 258 if ((cmd = mystrtok(&bufp, " =")) == 0 || STREQ(cmd, "?")) { 259 vstream_printf("usage: %s=value|%s=value|address=value\n", 260 VAR_MYNETWORKS, VAR_SERVER_ACL); 261 } else if ((value = mystrtok(&bufp, " =")) == 0) { 262 vstream_printf("missing value\n"); 263 } else if (STREQ(cmd, VAR_MYNETWORKS)) { 264 UPDATE_VAR(var_mynetworks, value); 265 } else if (STREQ(cmd, VAR_SERVER_ACL)) { 266 UPDATE_VAR(var_server_acl, value); 267 } else if (STREQ(cmd, "address")) { 268 server_acl_pre_jail_init(var_mynetworks, VAR_SERVER_ACL); 269 argv = server_acl_parse(var_server_acl, VAR_SERVER_ACL); 270 ret = server_acl_eval(value, argv, VAR_SERVER_ACL); 271 argv_free(argv); 272 vstream_printf("%s: %s\n", value, str_name_code(acl_map, ret)); 273 } else { 274 vstream_printf("unknown command: \"%s\"\n", cmd); 275 } 276 vstream_fflush(VSTREAM_OUT); 277 } 278 vstring_free(buf); 279 exit(0); 280 } 281 282 #endif 283