release 0.92.3
[cntlm.git] / acl.c
blob81aad9d37befdd9d7ebf0dad25190a352741371a
1 /*
2 * These are ACL routines for the main module of CNTLM
4 * CNTLM is free software; you can redistribute it and/or modify it under the
5 * terms of the GNU General Public License as published by the Free Software
6 * Foundation; either version 2 of the License, or (at your option) any later
7 * version.
9 * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY
10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 * details.
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
16 * St, Fifth Floor, Boston, MA 02110-1301, USA.
18 * Copyright (c) 2007 David Kubicek
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <syslog.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
30 #include "acl.h"
31 #include "socket.h"
32 #include "swap.h"
35 * TODO: retest ACLs on big-endian
39 * Add the rule spec to the ACL list.
41 int acl_add(plist_t *rules, char *spec, enum acl_t acl) {
42 struct in_addr source;
43 network_t *aux;
44 int i, mask = 32;
45 char *tmp;
47 if (rules == NULL)
48 return 0;
50 spec = strdup(spec);
51 aux = (network_t *)new(sizeof(network_t));
52 i = strcspn(spec, "/");
53 if (i < strlen(spec)) {
54 spec[i] = 0;
55 mask = strtol(spec+i+1, &tmp, 10);
56 if (mask < 0 || mask > 32 || spec[i+1] == 0 || *tmp != 0) {
57 syslog(LOG_ERR, "ACL netmask for %s is invalid\n", spec);
58 free(aux);
59 free(spec);
60 return 0;
64 if (!strcmp("*", spec)) {
65 source.s_addr = 0;
66 mask = 0;
67 } else {
68 if (!strcmp("0", spec)) {
69 source.s_addr = 0;
70 } else if (!so_resolv(&source, spec)) {
71 syslog(LOG_ERR, "ACL source address %s is invalid\n", spec);
72 free(aux);
73 free(spec);
74 return 0;
78 aux->ip = source.s_addr;
79 aux->mask = mask;
80 mask = swap32(~(((uint64_t)1 << (32-mask)) - 1));
81 if ((source.s_addr & mask) != source.s_addr)
82 syslog(LOG_WARNING, "Subnet definition might be incorrect: %s/%d\n", inet_ntoa(source), aux->mask);
84 syslog(LOG_INFO, "New ACL rule: %s %s/%d\n", (acl == ACL_ALLOW ? "allow" : "deny"), inet_ntoa(source), aux->mask);
85 *rules = plist_add(*rules, acl, (char *)aux);
87 free(spec);
88 return 1;
92 * Takes client IP address (network order) and walks the
93 * ACL rules until a match is found, returning ACL_ALLOW
94 * or ACL_DENY accordingly. If no rule matches, connection
95 * is allowed (such is the case with no ACLs).
97 * Proper policy should always end with a default rule,
98 * targetting either "*" or "0/0" to explicitly express
99 * one's intentions.
101 enum acl_t acl_check(plist_t rules, struct in_addr naddr) {
102 network_t *aux;
103 int mask;
105 while (rules) {
106 aux = (network_t *)rules->aux;
107 mask = swap32(~(((uint64_t)1 << (32-aux->mask)) - 1));
109 if ((naddr.s_addr & mask) == (aux->ip & mask))
110 return rules->key;
112 rules = rules->next;
115 return ACL_ALLOW;