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 /* Network ACL routines */
26
27 #include "conf.h"
28
29 extern int ServerUseReverseDNS;
30
31 struct pr_netacl_t {
32 pr_netacl_type_t type;
33 const char *aclstr;
34
35 char *pattern;
36 int negated;
37 const pr_netaddr_t *addr;
38 unsigned int masklen;
39 };
40
41 static const char *trace_channel = "netacl";
42
pr_netacl_get_type(const pr_netacl_t * acl)43 pr_netacl_type_t pr_netacl_get_type(const pr_netacl_t *acl) {
44 return acl->type;
45 }
46
47 /* Returns 1 if there was a positive match, -1 if there was a negative
48 * match, -2 if there was an error, and zero if there was no match at all.
49 */
pr_netacl_match(const pr_netacl_t * acl,const pr_netaddr_t * addr)50 int pr_netacl_match(const pr_netacl_t *acl, const pr_netaddr_t *addr) {
51 pool *tmp_pool;
52 int res = 0;
53
54 if (acl == NULL ||
55 addr == NULL) {
56 errno = EINVAL;
57 return -2;
58 }
59
60 tmp_pool = make_sub_pool(permanent_pool);
61
62 switch (acl->type) {
63 case PR_NETACL_TYPE_ALL:
64 pr_trace_msg(trace_channel, 10, "addr '%s' matched rule 'ALL' ('%s')",
65 pr_netaddr_get_ipstr(addr), pr_netacl_get_str(tmp_pool, acl));
66 res = 1;
67 break;
68
69 case PR_NETACL_TYPE_NONE:
70 pr_trace_msg(trace_channel, 10, "addr '%s' matched rule 'NONE'",
71 pr_netaddr_get_ipstr(addr));
72 res = -1;
73 break;
74
75 case PR_NETACL_TYPE_IPMASK:
76 pr_trace_msg(trace_channel, 10,
77 "checking addr '%s' against IP mask rule '%s'",
78 pr_netaddr_get_ipstr(addr), acl->aclstr);
79
80 if (pr_netaddr_ncmp(addr, acl->addr, acl->masklen) == 0) {
81 pr_trace_msg(trace_channel, 10, "addr '%s' matched IP mask rule '%s'",
82 pr_netaddr_get_ipstr(addr), acl->aclstr);
83
84 if (acl->negated) {
85 res = -1;
86
87 } else {
88 res = 1;
89 }
90
91 } else {
92 pr_trace_msg(trace_channel, 10,
93 "addr '%s' did NOT match IP mask rule '%s'",
94 pr_netaddr_get_ipstr(addr), acl->aclstr);
95
96 if (acl->negated) {
97 res = 1;
98 }
99 }
100 break;
101
102 case PR_NETACL_TYPE_IPMATCH:
103 pr_trace_msg(trace_channel, 10,
104 "checking addr '%s' against IP address rule '%s'",
105 pr_netaddr_get_ipstr(addr), acl->aclstr);
106
107 if (pr_netaddr_cmp(addr, acl->addr) == 0) {
108 pr_trace_msg(trace_channel, 10,
109 "addr '%s' matched IP address rule '%s'",
110 pr_netaddr_get_ipstr(addr), acl->aclstr);
111
112 if (acl->negated) {
113 res = -1;
114
115 } else {
116 res = 1;
117 }
118
119 } else {
120 pr_trace_msg(trace_channel, 10,
121 "addr '%s' did NOT match IP address rule '%s'",
122 pr_netaddr_get_ipstr(addr), acl->aclstr);
123
124 if (acl->negated) {
125 res = 1;
126 }
127 }
128 break;
129
130 case PR_NETACL_TYPE_DNSMATCH:
131 pr_trace_msg(trace_channel, 10,
132 "checking addr '%s' against DNS name rule '%s'",
133 pr_netaddr_get_dnsstr(addr), acl->pattern);
134
135 if (strcmp(pr_netaddr_get_dnsstr(addr), acl->pattern) == 0) {
136 pr_trace_msg(trace_channel, 10,
137 "addr '%s' (%s) matched DNS name rule '%s'",
138 pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr),
139 acl->aclstr);
140
141 if (acl->negated) {
142 res = -1;
143
144 } else {
145 res = 1;
146 }
147
148 } else {
149 pr_trace_msg(trace_channel, 10,
150 "addr '%s' (%s) did NOT match DNS name rule '%s'",
151 pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr),
152 acl->aclstr);
153
154 if (acl->negated) {
155 res = 1;
156 }
157 }
158 break;
159
160 case PR_NETACL_TYPE_IPGLOB:
161 pr_trace_msg(trace_channel, 10,
162 "checking addr '%s' against IP glob rule '%s'",
163 pr_netaddr_get_ipstr(addr), acl->aclstr);
164
165 if (pr_netaddr_fnmatch(addr, acl->pattern,
166 PR_NETADDR_MATCH_IP) == TRUE) {
167 pr_trace_msg(trace_channel, 10,
168 "addr '%s' matched IP glob rule '%s'",
169 pr_netaddr_get_ipstr(addr), acl->aclstr);
170
171 if (acl->negated) {
172 res = -1;
173
174 } else {
175 res = 1;
176 }
177
178 } else {
179 pr_trace_msg(trace_channel, 10,
180 "addr '%s' did NOT match IP glob rule '%s'",
181 pr_netaddr_get_ipstr(addr), acl->aclstr);
182
183 if (acl->negated) {
184 res = 1;
185 }
186 }
187 break;
188
189 case PR_NETACL_TYPE_DNSGLOB:
190 if (ServerUseReverseDNS) {
191 pr_trace_msg(trace_channel, 10,
192 "checking addr '%s' against DNS glob rule '%s'",
193 pr_netaddr_get_dnsstr(addr), acl->pattern);
194
195 if (pr_netaddr_fnmatch(addr, acl->pattern,
196 PR_NETADDR_MATCH_DNS) == TRUE) {
197 pr_trace_msg(trace_channel, 10,
198 "addr '%s' (%s) matched DNS glob rule '%s'",
199 pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr),
200 acl->aclstr);
201
202 if (acl->negated) {
203 res = -1;
204
205 } else {
206 res = 1;
207 }
208
209 } else {
210 pr_trace_msg(trace_channel, 10,
211 "addr '%s' (%s) did NOT match DNS glob rule '%s'",
212 pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr),
213 acl->aclstr);
214
215 if (acl->negated) {
216 res = 1;
217 }
218 }
219
220 } else {
221 pr_trace_msg(trace_channel, 10,
222 "skipping comparing addr '%s' (%s) against DNS glob rule '%s' "
223 "because UseReverseDNS is off", pr_netaddr_get_ipstr(addr),
224 pr_netaddr_get_dnsstr(addr), acl->aclstr);
225 }
226 break;
227 }
228
229 destroy_pool(tmp_pool);
230 return res;
231 }
232
pr_netacl_create(pool * p,char * aclstr)233 pr_netacl_t *pr_netacl_create(pool *p, char *aclstr) {
234 pr_netacl_t *acl;
235 char *cp, *aclstr_dup;
236
237 if (p == NULL ||
238 aclstr == NULL) {
239 errno = EINVAL;
240 return NULL;
241 }
242
243 if (strlen(aclstr) == 0) {
244 errno = EINVAL;
245 return NULL;
246 }
247
248 /* Parse the given rule into a netacl object. */
249 acl = pcalloc(p, sizeof(pr_netacl_t));
250
251 aclstr_dup = pstrdup(p, aclstr);
252
253 if (strncasecmp(aclstr, "all", 4) == 0) {
254 aclstr_dup = pstrdup(p, "all");
255 acl->type = PR_NETACL_TYPE_ALL;
256
257 } else if (strncasecmp(aclstr, "none", 5) == 0) {
258 aclstr_dup = pstrdup(p, "none");
259 acl->type = PR_NETACL_TYPE_NONE;
260
261 } else if ((cp = strchr(aclstr, '/')) != NULL) {
262 char *tmp;
263
264 acl->type = PR_NETACL_TYPE_IPMASK;
265 *cp = '\0';
266
267 /* Check if the given rule is negated. */
268 if (*aclstr == '!') {
269 acl->negated = TRUE;
270 aclstr++;
271 }
272
273 /* We have some type of IP/mask, either IPv4 or IPv6. We know that colons
274 * will only appear in IPv6 addresses, so...
275 */
276
277 if (strspn(aclstr, "0123456789ABCDEFabcdef.:") != strlen(aclstr)) {
278 errno = EINVAL;
279 return NULL;
280 }
281
282 acl->addr = pr_netaddr_get_addr(p, aclstr, NULL);
283 if (acl->addr == NULL) {
284 return NULL;
285 }
286
287 /* Determine what the given bitmask is. */
288 acl->masklen = strtol(cp + 1, &tmp, 10);
289
290 if (tmp && *tmp) {
291 /* Invalid bitmask syntax. */
292 errno = EINVAL;
293 return NULL;
294 }
295
296 *cp = '/';
297
298 /* Make sure the given mask length is appropriate for the address. */
299 switch (pr_netaddr_get_family(acl->addr)) {
300 case AF_INET: {
301 /* Make sure that the given number of bits is not more than supported
302 * for IPv4 addresses (32).
303 */
304 if (acl->masklen > 32) {
305 errno = EINVAL;
306 return NULL;
307 }
308
309 break;
310 }
311
312 #ifdef PR_USE_IPV6
313 case AF_INET6: {
314 if (pr_netaddr_use_ipv6()) {
315 if (acl->masklen > 128) {
316 errno = EINVAL;
317 return NULL;
318
319 } else if (pr_netaddr_is_v4mappedv6(acl->addr) == TRUE &&
320 acl->masklen > 32) {
321 /* The admin may be trying to use IPv6-style masks on IPv4-mapped
322 * IPv6 addresses, which of course will not work as expected.
323 * If the mask is 32 bits or more, warn the admin.
324 */
325 pr_log_pri(PR_LOG_WARNING, "warning: possibly using IPv6-style netmask on IPv4-mapped IPv6 address, which will not work as expected");
326 pr_trace_msg(trace_channel, 1, "possibly using IPv6-style netmask on IPv4-mapped IPv6 address (%s), which will not work as expected", aclstr);
327
328 break;
329 }
330 }
331 }
332 #endif /* PR_USE_IPV6 */
333
334 default:
335 break;
336 }
337
338 #ifdef PR_USE_IPV6
339 } else if (pr_netaddr_use_ipv6() &&
340 strspn(aclstr, "0123456789ABCDEFabcdef.:!") != strlen(aclstr)) {
341 #else
342 } else if (strspn(aclstr, "0123456789.!") != strlen(aclstr)) {
343 #endif /* PR_USE_IPV6 */
344
345 /* Check if the given rule is negated. */
346 if (*aclstr == '!') {
347 acl->negated = TRUE;
348 aclstr++;
349 }
350
351 /* If there are any glob characters (e.g. '{', '[', '*', or '?'), or if the
352 * first character is a '.', then treat the rule as a glob.
353 */
354 if (strpbrk(aclstr, "{[*?")) {
355 register unsigned int i;
356 size_t aclstr_len = strlen(aclstr);
357 pr_netacl_type_t netacl_type = PR_NETACL_TYPE_IPGLOB;
358
359 /* Is this a DNS glob, or an IP address glob? To find out, see if there
360 * are any non-IP characters (i.e. alphabetical characters, taking IPv6
361 * into account).
362 */
363 for (i = 0; i < aclstr_len; i++) {
364 if (PR_ISALPHA(aclstr[i])) {
365 #ifdef PR_USE_IPV6
366 if (pr_netaddr_use_ipv6()) {
367 if (aclstr[i] == 'A' || aclstr[i] == 'a' ||
368 aclstr[i] == 'B' || aclstr[i] == 'b' ||
369 aclstr[i] == 'C' || aclstr[i] == 'c' ||
370 aclstr[i] == 'D' || aclstr[i] == 'd' ||
371 aclstr[i] == 'E' || aclstr[i] == 'e' ||
372 aclstr[i] == 'F' || aclstr[i] == 'f') {
373 continue;
374 }
375
376 netacl_type = PR_NETACL_TYPE_DNSGLOB;
377 break;
378
379 } else {
380 netacl_type = PR_NETACL_TYPE_DNSGLOB;
381 break;
382 }
383 #else
384 netacl_type = PR_NETACL_TYPE_DNSGLOB;
385 break;
386 #endif /* PR_USE_IPV6 */
387 }
388 }
389
390 acl->type = netacl_type;
391 acl->pattern = pstrdup(p, aclstr);
392
393 } else if (*aclstr == '.') {
394 acl->type = PR_NETACL_TYPE_DNSGLOB;
395 acl->pattern = pstrcat(p, "*", aclstr, NULL);
396
397 } else {
398 acl->type = PR_NETACL_TYPE_DNSMATCH;
399 acl->pattern = pstrdup(p, aclstr);
400 }
401
402 } else if (strchr(aclstr, '.') == NULL) {
403 int use_glob = FALSE, use_dns = FALSE;
404
405 /* Is this a DNS glob, DNS match, or an IPv6 glob/match? */
406
407 /* Check if the given rule is negated. */
408 if (*aclstr == '!') {
409 acl->negated = TRUE;
410 aclstr++;
411 }
412
413 /* If there are any glob characters (e.g. '{', '[', '*', or '?'), or if the
414 * first character is a '.', then treat the rule as a glob.
415 */
416 if (strpbrk(aclstr, "{[*?")) {
417 use_glob = TRUE;
418 }
419
420 /* IPv6 addresses use colons, thus if we do not see any, it's a good
421 * bet that this is a DNS glob/match.
422 */
423 if (strchr(aclstr, ':') == NULL) {
424 use_dns = TRUE;
425
426 } else {
427 #ifdef PR_USE_IPV6
428 if (pr_netaddr_use_ipv6() == FALSE) {
429 pr_trace_msg(trace_channel, 1, "possibly using IPv6 %s '%s' when "
430 "IPv6 support is disabled, which will not work as expected",
431 use_glob ? "glob" : "match", aclstr);
432 pr_log_pri(PR_LOG_WARNING, "warning: possibly using IPv6 %s '%s' when "
433 "IPv6 support is disabled, which will not work as expected",
434 use_glob ? "glob" : "match", aclstr);
435 }
436 #else
437 pr_trace_msg(trace_channel, 1, "possibly using IPv6 %s '%s' when "
438 "IPv6 support is disabled, which will not work as expected",
439 use_glob ? "glob" : "match", aclstr);
440 pr_log_pri(PR_LOG_WARNING, "warning: possibly using IPv6 %s '%s' when "
441 "IPv6 support is disabled, which will not work as expected",
442 use_glob ? "glob" : "match", aclstr);
443 #endif /* PR_USE_IPV6 */
444 }
445
446 if (use_dns == TRUE) {
447 acl->pattern = pstrdup(p, aclstr);
448 acl->type = use_glob ? PR_NETACL_TYPE_DNSGLOB : PR_NETACL_TYPE_DNSMATCH;
449
450 } else {
451 acl->addr = pr_netaddr_get_addr(p, aclstr, NULL);
452 if (acl->addr == NULL) {
453 int xerrno = errno;
454
455 pr_trace_msg(trace_channel, 3, "unable to resolve '%s': %s", aclstr,
456 strerror(xerrno));
457
458 errno = xerrno;
459 return NULL;
460 }
461
462 acl->pattern = pstrdup(p, aclstr);
463 acl->type = use_glob ? PR_NETACL_TYPE_IPGLOB : PR_NETACL_TYPE_IPMATCH;
464 }
465
466 } else {
467
468 /* Check if the given rule is negated. */
469 if (*aclstr == '!') {
470 acl->negated = TRUE;
471 aclstr++;
472 }
473
474 /* If the last character is a '.', then treat the rule as an IP glob. */
475 if (aclstr[strlen(aclstr)-1] == '.') {
476 acl->type = PR_NETACL_TYPE_IPGLOB;
477 acl->pattern = pstrcat(p, aclstr, "*", NULL);
478
479 } else {
480 register unsigned int i;
481 int use_glob = FALSE, use_dns = FALSE;
482 size_t aclstr_len;
483
484 /* Is this a DNS glob, DNS match, IP glob, or IP match?
485 *
486 * First, check for any glob characters. After that, determine whether
487 * it's a DNS or IP type ACL.
488 */
489
490 /* If there are any glob characters (e.g. '{', '[', '*', or '?'), or
491 * if the first character is a '.', then treat the rule as a glob.
492 */
493 use_glob = (strpbrk(aclstr, "{[*?") != NULL);
494
495 aclstr_len = strlen(aclstr);
496 for (i = 0; i < aclstr_len; i++) {
497 if (PR_ISALPHA(aclstr[i])) {
498 #ifdef PR_USE_IPV6
499 if (pr_netaddr_use_ipv6()) {
500 if (aclstr[i] == 'A' || aclstr[i] == 'a' ||
501 aclstr[i] == 'B' || aclstr[i] == 'b' ||
502 aclstr[i] == 'C' || aclstr[i] == 'c' ||
503 aclstr[i] == 'D' || aclstr[i] == 'd' ||
504 aclstr[i] == 'E' || aclstr[i] == 'e' ||
505 aclstr[i] == 'F' || aclstr[i] == 'f') {
506 continue;
507 }
508
509 use_dns = TRUE;
510 break;
511
512 } else {
513 use_dns = TRUE;
514 break;
515 }
516 #else
517 use_dns = TRUE;
518 break;
519 #endif /* PR_USE_IPV6 */
520 }
521 }
522
523 if (!use_dns) {
524 acl->type = use_glob ? PR_NETACL_TYPE_IPGLOB : PR_NETACL_TYPE_IPMATCH;
525 acl->addr = pr_netaddr_get_addr(p, aclstr, NULL);
526 if (acl->addr == NULL) {
527 return NULL;
528 }
529
530 } else {
531 if (use_glob) {
532 acl->type = PR_NETACL_TYPE_DNSGLOB;
533 acl->pattern = pstrdup(p, aclstr);
534
535 } else if (*aclstr == '.') {
536 acl->type = PR_NETACL_TYPE_DNSGLOB;
537 acl->pattern = pstrcat(p, "*", aclstr, NULL);
538
539 } else {
540 acl->type = PR_NETACL_TYPE_DNSMATCH;
541 acl->pattern = pstrdup(p, aclstr);
542 }
543 }
544 }
545 }
546
547 acl->aclstr = aclstr_dup;
548 return acl;
549 }
550
pr_netacl_dup(pool * p,const pr_netacl_t * acl)551 pr_netacl_t *pr_netacl_dup(pool *p, const pr_netacl_t *acl) {
552 pr_netacl_t *acl2;
553
554 if (p == NULL ||
555 acl == NULL) {
556 errno = EINVAL;
557 return NULL;
558 }
559
560 acl2 = pcalloc(p, sizeof(pr_netacl_t));
561
562 /* A simple memcpy(3) won't suffice; we need a deep copy. */
563 acl2->type = acl->type;
564
565 if (acl->pattern != NULL) {
566 acl2->pattern = pstrdup(p, acl->pattern);
567 }
568
569 acl2->negated = acl->negated;
570
571 if (acl->addr != NULL) {
572 pr_netaddr_t *addr;
573
574 addr = pr_netaddr_alloc(p);
575 pr_netaddr_set_family(addr, pr_netaddr_get_family(acl->addr));
576 pr_netaddr_set_sockaddr(addr, pr_netaddr_get_sockaddr(acl->addr));
577
578 acl2->addr = addr;
579 }
580
581 acl2->masklen = acl->masklen;
582
583 if (acl->aclstr != NULL) {
584 acl2->aclstr = pstrdup(p, acl->aclstr);
585 }
586
587 return acl2;
588 }
589
pr_netacl_get_negated(const pr_netacl_t * acl)590 int pr_netacl_get_negated(const pr_netacl_t *acl) {
591 if (acl == NULL) {
592 errno = EINVAL;
593 return -1;
594 }
595
596 return acl->negated;
597 }
598
pr_netacl_get_str2(pool * p,const pr_netacl_t * acl,int flags)599 const char *pr_netacl_get_str2(pool *p, const pr_netacl_t *acl, int flags) {
600 char *res = "";
601
602 if (p == NULL ||
603 acl == NULL) {
604 errno = EINVAL;
605 return NULL;
606 }
607
608 switch (acl->type) {
609 case PR_NETACL_TYPE_ALL:
610 res = pstrcat(p, res, acl->aclstr, NULL);
611 if (!(flags & PR_NETACL_FL_STR_NO_DESC)) {
612 res = pstrcat(p, res, " <all>", NULL);
613 }
614 return res;
615
616 case PR_NETACL_TYPE_NONE:
617 res = pstrcat(p, res, acl->aclstr, NULL);
618 if (!(flags & PR_NETACL_FL_STR_NO_DESC)) {
619 res = pstrcat(p, res, " <none>", NULL);
620 }
621 return res;
622
623 case PR_NETACL_TYPE_IPMASK: {
624 res = pstrcat(p, res, acl->aclstr, NULL);
625
626 if (!(flags & PR_NETACL_FL_STR_NO_DESC)) {
627 char masklenstr[64];
628
629 memset(masklenstr, '\0', sizeof(masklenstr));
630 pr_snprintf(masklenstr, sizeof(masklenstr)-1, "%u", acl->masklen);
631 res = pstrcat(p, res, " <IP address mask, ", masklenstr, "-bit mask",
632 NULL);
633 }
634 break;
635 }
636
637 case PR_NETACL_TYPE_IPMATCH:
638 res = pstrcat(p, res, acl->aclstr, NULL);
639 if (!(flags & PR_NETACL_FL_STR_NO_DESC)) {
640 res = pstrcat(p, res, " <IP address match", NULL);
641 }
642 break;
643
644 case PR_NETACL_TYPE_DNSMATCH:
645 res = pstrcat(p, res, acl->aclstr, NULL);
646 if (!(flags & PR_NETACL_FL_STR_NO_DESC)) {
647 res = pstrcat(p, res, " <DNS hostname match", NULL);
648 }
649 break;
650
651 case PR_NETACL_TYPE_IPGLOB:
652 res = pstrcat(p, res, acl->pattern, NULL);
653 if (!(flags & PR_NETACL_FL_STR_NO_DESC)) {
654 res = pstrcat(p, res, " <IP address glob", NULL);
655 }
656 break;
657
658 case PR_NETACL_TYPE_DNSGLOB:
659 res = pstrcat(p, res, acl->pattern, NULL);
660 if (!(flags & PR_NETACL_FL_STR_NO_DESC)) {
661 res = pstrcat(p, res, " <DNS hostname glob", NULL);
662 }
663 break;
664 }
665
666 if (!(flags & PR_NETACL_FL_STR_NO_DESC)) {
667 if (!acl->negated) {
668 res = pstrcat(p, res, ">", NULL);
669
670 } else {
671 res = pstrcat(p, res, ", inverted>", NULL);
672 }
673 }
674
675 return res;
676 }
677
pr_netacl_get_str(pool * p,const pr_netacl_t * acl)678 const char *pr_netacl_get_str(pool *p, const pr_netacl_t *acl) {
679 return pr_netacl_get_str2(p, acl, 0);
680 }
681