#include <sys_defs.h>
#include <string.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
#include <dict.h>
#include <mail_params.h>
#include <addr_match_list.h>
#include <match_parent_style.h>
#include <mynetworks.h>
#include <server_acl.h>
static ADDR_MATCH_LIST *server_acl_mynetworks;
static ADDR_MATCH_LIST *server_acl_mynetworks_host;
#define STR vstring_str
void server_acl_pre_jail_init(const char *mynetworks, const char *origin)
{
if (server_acl_mynetworks) {
addr_match_list_free(server_acl_mynetworks);
if (server_acl_mynetworks_host)
addr_match_list_free(server_acl_mynetworks_host);
}
server_acl_mynetworks =
addr_match_list_init(origin, MATCH_FLAG_RETURN
| match_parent_style(origin), mynetworks);
if (warn_compat_break_mynetworks_style)
server_acl_mynetworks_host =
addr_match_list_init(origin, MATCH_FLAG_RETURN
| match_parent_style(origin), mynetworks_host());
}
SERVER_ACL *server_acl_parse(const char *extern_acl, const char *origin)
{
char *saved_acl = mystrdup(extern_acl);
SERVER_ACL *intern_acl = argv_alloc(1);
char *bp = saved_acl;
char *acl;
#define STREQ(x,y) (strcasecmp((x), (y)) == 0)
#define STRNE(x,y) (strcasecmp((x), (y)) != 0)
while ((acl = mystrtokq(&bp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
if (strchr(acl, ':') != 0) {
if (strchr(origin, ':') != 0) {
msg_warn("table %s: lookup result \"%s\" is not allowed"
" -- ignoring remainder of access list",
origin, acl);
argv_add(intern_acl, SERVER_ACL_NAME_DUNNO, (char *) 0);
break;
} else {
if (dict_handle(acl) == 0)
dict_register(acl, dict_open(acl, O_RDONLY, DICT_FLAG_LOCK
| DICT_FLAG_FOLD_FIX
| DICT_FLAG_UTF8_REQUEST));
}
}
argv_add(intern_acl, acl, (char *) 0);
}
argv_terminate(intern_acl);
myfree(saved_acl);
return (intern_acl);
}
int server_acl_eval(const char *client_addr, SERVER_ACL * intern_acl,
const char *origin)
{
const char *myname = "server_acl_eval";
char **cpp;
DICT *dict;
SERVER_ACL *argv;
const char *acl;
const char *dict_val;
int ret;
for (cpp = intern_acl->argv; (acl = *cpp) != 0; cpp++) {
if (msg_verbose)
msg_info("source=%s address=%s acl=%s",
origin, client_addr, acl);
if (STREQ(acl, SERVER_ACL_NAME_REJECT)) {
return (SERVER_ACL_ACT_REJECT);
} else if (STREQ(acl, SERVER_ACL_NAME_PERMIT)) {
return (SERVER_ACL_ACT_PERMIT);
} else if (STREQ(acl, SERVER_ACL_NAME_WL_MYNETWORKS)) {
if (addr_match_list_match(server_acl_mynetworks, client_addr)) {
if (warn_compat_break_mynetworks_style
&& !addr_match_list_match(server_acl_mynetworks_host,
client_addr))
msg_info("using backwards-compatible default setting "
VAR_MYNETWORKS_STYLE "=%s to permit "
"request from client \"%s\"",
var_mynetworks_style, client_addr);
return (SERVER_ACL_ACT_PERMIT);
}
if (server_acl_mynetworks->error != 0) {
msg_warn("%s: %s: mynetworks lookup error -- ignoring the "
"remainder of this access list", origin, acl);
return (SERVER_ACL_ACT_ERROR);
}
} else if (strchr(acl, ':') != 0) {
if ((dict = dict_handle(acl)) == 0)
msg_panic("%s: unexpected dictionary: %s", myname, acl);
if ((dict_val = dict_get(dict, client_addr)) != 0) {
if (dict_val[strcspn(dict_val, ":" CHARS_COMMA_SP)] == 0) {
ARGV_FAKE_BEGIN(fake_argv, dict_val);
ret = server_acl_eval(client_addr, &fake_argv, acl);
ARGV_FAKE_END;
} else {
argv = server_acl_parse(dict_val, acl);
ret = server_acl_eval(client_addr, argv, acl);
argv_free(argv);
}
if (ret != SERVER_ACL_ACT_DUNNO)
return (ret);
} else if (dict->error != 0) {
msg_warn("%s: %s: table lookup error -- ignoring the remainder "
"of this access list", origin, acl);
return (SERVER_ACL_ACT_ERROR);
}
} else if (STREQ(acl, SERVER_ACL_NAME_DUNNO)) {
return (SERVER_ACL_ACT_DUNNO);
} else {
msg_warn("%s: unknown command: %s -- ignoring the remainder "
"of this access list", origin, acl);
return (SERVER_ACL_ACT_ERROR);
}
}
if (msg_verbose)
msg_info("source=%s address=%s - no match",
origin, client_addr);
return (SERVER_ACL_ACT_DUNNO);
}
#ifdef TEST
#include <unistd.h>
#include <stdlib.h>
#include <vstring_vstream.h>
#include <name_code.h>
#include <split_at.h>
char *var_par_dom_match = DEF_PAR_DOM_MATCH;
char *var_mynetworks = "";
char *var_server_acl = "";
#define UPDATE_VAR(s,v) do { if (*(s)) myfree(s); (s) = mystrdup(v); } while (0)
int main(void)
{
VSTRING *buf = vstring_alloc(100);
SERVER_ACL *argv;
int ret;
int have_tty = isatty(0);
char *bufp;
char *cmd;
char *value;
const NAME_CODE acl_map[] = {
SERVER_ACL_NAME_ERROR, SERVER_ACL_ACT_ERROR,
SERVER_ACL_NAME_PERMIT, SERVER_ACL_ACT_PERMIT,
SERVER_ACL_NAME_REJECT, SERVER_ACL_ACT_REJECT,
SERVER_ACL_NAME_DUNNO, SERVER_ACL_ACT_DUNNO,
0,
};
#define VAR_SERVER_ACL "server_acl"
while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
bufp = STR(buf);
if (have_tty == 0) {
vstream_printf("> %s\n", bufp);
vstream_fflush(VSTREAM_OUT);
}
if (*bufp == '#')
continue;
if ((cmd = mystrtok(&bufp, " =")) == 0 || STREQ(cmd, "?")) {
vstream_printf("usage: %s=value|%s=value|address=value\n",
VAR_MYNETWORKS, VAR_SERVER_ACL);
} else if ((value = mystrtok(&bufp, " =")) == 0) {
vstream_printf("missing value\n");
} else if (STREQ(cmd, VAR_MYNETWORKS)) {
UPDATE_VAR(var_mynetworks, value);
} else if (STREQ(cmd, VAR_SERVER_ACL)) {
UPDATE_VAR(var_server_acl, value);
} else if (STREQ(cmd, "address")) {
server_acl_pre_jail_init(var_mynetworks, VAR_MYNETWORKS);
argv = server_acl_parse(var_server_acl, VAR_SERVER_ACL);
ret = server_acl_eval(value, argv, VAR_SERVER_ACL);
argv_free(argv);
vstream_printf("%s: %s\n", value, str_name_code(acl_map, ret));
} else {
vstream_printf("unknown command: \"%s\"\n", cmd);
}
vstream_fflush(VSTREAM_OUT);
}
vstring_free(buf);
exit(0);
}
#endif