Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/security/mac_ipacl/mac_ipacl.c
39476 views
1
/*-
2
* Copyright (c) 2003-2004 Networks Associates Technology, Inc.
3
* Copyright (c) 2006 SPARTA, Inc.
4
* Copyright (c) 2019, 2023 Shivank Garg <[email protected]>
5
*
6
* This software was developed for the FreeBSD Project by Network
7
* Associates Laboratories, the Security Research Division of Network
8
* Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
9
* as part of the DARPA CHATS research program.
10
*
11
* This software was enhanced by SPARTA ISSO under SPAWAR contract
12
* N66001-04-C-6019 ("SEFOS").
13
*
14
* This code was developed as a Google Summer of Code 2019 project
15
* under the guidance of Bjoern A. Zeeb.
16
*
17
* Redistribution and use in source and binary forms, with or without
18
* modification, are permitted provided that the following conditions
19
* are met:
20
* 1. Redistributions of source code must retain the above copyright
21
* notice, this list of conditions and the following disclaimer.
22
* 2. Redistributions in binary form must reproduce the above copyright
23
* notice, this list of conditions and the following disclaimer in the
24
* documentation and/or other materials provided with the distribution.
25
*
26
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
30
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36
* SUCH DAMAGE.
37
*/
38
39
/*
40
* The IP address access control policy module - mac_ipacl allows the root of
41
* the host to limit the VNET jail's privileges of setting IPv4 and IPv6
42
* addresses via sysctl(8) interface. So, the host can define rules for jails
43
* and their interfaces about IP addresses.
44
* sysctl(8) is to be used to modify the rules string in following format-
45
* "jail_id,allow,interface,address_family,IP_addr/prefix_length[@jail_id,...]"
46
*/
47
48
#include "opt_inet.h"
49
#include "opt_inet6.h"
50
51
#include <sys/param.h>
52
#include <sys/module.h>
53
#include <sys/errno.h>
54
#include <sys/kernel.h>
55
#include <sys/mutex.h>
56
#include <sys/priv.h>
57
#include <sys/queue.h>
58
#include <sys/socket.h>
59
#include <sys/sysctl.h>
60
#include <sys/systm.h>
61
#include <sys/types.h>
62
#include <sys/ucred.h>
63
#include <sys/jail.h>
64
65
#include <net/if.h>
66
#include <net/if_var.h>
67
68
#include <netinet/in.h>
69
#include <netinet6/scope6_var.h>
70
71
#include <security/mac/mac_policy.h>
72
73
static SYSCTL_NODE(_security_mac, OID_AUTO, ipacl, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
74
"TrustedBSD mac_ipacl policy controls");
75
76
#ifdef INET
77
static int ipacl_ipv4 = 1;
78
SYSCTL_INT(_security_mac_ipacl, OID_AUTO, ipv4, CTLFLAG_RWTUN,
79
&ipacl_ipv4, 0, "Enforce mac_ipacl for IPv4 addresses");
80
#endif
81
82
#ifdef INET6
83
static int ipacl_ipv6 = 1;
84
SYSCTL_INT(_security_mac_ipacl, OID_AUTO, ipv6, CTLFLAG_RWTUN,
85
&ipacl_ipv6, 0, "Enforce mac_ipacl for IPv6 addresses");
86
#endif
87
88
static MALLOC_DEFINE(M_IPACL, "ipacl_rule", "Rules for mac_ipacl");
89
90
#define MAC_RULE_STRING_LEN 1024
91
92
struct ipacl_addr {
93
union {
94
#ifdef INET
95
struct in_addr ipv4;
96
#endif
97
#ifdef INET6
98
struct in6_addr ipv6;
99
#endif
100
u_int8_t addr8[16];
101
u_int16_t addr16[8];
102
u_int32_t addr32[4];
103
} ipa; /* 128 bit address*/
104
#ifdef INET
105
#define v4 ipa.ipv4
106
#endif
107
#ifdef INET6
108
#define v6 ipa.ipv6
109
#endif
110
#define addr8 ipa.addr8
111
#define addr16 ipa.addr16
112
#define addr32 ipa.addr32
113
};
114
115
struct ip_rule {
116
int jid;
117
bool allow;
118
bool subnet_apply; /* Apply rule on whole subnet. */
119
char if_name[IFNAMSIZ];
120
int af; /* Address family. */
121
struct ipacl_addr addr;
122
struct ipacl_addr mask;
123
TAILQ_ENTRY(ip_rule) r_entries;
124
};
125
126
static struct mtx rule_mtx;
127
static TAILQ_HEAD(rulehead, ip_rule) rule_head;
128
static char rule_string[MAC_RULE_STRING_LEN];
129
130
static void
131
destroy_rules(struct rulehead *head)
132
{
133
struct ip_rule *rule;
134
135
while ((rule = TAILQ_FIRST(head)) != NULL) {
136
TAILQ_REMOVE(head, rule, r_entries);
137
free(rule, M_IPACL);
138
}
139
}
140
141
static void
142
ipacl_init(struct mac_policy_conf *conf)
143
{
144
mtx_init(&rule_mtx, "rule_mtx", NULL, MTX_DEF);
145
TAILQ_INIT(&rule_head);
146
}
147
148
static void
149
ipacl_destroy(struct mac_policy_conf *conf)
150
{
151
mtx_destroy(&rule_mtx);
152
destroy_rules(&rule_head);
153
}
154
155
/*
156
* Note: parsing routines are destructive on the passed string.
157
*/
158
static int
159
parse_rule_element(char *element, struct ip_rule *rule)
160
{
161
char *tok, *p;
162
int prefix;
163
#ifdef INET6
164
int i;
165
#endif
166
167
/* Should we support a jail wildcard? */
168
tok = strsep(&element, ",");
169
if (tok == NULL)
170
return (EINVAL);
171
rule->jid = strtol(tok, &p, 10);
172
if (*p != '\0')
173
return (EINVAL);
174
tok = strsep(&element, ",");
175
if (tok == NULL)
176
return (EINVAL);
177
rule->allow = strtol(tok, &p, 10);
178
if (*p != '\0')
179
return (EINVAL);
180
tok = strsep(&element, ",");
181
if (strlen(tok) + 1 > IFNAMSIZ)
182
return (EINVAL);
183
/* Empty interface name is wildcard to all interfaces. */
184
strlcpy(rule->if_name, tok, strlen(tok) + 1);
185
tok = strsep(&element, ",");
186
if (tok == NULL)
187
return (EINVAL);
188
rule->af = (strcmp(tok, "AF_INET") == 0) ? AF_INET :
189
(strcmp(tok, "AF_INET6") == 0) ? AF_INET6 : -1;
190
if (rule->af == -1)
191
return (EINVAL);
192
tok = strsep(&element, "/");
193
if (tok == NULL)
194
return (EINVAL);
195
if (inet_pton(rule->af, tok, rule->addr.addr32) != 1)
196
return (EINVAL);
197
tok = element;
198
if (tok == NULL)
199
return (EINVAL);
200
prefix = strtol(tok, &p, 10);
201
if (*p != '\0')
202
return (EINVAL);
203
/* Value -1 for prefix make policy applicable to individual IP only. */
204
if (prefix == -1)
205
rule->subnet_apply = false;
206
else {
207
rule->subnet_apply = true;
208
switch (rule->af) {
209
#ifdef INET
210
case AF_INET:
211
if (prefix < 0 || prefix > 32)
212
return (EINVAL);
213
214
if (prefix == 0)
215
rule->mask.addr32[0] = htonl(0);
216
else
217
rule->mask.addr32[0] =
218
htonl(~((1 << (32 - prefix)) - 1));
219
rule->addr.addr32[0] &= rule->mask.addr32[0];
220
break;
221
#endif
222
#ifdef INET6
223
case AF_INET6:
224
if (prefix < 0 || prefix > 128)
225
return (EINVAL);
226
227
for (i = 0; prefix > 0; prefix -= 8, i++)
228
rule->mask.addr8[i] = prefix >= 8 ? 0xFF :
229
(u_int8_t)((0xFFU << (8 - prefix)) & 0xFFU);
230
for (i = 0; i < 16; i++)
231
rule->addr.addr8[i] &= rule->mask.addr8[i];
232
break;
233
#endif
234
}
235
}
236
return (0);
237
}
238
239
/*
240
* Format of Rule- jid,allow,interface_name,addr_family,ip_addr/subnet_mask
241
* Example: sysctl security.mac.ipacl.rules=1,1,epair0b,AF_INET,192.0.2.2/24
242
*/
243
static int
244
parse_rules(char *string, struct rulehead *head)
245
{
246
struct ip_rule *new;
247
char *element;
248
int error;
249
250
error = 0;
251
while ((element = strsep(&string, "@")) != NULL) {
252
if (strlen(element) == 0)
253
continue;
254
255
new = malloc(sizeof(*new), M_IPACL, M_ZERO | M_WAITOK);
256
error = parse_rule_element(element, new);
257
if (error != 0) {
258
free(new, M_IPACL);
259
goto out;
260
}
261
TAILQ_INSERT_TAIL(head, new, r_entries);
262
}
263
out:
264
if (error != 0)
265
destroy_rules(head);
266
return (error);
267
}
268
269
static int
270
sysctl_rules(SYSCTL_HANDLER_ARGS)
271
{
272
char *string, *copy_string, *new_string;
273
struct rulehead head, save_head;
274
int error;
275
276
new_string = NULL;
277
if (req->newptr != NULL) {
278
new_string = malloc(MAC_RULE_STRING_LEN, M_IPACL,
279
M_WAITOK | M_ZERO);
280
mtx_lock(&rule_mtx);
281
strcpy(new_string, rule_string);
282
mtx_unlock(&rule_mtx);
283
string = new_string;
284
} else
285
string = rule_string;
286
287
error = sysctl_handle_string(oidp, string, MAC_RULE_STRING_LEN, req);
288
if (error)
289
goto out;
290
291
if (req->newptr != NULL) {
292
copy_string = strdup(string, M_IPACL);
293
TAILQ_INIT(&head);
294
error = parse_rules(copy_string, &head);
295
free(copy_string, M_IPACL);
296
if (error)
297
goto out;
298
299
TAILQ_INIT(&save_head);
300
mtx_lock(&rule_mtx);
301
TAILQ_CONCAT(&save_head, &rule_head, r_entries);
302
TAILQ_CONCAT(&rule_head, &head, r_entries);
303
strcpy(rule_string, string);
304
mtx_unlock(&rule_mtx);
305
destroy_rules(&save_head);
306
}
307
out:
308
if (new_string != NULL)
309
free(new_string, M_IPACL);
310
return (error);
311
}
312
SYSCTL_PROC(_security_mac_ipacl, OID_AUTO, rules,
313
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
314
0, sysctl_rules, "A", "IP ACL Rules");
315
316
static int
317
rules_check(struct ucred *cred,
318
struct ipacl_addr *ip_addr, if_t ifp)
319
{
320
struct ip_rule *rule;
321
int error;
322
#ifdef INET6
323
int i;
324
bool same_subnet;
325
#endif
326
327
error = EPERM;
328
329
mtx_lock(&rule_mtx);
330
331
/*
332
* In the case where multiple rules are applicable to an IP address or
333
* a set of IP addresses, the rule that is defined later in the list
334
* determines the outcome, disregarding any previous rule for that IP
335
* address.
336
* Walk the policy rules list in reverse order until rule applicable
337
* to the requested IP address is found.
338
*/
339
TAILQ_FOREACH_REVERSE(rule, &rule_head, rulehead, r_entries) {
340
/* Skip if current rule applies to different jail. */
341
if (cred->cr_prison->pr_id != rule->jid)
342
continue;
343
344
if (strcmp(rule->if_name, "\0") &&
345
strcmp(rule->if_name, if_name(ifp)))
346
continue;
347
348
switch (rule->af) {
349
#ifdef INET
350
case AF_INET:
351
if (rule->subnet_apply) {
352
if (rule->addr.v4.s_addr !=
353
(ip_addr->v4.s_addr & rule->mask.v4.s_addr))
354
continue;
355
} else
356
if (ip_addr->v4.s_addr != rule->addr.v4.s_addr)
357
continue;
358
break;
359
#endif
360
#ifdef INET6
361
case AF_INET6:
362
if (rule->subnet_apply) {
363
same_subnet = true;
364
for (i = 0; i < 16; i++)
365
if (rule->addr.v6.s6_addr[i] !=
366
(ip_addr->v6.s6_addr[i] &
367
rule->mask.v6.s6_addr[i])) {
368
same_subnet = false;
369
break;
370
}
371
if (!same_subnet)
372
continue;
373
} else
374
if (bcmp(&rule->addr, ip_addr,
375
sizeof(*ip_addr)))
376
continue;
377
break;
378
#endif
379
}
380
381
if (rule->allow)
382
error = 0;
383
break;
384
}
385
386
mtx_unlock(&rule_mtx);
387
388
return (error);
389
}
390
391
/*
392
* Feature request: Can we make this sysctl policy apply to jails by default,
393
* but also allow it to be changed to apply to the base system?
394
*/
395
#ifdef INET
396
static int
397
ipacl_ip4_check_jail(struct ucred *cred,
398
const struct in_addr *ia, if_t ifp)
399
{
400
struct ipacl_addr ip4_addr;
401
402
ip4_addr.v4 = *ia;
403
404
if (!jailed(cred))
405
return (0);
406
407
/* Checks with the policy only when it is enforced for ipv4. */
408
if (ipacl_ipv4)
409
return rules_check(cred, &ip4_addr, ifp);
410
411
return (0);
412
}
413
#endif
414
415
#ifdef INET6
416
static int
417
ipacl_ip6_check_jail(struct ucred *cred,
418
const struct in6_addr *ia6, if_t ifp)
419
{
420
struct ipacl_addr ip6_addr;
421
422
ip6_addr.v6 = *ia6; /* Make copy to not alter the original. */
423
in6_clearscope(&ip6_addr.v6); /* Clear the scope id. */
424
425
if (!jailed(cred))
426
return (0);
427
428
/* Checks with the policy when it is enforced for ipv6. */
429
if (ipacl_ipv6)
430
return rules_check(cred, &ip6_addr, ifp);
431
432
return (0);
433
}
434
#endif
435
436
static struct mac_policy_ops ipacl_ops =
437
{
438
.mpo_init = ipacl_init,
439
.mpo_destroy = ipacl_destroy,
440
#ifdef INET
441
.mpo_ip4_check_jail = ipacl_ip4_check_jail,
442
#endif
443
#ifdef INET6
444
.mpo_ip6_check_jail = ipacl_ip6_check_jail,
445
#endif
446
};
447
448
MAC_POLICY_SET(&ipacl_ops, mac_ipacl, "TrustedBSD MAC/ipacl",
449
MPC_LOADTIME_FLAG_UNLOADOK, NULL);
450
451