-
Notifications
You must be signed in to change notification settings - Fork 40
/
acl.c
137 lines (120 loc) · 3.69 KB
/
acl.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
* These are ACL routines for the main module of CNTLM
*
* CNTLM is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
* St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2007 David Kubicek
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "acl.h"
#include "socket.h"
#include "swap.h"
/*
* TODO: retest ACLs on big-endian
*/
/*
* Add the rule spec to the ACL list.
*/
int acl_add(plist_t *rules, char *spec, enum acl_t acl) {
struct addrinfo *addresses = NULL;
struct sockaddr_in *naddr = NULL;
network_t *aux;
int mask = 32;
size_t i;
char *tmp;
if (rules == NULL)
return 0;
spec = strdup(spec);
aux = (network_t *)zmalloc(sizeof(network_t));
i = strcspn(spec, "/");
if (i < strlen(spec)) {
spec[i] = 0;
mask = (int)strtol(spec+i+1, &tmp, 10);
if (mask < 0 || mask > 32 || spec[i+1] == 0 || *tmp != 0) {
syslog(LOG_ERR, "ACL netmask for %s is invalid\n", spec);
free(aux);
free(spec);
return 0;
}
}
if (!strcmp("*", spec)) {
aux->ip = 0;
mask = 0;
} else if (!strcmp("0", spec)) {
aux->ip = 0;
} else if (!so_resolv(&addresses, spec, 0)) {
syslog(LOG_ERR, "ACL source address %s is invalid\n", spec);
free(aux);
free(spec);
return 0;
}
if (addresses != NULL) {
// TODO only ipv4 client addresses are supported for now
for (struct addrinfo *p = addresses; p != NULL; p = p->ai_next) {
if (p->ai_family == AF_INET) {
naddr = (struct sockaddr_in*)p->ai_addr;
break;
}
}
if (naddr == NULL) {
syslog(LOG_ERR, "ACL only ipv4 source addresses are supported (%s)\n", spec);
free(aux);
free(spec);
freeaddrinfo(addresses);
return 0;
}
aux->ip = naddr->sin_addr.s_addr;
}
aux->mask = mask;
mask = swap32(~(((uint64_t)1 << (32-mask)) - 1));
if ((aux->ip & mask) != aux->ip)
syslog(LOG_WARNING, "Subnet definition might be incorrect: %s/%d\n", naddr ? inet_ntoa(naddr->sin_addr) : spec, aux->mask);
syslog(LOG_INFO, "New ACL rule: %s %s/%d\n", (acl == ACL_ALLOW ? "allow" : "deny"), naddr ? inet_ntoa(naddr->sin_addr) : spec, aux->mask);
*rules = plist_add(*rules, acl, (char *)aux);
free(spec);
freeaddrinfo(addresses);
return 1;
}
/*
* Takes client IP address (network order) and walks the
* ACL rules until a match is found, returning ACL_ALLOW
* or ACL_DENY accordingly. If no rule matches, connection
* is allowed (such is the case with no ACLs).
*
* Proper policy should always end with a default rule,
* targeting either "*" or "0/0" to explicitly express
* one's intentions.
*/
enum acl_t acl_check(plist_const_t rules, struct sockaddr *caddr) {
// TODO only ipv4 client addresses are supported for now
if (rules && caddr->sa_family == AF_INET) {
const struct sockaddr_in* naddr = (struct sockaddr_in*)caddr;
while (rules) {
const network_t * const aux = (network_t *)rules->aux;
const unsigned int mask = swap32(~(((uint64_t)1 << (32-aux->mask)) - 1));
if ((naddr->sin_addr.s_addr & mask) == (aux->ip & mask))
return (enum acl_t)rules->key;
rules = rules->next;
}
}
return ACL_ALLOW;
}