1/* $NetBSD: postscreen_access.c,v 1.1.1.1 2011/03/02 19:32:26 tron Exp $ */ 2 3/*++ 4/* NAME 5/* postscreen_access 3 6/* SUMMARY 7/* postscreen access list support 8/* SYNOPSIS 9/* #include <postscreen.h> 10/* 11/* void psc_acl_pre_jail_init() 12/* 13/* ARGV *psc_acl_parse(raw_acl, origin) 14/* const char *raw_acl; 15/* const char *origin; 16/* 17/* int psc_acl_eval(state, cooked_acl, origin) 18/* PSC_STATE *state; 19/* ARGV *cooked_acl; 20/* const char *origin; 21/* DESCRIPTION 22/* This module implements the permanent black/whitelist that 23/* is evaluated immediately after a client connects to postscreen. 24/* 25/* psc_acl_pre_jail_init() does before-chroot initialization. 26/* 27/* psc_acl_parse() converts an access list from raw string 28/* form to binary form. 29/* 30/* psc_acl_eval() evaluates an access list for the specified 31/* SMTP session. 32/* 33/* Arguments: 34/* .IP raw_acl 35/* String with space/comma separated commands. 36/* .IP cooked_acl 37/* The parsed access list. 38/* .IP origin 39/* This should be "postscreen_access_list" for an access list 40/* from main.cf, and the type:name of a lookup table otherwise. 41/* The information is used for error reporting (nested table, 42/* unknown keyword). 43/* .IP state 44/* Connection state. 45/* LICENSE 46/* .ad 47/* .fi 48/* The Secure Mailer license must be distributed with this software. 49/* AUTHOR(S) 50/* Wietse Venema 51/* IBM T.J. Watson Research 52/* P.O. Box 704 53/* Yorktown Heights, NY 10598, USA 54/*--*/ 55 56/* System library. */ 57 58#include <sys_defs.h> 59#include <string.h> 60 61#ifdef STRCASECMP_IN_STRINGS_H 62#include <strings.h> 63#endif 64 65/* Utility library. */ 66 67#include <msg.h> 68#include <mymalloc.h> 69#include <stringops.h> 70 71/* Global library. */ 72 73#include <mail_params.h> 74#include <addr_match_list.h> 75#include <match_parent_style.h> 76 77/* Application-specific. */ 78 79#include <postscreen.h> 80 81#define PSC_ACL_SEPARATORS ", \t\r" 82 83static ADDR_MATCH_LIST *psc_mynetworks; 84 85/* psc_acl_pre_jail_init - initialize */ 86 87void psc_acl_pre_jail_init(void) 88{ 89 if (psc_mynetworks) 90 addr_match_list_free(psc_mynetworks); 91 psc_mynetworks = addr_match_list_init(match_parent_style(VAR_MYNETWORKS), 92 var_mynetworks); 93} 94 95/* psc_acl_parse - parse access list */ 96 97ARGV *psc_acl_parse(const char *acl, const char *origin) 98{ 99 char *saved_checks = mystrdup(acl); 100 ARGV *argv = argv_alloc(1); 101 char *bp = saved_checks; 102 char *name; 103 104#define STREQ(x,y) (strcasecmp((x), (y)) == 0) 105#define STRNE(x,y) (strcasecmp((x), (y)) != 0) 106 107 /* 108 * Nested tables are not allowed. Tables are opened before entering the 109 * chroot jail, while access lists are evaluated after entering the 110 * chroot jail. 111 */ 112 while ((name = mystrtok(&bp, PSC_ACL_SEPARATORS)) != 0) { 113 if (strchr(name, ':') != 0) { 114 if (STRNE(origin, VAR_PSC_ACL)) { 115 msg_warn("table %s: lookup result \"%s\" is not allowed" 116 " -- ignoring remainder of access list", 117 origin, name); 118 argv_add(argv, PSC_ACL_NAME_DUNNO, (char *) 0); 119 break; 120 } else { 121 if (dict_handle(name) == 0) 122 dict_register(name, dict_open(name, O_RDONLY, DICT_FLAG_LOCK 123 | DICT_FLAG_FOLD_FIX)); 124 } 125 } 126 argv_add(argv, name, (char *) 0); 127 } 128 argv_terminate(argv); 129 130 /* 131 * Cleanup. 132 */ 133 myfree(saved_checks); 134 return (argv); 135} 136 137/* psc_acl_eval - evaluate access list */ 138 139int psc_acl_eval(PSC_STATE *state, ARGV *acl, const char *origin) 140{ 141 const char *myname = "psc_acl_eval"; 142 char **cpp; 143 DICT *dict; 144 ARGV *argv; 145 const char *name; 146 const char *dict_val; 147 int ret; 148 149 for (cpp = acl->argv; (name = *cpp) != 0; cpp++) { 150 if (msg_verbose) 151 msg_info("source=%s address=%s acl=%s", 152 origin, state->smtp_client_addr, name); 153 if (STREQ(name, PSC_ACL_NAME_BLACKLIST)) { 154 return (PSC_ACL_ACT_BLACKLIST); 155 } else if (STREQ(name, PSC_ACL_NAME_WHITELIST)) { 156 return (PSC_ACL_ACT_WHITELIST); 157 } else if (STREQ(name, PSC_ACL_NAME_WL_MYNETWORKS)) { 158 if (addr_match_list_match(psc_mynetworks, state->smtp_client_addr)) 159 return (PSC_ACL_ACT_WHITELIST); 160 } else if (strchr(name, ':') != 0) { 161 if ((dict = dict_handle(name)) == 0) 162 msg_panic("%s: unexpected dictionary: %s", myname, name); 163 if ((dict_val = dict_get(dict, state->smtp_client_addr)) != 0) { 164 argv = psc_acl_parse(dict_val, name); 165 ret = psc_acl_eval(state, argv, name); 166 argv_free(argv); 167 if (ret != PSC_ACL_ACT_DUNNO) 168 return (ret); 169 } else if (dict_errno != 0) { 170 msg_warn("%s: table lookup error -- ignoring the remainder " 171 "of this access list", name); 172 return (PSC_ACL_ACT_ERROR); 173 } 174 } else if (STREQ(name, PSC_ACL_NAME_DUNNO)) { 175 return (PSC_ACL_ACT_DUNNO); 176 } else { 177 msg_warn("%s: unknown command: %s -- ignoring the remainder " 178 "of this access list", origin, name); 179 return (PSC_ACL_ACT_ERROR); 180 } 181 } 182 if (msg_verbose) 183 msg_info("source=%s address=%s - no match", 184 origin, state->smtp_client_addr); 185 return (PSC_ACL_ACT_DUNNO); 186} 187 188 /* 189 * Access lists need testing. Not only with good inputs; error cases must 190 * also be handled appropriately. 191 */ 192#ifdef TEST 193#include <unistd.h> 194#include <stdlib.h> 195#include <vstring_vstream.h> 196#include <name_code.h> 197#include <split_at.h> 198 199char *var_par_dom_match = DEF_PAR_DOM_MATCH; 200char *var_mynetworks = ""; 201char *var_psc_acl = ""; 202 203#define UPDATE_VAR(s,v) do { if (*(s)) myfree(s); (s) = mystrdup(v); } while (0) 204 205int main(void) 206{ 207 VSTRING *buf = vstring_alloc(100); 208 PSC_STATE state; 209 ARGV *argv; 210 int ret; 211 int have_tty = isatty(0); 212 char *bufp; 213 char *cmd; 214 char *value; 215 const NAME_CODE acl_map[] = { 216 PSC_ACL_NAME_ERROR, PSC_ACL_ACT_ERROR, 217 PSC_ACL_NAME_WHITELIST, PSC_ACL_ACT_WHITELIST, 218 PSC_ACL_NAME_BLACKLIST, PSC_ACL_ACT_BLACKLIST, 219 PSC_ACL_NAME_DUNNO, PSC_ACL_ACT_DUNNO, 220 0, 221 }; 222 223 while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { 224 bufp = STR(buf); 225 if (have_tty == 0) { 226 vstream_printf("> %s\n", bufp); 227 vstream_fflush(VSTREAM_OUT); 228 } 229 if (*bufp == '#') 230 continue; 231 if ((cmd = mystrtok(&bufp, " =")) == 0 || STREQ(cmd, "?")) { 232 vstream_printf("usage: %s=value|%s=value|address=value\n", 233 VAR_MYNETWORKS, VAR_PSC_ACL); 234 } else if ((value = mystrtok(&bufp, " =")) == 0) { 235 vstream_printf("missing value\n"); 236 } else if (STREQ(cmd, VAR_MYNETWORKS)) { 237 UPDATE_VAR(var_mynetworks, value); 238 } else if (STREQ(cmd, VAR_PSC_ACL)) { 239 UPDATE_VAR(var_psc_acl, value); 240 } else if (STREQ(cmd, "address")) { 241 psc_acl_pre_jail_init(); 242 argv = psc_acl_parse(var_psc_acl, VAR_PSC_ACL); 243 state.smtp_client_addr = value; 244 ret = psc_acl_eval(&state, argv, VAR_PSC_ACL); 245 argv_free(argv); 246 vstream_printf("%s: %s\n", value, str_name_code(acl_map, ret)); 247 } else { 248 vstream_printf("unknown command: \"%s\"\n", cmd); 249 } 250 vstream_fflush(VSTREAM_OUT); 251 } 252 vstring_free(buf); 253 exit(0); 254} 255 256#endif 257