1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2003-2020 The ProFTPD Project team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, the ProFTPD Project team and other respective
20 * copyright holders give permission to link this program with OpenSSL, and
21 * distribute the resulting executable, without including the source code for
22 * OpenSSL in the source distribution.
23 */
24
25 /* Class routines */
26
27 #include "conf.h"
28
29 static const char *trace_channel = "class";
30
31 /* Store the defined Classes in a linked list. If many Classes are defined,
32 * this may need to be redefined to be a collision-chained hash.
33 */
34 static pr_class_t *class_list = NULL;
35 static pr_class_t *curr_cls = NULL;
36
pr_class_get(const pr_class_t * prev)37 const pr_class_t *pr_class_get(const pr_class_t *prev) {
38 if (prev != NULL) {
39 return prev->cls_next;
40 }
41
42 if (class_list == NULL) {
43 errno = ENOENT;
44 }
45
46 return class_list;
47 }
48
pr_class_satisfied(pool * p,const pr_class_t * cls,const pr_netaddr_t * addr)49 int pr_class_satisfied(pool *p, const pr_class_t *cls,
50 const pr_netaddr_t *addr) {
51 register unsigned int i;
52 array_header *acl_list;
53 const pr_netacl_t **acls;
54 int next_class = FALSE;
55
56 if (cls == NULL ||
57 addr == NULL) {
58 errno = EINVAL;
59 return -1;
60 }
61
62 acl_list = cls->cls_acls;
63 acls = acl_list->elts;
64
65 /* For each ACL rule in this class, compare the rule against the given
66 * address. The address matches the given class depending on the
67 * Satisfy setting: if "any", the class matches if any rule matches;
68 * if "all", the class matches only if _all_ rules match.
69 */
70 for (i = 0; i < acl_list->nelts; i++) {
71 int res;
72
73 pr_signals_handle();
74
75 if (next_class) {
76 break;
77 }
78
79 if (acls[i] == NULL) {
80 continue;
81 }
82
83 switch (cls->cls_satisfy) {
84 case PR_CLASS_SATISFY_ANY:
85 pr_trace_msg(trace_channel, 6,
86 "checking addr '%s' (%s) against class '%s' rule: %s "
87 "(requires any ACL matching)", pr_netaddr_get_ipstr(addr),
88 pr_netaddr_get_dnsstr(addr), cls->cls_name,
89 pr_netacl_get_str(p, acls[i]));
90
91 res = pr_netacl_match(acls[i], addr);
92 if (res == 1) {
93 return TRUE;
94 }
95 break;
96
97 case PR_CLASS_SATISFY_ALL:
98 pr_trace_msg(trace_channel, 6,
99 "checking addr '%s' (%s) against class '%s' ACL: %s "
100 "(requires all ACLs matching)", pr_netaddr_get_ipstr(addr),
101 pr_netaddr_get_dnsstr(addr), cls->cls_name,
102 pr_netacl_get_str(p, acls[i]));
103
104 res = pr_netacl_match(acls[i], addr);
105 if (res <= 0) {
106 next_class = TRUE;
107 }
108 break;
109 }
110 }
111
112 /* If this is a "Satisfy all" class, and all rules have matched
113 * (positively or negatively), then it matches the address.
114 */
115 if (next_class == FALSE &&
116 cls->cls_satisfy == PR_CLASS_SATISFY_ALL &&
117 i == acl_list->nelts) {
118 return TRUE;
119 }
120
121 return FALSE;
122 }
123
pr_class_match_addr(const pr_netaddr_t * addr)124 const pr_class_t *pr_class_match_addr(const pr_netaddr_t *addr) {
125 pr_class_t *cls = NULL, *iter;
126 pool *tmp_pool;
127
128 if (addr == NULL) {
129 errno = EINVAL;
130 return NULL;
131 }
132
133 tmp_pool = make_sub_pool(permanent_pool);
134
135 for (iter = class_list; iter; iter = iter->cls_next) {
136 int res;
137
138 res = pr_class_satisfied(tmp_pool, iter, addr);
139 if (res == TRUE) {
140 cls = iter;
141 break;
142 }
143 }
144
145 destroy_pool(tmp_pool);
146
147 if (cls == NULL) {
148 errno = ENOENT;
149 }
150
151 return cls;
152 }
153
pr_class_find(const char * name)154 const pr_class_t *pr_class_find(const char *name) {
155 pr_class_t *cls;
156
157 if (name == NULL) {
158 errno = EINVAL;
159 return NULL;
160 }
161
162 for (cls = class_list; cls; cls = cls->cls_next) {
163 pr_signals_handle();
164 if (strcmp(cls->cls_name, name) == 0) {
165 return cls;
166 }
167 }
168
169 errno = ENOENT;
170 return NULL;
171 }
172
pr_class_add_acl(const pr_netacl_t * acl)173 int pr_class_add_acl(const pr_netacl_t *acl) {
174
175 if (acl == NULL) {
176 errno = EINVAL;
177 return -1;
178 }
179
180 if (curr_cls == NULL) {
181 errno = EPERM;
182 return -1;
183 }
184
185 /* Add this ACL rule to the current Class. */
186 if (curr_cls->cls_acls == NULL) {
187 curr_cls->cls_acls = make_array(curr_cls->cls_pool, 1,
188 sizeof(pr_netacl_t *));
189 }
190
191 *((pr_netacl_t **) push_array(curr_cls->cls_acls)) =
192 pr_netacl_dup(curr_cls->cls_pool, acl);
193
194 return 0;
195 }
196
pr_class_set_satisfy(int satisfy)197 int pr_class_set_satisfy(int satisfy) {
198 if (curr_cls == NULL) {
199 errno = EPERM;
200 return -1;
201 }
202
203 if (satisfy != PR_CLASS_SATISFY_ANY &&
204 satisfy != PR_CLASS_SATISFY_ALL) {
205 errno = EINVAL;
206 return -1;
207 }
208
209 /* Set the Satisfy flag on the current Class. */
210 curr_cls->cls_satisfy = satisfy;
211
212 return 0;
213 }
214
pr_class_add_note(const char * key,void * value,size_t valuesz)215 int pr_class_add_note(const char *key, void *value, size_t valuesz) {
216 int res;
217
218 if (key == NULL) {
219 errno = EINVAL;
220 return -1;
221 }
222
223 if (curr_cls == NULL) {
224 errno = EPERM;
225 return -1;
226 }
227
228 res = pr_table_add(curr_cls->cls_notes, key, value, valuesz);
229 return res;
230 }
231
pr_class_open(pool * p,const char * name)232 int pr_class_open(pool *p, const char *name) {
233 pr_class_t *cls;
234 pool *cls_pool;
235
236 if (p == NULL ||
237 name == NULL) {
238 errno = EINVAL;
239 return -1;
240 }
241
242 /* Allocate a sub pool from the given pool, from which a new Class will
243 * be allocated.
244 */
245 cls_pool = make_sub_pool(p);
246 pr_pool_tag(cls_pool, "<Class> Pool");
247
248 cls = pcalloc(cls_pool, sizeof(pr_class_t));
249 cls->cls_pool = cls_pool;
250 cls->cls_name = pstrdup(cls->cls_pool, name);
251 cls->cls_satisfy = PR_CLASS_SATISFY_ANY;
252 cls->cls_notes = pr_table_nalloc(cls_pool, 0, 1);
253
254 /* Change the configuration context type. */
255 main_server->config_type = CONF_CLASS;
256
257 curr_cls = cls;
258 return 0;
259 }
260
pr_class_close(void)261 int pr_class_close(void) {
262
263 /* If there is no current Class, there is nothing to do. */
264 if (curr_cls == NULL) {
265 return 0;
266 }
267
268 /* If there are no client rules in this class, simply remove it. No need
269 * to waste space.
270 */
271 if (curr_cls->cls_acls == NULL) {
272 destroy_pool(curr_cls->cls_pool);
273 curr_cls = NULL;
274
275 /* Restore the configuration context type. */
276 main_server->config_type = CONF_ROOT;
277
278 errno = EINVAL;
279 return -1;
280 }
281
282 /* Make sure the list of clients is NULL-terminated. */
283 push_array(curr_cls->cls_acls);
284
285 /* Now add the current Class to the end of the list. */
286 if (class_list) {
287 pr_class_t *ci;
288
289 ci = class_list;
290 while (ci != NULL &&
291 ci->cls_next != NULL) {
292 ci = ci->cls_next;
293 }
294
295 ci->cls_next = curr_cls;
296
297 } else {
298 class_list = curr_cls;
299 }
300
301 curr_cls = NULL;
302
303 /* Restore the configuration context type. */
304 main_server->config_type = CONF_ROOT;
305
306 return 0;
307 }
308
init_class(void)309 void init_class(void) {
310 class_list = NULL;
311 }
312