xref: /freebsd/sys/security/mac_portacl/mac_portacl.c (revision e0c4386e7e71d93b0edc0c8fa156263fc4a8b0b6)
1 /*-
2  * Copyright (c) 2003-2004 Networks Associates Technology, Inc.
3  * Copyright (c) 2006 SPARTA, Inc.
4  * All rights reserved.
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  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 /*
37  * Developed by the TrustedBSD Project.
38  *
39  * Administratively limit access to local UDP/TCP ports for binding purposes.
40  * Intended to be combined with net.inet.ip.portrange.reservedhigh to allow
41  * specific uids and gids to bind specific ports for specific purposes,
42  * while not opening the door to any user replacing an "official" service
43  * while you're restarting it.  This only affects ports explicitly bound by
44  * the user process (either for listen/outgoing socket for TCP, or send/
45  * receive for UDP).  This module will not limit ports bound implicitly for
46  * out-going connections where the process hasn't explicitly selected a port:
47  * these are automatically selected by the IP stack.
48  *
49  * To use this module, security.mac.enforce_socket must be enabled, and you
50  * will probably want to twiddle the net.inet sysctl listed above.  Then use
51  * sysctl(8) to modify the rules string:
52  *
53  * # sysctl security.mac.portacl.rules="uid:425:tcp:80,uid:425:tcp:79"
54  *
55  * This ruleset, for example, permits uid 425 to bind TCP ports 80 (http) and
56  * 79 (finger).  User names and group names can't be used directly because
57  * the kernel only knows about uids and gids.
58  */
59 
60 #include <sys/param.h>
61 #include <sys/domain.h>
62 #include <sys/kernel.h>
63 #include <sys/lock.h>
64 #include <sys/malloc.h>
65 #include <sys/module.h>
66 #include <sys/mutex.h>
67 #include <sys/priv.h>
68 #include <sys/proc.h>
69 #include <sys/protosw.h>
70 #include <sys/queue.h>
71 #include <sys/systm.h>
72 #include <sys/sbuf.h>
73 #include <sys/socket.h>
74 #include <sys/socketvar.h>
75 #include <sys/sysctl.h>
76 
77 #include <netinet/in.h>
78 #include <netinet/in_pcb.h>
79 
80 #include <security/mac/mac_policy.h>
81 
82 SYSCTL_DECL(_security_mac);
83 
84 static SYSCTL_NODE(_security_mac, OID_AUTO, portacl,
85     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
86     "TrustedBSD mac_portacl policy controls");
87 
88 static int	portacl_enabled = 1;
89 SYSCTL_INT(_security_mac_portacl, OID_AUTO, enabled, CTLFLAG_RWTUN,
90     &portacl_enabled, 0, "Enforce portacl policy");
91 
92 static int	portacl_suser_exempt = 1;
93 SYSCTL_INT(_security_mac_portacl, OID_AUTO, suser_exempt, CTLFLAG_RWTUN,
94     &portacl_suser_exempt, 0, "Privilege permits binding of any port");
95 
96 static int	portacl_autoport_exempt = 1;
97 SYSCTL_INT(_security_mac_portacl, OID_AUTO, autoport_exempt, CTLFLAG_RWTUN,
98     &portacl_autoport_exempt, 0, "Allow automatic allocation through "
99     "binding port 0 if not IP_PORTRANGELOW");
100 
101 static int	portacl_port_high = 1023;
102 SYSCTL_INT(_security_mac_portacl, OID_AUTO, port_high, CTLFLAG_RWTUN,
103     &portacl_port_high, 0, "Highest port to enforce for");
104 
105 static MALLOC_DEFINE(M_PORTACL, "portacl_rule", "Rules for mac_portacl");
106 
107 #define	MAC_RULE_STRING_LEN	1024
108 
109 #define	RULE_GID	1
110 #define	RULE_UID	2
111 #define	RULE_PROTO_TCP	1
112 #define	RULE_PROTO_UDP	2
113 struct rule {
114 	id_t			r_id;
115 	int			r_idtype;
116 	u_int16_t		r_port;
117 	int			r_protocol;
118 
119 	TAILQ_ENTRY(rule)	r_entries;
120 };
121 
122 #define	GID_STRING	"gid"
123 #define	TCP_STRING	"tcp"
124 #define	UID_STRING	"uid"
125 #define	UDP_STRING	"udp"
126 
127 /*
128  * Text format for the rule string is that a rule consists of a
129  * comma-separated list of elements.  Each element is in the form
130  * idtype:id:protocol:portnumber, and constitutes granting of permission
131  * for the specified binding.
132  */
133 
134 static struct mtx			rule_mtx;
135 static TAILQ_HEAD(rulehead, rule)	rule_head;
136 static char				rule_string[MAC_RULE_STRING_LEN];
137 
138 static void
139 toast_rules(struct rulehead *head)
140 {
141 	struct rule *rule;
142 
143 	while ((rule = TAILQ_FIRST(head)) != NULL) {
144 		TAILQ_REMOVE(head, rule, r_entries);
145 		free(rule, M_PORTACL);
146 	}
147 }
148 
149 /*
150  * Note that there is an inherent race condition in the unload of modules
151  * and access via sysctl.
152  */
153 static void
154 destroy(struct mac_policy_conf *mpc)
155 {
156 
157 	mtx_destroy(&rule_mtx);
158 	toast_rules(&rule_head);
159 }
160 
161 static void
162 init(struct mac_policy_conf *mpc)
163 {
164 
165 	mtx_init(&rule_mtx, "rule_mtx", NULL, MTX_DEF);
166 	TAILQ_INIT(&rule_head);
167 }
168 
169 /*
170  * Note: parsing routines are destructive on the passed string.
171  */
172 static int
173 parse_rule_element(char *element, struct rule **rule)
174 {
175 	char *idtype, *id, *protocol, *portnumber, *p;
176 	struct rule *new;
177 	int error;
178 
179 	error = 0;
180 	new = malloc(sizeof(*new), M_PORTACL, M_ZERO | M_WAITOK);
181 
182 	idtype = strsep(&element, ":");
183 	if (idtype == NULL) {
184 		error = EINVAL;
185 		goto out;
186 	}
187 	id = strsep(&element, ":");
188 	if (id == NULL) {
189 		error = EINVAL;
190 		goto out;
191 	}
192 	new->r_id = strtol(id, &p, 10);
193 	if (*p != '\0') {
194 		error = EINVAL;
195 		goto out;
196 	}
197 	if (strcmp(idtype, UID_STRING) == 0)
198 		new->r_idtype = RULE_UID;
199 	else if (strcmp(idtype, GID_STRING) == 0)
200 		new->r_idtype = RULE_GID;
201 	else {
202 		error = EINVAL;
203 		goto out;
204 	}
205 	protocol = strsep(&element, ":");
206 	if (protocol == NULL) {
207 		error = EINVAL;
208 		goto out;
209 	}
210 	if (strcmp(protocol, TCP_STRING) == 0)
211 		new->r_protocol = RULE_PROTO_TCP;
212 	else if (strcmp(protocol, UDP_STRING) == 0)
213 		new->r_protocol = RULE_PROTO_UDP;
214 	else {
215 		error = EINVAL;
216 		goto out;
217 	}
218 	portnumber = element;
219 	if (portnumber == NULL) {
220 		error = EINVAL;
221 		goto out;
222 	}
223 	new->r_port = strtol(portnumber, &p, 10);
224 	if (*p != '\0') {
225 		error = EINVAL;
226 		goto out;
227 	}
228 
229 out:
230 	if (error != 0) {
231 		free(new, M_PORTACL);
232 		*rule = NULL;
233 	} else
234 		*rule = new;
235 	return (error);
236 }
237 
238 static int
239 parse_rules(char *string, struct rulehead *head)
240 {
241 	struct rule *new;
242 	char *element;
243 	int error;
244 
245 	error = 0;
246 	while ((element = strsep(&string, ",")) != NULL) {
247 		if (strlen(element) == 0)
248 			continue;
249 		error = parse_rule_element(element, &new);
250 		if (error)
251 			goto out;
252 		TAILQ_INSERT_TAIL(head, new, r_entries);
253 	}
254 out:
255 	if (error != 0)
256 		toast_rules(head);
257 	return (error);
258 }
259 
260 /*
261  * rule_printf() and rules_to_string() are unused currently because they rely
262  * on sbufs with auto-extension, which may sleep while holding a mutex.
263  * Instead, the non-canonical user-generated rule string is returned to the
264  * user when the rules are queried, which is faster anyway.
265  */
266 #if 0
267 static void
268 rule_printf(struct sbuf *sb, struct rule *rule)
269 {
270 	const char *idtype, *protocol;
271 
272 	switch(rule->r_idtype) {
273 	case RULE_GID:
274 		idtype = GID_STRING;
275 		break;
276 	case RULE_UID:
277 		idtype = UID_STRING;
278 		break;
279 	default:
280 		panic("rule_printf: unknown idtype (%d)\n", rule->r_idtype);
281 	}
282 
283 	switch (rule->r_protocol) {
284 	case RULE_PROTO_TCP:
285 		protocol = TCP_STRING;
286 		break;
287 	case RULE_PROTO_UDP:
288 		protocol = UDP_STRING;
289 		break;
290 	default:
291 		panic("rule_printf: unknown protocol (%d)\n",
292 		    rule->r_protocol);
293 	}
294 	sbuf_printf(sb, "%s:%jd:%s:%d", idtype, (intmax_t)rule->r_id,
295 	    protocol, rule->r_port);
296 }
297 
298 static char *
299 rules_to_string(void)
300 {
301 	struct rule *rule;
302 	struct sbuf *sb;
303 	int needcomma;
304 	char *temp;
305 
306 	sb = sbuf_new_auto();
307 	needcomma = 0;
308 	mtx_lock(&rule_mtx);
309 	for (rule = TAILQ_FIRST(&rule_head); rule != NULL;
310 	    rule = TAILQ_NEXT(rule, r_entries)) {
311 		if (!needcomma)
312 			needcomma = 1;
313 		else
314 			sbuf_printf(sb, ",");
315 		rule_printf(sb, rule);
316 	}
317 	mtx_unlock(&rule_mtx);
318 	sbuf_finish(sb);
319 	temp = strdup(sbuf_data(sb), M_PORTACL);
320 	sbuf_delete(sb);
321 	return (temp);
322 }
323 #endif
324 
325 /*
326  * Note: due to races, there is not a single serializable order
327  * between parallel calls to the sysctl.
328  */
329 static int
330 sysctl_rules(SYSCTL_HANDLER_ARGS)
331 {
332 	char *string, *copy_string, *new_string;
333 	struct rulehead head, save_head;
334 	int error;
335 
336 	new_string = NULL;
337 	if (req->newptr != NULL) {
338 		new_string = malloc(MAC_RULE_STRING_LEN, M_PORTACL,
339 		    M_WAITOK | M_ZERO);
340 		mtx_lock(&rule_mtx);
341 		strcpy(new_string, rule_string);
342 		mtx_unlock(&rule_mtx);
343 		string = new_string;
344 	} else
345 		string = rule_string;
346 
347 	error = sysctl_handle_string(oidp, string, MAC_RULE_STRING_LEN, req);
348 	if (error)
349 		goto out;
350 
351 	if (req->newptr != NULL) {
352 		copy_string = strdup(string, M_PORTACL);
353 		TAILQ_INIT(&head);
354 		error = parse_rules(copy_string, &head);
355 		free(copy_string, M_PORTACL);
356 		if (error)
357 			goto out;
358 
359 		TAILQ_INIT(&save_head);
360 		mtx_lock(&rule_mtx);
361 		TAILQ_CONCAT(&save_head, &rule_head, r_entries);
362 		TAILQ_CONCAT(&rule_head, &head, r_entries);
363 		strcpy(rule_string, string);
364 		mtx_unlock(&rule_mtx);
365 		toast_rules(&save_head);
366 	}
367 out:
368 	if (new_string != NULL)
369 		free(new_string, M_PORTACL);
370 	return (error);
371 }
372 
373 SYSCTL_PROC(_security_mac_portacl, OID_AUTO, rules,
374     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
375     0, 0, sysctl_rules, "A",
376     "Rules");
377 
378 static int
379 rules_check(struct ucred *cred, int family, int type, u_int16_t port)
380 {
381 	struct rule *rule;
382 	int error;
383 
384 #if 0
385 	printf("Check requested for euid %d, family %d, type %d, port %d\n",
386 	    cred->cr_uid, family, type, port);
387 #endif
388 
389 	if (port > portacl_port_high)
390 		return (0);
391 
392 	error = EPERM;
393 	mtx_lock(&rule_mtx);
394 	for (rule = TAILQ_FIRST(&rule_head);
395 	    rule != NULL;
396 	    rule = TAILQ_NEXT(rule, r_entries)) {
397 		if (type == SOCK_DGRAM && rule->r_protocol != RULE_PROTO_UDP)
398 			continue;
399 		if (type == SOCK_STREAM && rule->r_protocol != RULE_PROTO_TCP)
400 			continue;
401 		if (port != rule->r_port)
402 			continue;
403 		if (rule->r_idtype == RULE_UID) {
404 			if (cred->cr_uid == rule->r_id) {
405 				error = 0;
406 				break;
407 			}
408 		} else if (rule->r_idtype == RULE_GID) {
409 			if (cred->cr_gid == rule->r_id) {
410 				error = 0;
411 				break;
412 			} else if (groupmember(rule->r_id, cred)) {
413 				error = 0;
414 				break;
415 			}
416 		} else
417 			panic("rules_check: unknown rule type %d",
418 			    rule->r_idtype);
419 	}
420 	mtx_unlock(&rule_mtx);
421 
422 	if (error != 0 && portacl_suser_exempt != 0)
423 		error = priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT);
424 
425 	return (error);
426 }
427 
428 /*
429  * Note, this only limits the ability to explicitly bind a port, it
430  * doesn't limit implicitly bound ports for outgoing connections where
431  * the source port is left up to the IP stack to determine automatically.
432  */
433 static int
434 socket_check_bind(struct ucred *cred, struct socket *so,
435     struct label *solabel, struct sockaddr *sa)
436 {
437 	struct sockaddr_in *sin;
438 	struct inpcb *inp;
439 	int family, type;
440 	u_int16_t port;
441 
442 	/* Only run if we are enabled. */
443 	if (portacl_enabled == 0)
444 		return (0);
445 
446 	/* Only interested in IPv4 and IPv6 sockets. */
447 	if (so->so_proto->pr_domain->dom_family != PF_INET &&
448 	    so->so_proto->pr_domain->dom_family != PF_INET6)
449 		return (0);
450 
451 	/* Currently, we don't attempt to deal with SOCK_RAW, etc. */
452 	if (so->so_type != SOCK_DGRAM &&
453 	    so->so_type != SOCK_STREAM)
454 		return (0);
455 
456 	/* Reject addresses we don't understand; fail closed. */
457 	if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
458 		return (EINVAL);
459 
460 	family = so->so_proto->pr_domain->dom_family;
461 	type = so->so_type;
462 	sin = (struct sockaddr_in *) sa;
463 	port = ntohs(sin->sin_port);
464 
465 	/*
466 	 * Sockets are frequently bound with a specific IP address but a port
467 	 * number of '0' to request automatic port allocation.  This is often
468 	 * desirable as long as IP_PORTRANGELOW isn't set, which might permit
469 	 * automatic allocation of a "privileged" port.  The autoport exempt
470 	 * flag exempts port 0 allocation from rule checking as long as a low
471 	 * port isn't required.
472 	 */
473 	if (portacl_autoport_exempt && port == 0) {
474 		inp = sotoinpcb(so);
475 		if ((inp->inp_flags & INP_LOWPORT) == 0)
476 			return (0);
477 	}
478 
479 	return (rules_check(cred, family, type, port));
480 }
481 
482 static struct mac_policy_ops portacl_ops =
483 {
484 	.mpo_destroy = destroy,
485 	.mpo_init = init,
486 	.mpo_socket_check_bind = socket_check_bind,
487 };
488 
489 MAC_POLICY_SET(&portacl_ops, mac_portacl, "TrustedBSD MAC/portacl",
490     MPC_LOADTIME_FLAG_UNLOADOK, NULL);
491 MODULE_VERSION(mac_portacl, 1);
492