1 From e83b058f391e96a2a640fb2e2812d50ef67e0f43 Mon Sep 17 00:00:00 2001
2 From: John Johansen <john.johansen@canonical.com>
3 Date: Mon, 4 Oct 2010 15:03:36 -0700
4 Subject: [PATCH 2/4] UBUNTU: SAUCE: AppArmor: basic networking rules
6 Base support for network mediation.
8 Signed-off-by: John Johansen <john.johansen@canonical.com>
10 security/apparmor/.gitignore | 1 +
11 security/apparmor/Makefile | 42 +++++++++-
12 security/apparmor/apparmorfs.c | 1 +
13 security/apparmor/include/audit.h | 4 +
14 security/apparmor/include/net.h | 44 ++++++++++
15 security/apparmor/include/policy.h | 3 +
16 security/apparmor/lsm.c | 112 +++++++++++++++++++++++++
17 security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++
18 security/apparmor/policy.c | 1 +
19 security/apparmor/policy_unpack.c | 46 +++++++++++
20 10 files changed, 414 insertions(+), 2 deletions(-)
21 create mode 100644 security/apparmor/include/net.h
22 create mode 100644 security/apparmor/net.c
24 diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore
25 index 9cdec70..d5b291e 100644
26 --- a/security/apparmor/.gitignore
27 +++ b/security/apparmor/.gitignore
30 # Generated include files
35 diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
36 index 5706b74..e270692 100644
37 --- a/security/apparmor/Makefile
38 +++ b/security/apparmor/Makefile
39 @@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
41 apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
42 path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
43 - resource.o sid.o file.o
44 + resource.o sid.o file.o net.o
46 -clean-files := capability_names.h rlim_names.h
47 +clean-files := capability_names.h rlim_names.h net_names.h
50 # Build a lower case string table of capability names
51 @@ -20,6 +20,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\
52 -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\
55 +# Build a lower case string table of address family names
56 +# Transform lines from
57 +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */
58 +# #define AF_INET 2 /* Internet IP Protocol */
63 +# and build the securityfs entries for the mapping.
64 +# Transforms lines from
65 +# #define AF_INET 2 /* Internet IP Protocol */
67 +# #define AA_FS_AF_MASK "local inet"
68 +quiet_cmd_make-af = GEN $@
69 +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
70 + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
71 + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
73 + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\
74 + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\
75 + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
77 +# Build a lower case string table of sock type names
78 +# Transform lines from
82 +quiet_cmd_make-sock = GEN $@
83 +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\
85 + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\
88 # Build a lower case string table of rlimit names.
89 # Transforms lines from
90 @@ -56,6 +88,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \
91 tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@
93 $(obj)/capability.o : $(obj)/capability_names.h
94 +$(obj)/net.o : $(obj)/net_names.h
95 $(obj)/resource.o : $(obj)/rlim_names.h
96 $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
98 @@ -63,3 +96,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
99 $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \
101 $(call cmd,make-rlim)
102 +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \
103 + $(srctree)/include/linux/net.h \
105 + $(call cmd,make-af)
106 + $(call cmd,make-sock)
107 diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
108 index 42b7c9f..114fb23 100644
109 --- a/security/apparmor/apparmorfs.c
110 +++ b/security/apparmor/apparmorfs.c
111 @@ -429,6 +429,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = {
112 static struct aa_fs_entry aa_fs_entry_features[] = {
113 AA_FS_DIR("domain", aa_fs_entry_domain),
114 AA_FS_DIR("file", aa_fs_entry_file),
115 + AA_FS_DIR("network", aa_fs_entry_network),
116 AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK),
117 AA_FS_DIR("rlimit", aa_fs_entry_rlimit),
119 diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
120 index 69d8cae..4af6523 100644
121 --- a/security/apparmor/include/audit.h
122 +++ b/security/apparmor/include/audit.h
123 @@ -127,6 +127,10 @@ struct apparmor_audit_data {
128 + int type, protocol;
134 diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
136 index 0000000..cb8a121
138 +++ b/security/apparmor/include/net.h
141 + * AppArmor security module
143 + * This file contains AppArmor network mediation definitions.
145 + * Copyright (C) 1998-2008 Novell/SUSE
146 + * Copyright 2009-2012 Canonical Ltd.
148 + * This program is free software; you can redistribute it and/or
149 + * modify it under the terms of the GNU General Public License as
150 + * published by the Free Software Foundation, version 2 of the
157 +#include <net/sock.h>
159 +#include "apparmorfs.h"
161 +/* struct aa_net - network confinement data
162 + * @allowed: basic network families permissions
163 + * @audit_network: which network permissions to force audit
164 + * @quiet_network: which network permissions to quiet rejects
172 +extern struct aa_fs_entry aa_fs_entry_network[];
174 +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family,
175 + int type, int protocol, struct sock *sk);
176 +extern int aa_revalidate_sk(int op, struct sock *sk);
178 +static inline void aa_free_net_rules(struct aa_net *new)
183 +#endif /* __AA_NET_H */
184 diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
185 index bda4569..eb13a73 100644
186 --- a/security/apparmor/include/policy.h
187 +++ b/security/apparmor/include/policy.h
189 #include "capability.h"
193 #include "resource.h"
195 extern const char *const profile_mode_names[];
196 @@ -157,6 +158,7 @@ struct aa_policydb {
197 * @policy: general match rules governing policy
198 * @file: The set of rules governing basic file access and domain transitions
199 * @caps: capabilities for the profile
200 + * @net: network controls for the profile
201 * @rlimits: rlimits for the profile
203 * The AppArmor profile contains the basic confinement data. Each profile
204 @@ -194,6 +196,7 @@ struct aa_profile {
205 struct aa_policydb policy;
206 struct aa_file_rules file;
209 struct aa_rlimit rlimits;
212 diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
213 index b21830e..1bce440 100644
214 --- a/security/apparmor/lsm.c
215 +++ b/security/apparmor/lsm.c
217 #include "include/context.h"
218 #include "include/file.h"
219 #include "include/ipc.h"
220 +#include "include/net.h"
221 #include "include/path.h"
222 #include "include/policy.h"
223 #include "include/procattr.h"
224 @@ -614,6 +615,104 @@ static int apparmor_task_setrlimit(struct task_struct *task,
228 +static int apparmor_socket_create(int family, int type, int protocol, int kern)
230 + struct aa_profile *profile;
236 + profile = __aa_current_profile();
237 + if (!unconfined(profile))
238 + error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
243 +static int apparmor_socket_bind(struct socket *sock,
244 + struct sockaddr *address, int addrlen)
246 + struct sock *sk = sock->sk;
248 + return aa_revalidate_sk(OP_BIND, sk);
251 +static int apparmor_socket_connect(struct socket *sock,
252 + struct sockaddr *address, int addrlen)
254 + struct sock *sk = sock->sk;
256 + return aa_revalidate_sk(OP_CONNECT, sk);
259 +static int apparmor_socket_listen(struct socket *sock, int backlog)
261 + struct sock *sk = sock->sk;
263 + return aa_revalidate_sk(OP_LISTEN, sk);
266 +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
268 + struct sock *sk = sock->sk;
270 + return aa_revalidate_sk(OP_ACCEPT, sk);
273 +static int apparmor_socket_sendmsg(struct socket *sock,
274 + struct msghdr *msg, int size)
276 + struct sock *sk = sock->sk;
278 + return aa_revalidate_sk(OP_SENDMSG, sk);
281 +static int apparmor_socket_recvmsg(struct socket *sock,
282 + struct msghdr *msg, int size, int flags)
284 + struct sock *sk = sock->sk;
286 + return aa_revalidate_sk(OP_RECVMSG, sk);
289 +static int apparmor_socket_getsockname(struct socket *sock)
291 + struct sock *sk = sock->sk;
293 + return aa_revalidate_sk(OP_GETSOCKNAME, sk);
296 +static int apparmor_socket_getpeername(struct socket *sock)
298 + struct sock *sk = sock->sk;
300 + return aa_revalidate_sk(OP_GETPEERNAME, sk);
303 +static int apparmor_socket_getsockopt(struct socket *sock, int level,
306 + struct sock *sk = sock->sk;
308 + return aa_revalidate_sk(OP_GETSOCKOPT, sk);
311 +static int apparmor_socket_setsockopt(struct socket *sock, int level,
314 + struct sock *sk = sock->sk;
316 + return aa_revalidate_sk(OP_SETSOCKOPT, sk);
319 +static int apparmor_socket_shutdown(struct socket *sock, int how)
321 + struct sock *sk = sock->sk;
323 + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk);
326 static struct security_operations apparmor_ops = {
329 @@ -646,6 +745,19 @@ static struct security_operations apparmor_ops = {
330 .getprocattr = apparmor_getprocattr,
331 .setprocattr = apparmor_setprocattr,
333 + .socket_create = apparmor_socket_create,
334 + .socket_bind = apparmor_socket_bind,
335 + .socket_connect = apparmor_socket_connect,
336 + .socket_listen = apparmor_socket_listen,
337 + .socket_accept = apparmor_socket_accept,
338 + .socket_sendmsg = apparmor_socket_sendmsg,
339 + .socket_recvmsg = apparmor_socket_recvmsg,
340 + .socket_getsockname = apparmor_socket_getsockname,
341 + .socket_getpeername = apparmor_socket_getpeername,
342 + .socket_getsockopt = apparmor_socket_getsockopt,
343 + .socket_setsockopt = apparmor_socket_setsockopt,
344 + .socket_shutdown = apparmor_socket_shutdown,
346 .cred_alloc_blank = apparmor_cred_alloc_blank,
347 .cred_free = apparmor_cred_free,
348 .cred_prepare = apparmor_cred_prepare,
349 diff --git a/security/apparmor/net.c b/security/apparmor/net.c
351 index 0000000..003dd18
353 +++ b/security/apparmor/net.c
356 + * AppArmor security module
358 + * This file contains AppArmor network mediation
360 + * Copyright (C) 1998-2008 Novell/SUSE
361 + * Copyright 2009-2012 Canonical Ltd.
363 + * This program is free software; you can redistribute it and/or
364 + * modify it under the terms of the GNU General Public License as
365 + * published by the Free Software Foundation, version 2 of the
369 +#include "include/apparmor.h"
370 +#include "include/audit.h"
371 +#include "include/context.h"
372 +#include "include/net.h"
373 +#include "include/policy.h"
375 +#include "net_names.h"
377 +struct aa_fs_entry aa_fs_entry_network[] = {
378 + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK),
382 +/* audit callback for net specific fields */
383 +static void audit_cb(struct audit_buffer *ab, void *va)
385 + struct common_audit_data *sa = va;
387 + audit_log_format(ab, " family=");
388 + if (address_family_names[sa->u.net->family]) {
389 + audit_log_string(ab, address_family_names[sa->u.net->family]);
391 + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family);
393 + audit_log_format(ab, " sock_type=");
394 + if (sock_type_names[sa->aad->net.type]) {
395 + audit_log_string(ab, sock_type_names[sa->aad->net.type]);
397 + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type);
399 + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol);
403 + * audit_net - audit network access
404 + * @profile: profile being enforced (NOT NULL)
405 + * @op: operation being checked
406 + * @family: network family
407 + * @type: network type
408 + * @protocol: network protocol
409 + * @sk: socket auditing is being applied to
410 + * @error: error code for failure else 0
412 + * Returns: %0 or sa->error else other errorcode on failure
414 +static int audit_net(struct aa_profile *profile, int op, u16 family, int type,
415 + int protocol, struct sock *sk, int error)
417 + int audit_type = AUDIT_APPARMOR_AUTO;
418 + struct common_audit_data sa;
419 + struct apparmor_audit_data aad = { };
420 + struct lsm_network_audit net = { };
422 + sa.type = LSM_AUDIT_DATA_NET;
424 + sa.type = LSM_AUDIT_DATA_NONE;
426 + /* todo fill in socket addr info */
430 + sa.u.net->family = family;
432 + sa.aad->net.type = type;
433 + sa.aad->net.protocol = protocol;
434 + sa.aad->error = error;
436 + if (likely(!sa.aad->error)) {
437 + u16 audit_mask = profile->net.audit[sa.u.net->family];
438 + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
439 + !(1 << sa.aad->net.type & audit_mask)))
441 + audit_type = AUDIT_APPARMOR_AUDIT;
443 + u16 quiet_mask = profile->net.quiet[sa.u.net->family];
445 + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask;
447 + if (denied & kill_mask)
448 + audit_type = AUDIT_APPARMOR_KILL;
450 + if ((denied & quiet_mask) &&
451 + AUDIT_MODE(profile) != AUDIT_NOQUIET &&
452 + AUDIT_MODE(profile) != AUDIT_ALL)
453 + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error;
456 + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb);
460 + * aa_net_perm - very course network access check
461 + * @op: operation being checked
462 + * @profile: profile being enforced (NOT NULL)
463 + * @family: network family
464 + * @type: network type
465 + * @protocol: network protocol
467 + * Returns: %0 else error if permission denied
469 +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type,
470 + int protocol, struct sock *sk)
475 + if ((family < 0) || (family >= AF_MAX))
478 + if ((type < 0) || (type >= SOCK_MAX))
481 + /* unix domain and netlink sockets are handled by ipc */
482 + if (family == AF_UNIX || family == AF_NETLINK)
485 + family_mask = profile->net.allow[family];
487 + error = (family_mask & (1 << type)) ? 0 : -EACCES;
489 + return audit_net(profile, op, family, type, protocol, sk, error);
493 + * aa_revalidate_sk - Revalidate access to a sock
494 + * @op: operation being checked
495 + * @sk: sock being revalidated (NOT NULL)
497 + * Returns: %0 else error if permission denied
499 +int aa_revalidate_sk(int op, struct sock *sk)
501 + struct aa_profile *profile;
504 + /* aa_revalidate_sk should not be called from interrupt context
505 + * don't mediate these calls as they are not task related
507 + if (in_interrupt())
510 + profile = __aa_current_profile();
511 + if (!unconfined(profile))
512 + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
513 + sk->sk_protocol, sk);
517 diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
518 index 8132003..56e5304 100644
519 --- a/security/apparmor/policy.c
520 +++ b/security/apparmor/policy.c
521 @@ -747,6 +747,7 @@ static void free_profile(struct aa_profile *profile)
523 aa_free_file_rules(&profile->file);
524 aa_free_cap_rules(&profile->caps);
525 + aa_free_net_rules(&profile->net);
526 aa_free_rlimit_rules(&profile->rlimits);
528 aa_free_sid(profile->sid);
529 diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
530 index 329b1fd..1b90dfa 100644
531 --- a/security/apparmor/policy_unpack.c
532 +++ b/security/apparmor/policy_unpack.c
533 @@ -193,6 +193,19 @@ fail:
537 +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
539 + if (unpack_nameX(e, AA_U16, name)) {
540 + if (!inbounds(e, sizeof(u16)))
543 + *data = le16_to_cpu(get_unaligned((u16 *) e->pos));
544 + e->pos += sizeof(u16);
550 static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
552 if (unpack_nameX(e, AA_U32, name)) {
553 @@ -471,6 +484,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
555 struct aa_profile *profile = NULL;
556 const char *name = NULL;
558 int i, error = -EPROTO;
561 @@ -564,6 +578,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
562 if (!unpack_rlimits(e, profile))
565 + size = unpack_array(e, "net_allowed_af");
568 + for (i = 0; i < size; i++) {
569 + /* discard extraneous rules that this kernel will
574 + if (!unpack_u16(e, &tmp, NULL) ||
575 + !unpack_u16(e, &tmp, NULL) ||
576 + !unpack_u16(e, &tmp, NULL))
580 + if (!unpack_u16(e, &profile->net.allow[i], NULL))
582 + if (!unpack_u16(e, &profile->net.audit[i], NULL))
584 + if (!unpack_u16(e, &profile->net.quiet[i], NULL))
587 + if (!unpack_nameX(e, AA_ARRAYEND, NULL))
591 + * allow unix domain and netlink sockets they are handled
594 + profile->net.allow[AF_UNIX] = 0xffff;
595 + profile->net.allow[AF_NETLINK] = 0xffff;
597 if (unpack_nameX(e, AA_STRUCT, "policydb")) {
598 /* generic policy dfa - optional and may be NULL */
599 profile->policy.dfa = unpack_dfa(e);