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