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