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