xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/server_acl.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
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