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