diff options
Diffstat (limited to 'security')
35 files changed, 2253 insertions, 322 deletions
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore index 9cdec70d72b8..d5b291e94264 100644 --- a/security/apparmor/.gitignore +++ b/security/apparmor/.gitignore | |||
@@ -1,5 +1,6 @@ | |||
1 | # | 1 | # |
2 | # Generated include files | 2 | # Generated include files |
3 | # | 3 | # |
4 | net_names.h | ||
4 | capability_names.h | 5 | capability_names.h |
5 | rlim_names.h | 6 | rlim_names.h |
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index a16b195274de..dafdd387d42b 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile | |||
@@ -4,11 +4,44 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | |||
4 | 4 | ||
5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | 5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | 6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ |
7 | resource.o secid.o file.o policy_ns.o label.o | 7 | resource.o secid.o file.o policy_ns.o label.o mount.o net.o |
8 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o | 8 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o |
9 | 9 | ||
10 | clean-files := capability_names.h rlim_names.h | 10 | clean-files := capability_names.h rlim_names.h net_names.h |
11 | 11 | ||
12 | # Build a lower case string table of address family names | ||
13 | # Transform lines from | ||
14 | # #define AF_LOCAL 1 /* POSIX name for AF_UNIX */ | ||
15 | # #define AF_INET 2 /* Internet IP Protocol */ | ||
16 | # to | ||
17 | # [1] = "local", | ||
18 | # [2] = "inet", | ||
19 | # | ||
20 | # and build the securityfs entries for the mapping. | ||
21 | # Transforms lines from | ||
22 | # #define AF_INET 2 /* Internet IP Protocol */ | ||
23 | # to | ||
24 | # #define AA_SFS_AF_MASK "local inet" | ||
25 | quiet_cmd_make-af = GEN $@ | ||
26 | cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ | ||
27 | sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ | ||
28 | 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ | ||
29 | echo "};" >> $@ ;\ | ||
30 | printf '%s' '\#define AA_SFS_AF_MASK "' >> $@ ;\ | ||
31 | sed -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ | ||
32 | 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ | ||
33 | $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ | ||
34 | |||
35 | # Build a lower case string table of sock type names | ||
36 | # Transform lines from | ||
37 | # SOCK_STREAM = 1, | ||
38 | # to | ||
39 | # [1] = "stream", | ||
40 | quiet_cmd_make-sock = GEN $@ | ||
41 | cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ | ||
42 | sed $^ >>$@ -r -n \ | ||
43 | -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ | ||
44 | echo "};" >> $@ | ||
12 | 45 | ||
13 | # Build a lower case string table of capability names | 46 | # Build a lower case string table of capability names |
14 | # Transforms lines from | 47 | # Transforms lines from |
@@ -61,6 +94,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ | |||
61 | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ | 94 | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ |
62 | 95 | ||
63 | $(obj)/capability.o : $(obj)/capability_names.h | 96 | $(obj)/capability.o : $(obj)/capability_names.h |
97 | $(obj)/net.o : $(obj)/net_names.h | ||
64 | $(obj)/resource.o : $(obj)/rlim_names.h | 98 | $(obj)/resource.o : $(obj)/rlim_names.h |
65 | $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ | 99 | $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ |
66 | $(src)/Makefile | 100 | $(src)/Makefile |
@@ -68,3 +102,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ | |||
68 | $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ | 102 | $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ |
69 | $(src)/Makefile | 103 | $(src)/Makefile |
70 | $(call cmd,make-rlim) | 104 | $(call cmd,make-rlim) |
105 | $(obj)/net_names.h : $(srctree)/include/linux/socket.h \ | ||
106 | $(srctree)/include/linux/net.h \ | ||
107 | $(src)/Makefile | ||
108 | $(call cmd,make-af) | ||
109 | $(call cmd,make-sock) | ||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 853c2ec8e0c9..518d5928661b 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include "include/audit.h" | 32 | #include "include/audit.h" |
33 | #include "include/context.h" | 33 | #include "include/context.h" |
34 | #include "include/crypto.h" | 34 | #include "include/crypto.h" |
35 | #include "include/ipc.h" | ||
35 | #include "include/policy_ns.h" | 36 | #include "include/policy_ns.h" |
36 | #include "include/label.h" | 37 | #include "include/label.h" |
37 | #include "include/policy.h" | 38 | #include "include/policy.h" |
@@ -248,8 +249,10 @@ static struct dentry *aafs_create(const char *name, umode_t mode, | |||
248 | 249 | ||
249 | inode_lock(dir); | 250 | inode_lock(dir); |
250 | dentry = lookup_one_len(name, parent, strlen(name)); | 251 | dentry = lookup_one_len(name, parent, strlen(name)); |
251 | if (IS_ERR(dentry)) | 252 | if (IS_ERR(dentry)) { |
253 | error = PTR_ERR(dentry); | ||
252 | goto fail_lock; | 254 | goto fail_lock; |
255 | } | ||
253 | 256 | ||
254 | if (d_really_is_positive(dentry)) { | 257 | if (d_really_is_positive(dentry)) { |
255 | error = -EEXIST; | 258 | error = -EEXIST; |
@@ -1443,6 +1446,10 @@ void __aafs_profile_migrate_dents(struct aa_profile *old, | |||
1443 | { | 1446 | { |
1444 | int i; | 1447 | int i; |
1445 | 1448 | ||
1449 | AA_BUG(!old); | ||
1450 | AA_BUG(!new); | ||
1451 | AA_BUG(!mutex_is_locked(&profiles_ns(old)->lock)); | ||
1452 | |||
1446 | for (i = 0; i < AAFS_PROF_SIZEOF; i++) { | 1453 | for (i = 0; i < AAFS_PROF_SIZEOF; i++) { |
1447 | new->dents[i] = old->dents[i]; | 1454 | new->dents[i] = old->dents[i]; |
1448 | if (new->dents[i]) | 1455 | if (new->dents[i]) |
@@ -1506,6 +1513,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) | |||
1506 | struct dentry *dent = NULL, *dir; | 1513 | struct dentry *dent = NULL, *dir; |
1507 | int error; | 1514 | int error; |
1508 | 1515 | ||
1516 | AA_BUG(!profile); | ||
1517 | AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock)); | ||
1518 | |||
1509 | if (!parent) { | 1519 | if (!parent) { |
1510 | struct aa_profile *p; | 1520 | struct aa_profile *p; |
1511 | p = aa_deref_parent(profile); | 1521 | p = aa_deref_parent(profile); |
@@ -1731,6 +1741,7 @@ void __aafs_ns_rmdir(struct aa_ns *ns) | |||
1731 | 1741 | ||
1732 | if (!ns) | 1742 | if (!ns) |
1733 | return; | 1743 | return; |
1744 | AA_BUG(!mutex_is_locked(&ns->lock)); | ||
1734 | 1745 | ||
1735 | list_for_each_entry(child, &ns->base.profiles, base.list) | 1746 | list_for_each_entry(child, &ns->base.profiles, base.list) |
1736 | __aafs_profile_rmdir(child); | 1747 | __aafs_profile_rmdir(child); |
@@ -1903,6 +1914,10 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns) | |||
1903 | { | 1914 | { |
1904 | struct aa_ns *parent, *next; | 1915 | struct aa_ns *parent, *next; |
1905 | 1916 | ||
1917 | AA_BUG(!root); | ||
1918 | AA_BUG(!ns); | ||
1919 | AA_BUG(ns != root && !mutex_is_locked(&ns->parent->lock)); | ||
1920 | |||
1906 | /* is next namespace a child */ | 1921 | /* is next namespace a child */ |
1907 | if (!list_empty(&ns->sub_ns)) { | 1922 | if (!list_empty(&ns->sub_ns)) { |
1908 | next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); | 1923 | next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); |
@@ -1937,6 +1952,9 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns) | |||
1937 | static struct aa_profile *__first_profile(struct aa_ns *root, | 1952 | static struct aa_profile *__first_profile(struct aa_ns *root, |
1938 | struct aa_ns *ns) | 1953 | struct aa_ns *ns) |
1939 | { | 1954 | { |
1955 | AA_BUG(!root); | ||
1956 | AA_BUG(ns && !mutex_is_locked(&ns->lock)); | ||
1957 | |||
1940 | for (; ns; ns = __next_ns(root, ns)) { | 1958 | for (; ns; ns = __next_ns(root, ns)) { |
1941 | if (!list_empty(&ns->base.profiles)) | 1959 | if (!list_empty(&ns->base.profiles)) |
1942 | return list_first_entry(&ns->base.profiles, | 1960 | return list_first_entry(&ns->base.profiles, |
@@ -1959,6 +1977,8 @@ static struct aa_profile *__next_profile(struct aa_profile *p) | |||
1959 | struct aa_profile *parent; | 1977 | struct aa_profile *parent; |
1960 | struct aa_ns *ns = p->ns; | 1978 | struct aa_ns *ns = p->ns; |
1961 | 1979 | ||
1980 | AA_BUG(!mutex_is_locked(&profiles_ns(p)->lock)); | ||
1981 | |||
1962 | /* is next profile a child */ | 1982 | /* is next profile a child */ |
1963 | if (!list_empty(&p->base.profiles)) | 1983 | if (!list_empty(&p->base.profiles)) |
1964 | return list_first_entry(&p->base.profiles, typeof(*p), | 1984 | return list_first_entry(&p->base.profiles, typeof(*p), |
@@ -2127,6 +2147,11 @@ static struct aa_sfs_entry aa_sfs_entry_ptrace[] = { | |||
2127 | { } | 2147 | { } |
2128 | }; | 2148 | }; |
2129 | 2149 | ||
2150 | static struct aa_sfs_entry aa_sfs_entry_signal[] = { | ||
2151 | AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK), | ||
2152 | { } | ||
2153 | }; | ||
2154 | |||
2130 | static struct aa_sfs_entry aa_sfs_entry_domain[] = { | 2155 | static struct aa_sfs_entry aa_sfs_entry_domain[] = { |
2131 | AA_SFS_FILE_BOOLEAN("change_hat", 1), | 2156 | AA_SFS_FILE_BOOLEAN("change_hat", 1), |
2132 | AA_SFS_FILE_BOOLEAN("change_hatv", 1), | 2157 | AA_SFS_FILE_BOOLEAN("change_hatv", 1), |
@@ -2151,9 +2176,14 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = { | |||
2151 | { } | 2176 | { } |
2152 | }; | 2177 | }; |
2153 | 2178 | ||
2179 | static struct aa_sfs_entry aa_sfs_entry_mount[] = { | ||
2180 | AA_SFS_FILE_STRING("mask", "mount umount pivot_root"), | ||
2181 | { } | ||
2182 | }; | ||
2183 | |||
2154 | static struct aa_sfs_entry aa_sfs_entry_ns[] = { | 2184 | static struct aa_sfs_entry aa_sfs_entry_ns[] = { |
2155 | AA_SFS_FILE_BOOLEAN("profile", 1), | 2185 | AA_SFS_FILE_BOOLEAN("profile", 1), |
2156 | AA_SFS_FILE_BOOLEAN("pivot_root", 1), | 2186 | AA_SFS_FILE_BOOLEAN("pivot_root", 0), |
2157 | { } | 2187 | { } |
2158 | }; | 2188 | }; |
2159 | 2189 | ||
@@ -2172,22 +2202,25 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { | |||
2172 | AA_SFS_DIR("policy", aa_sfs_entry_policy), | 2202 | AA_SFS_DIR("policy", aa_sfs_entry_policy), |
2173 | AA_SFS_DIR("domain", aa_sfs_entry_domain), | 2203 | AA_SFS_DIR("domain", aa_sfs_entry_domain), |
2174 | AA_SFS_DIR("file", aa_sfs_entry_file), | 2204 | AA_SFS_DIR("file", aa_sfs_entry_file), |
2205 | AA_SFS_DIR("network", aa_sfs_entry_network), | ||
2206 | AA_SFS_DIR("mount", aa_sfs_entry_mount), | ||
2175 | AA_SFS_DIR("namespaces", aa_sfs_entry_ns), | 2207 | AA_SFS_DIR("namespaces", aa_sfs_entry_ns), |
2176 | AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | 2208 | AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), |
2177 | AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit), | 2209 | AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit), |
2178 | AA_SFS_DIR("caps", aa_sfs_entry_caps), | 2210 | AA_SFS_DIR("caps", aa_sfs_entry_caps), |
2179 | AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), | 2211 | AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), |
2212 | AA_SFS_DIR("signal", aa_sfs_entry_signal), | ||
2180 | AA_SFS_DIR("query", aa_sfs_entry_query), | 2213 | AA_SFS_DIR("query", aa_sfs_entry_query), |
2181 | { } | 2214 | { } |
2182 | }; | 2215 | }; |
2183 | 2216 | ||
2184 | static struct aa_sfs_entry aa_sfs_entry_apparmor[] = { | 2217 | static struct aa_sfs_entry aa_sfs_entry_apparmor[] = { |
2185 | AA_SFS_FILE_FOPS(".access", 0640, &aa_sfs_access), | 2218 | AA_SFS_FILE_FOPS(".access", 0666, &aa_sfs_access), |
2186 | AA_SFS_FILE_FOPS(".stacked", 0444, &seq_ns_stacked_fops), | 2219 | AA_SFS_FILE_FOPS(".stacked", 0444, &seq_ns_stacked_fops), |
2187 | AA_SFS_FILE_FOPS(".ns_stacked", 0444, &seq_ns_nsstacked_fops), | 2220 | AA_SFS_FILE_FOPS(".ns_stacked", 0444, &seq_ns_nsstacked_fops), |
2188 | AA_SFS_FILE_FOPS(".ns_level", 0666, &seq_ns_level_fops), | 2221 | AA_SFS_FILE_FOPS(".ns_level", 0444, &seq_ns_level_fops), |
2189 | AA_SFS_FILE_FOPS(".ns_name", 0640, &seq_ns_name_fops), | 2222 | AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_fops), |
2190 | AA_SFS_FILE_FOPS("profiles", 0440, &aa_sfs_profiles_fops), | 2223 | AA_SFS_FILE_FOPS("profiles", 0444, &aa_sfs_profiles_fops), |
2191 | AA_SFS_DIR("features", aa_sfs_entry_features), | 2224 | AA_SFS_DIR("features", aa_sfs_entry_features), |
2192 | { } | 2225 | { } |
2193 | }; | 2226 | }; |
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 17a601c67b62..dd754b7850a8 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c | |||
@@ -374,8 +374,8 @@ static const char *next_name(int xtype, const char *name) | |||
374 | * | 374 | * |
375 | * Returns: refcounted label, or NULL on failure (MAYBE NULL) | 375 | * Returns: refcounted label, or NULL on failure (MAYBE NULL) |
376 | */ | 376 | */ |
377 | static struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, | 377 | struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, |
378 | const char **name) | 378 | const char **name) |
379 | { | 379 | { |
380 | struct aa_label *label = NULL; | 380 | struct aa_label *label = NULL; |
381 | u32 xtype = xindex & AA_X_TYPE_MASK; | 381 | u32 xtype = xindex & AA_X_TYPE_MASK; |
diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 3382518b87fa..db80221891c6 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "include/context.h" | 21 | #include "include/context.h" |
22 | #include "include/file.h" | 22 | #include "include/file.h" |
23 | #include "include/match.h" | 23 | #include "include/match.h" |
24 | #include "include/net.h" | ||
24 | #include "include/path.h" | 25 | #include "include/path.h" |
25 | #include "include/policy.h" | 26 | #include "include/policy.h" |
26 | #include "include/label.h" | 27 | #include "include/label.h" |
@@ -566,6 +567,32 @@ static int __file_path_perm(const char *op, struct aa_label *label, | |||
566 | return error; | 567 | return error; |
567 | } | 568 | } |
568 | 569 | ||
570 | static int __file_sock_perm(const char *op, struct aa_label *label, | ||
571 | struct aa_label *flabel, struct file *file, | ||
572 | u32 request, u32 denied) | ||
573 | { | ||
574 | struct socket *sock = (struct socket *) file->private_data; | ||
575 | int error; | ||
576 | |||
577 | AA_BUG(!sock); | ||
578 | |||
579 | /* revalidation due to label out of date. No revocation at this time */ | ||
580 | if (!denied && aa_label_is_subset(flabel, label)) | ||
581 | return 0; | ||
582 | |||
583 | /* TODO: improve to skip profiles cached in flabel */ | ||
584 | error = aa_sock_file_perm(label, op, request, sock); | ||
585 | if (denied) { | ||
586 | /* TODO: improve to skip profiles checked above */ | ||
587 | /* check every profile in file label to is cached */ | ||
588 | last_error(error, aa_sock_file_perm(flabel, op, request, sock)); | ||
589 | } | ||
590 | if (!error) | ||
591 | update_file_ctx(file_ctx(file), label, request); | ||
592 | |||
593 | return error; | ||
594 | } | ||
595 | |||
569 | /** | 596 | /** |
570 | * aa_file_perm - do permission revalidation check & audit for @file | 597 | * aa_file_perm - do permission revalidation check & audit for @file |
571 | * @op: operation being checked | 598 | * @op: operation being checked |
@@ -610,6 +637,9 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, | |||
610 | error = __file_path_perm(op, label, flabel, file, request, | 637 | error = __file_path_perm(op, label, flabel, file, request, |
611 | denied); | 638 | denied); |
612 | 639 | ||
640 | else if (S_ISSOCK(file_inode(file)->i_mode)) | ||
641 | error = __file_sock_perm(op, label, flabel, file, request, | ||
642 | denied); | ||
613 | done: | 643 | done: |
614 | rcu_read_unlock(); | 644 | rcu_read_unlock(); |
615 | 645 | ||
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index aaf893f4e4f5..829082c35faa 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h | |||
@@ -27,7 +27,9 @@ | |||
27 | #define AA_CLASS_NET 4 | 27 | #define AA_CLASS_NET 4 |
28 | #define AA_CLASS_RLIMITS 5 | 28 | #define AA_CLASS_RLIMITS 5 |
29 | #define AA_CLASS_DOMAIN 6 | 29 | #define AA_CLASS_DOMAIN 6 |
30 | #define AA_CLASS_MOUNT 7 | ||
30 | #define AA_CLASS_PTRACE 9 | 31 | #define AA_CLASS_PTRACE 9 |
32 | #define AA_CLASS_SIGNAL 10 | ||
31 | #define AA_CLASS_LABEL 16 | 33 | #define AA_CLASS_LABEL 16 |
32 | 34 | ||
33 | #define AA_CLASS_LAST AA_CLASS_LABEL | 35 | #define AA_CLASS_LAST AA_CLASS_LABEL |
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index c68839a44351..ff4316e1068d 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h | |||
@@ -71,6 +71,10 @@ enum audit_type { | |||
71 | #define OP_FMPROT "file_mprotect" | 71 | #define OP_FMPROT "file_mprotect" |
72 | #define OP_INHERIT "file_inherit" | 72 | #define OP_INHERIT "file_inherit" |
73 | 73 | ||
74 | #define OP_PIVOTROOT "pivotroot" | ||
75 | #define OP_MOUNT "mount" | ||
76 | #define OP_UMOUNT "umount" | ||
77 | |||
74 | #define OP_CREATE "create" | 78 | #define OP_CREATE "create" |
75 | #define OP_POST_CREATE "post_create" | 79 | #define OP_POST_CREATE "post_create" |
76 | #define OP_BIND "bind" | 80 | #define OP_BIND "bind" |
@@ -86,6 +90,7 @@ enum audit_type { | |||
86 | #define OP_SHUTDOWN "socket_shutdown" | 90 | #define OP_SHUTDOWN "socket_shutdown" |
87 | 91 | ||
88 | #define OP_PTRACE "ptrace" | 92 | #define OP_PTRACE "ptrace" |
93 | #define OP_SIGNAL "signal" | ||
89 | 94 | ||
90 | #define OP_EXEC "exec" | 95 | #define OP_EXEC "exec" |
91 | 96 | ||
@@ -116,20 +121,36 @@ struct apparmor_audit_data { | |||
116 | /* these entries require a custom callback fn */ | 121 | /* these entries require a custom callback fn */ |
117 | struct { | 122 | struct { |
118 | struct aa_label *peer; | 123 | struct aa_label *peer; |
119 | struct { | 124 | union { |
120 | const char *target; | 125 | struct { |
121 | kuid_t ouid; | 126 | kuid_t ouid; |
122 | } fs; | 127 | const char *target; |
128 | } fs; | ||
129 | struct { | ||
130 | int type, protocol; | ||
131 | struct sock *peer_sk; | ||
132 | void *addr; | ||
133 | int addrlen; | ||
134 | } net; | ||
135 | int signal; | ||
136 | struct { | ||
137 | int rlim; | ||
138 | unsigned long max; | ||
139 | } rlim; | ||
140 | }; | ||
123 | }; | 141 | }; |
124 | struct { | 142 | struct { |
125 | const char *name; | 143 | struct aa_profile *profile; |
126 | long pos; | ||
127 | const char *ns; | 144 | const char *ns; |
145 | long pos; | ||
128 | } iface; | 146 | } iface; |
129 | struct { | 147 | struct { |
130 | int rlim; | 148 | const char *src_name; |
131 | unsigned long max; | 149 | const char *type; |
132 | } rlim; | 150 | const char *trans; |
151 | const char *data; | ||
152 | unsigned long flags; | ||
153 | } mnt; | ||
133 | }; | 154 | }; |
134 | }; | 155 | }; |
135 | 156 | ||
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index 24c5976d6143..ac9862ff7cdf 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h | |||
@@ -15,6 +15,8 @@ | |||
15 | #include <linux/binfmts.h> | 15 | #include <linux/binfmts.h> |
16 | #include <linux/types.h> | 16 | #include <linux/types.h> |
17 | 17 | ||
18 | #include "label.h" | ||
19 | |||
18 | #ifndef __AA_DOMAIN_H | 20 | #ifndef __AA_DOMAIN_H |
19 | #define __AA_DOMAIN_H | 21 | #define __AA_DOMAIN_H |
20 | 22 | ||
@@ -29,6 +31,9 @@ struct aa_domain { | |||
29 | #define AA_CHANGE_ONEXEC 4 | 31 | #define AA_CHANGE_ONEXEC 4 |
30 | #define AA_CHANGE_STACK 8 | 32 | #define AA_CHANGE_STACK 8 |
31 | 33 | ||
34 | struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, | ||
35 | const char **name); | ||
36 | |||
32 | int apparmor_bprm_set_creds(struct linux_binprm *bprm); | 37 | int apparmor_bprm_set_creds(struct linux_binprm *bprm); |
33 | 38 | ||
34 | void aa_free_domain_entries(struct aa_domain *domain); | 39 | void aa_free_domain_entries(struct aa_domain *domain); |
diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h index 656fdb81c8a0..5ffc218d1e74 100644 --- a/security/apparmor/include/ipc.h +++ b/security/apparmor/include/ipc.h | |||
@@ -27,8 +27,14 @@ struct aa_profile; | |||
27 | 27 | ||
28 | #define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ | 28 | #define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ |
29 | AA_MAY_BE_READ | AA_MAY_BE_TRACED) | 29 | AA_MAY_BE_READ | AA_MAY_BE_TRACED) |
30 | #define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE) | ||
31 | |||
32 | #define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \ | ||
33 | "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \ | ||
34 | "xcpu xfsz vtalrm prof winch io pwr sys emt lost" | ||
30 | 35 | ||
31 | int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, | 36 | int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, |
32 | u32 request); | 37 | u32 request); |
38 | int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig); | ||
33 | 39 | ||
34 | #endif /* __AA_IPC_H */ | 40 | #endif /* __AA_IPC_H */ |
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 9a283b722755..af22dcbbcb8a 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h | |||
@@ -310,6 +310,7 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp); | |||
310 | #define FLAG_SHOW_MODE 1 | 310 | #define FLAG_SHOW_MODE 1 |
311 | #define FLAG_VIEW_SUBNS 2 | 311 | #define FLAG_VIEW_SUBNS 2 |
312 | #define FLAG_HIDDEN_UNCONFINED 4 | 312 | #define FLAG_HIDDEN_UNCONFINED 4 |
313 | #define FLAG_ABS_ROOT 8 | ||
313 | int aa_label_snxprint(char *str, size_t size, struct aa_ns *view, | 314 | int aa_label_snxprint(char *str, size_t size, struct aa_ns *view, |
314 | struct aa_label *label, int flags); | 315 | struct aa_label *label, int flags); |
315 | int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label, | 316 | int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label, |
diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 000000000000..25d6067fa6ef --- /dev/null +++ b/security/apparmor/include/mount.h | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor file mediation function definitions. | ||
5 | * | ||
6 | * Copyright 2017 Canonical Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation, version 2 of the | ||
11 | * License. | ||
12 | */ | ||
13 | |||
14 | #ifndef __AA_MOUNT_H | ||
15 | #define __AA_MOUNT_H | ||
16 | |||
17 | #include <linux/fs.h> | ||
18 | #include <linux/path.h> | ||
19 | |||
20 | #include "domain.h" | ||
21 | #include "policy.h" | ||
22 | |||
23 | /* mount perms */ | ||
24 | #define AA_MAY_PIVOTROOT 0x01 | ||
25 | #define AA_MAY_MOUNT 0x02 | ||
26 | #define AA_MAY_UMOUNT 0x04 | ||
27 | #define AA_AUDIT_DATA 0x40 | ||
28 | #define AA_MNT_CONT_MATCH 0x40 | ||
29 | |||
30 | #define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) | ||
31 | |||
32 | int aa_remount(struct aa_label *label, const struct path *path, | ||
33 | unsigned long flags, void *data); | ||
34 | |||
35 | int aa_bind_mount(struct aa_label *label, const struct path *path, | ||
36 | const char *old_name, unsigned long flags); | ||
37 | |||
38 | |||
39 | int aa_mount_change_type(struct aa_label *label, const struct path *path, | ||
40 | unsigned long flags); | ||
41 | |||
42 | int aa_move_mount(struct aa_label *label, const struct path *path, | ||
43 | const char *old_name); | ||
44 | |||
45 | int aa_new_mount(struct aa_label *label, const char *dev_name, | ||
46 | const struct path *path, const char *type, unsigned long flags, | ||
47 | void *data); | ||
48 | |||
49 | int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags); | ||
50 | |||
51 | int aa_pivotroot(struct aa_label *label, const struct path *old_path, | ||
52 | const struct path *new_path); | ||
53 | |||
54 | #endif /* __AA_MOUNT_H */ | ||
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 000000000000..140c8efcf364 --- /dev/null +++ b/security/apparmor/include/net.h | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor network mediation definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2017 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_NET_H | ||
16 | #define __AA_NET_H | ||
17 | |||
18 | #include <net/sock.h> | ||
19 | #include <linux/path.h> | ||
20 | |||
21 | #include "apparmorfs.h" | ||
22 | #include "label.h" | ||
23 | #include "perms.h" | ||
24 | #include "policy.h" | ||
25 | |||
26 | #define AA_MAY_SEND AA_MAY_WRITE | ||
27 | #define AA_MAY_RECEIVE AA_MAY_READ | ||
28 | |||
29 | #define AA_MAY_SHUTDOWN AA_MAY_DELETE | ||
30 | |||
31 | #define AA_MAY_CONNECT AA_MAY_OPEN | ||
32 | #define AA_MAY_ACCEPT 0x00100000 | ||
33 | |||
34 | #define AA_MAY_BIND 0x00200000 | ||
35 | #define AA_MAY_LISTEN 0x00400000 | ||
36 | |||
37 | #define AA_MAY_SETOPT 0x01000000 | ||
38 | #define AA_MAY_GETOPT 0x02000000 | ||
39 | |||
40 | #define NET_PERMS_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ | ||
41 | AA_MAY_SHUTDOWN | AA_MAY_BIND | AA_MAY_LISTEN | \ | ||
42 | AA_MAY_CONNECT | AA_MAY_ACCEPT | AA_MAY_SETATTR | \ | ||
43 | AA_MAY_GETATTR | AA_MAY_SETOPT | AA_MAY_GETOPT) | ||
44 | |||
45 | #define NET_FS_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ | ||
46 | AA_MAY_SHUTDOWN | AA_MAY_CONNECT | AA_MAY_RENAME |\ | ||
47 | AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_CHMOD | \ | ||
48 | AA_MAY_CHOWN | AA_MAY_CHGRP | AA_MAY_LOCK | \ | ||
49 | AA_MAY_MPROT) | ||
50 | |||
51 | #define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \ | ||
52 | AA_MAY_ACCEPT) | ||
53 | struct aa_sk_ctx { | ||
54 | struct aa_label *label; | ||
55 | struct aa_label *peer; | ||
56 | struct path path; | ||
57 | }; | ||
58 | |||
59 | #define SK_CTX(X) ((X)->sk_security) | ||
60 | #define SOCK_ctx(X) SOCK_INODE(X)->i_security | ||
61 | #define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \ | ||
62 | struct lsm_network_audit NAME ## _net = { .sk = (SK), \ | ||
63 | .family = (F)}; \ | ||
64 | DEFINE_AUDIT_DATA(NAME, \ | ||
65 | ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \ | ||
66 | LSM_AUDIT_DATA_NONE, \ | ||
67 | OP); \ | ||
68 | NAME.u.net = &(NAME ## _net); \ | ||
69 | aad(&NAME)->net.type = (T); \ | ||
70 | aad(&NAME)->net.protocol = (P) | ||
71 | |||
72 | #define DEFINE_AUDIT_SK(NAME, OP, SK) \ | ||
73 | DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ | ||
74 | (SK)->sk_protocol) | ||
75 | |||
76 | /* struct aa_net - network confinement data | ||
77 | * @allow: basic network families permissions | ||
78 | * @audit: which network permissions to force audit | ||
79 | * @quiet: which network permissions to quiet rejects | ||
80 | */ | ||
81 | struct aa_net { | ||
82 | u16 allow[AF_MAX]; | ||
83 | u16 audit[AF_MAX]; | ||
84 | u16 quiet[AF_MAX]; | ||
85 | }; | ||
86 | |||
87 | |||
88 | extern struct aa_sfs_entry aa_sfs_entry_network[]; | ||
89 | |||
90 | void audit_net_cb(struct audit_buffer *ab, void *va); | ||
91 | int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, | ||
92 | u32 request, u16 family, int type); | ||
93 | int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, | ||
94 | int type, int protocol); | ||
95 | static inline int aa_profile_af_sk_perm(struct aa_profile *profile, | ||
96 | struct common_audit_data *sa, | ||
97 | u32 request, | ||
98 | struct sock *sk) | ||
99 | { | ||
100 | return aa_profile_af_perm(profile, sa, request, sk->sk_family, | ||
101 | sk->sk_type); | ||
102 | } | ||
103 | int aa_sk_perm(const char *op, u32 request, struct sock *sk); | ||
104 | |||
105 | int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, | ||
106 | struct socket *sock); | ||
107 | |||
108 | |||
109 | static inline void aa_free_net_rules(struct aa_net *new) | ||
110 | { | ||
111 | /* NOP */ | ||
112 | } | ||
113 | |||
114 | #endif /* __AA_NET_H */ | ||
diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h index 2b27bb79aec4..af04d5a7d73d 100644 --- a/security/apparmor/include/perms.h +++ b/security/apparmor/include/perms.h | |||
@@ -135,9 +135,10 @@ extern struct aa_perms allperms; | |||
135 | 135 | ||
136 | 136 | ||
137 | void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask); | 137 | void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask); |
138 | void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask); | 138 | void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, |
139 | u32 mask); | ||
139 | void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, | 140 | void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, |
140 | u32 chrsmask, const char **names, u32 namesmask); | 141 | u32 chrsmask, const char * const *names, u32 namesmask); |
141 | void aa_apply_modes_to_perms(struct aa_profile *profile, | 142 | void aa_apply_modes_to_perms(struct aa_profile *profile, |
142 | struct aa_perms *perms); | 143 | struct aa_perms *perms); |
143 | void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, | 144 | void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, |
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 17fe41a9cac3..4364088a0b9e 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "file.h" | 30 | #include "file.h" |
31 | #include "lib.h" | 31 | #include "lib.h" |
32 | #include "label.h" | 32 | #include "label.h" |
33 | #include "net.h" | ||
33 | #include "perms.h" | 34 | #include "perms.h" |
34 | #include "resource.h" | 35 | #include "resource.h" |
35 | 36 | ||
@@ -111,6 +112,7 @@ struct aa_data { | |||
111 | * @policy: general match rules governing policy | 112 | * @policy: general match rules governing policy |
112 | * @file: The set of rules governing basic file access and domain transitions | 113 | * @file: The set of rules governing basic file access and domain transitions |
113 | * @caps: capabilities for the profile | 114 | * @caps: capabilities for the profile |
115 | * @net: network controls for the profile | ||
114 | * @rlimits: rlimits for the profile | 116 | * @rlimits: rlimits for the profile |
115 | * | 117 | * |
116 | * @dents: dentries for the profiles file entries in apparmorfs | 118 | * @dents: dentries for the profiles file entries in apparmorfs |
@@ -148,6 +150,7 @@ struct aa_profile { | |||
148 | struct aa_policydb policy; | 150 | struct aa_policydb policy; |
149 | struct aa_file_rules file; | 151 | struct aa_file_rules file; |
150 | struct aa_caps caps; | 152 | struct aa_caps caps; |
153 | struct aa_net net; | ||
151 | struct aa_rlimit rlimits; | 154 | struct aa_rlimit rlimits; |
152 | 155 | ||
153 | struct aa_loaddata *rawdata; | 156 | struct aa_loaddata *rawdata; |
@@ -220,6 +223,16 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, | |||
220 | return 0; | 223 | return 0; |
221 | } | 224 | } |
222 | 225 | ||
226 | static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, | ||
227 | u16 AF) { | ||
228 | unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); | ||
229 | u16 be_af = cpu_to_be16(AF); | ||
230 | |||
231 | if (!state) | ||
232 | return 0; | ||
233 | return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); | ||
234 | } | ||
235 | |||
223 | /** | 236 | /** |
224 | * aa_get_profile - increment refcount on profile @p | 237 | * aa_get_profile - increment refcount on profile @p |
225 | * @p: profile (MAYBE NULL) | 238 | * @p: profile (MAYBE NULL) |
diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h new file mode 100644 index 000000000000..92e62fe95292 --- /dev/null +++ b/security/apparmor/include/sig_names.h | |||
@@ -0,0 +1,98 @@ | |||
1 | #include <linux/signal.h> | ||
2 | |||
3 | #define SIGUNKNOWN 0 | ||
4 | #define MAXMAPPED_SIG 35 | ||
5 | /* provide a mapping of arch signal to internal signal # for mediation | ||
6 | * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO | ||
7 | * map to the same entry those that may/or may not get a separate entry | ||
8 | */ | ||
9 | static const int sig_map[MAXMAPPED_SIG] = { | ||
10 | [0] = MAXMAPPED_SIG, /* existence test */ | ||
11 | [SIGHUP] = 1, | ||
12 | [SIGINT] = 2, | ||
13 | [SIGQUIT] = 3, | ||
14 | [SIGILL] = 4, | ||
15 | [SIGTRAP] = 5, /* -, 5, - */ | ||
16 | [SIGABRT] = 6, /* SIGIOT: -, 6, - */ | ||
17 | [SIGBUS] = 7, /* 10, 7, 10 */ | ||
18 | [SIGFPE] = 8, | ||
19 | [SIGKILL] = 9, | ||
20 | [SIGUSR1] = 10, /* 30, 10, 16 */ | ||
21 | [SIGSEGV] = 11, | ||
22 | [SIGUSR2] = 12, /* 31, 12, 17 */ | ||
23 | [SIGPIPE] = 13, | ||
24 | [SIGALRM] = 14, | ||
25 | [SIGTERM] = 15, | ||
26 | #ifdef SIGSTKFLT | ||
27 | [SIGSTKFLT] = 16, /* -, 16, - */ | ||
28 | #endif | ||
29 | [SIGCHLD] = 17, /* 20, 17, 18. SIGCHLD -, -, 18 */ | ||
30 | [SIGCONT] = 18, /* 19, 18, 25 */ | ||
31 | [SIGSTOP] = 19, /* 17, 19, 23 */ | ||
32 | [SIGTSTP] = 20, /* 18, 20, 24 */ | ||
33 | [SIGTTIN] = 21, /* 21, 21, 26 */ | ||
34 | [SIGTTOU] = 22, /* 22, 22, 27 */ | ||
35 | [SIGURG] = 23, /* 16, 23, 21 */ | ||
36 | [SIGXCPU] = 24, /* 24, 24, 30 */ | ||
37 | [SIGXFSZ] = 25, /* 25, 25, 31 */ | ||
38 | [SIGVTALRM] = 26, /* 26, 26, 28 */ | ||
39 | [SIGPROF] = 27, /* 27, 27, 29 */ | ||
40 | [SIGWINCH] = 28, /* 28, 28, 20 */ | ||
41 | [SIGIO] = 29, /* SIGPOLL: 23, 29, 22 */ | ||
42 | [SIGPWR] = 30, /* 29, 30, 19. SIGINFO 29, -, - */ | ||
43 | #ifdef SIGSYS | ||
44 | [SIGSYS] = 31, /* 12, 31, 12. often SIG LOST/UNUSED */ | ||
45 | #endif | ||
46 | #ifdef SIGEMT | ||
47 | [SIGEMT] = 32, /* 7, - , 7 */ | ||
48 | #endif | ||
49 | #if defined(SIGLOST) && SIGPWR != SIGLOST /* sparc */ | ||
50 | [SIGLOST] = 33, /* unused on Linux */ | ||
51 | #endif | ||
52 | #if defined(SIGUNUSED) && \ | ||
53 | defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS | ||
54 | [SIGUNUSED] = 34, /* -, 31, - */ | ||
55 | #endif | ||
56 | }; | ||
57 | |||
58 | /* this table is ordered post sig_map[sig] mapping */ | ||
59 | static const char *const sig_names[MAXMAPPED_SIG + 1] = { | ||
60 | "unknown", | ||
61 | "hup", | ||
62 | "int", | ||
63 | "quit", | ||
64 | "ill", | ||
65 | "trap", | ||
66 | "abrt", | ||
67 | "bus", | ||
68 | "fpe", | ||
69 | "kill", | ||
70 | "usr1", | ||
71 | "segv", | ||
72 | "usr2", | ||
73 | "pipe", | ||
74 | "alrm", | ||
75 | "term", | ||
76 | "stkflt", | ||
77 | "chld", | ||
78 | "cont", | ||
79 | "stop", | ||
80 | "stp", | ||
81 | "ttin", | ||
82 | "ttou", | ||
83 | "urg", | ||
84 | "xcpu", | ||
85 | "xfsz", | ||
86 | "vtalrm", | ||
87 | "prof", | ||
88 | "winch", | ||
89 | "io", | ||
90 | "pwr", | ||
91 | "sys", | ||
92 | "emt", | ||
93 | "lost", | ||
94 | "unused", | ||
95 | |||
96 | "exists", /* always last existence test mapped to MAXMAPPED_SIG */ | ||
97 | }; | ||
98 | |||
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 11e66b5bbc42..66fb9ede9447 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include "include/context.h" | 20 | #include "include/context.h" |
21 | #include "include/policy.h" | 21 | #include "include/policy.h" |
22 | #include "include/ipc.h" | 22 | #include "include/ipc.h" |
23 | #include "include/sig_names.h" | ||
23 | 24 | ||
24 | /** | 25 | /** |
25 | * audit_ptrace_mask - convert mask to permission string | 26 | * audit_ptrace_mask - convert mask to permission string |
@@ -121,3 +122,101 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, | |||
121 | } | 122 | } |
122 | 123 | ||
123 | 124 | ||
125 | static inline int map_signal_num(int sig) | ||
126 | { | ||
127 | if (sig > SIGRTMAX) | ||
128 | return SIGUNKNOWN; | ||
129 | else if (sig >= SIGRTMIN) | ||
130 | return sig - SIGRTMIN + 128; /* rt sigs mapped to 128 */ | ||
131 | else if (sig <= MAXMAPPED_SIG) | ||
132 | return sig_map[sig]; | ||
133 | return SIGUNKNOWN; | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * audit_file_mask - convert mask to permission string | ||
138 | * @buffer: buffer to write string to (NOT NULL) | ||
139 | * @mask: permission mask to convert | ||
140 | */ | ||
141 | static void audit_signal_mask(struct audit_buffer *ab, u32 mask) | ||
142 | { | ||
143 | if (mask & MAY_READ) | ||
144 | audit_log_string(ab, "receive"); | ||
145 | if (mask & MAY_WRITE) | ||
146 | audit_log_string(ab, "send"); | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * audit_cb - call back for signal specific audit fields | ||
151 | * @ab: audit_buffer (NOT NULL) | ||
152 | * @va: audit struct to audit values of (NOT NULL) | ||
153 | */ | ||
154 | static void audit_signal_cb(struct audit_buffer *ab, void *va) | ||
155 | { | ||
156 | struct common_audit_data *sa = va; | ||
157 | |||
158 | if (aad(sa)->request & AA_SIGNAL_PERM_MASK) { | ||
159 | audit_log_format(ab, " requested_mask="); | ||
160 | audit_signal_mask(ab, aad(sa)->request); | ||
161 | if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) { | ||
162 | audit_log_format(ab, " denied_mask="); | ||
163 | audit_signal_mask(ab, aad(sa)->denied); | ||
164 | } | ||
165 | } | ||
166 | if (aad(sa)->signal <= MAXMAPPED_SIG) | ||
167 | audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]); | ||
168 | else | ||
169 | audit_log_format(ab, " signal=rtmin+%d", | ||
170 | aad(sa)->signal - 128); | ||
171 | audit_log_format(ab, " peer="); | ||
172 | aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, | ||
173 | FLAGS_NONE, GFP_ATOMIC); | ||
174 | } | ||
175 | |||
176 | /* TODO: update to handle compound name&name2, conditionals */ | ||
177 | static void profile_match_signal(struct aa_profile *profile, const char *label, | ||
178 | int signal, struct aa_perms *perms) | ||
179 | { | ||
180 | unsigned int state; | ||
181 | |||
182 | /* TODO: secondary cache check <profile, profile, perm> */ | ||
183 | state = aa_dfa_next(profile->policy.dfa, | ||
184 | profile->policy.start[AA_CLASS_SIGNAL], | ||
185 | signal); | ||
186 | state = aa_dfa_match(profile->policy.dfa, state, label); | ||
187 | aa_compute_perms(profile->policy.dfa, state, perms); | ||
188 | } | ||
189 | |||
190 | static int profile_signal_perm(struct aa_profile *profile, | ||
191 | struct aa_profile *peer, u32 request, | ||
192 | struct common_audit_data *sa) | ||
193 | { | ||
194 | struct aa_perms perms; | ||
195 | |||
196 | if (profile_unconfined(profile) || | ||
197 | !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL)) | ||
198 | return 0; | ||
199 | |||
200 | aad(sa)->peer = &peer->label; | ||
201 | profile_match_signal(profile, peer->base.hname, aad(sa)->signal, | ||
202 | &perms); | ||
203 | aa_apply_modes_to_perms(profile, &perms); | ||
204 | return aa_check_perms(profile, &perms, request, sa, audit_signal_cb); | ||
205 | } | ||
206 | |||
207 | static int aa_signal_cross_perm(struct aa_profile *sender, | ||
208 | struct aa_profile *target, | ||
209 | struct common_audit_data *sa) | ||
210 | { | ||
211 | return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa), | ||
212 | profile_signal_perm(target, sender, MAY_READ, sa)); | ||
213 | } | ||
214 | |||
215 | int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig) | ||
216 | { | ||
217 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL); | ||
218 | |||
219 | aad(&sa)->signal = map_signal_num(sig); | ||
220 | return xcheck_labels_profiles(sender, target, aa_signal_cross_perm, | ||
221 | &sa); | ||
222 | } | ||
diff --git a/security/apparmor/label.c b/security/apparmor/label.c index e052eaba1cf6..c5b99b954580 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c | |||
@@ -49,7 +49,7 @@ static void free_proxy(struct aa_proxy *proxy) | |||
49 | /* p->label will not updated any more as p is dead */ | 49 | /* p->label will not updated any more as p is dead */ |
50 | aa_put_label(rcu_dereference_protected(proxy->label, true)); | 50 | aa_put_label(rcu_dereference_protected(proxy->label, true)); |
51 | memset(proxy, 0, sizeof(*proxy)); | 51 | memset(proxy, 0, sizeof(*proxy)); |
52 | proxy->label = (struct aa_label *) PROXY_POISON; | 52 | RCU_INIT_POINTER(proxy->label, (struct aa_label *)PROXY_POISON); |
53 | kfree(proxy); | 53 | kfree(proxy); |
54 | } | 54 | } |
55 | } | 55 | } |
@@ -1450,9 +1450,11 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp) | |||
1450 | * cached label name is present and visible | 1450 | * cached label name is present and visible |
1451 | * @label->hname only exists if label is namespace hierachical | 1451 | * @label->hname only exists if label is namespace hierachical |
1452 | */ | 1452 | */ |
1453 | static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label) | 1453 | static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label, |
1454 | int flags) | ||
1454 | { | 1455 | { |
1455 | if (label->hname && labels_ns(label) == ns) | 1456 | if (label->hname && (!ns || labels_ns(label) == ns) && |
1457 | !(flags & ~FLAG_SHOW_MODE)) | ||
1456 | return true; | 1458 | return true; |
1457 | 1459 | ||
1458 | return false; | 1460 | return false; |
@@ -1495,7 +1497,7 @@ static int aa_profile_snxprint(char *str, size_t size, struct aa_ns *view, | |||
1495 | view = profiles_ns(profile); | 1497 | view = profiles_ns(profile); |
1496 | 1498 | ||
1497 | if (view != profile->ns && | 1499 | if (view != profile->ns && |
1498 | (!prev_ns || (prev_ns && *prev_ns != profile->ns))) { | 1500 | (!prev_ns || (*prev_ns != profile->ns))) { |
1499 | if (prev_ns) | 1501 | if (prev_ns) |
1500 | *prev_ns = profile->ns; | 1502 | *prev_ns = profile->ns; |
1501 | ns_name = aa_ns_name(view, profile->ns, | 1503 | ns_name = aa_ns_name(view, profile->ns, |
@@ -1605,8 +1607,13 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns, | |||
1605 | AA_BUG(!str && size != 0); | 1607 | AA_BUG(!str && size != 0); |
1606 | AA_BUG(!label); | 1608 | AA_BUG(!label); |
1607 | 1609 | ||
1608 | if (!ns) | 1610 | if (flags & FLAG_ABS_ROOT) { |
1611 | ns = root_ns; | ||
1612 | len = snprintf(str, size, "="); | ||
1613 | update_for_len(total, len, size, str); | ||
1614 | } else if (!ns) { | ||
1609 | ns = labels_ns(label); | 1615 | ns = labels_ns(label); |
1616 | } | ||
1610 | 1617 | ||
1611 | label_for_each(i, label, profile) { | 1618 | label_for_each(i, label, profile) { |
1612 | if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) { | 1619 | if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) { |
@@ -1710,10 +1717,8 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns, | |||
1710 | AA_BUG(!ab); | 1717 | AA_BUG(!ab); |
1711 | AA_BUG(!label); | 1718 | AA_BUG(!label); |
1712 | 1719 | ||
1713 | if (!ns) | 1720 | if (!use_label_hname(ns, label, flags) || |
1714 | ns = labels_ns(label); | 1721 | display_mode(ns, label, flags)) { |
1715 | |||
1716 | if (!use_label_hname(ns, label) || display_mode(ns, label, flags)) { | ||
1717 | len = aa_label_asxprint(&name, ns, label, flags, gfp); | 1722 | len = aa_label_asxprint(&name, ns, label, flags, gfp); |
1718 | if (len == -1) { | 1723 | if (len == -1) { |
1719 | AA_DEBUG("label print error"); | 1724 | AA_DEBUG("label print error"); |
@@ -1738,10 +1743,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, | |||
1738 | AA_BUG(!f); | 1743 | AA_BUG(!f); |
1739 | AA_BUG(!label); | 1744 | AA_BUG(!label); |
1740 | 1745 | ||
1741 | if (!ns) | 1746 | if (!use_label_hname(ns, label, flags)) { |
1742 | ns = labels_ns(label); | ||
1743 | |||
1744 | if (!use_label_hname(ns, label)) { | ||
1745 | char *str; | 1747 | char *str; |
1746 | int len; | 1748 | int len; |
1747 | 1749 | ||
@@ -1764,10 +1766,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, | |||
1764 | { | 1766 | { |
1765 | AA_BUG(!label); | 1767 | AA_BUG(!label); |
1766 | 1768 | ||
1767 | if (!ns) | 1769 | if (!use_label_hname(ns, label, flags)) { |
1768 | ns = labels_ns(label); | ||
1769 | |||
1770 | if (!use_label_hname(ns, label)) { | ||
1771 | char *str; | 1770 | char *str; |
1772 | int len; | 1771 | int len; |
1773 | 1772 | ||
@@ -1874,6 +1873,9 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str, | |||
1874 | if (*str == '&') | 1873 | if (*str == '&') |
1875 | str++; | 1874 | str++; |
1876 | } | 1875 | } |
1876 | if (*str == '=') | ||
1877 | base = &root_ns->unconfined->label; | ||
1878 | |||
1877 | error = vec_setup(profile, vec, len, gfp); | 1879 | error = vec_setup(profile, vec, len, gfp); |
1878 | if (error) | 1880 | if (error) |
1879 | return ERR_PTR(error); | 1881 | return ERR_PTR(error); |
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 08ca26bcca77..8818621b5d95 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c | |||
@@ -211,7 +211,8 @@ void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask) | |||
211 | *str = '\0'; | 211 | *str = '\0'; |
212 | } | 212 | } |
213 | 213 | ||
214 | void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) | 214 | void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, |
215 | u32 mask) | ||
215 | { | 216 | { |
216 | const char *fmt = "%s"; | 217 | const char *fmt = "%s"; |
217 | unsigned int i, perm = 1; | 218 | unsigned int i, perm = 1; |
@@ -229,7 +230,7 @@ void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) | |||
229 | } | 230 | } |
230 | 231 | ||
231 | void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, | 232 | void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, |
232 | u32 chrsmask, const char **names, u32 namesmask) | 233 | u32 chrsmask, const char * const *names, u32 namesmask) |
233 | { | 234 | { |
234 | char str[33]; | 235 | char str[33]; |
235 | 236 | ||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 7a82c0f61452..72b915dfcaf7 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c | |||
@@ -33,11 +33,13 @@ | |||
33 | #include "include/context.h" | 33 | #include "include/context.h" |
34 | #include "include/file.h" | 34 | #include "include/file.h" |
35 | #include "include/ipc.h" | 35 | #include "include/ipc.h" |
36 | #include "include/net.h" | ||
36 | #include "include/path.h" | 37 | #include "include/path.h" |
37 | #include "include/label.h" | 38 | #include "include/label.h" |
38 | #include "include/policy.h" | 39 | #include "include/policy.h" |
39 | #include "include/policy_ns.h" | 40 | #include "include/policy_ns.h" |
40 | #include "include/procattr.h" | 41 | #include "include/procattr.h" |
42 | #include "include/mount.h" | ||
41 | 43 | ||
42 | /* Flag indicating whether initialization completed */ | 44 | /* Flag indicating whether initialization completed */ |
43 | int apparmor_initialized; | 45 | int apparmor_initialized; |
@@ -511,6 +513,65 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, | |||
511 | !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); | 513 | !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); |
512 | } | 514 | } |
513 | 515 | ||
516 | static int apparmor_sb_mount(const char *dev_name, const struct path *path, | ||
517 | const char *type, unsigned long flags, void *data) | ||
518 | { | ||
519 | struct aa_label *label; | ||
520 | int error = 0; | ||
521 | |||
522 | /* Discard magic */ | ||
523 | if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | ||
524 | flags &= ~MS_MGC_MSK; | ||
525 | |||
526 | flags &= ~AA_MS_IGNORE_MASK; | ||
527 | |||
528 | label = __begin_current_label_crit_section(); | ||
529 | if (!unconfined(label)) { | ||
530 | if (flags & MS_REMOUNT) | ||
531 | error = aa_remount(label, path, flags, data); | ||
532 | else if (flags & MS_BIND) | ||
533 | error = aa_bind_mount(label, path, dev_name, flags); | ||
534 | else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | | ||
535 | MS_UNBINDABLE)) | ||
536 | error = aa_mount_change_type(label, path, flags); | ||
537 | else if (flags & MS_MOVE) | ||
538 | error = aa_move_mount(label, path, dev_name); | ||
539 | else | ||
540 | error = aa_new_mount(label, dev_name, path, type, | ||
541 | flags, data); | ||
542 | } | ||
543 | __end_current_label_crit_section(label); | ||
544 | |||
545 | return error; | ||
546 | } | ||
547 | |||
548 | static int apparmor_sb_umount(struct vfsmount *mnt, int flags) | ||
549 | { | ||
550 | struct aa_label *label; | ||
551 | int error = 0; | ||
552 | |||
553 | label = __begin_current_label_crit_section(); | ||
554 | if (!unconfined(label)) | ||
555 | error = aa_umount(label, mnt, flags); | ||
556 | __end_current_label_crit_section(label); | ||
557 | |||
558 | return error; | ||
559 | } | ||
560 | |||
561 | static int apparmor_sb_pivotroot(const struct path *old_path, | ||
562 | const struct path *new_path) | ||
563 | { | ||
564 | struct aa_label *label; | ||
565 | int error = 0; | ||
566 | |||
567 | label = aa_get_current_label(); | ||
568 | if (!unconfined(label)) | ||
569 | error = aa_pivotroot(label, old_path, new_path); | ||
570 | aa_put_label(label); | ||
571 | |||
572 | return error; | ||
573 | } | ||
574 | |||
514 | static int apparmor_getprocattr(struct task_struct *task, char *name, | 575 | static int apparmor_getprocattr(struct task_struct *task, char *name, |
515 | char **value) | 576 | char **value) |
516 | { | 577 | { |
@@ -656,12 +717,398 @@ static int apparmor_task_setrlimit(struct task_struct *task, | |||
656 | return error; | 717 | return error; |
657 | } | 718 | } |
658 | 719 | ||
720 | static int apparmor_task_kill(struct task_struct *target, struct siginfo *info, | ||
721 | int sig, u32 secid) | ||
722 | { | ||
723 | struct aa_label *cl, *tl; | ||
724 | int error; | ||
725 | |||
726 | if (secid) | ||
727 | /* TODO: after secid to label mapping is done. | ||
728 | * Dealing with USB IO specific behavior | ||
729 | */ | ||
730 | return 0; | ||
731 | cl = __begin_current_label_crit_section(); | ||
732 | tl = aa_get_task_label(target); | ||
733 | error = aa_may_signal(cl, tl, sig); | ||
734 | aa_put_label(tl); | ||
735 | __end_current_label_crit_section(cl); | ||
736 | |||
737 | return error; | ||
738 | } | ||
739 | |||
740 | /** | ||
741 | * apparmor_sk_alloc_security - allocate and attach the sk_security field | ||
742 | */ | ||
743 | static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags) | ||
744 | { | ||
745 | struct aa_sk_ctx *ctx; | ||
746 | |||
747 | ctx = kzalloc(sizeof(*ctx), flags); | ||
748 | if (!ctx) | ||
749 | return -ENOMEM; | ||
750 | |||
751 | SK_CTX(sk) = ctx; | ||
752 | |||
753 | return 0; | ||
754 | } | ||
755 | |||
756 | /** | ||
757 | * apparmor_sk_free_security - free the sk_security field | ||
758 | */ | ||
759 | static void apparmor_sk_free_security(struct sock *sk) | ||
760 | { | ||
761 | struct aa_sk_ctx *ctx = SK_CTX(sk); | ||
762 | |||
763 | SK_CTX(sk) = NULL; | ||
764 | aa_put_label(ctx->label); | ||
765 | aa_put_label(ctx->peer); | ||
766 | path_put(&ctx->path); | ||
767 | kfree(ctx); | ||
768 | } | ||
769 | |||
770 | /** | ||
771 | * apparmor_clone_security - clone the sk_security field | ||
772 | */ | ||
773 | static void apparmor_sk_clone_security(const struct sock *sk, | ||
774 | struct sock *newsk) | ||
775 | { | ||
776 | struct aa_sk_ctx *ctx = SK_CTX(sk); | ||
777 | struct aa_sk_ctx *new = SK_CTX(newsk); | ||
778 | |||
779 | new->label = aa_get_label(ctx->label); | ||
780 | new->peer = aa_get_label(ctx->peer); | ||
781 | new->path = ctx->path; | ||
782 | path_get(&new->path); | ||
783 | } | ||
784 | |||
785 | static int aa_sock_create_perm(struct aa_label *label, int family, int type, | ||
786 | int protocol) | ||
787 | { | ||
788 | AA_BUG(!label); | ||
789 | AA_BUG(in_interrupt()); | ||
790 | |||
791 | return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, | ||
792 | protocol); | ||
793 | } | ||
794 | |||
795 | |||
796 | /** | ||
797 | * apparmor_socket_create - check perms before creating a new socket | ||
798 | */ | ||
799 | static int apparmor_socket_create(int family, int type, int protocol, int kern) | ||
800 | { | ||
801 | struct aa_label *label; | ||
802 | int error = 0; | ||
803 | |||
804 | label = begin_current_label_crit_section(); | ||
805 | if (!(kern || unconfined(label))) | ||
806 | error = aa_sock_create_perm(label, family, type, protocol); | ||
807 | end_current_label_crit_section(label); | ||
808 | |||
809 | return error; | ||
810 | } | ||
811 | |||
812 | /** | ||
813 | * apparmor_socket_post_create - setup the per-socket security struct | ||
814 | * | ||
815 | * Note: | ||
816 | * - kernel sockets currently labeled unconfined but we may want to | ||
817 | * move to a special kernel label | ||
818 | * - socket may not have sk here if created with sock_create_lite or | ||
819 | * sock_alloc. These should be accept cases which will be handled in | ||
820 | * sock_graft. | ||
821 | */ | ||
822 | static int apparmor_socket_post_create(struct socket *sock, int family, | ||
823 | int type, int protocol, int kern) | ||
824 | { | ||
825 | struct aa_label *label; | ||
826 | |||
827 | if (kern) { | ||
828 | struct aa_ns *ns = aa_get_current_ns(); | ||
829 | |||
830 | label = aa_get_label(ns_unconfined(ns)); | ||
831 | aa_put_ns(ns); | ||
832 | } else | ||
833 | label = aa_get_current_label(); | ||
834 | |||
835 | if (sock->sk) { | ||
836 | struct aa_sk_ctx *ctx = SK_CTX(sock->sk); | ||
837 | |||
838 | aa_put_label(ctx->label); | ||
839 | ctx->label = aa_get_label(label); | ||
840 | } | ||
841 | aa_put_label(label); | ||
842 | |||
843 | return 0; | ||
844 | } | ||
845 | |||
846 | /** | ||
847 | * apparmor_socket_bind - check perms before bind addr to socket | ||
848 | */ | ||
849 | static int apparmor_socket_bind(struct socket *sock, | ||
850 | struct sockaddr *address, int addrlen) | ||
851 | { | ||
852 | AA_BUG(!sock); | ||
853 | AA_BUG(!sock->sk); | ||
854 | AA_BUG(!address); | ||
855 | AA_BUG(in_interrupt()); | ||
856 | |||
857 | return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); | ||
858 | } | ||
859 | |||
860 | /** | ||
861 | * apparmor_socket_connect - check perms before connecting @sock to @address | ||
862 | */ | ||
863 | static int apparmor_socket_connect(struct socket *sock, | ||
864 | struct sockaddr *address, int addrlen) | ||
865 | { | ||
866 | AA_BUG(!sock); | ||
867 | AA_BUG(!sock->sk); | ||
868 | AA_BUG(!address); | ||
869 | AA_BUG(in_interrupt()); | ||
870 | |||
871 | return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); | ||
872 | } | ||
873 | |||
874 | /** | ||
875 | * apparmor_socket_list - check perms before allowing listen | ||
876 | */ | ||
877 | static int apparmor_socket_listen(struct socket *sock, int backlog) | ||
878 | { | ||
879 | AA_BUG(!sock); | ||
880 | AA_BUG(!sock->sk); | ||
881 | AA_BUG(in_interrupt()); | ||
882 | |||
883 | return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); | ||
884 | } | ||
885 | |||
886 | /** | ||
887 | * apparmor_socket_accept - check perms before accepting a new connection. | ||
888 | * | ||
889 | * Note: while @newsock is created and has some information, the accept | ||
890 | * has not been done. | ||
891 | */ | ||
892 | static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) | ||
893 | { | ||
894 | AA_BUG(!sock); | ||
895 | AA_BUG(!sock->sk); | ||
896 | AA_BUG(!newsock); | ||
897 | AA_BUG(in_interrupt()); | ||
898 | |||
899 | return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); | ||
900 | } | ||
901 | |||
902 | static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, | ||
903 | struct msghdr *msg, int size) | ||
904 | { | ||
905 | AA_BUG(!sock); | ||
906 | AA_BUG(!sock->sk); | ||
907 | AA_BUG(!msg); | ||
908 | AA_BUG(in_interrupt()); | ||
909 | |||
910 | return aa_sk_perm(op, request, sock->sk); | ||
911 | } | ||
912 | |||
913 | /** | ||
914 | * apparmor_socket_sendmsg - check perms before sending msg to another socket | ||
915 | */ | ||
916 | static int apparmor_socket_sendmsg(struct socket *sock, | ||
917 | struct msghdr *msg, int size) | ||
918 | { | ||
919 | return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); | ||
920 | } | ||
921 | |||
922 | /** | ||
923 | * apparmor_socket_recvmsg - check perms before receiving a message | ||
924 | */ | ||
925 | static int apparmor_socket_recvmsg(struct socket *sock, | ||
926 | struct msghdr *msg, int size, int flags) | ||
927 | { | ||
928 | return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); | ||
929 | } | ||
930 | |||
931 | /* revaliation, get/set attr, shutdown */ | ||
932 | static int aa_sock_perm(const char *op, u32 request, struct socket *sock) | ||
933 | { | ||
934 | AA_BUG(!sock); | ||
935 | AA_BUG(!sock->sk); | ||
936 | AA_BUG(in_interrupt()); | ||
937 | |||
938 | return aa_sk_perm(op, request, sock->sk); | ||
939 | } | ||
940 | |||
941 | /** | ||
942 | * apparmor_socket_getsockname - check perms before getting the local address | ||
943 | */ | ||
944 | static int apparmor_socket_getsockname(struct socket *sock) | ||
945 | { | ||
946 | return aa_sock_perm(OP_GETSOCKNAME, AA_MAY_GETATTR, sock); | ||
947 | } | ||
948 | |||
949 | /** | ||
950 | * apparmor_socket_getpeername - check perms before getting remote address | ||
951 | */ | ||
952 | static int apparmor_socket_getpeername(struct socket *sock) | ||
953 | { | ||
954 | return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); | ||
955 | } | ||
956 | |||
957 | /* revaliation, get/set attr, opt */ | ||
958 | static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, | ||
959 | int level, int optname) | ||
960 | { | ||
961 | AA_BUG(!sock); | ||
962 | AA_BUG(!sock->sk); | ||
963 | AA_BUG(in_interrupt()); | ||
964 | |||
965 | return aa_sk_perm(op, request, sock->sk); | ||
966 | } | ||
967 | |||
968 | /** | ||
969 | * apparmor_getsockopt - check perms before getting socket options | ||
970 | */ | ||
971 | static int apparmor_socket_getsockopt(struct socket *sock, int level, | ||
972 | int optname) | ||
973 | { | ||
974 | return aa_sock_opt_perm(OP_GETSOCKOPT, AA_MAY_GETOPT, sock, | ||
975 | level, optname); | ||
976 | } | ||
977 | |||
978 | /** | ||
979 | * apparmor_setsockopt - check perms before setting socket options | ||
980 | */ | ||
981 | static int apparmor_socket_setsockopt(struct socket *sock, int level, | ||
982 | int optname) | ||
983 | { | ||
984 | return aa_sock_opt_perm(OP_SETSOCKOPT, AA_MAY_SETOPT, sock, | ||
985 | level, optname); | ||
986 | } | ||
987 | |||
988 | /** | ||
989 | * apparmor_socket_shutdown - check perms before shutting down @sock conn | ||
990 | */ | ||
991 | static int apparmor_socket_shutdown(struct socket *sock, int how) | ||
992 | { | ||
993 | return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock); | ||
994 | } | ||
995 | |||
996 | /** | ||
997 | * apparmor_socket_sock_recv_skb - check perms before associating skb to sk | ||
998 | * | ||
999 | * Note: can not sleep may be called with locks held | ||
1000 | * | ||
1001 | * dont want protocol specific in __skb_recv_datagram() | ||
1002 | * to deny an incoming connection socket_sock_rcv_skb() | ||
1003 | */ | ||
1004 | static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | ||
1005 | { | ||
1006 | return 0; | ||
1007 | } | ||
1008 | |||
1009 | |||
1010 | static struct aa_label *sk_peer_label(struct sock *sk) | ||
1011 | { | ||
1012 | struct aa_sk_ctx *ctx = SK_CTX(sk); | ||
1013 | |||
1014 | if (ctx->peer) | ||
1015 | return ctx->peer; | ||
1016 | |||
1017 | return ERR_PTR(-ENOPROTOOPT); | ||
1018 | } | ||
1019 | |||
1020 | /** | ||
1021 | * apparmor_socket_getpeersec_stream - get security context of peer | ||
1022 | * | ||
1023 | * Note: for tcp only valid if using ipsec or cipso on lan | ||
1024 | */ | ||
1025 | static int apparmor_socket_getpeersec_stream(struct socket *sock, | ||
1026 | char __user *optval, | ||
1027 | int __user *optlen, | ||
1028 | unsigned int len) | ||
1029 | { | ||
1030 | char *name; | ||
1031 | int slen, error = 0; | ||
1032 | struct aa_label *label; | ||
1033 | struct aa_label *peer; | ||
1034 | |||
1035 | label = begin_current_label_crit_section(); | ||
1036 | peer = sk_peer_label(sock->sk); | ||
1037 | if (IS_ERR(peer)) { | ||
1038 | error = PTR_ERR(peer); | ||
1039 | goto done; | ||
1040 | } | ||
1041 | slen = aa_label_asxprint(&name, labels_ns(label), peer, | ||
1042 | FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | | ||
1043 | FLAG_HIDDEN_UNCONFINED, GFP_KERNEL); | ||
1044 | /* don't include terminating \0 in slen, it breaks some apps */ | ||
1045 | if (slen < 0) { | ||
1046 | error = -ENOMEM; | ||
1047 | } else { | ||
1048 | if (slen > len) { | ||
1049 | error = -ERANGE; | ||
1050 | } else if (copy_to_user(optval, name, slen)) { | ||
1051 | error = -EFAULT; | ||
1052 | goto out; | ||
1053 | } | ||
1054 | if (put_user(slen, optlen)) | ||
1055 | error = -EFAULT; | ||
1056 | out: | ||
1057 | kfree(name); | ||
1058 | |||
1059 | } | ||
1060 | |||
1061 | done: | ||
1062 | end_current_label_crit_section(label); | ||
1063 | |||
1064 | return error; | ||
1065 | } | ||
1066 | |||
1067 | /** | ||
1068 | * apparmor_socket_getpeersec_dgram - get security label of packet | ||
1069 | * @sock: the peer socket | ||
1070 | * @skb: packet data | ||
1071 | * @secid: pointer to where to put the secid of the packet | ||
1072 | * | ||
1073 | * Sets the netlabel socket state on sk from parent | ||
1074 | */ | ||
1075 | static int apparmor_socket_getpeersec_dgram(struct socket *sock, | ||
1076 | struct sk_buff *skb, u32 *secid) | ||
1077 | |||
1078 | { | ||
1079 | /* TODO: requires secid support */ | ||
1080 | return -ENOPROTOOPT; | ||
1081 | } | ||
1082 | |||
1083 | /** | ||
1084 | * apparmor_sock_graft - Initialize newly created socket | ||
1085 | * @sk: child sock | ||
1086 | * @parent: parent socket | ||
1087 | * | ||
1088 | * Note: could set off of SOCK_CTX(parent) but need to track inode and we can | ||
1089 | * just set sk security information off of current creating process label | ||
1090 | * Labeling of sk for accept case - probably should be sock based | ||
1091 | * instead of task, because of the case where an implicitly labeled | ||
1092 | * socket is shared by different tasks. | ||
1093 | */ | ||
1094 | static void apparmor_sock_graft(struct sock *sk, struct socket *parent) | ||
1095 | { | ||
1096 | struct aa_sk_ctx *ctx = SK_CTX(sk); | ||
1097 | |||
1098 | if (!ctx->label) | ||
1099 | ctx->label = aa_get_current_label(); | ||
1100 | } | ||
1101 | |||
659 | static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { | 1102 | static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { |
660 | LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), | 1103 | LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), |
661 | LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), | 1104 | LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), |
662 | LSM_HOOK_INIT(capget, apparmor_capget), | 1105 | LSM_HOOK_INIT(capget, apparmor_capget), |
663 | LSM_HOOK_INIT(capable, apparmor_capable), | 1106 | LSM_HOOK_INIT(capable, apparmor_capable), |
664 | 1107 | ||
1108 | LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), | ||
1109 | LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), | ||
1110 | LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), | ||
1111 | |||
665 | LSM_HOOK_INIT(path_link, apparmor_path_link), | 1112 | LSM_HOOK_INIT(path_link, apparmor_path_link), |
666 | LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), | 1113 | LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), |
667 | LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), | 1114 | LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), |
@@ -686,6 +1133,30 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { | |||
686 | LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), | 1133 | LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), |
687 | LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), | 1134 | LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), |
688 | 1135 | ||
1136 | LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security), | ||
1137 | LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), | ||
1138 | LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), | ||
1139 | |||
1140 | LSM_HOOK_INIT(socket_create, apparmor_socket_create), | ||
1141 | LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), | ||
1142 | LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), | ||
1143 | LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), | ||
1144 | LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), | ||
1145 | LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), | ||
1146 | LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), | ||
1147 | LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), | ||
1148 | LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), | ||
1149 | LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), | ||
1150 | LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), | ||
1151 | LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), | ||
1152 | LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), | ||
1153 | LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb), | ||
1154 | LSM_HOOK_INIT(socket_getpeersec_stream, | ||
1155 | apparmor_socket_getpeersec_stream), | ||
1156 | LSM_HOOK_INIT(socket_getpeersec_dgram, | ||
1157 | apparmor_socket_getpeersec_dgram), | ||
1158 | LSM_HOOK_INIT(sock_graft, apparmor_sock_graft), | ||
1159 | |||
689 | LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), | 1160 | LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), |
690 | LSM_HOOK_INIT(cred_free, apparmor_cred_free), | 1161 | LSM_HOOK_INIT(cred_free, apparmor_cred_free), |
691 | LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), | 1162 | LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), |
@@ -696,6 +1167,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { | |||
696 | LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds), | 1167 | LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds), |
697 | 1168 | ||
698 | LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), | 1169 | LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), |
1170 | LSM_HOOK_INIT(task_kill, apparmor_task_kill), | ||
699 | }; | 1171 | }; |
700 | 1172 | ||
701 | /* | 1173 | /* |
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c new file mode 100644 index 000000000000..82a64b58041d --- /dev/null +++ b/security/apparmor/mount.c | |||
@@ -0,0 +1,696 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor mediation of files | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2017 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/fs.h> | ||
16 | #include <linux/mount.h> | ||
17 | #include <linux/namei.h> | ||
18 | |||
19 | #include "include/apparmor.h" | ||
20 | #include "include/audit.h" | ||
21 | #include "include/context.h" | ||
22 | #include "include/domain.h" | ||
23 | #include "include/file.h" | ||
24 | #include "include/match.h" | ||
25 | #include "include/mount.h" | ||
26 | #include "include/path.h" | ||
27 | #include "include/policy.h" | ||
28 | |||
29 | |||
30 | static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) | ||
31 | { | ||
32 | if (flags & MS_RDONLY) | ||
33 | audit_log_format(ab, "ro"); | ||
34 | else | ||
35 | audit_log_format(ab, "rw"); | ||
36 | if (flags & MS_NOSUID) | ||
37 | audit_log_format(ab, ", nosuid"); | ||
38 | if (flags & MS_NODEV) | ||
39 | audit_log_format(ab, ", nodev"); | ||
40 | if (flags & MS_NOEXEC) | ||
41 | audit_log_format(ab, ", noexec"); | ||
42 | if (flags & MS_SYNCHRONOUS) | ||
43 | audit_log_format(ab, ", sync"); | ||
44 | if (flags & MS_REMOUNT) | ||
45 | audit_log_format(ab, ", remount"); | ||
46 | if (flags & MS_MANDLOCK) | ||
47 | audit_log_format(ab, ", mand"); | ||
48 | if (flags & MS_DIRSYNC) | ||
49 | audit_log_format(ab, ", dirsync"); | ||
50 | if (flags & MS_NOATIME) | ||
51 | audit_log_format(ab, ", noatime"); | ||
52 | if (flags & MS_NODIRATIME) | ||
53 | audit_log_format(ab, ", nodiratime"); | ||
54 | if (flags & MS_BIND) | ||
55 | audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); | ||
56 | if (flags & MS_MOVE) | ||
57 | audit_log_format(ab, ", move"); | ||
58 | if (flags & MS_SILENT) | ||
59 | audit_log_format(ab, ", silent"); | ||
60 | if (flags & MS_POSIXACL) | ||
61 | audit_log_format(ab, ", acl"); | ||
62 | if (flags & MS_UNBINDABLE) | ||
63 | audit_log_format(ab, flags & MS_REC ? ", runbindable" : | ||
64 | ", unbindable"); | ||
65 | if (flags & MS_PRIVATE) | ||
66 | audit_log_format(ab, flags & MS_REC ? ", rprivate" : | ||
67 | ", private"); | ||
68 | if (flags & MS_SLAVE) | ||
69 | audit_log_format(ab, flags & MS_REC ? ", rslave" : | ||
70 | ", slave"); | ||
71 | if (flags & MS_SHARED) | ||
72 | audit_log_format(ab, flags & MS_REC ? ", rshared" : | ||
73 | ", shared"); | ||
74 | if (flags & MS_RELATIME) | ||
75 | audit_log_format(ab, ", relatime"); | ||
76 | if (flags & MS_I_VERSION) | ||
77 | audit_log_format(ab, ", iversion"); | ||
78 | if (flags & MS_STRICTATIME) | ||
79 | audit_log_format(ab, ", strictatime"); | ||
80 | if (flags & MS_NOUSER) | ||
81 | audit_log_format(ab, ", nouser"); | ||
82 | } | ||
83 | |||
84 | /** | ||
85 | * audit_cb - call back for mount specific audit fields | ||
86 | * @ab: audit_buffer (NOT NULL) | ||
87 | * @va: audit struct to audit values of (NOT NULL) | ||
88 | */ | ||
89 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
90 | { | ||
91 | struct common_audit_data *sa = va; | ||
92 | |||
93 | if (aad(sa)->mnt.type) { | ||
94 | audit_log_format(ab, " fstype="); | ||
95 | audit_log_untrustedstring(ab, aad(sa)->mnt.type); | ||
96 | } | ||
97 | if (aad(sa)->mnt.src_name) { | ||
98 | audit_log_format(ab, " srcname="); | ||
99 | audit_log_untrustedstring(ab, aad(sa)->mnt.src_name); | ||
100 | } | ||
101 | if (aad(sa)->mnt.trans) { | ||
102 | audit_log_format(ab, " trans="); | ||
103 | audit_log_untrustedstring(ab, aad(sa)->mnt.trans); | ||
104 | } | ||
105 | if (aad(sa)->mnt.flags) { | ||
106 | audit_log_format(ab, " flags=\""); | ||
107 | audit_mnt_flags(ab, aad(sa)->mnt.flags); | ||
108 | audit_log_format(ab, "\""); | ||
109 | } | ||
110 | if (aad(sa)->mnt.data) { | ||
111 | audit_log_format(ab, " options="); | ||
112 | audit_log_untrustedstring(ab, aad(sa)->mnt.data); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * audit_mount - handle the auditing of mount operations | ||
118 | * @profile: the profile being enforced (NOT NULL) | ||
119 | * @op: operation being mediated (NOT NULL) | ||
120 | * @name: name of object being mediated (MAYBE NULL) | ||
121 | * @src_name: src_name of object being mediated (MAYBE_NULL) | ||
122 | * @type: type of filesystem (MAYBE_NULL) | ||
123 | * @trans: name of trans (MAYBE NULL) | ||
124 | * @flags: filesystem idependent mount flags | ||
125 | * @data: filesystem mount flags | ||
126 | * @request: permissions requested | ||
127 | * @perms: the permissions computed for the request (NOT NULL) | ||
128 | * @info: extra information message (MAYBE NULL) | ||
129 | * @error: 0 if operation allowed else failure error code | ||
130 | * | ||
131 | * Returns: %0 or error on failure | ||
132 | */ | ||
133 | static int audit_mount(struct aa_profile *profile, const char *op, | ||
134 | const char *name, const char *src_name, | ||
135 | const char *type, const char *trans, | ||
136 | unsigned long flags, const void *data, u32 request, | ||
137 | struct aa_perms *perms, const char *info, int error) | ||
138 | { | ||
139 | int audit_type = AUDIT_APPARMOR_AUTO; | ||
140 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); | ||
141 | |||
142 | if (likely(!error)) { | ||
143 | u32 mask = perms->audit; | ||
144 | |||
145 | if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) | ||
146 | mask = 0xffff; | ||
147 | |||
148 | /* mask off perms that are not being force audited */ | ||
149 | request &= mask; | ||
150 | |||
151 | if (likely(!request)) | ||
152 | return 0; | ||
153 | audit_type = AUDIT_APPARMOR_AUDIT; | ||
154 | } else { | ||
155 | /* only report permissions that were denied */ | ||
156 | request = request & ~perms->allow; | ||
157 | |||
158 | if (request & perms->kill) | ||
159 | audit_type = AUDIT_APPARMOR_KILL; | ||
160 | |||
161 | /* quiet known rejects, assumes quiet and kill do not overlap */ | ||
162 | if ((request & perms->quiet) && | ||
163 | AUDIT_MODE(profile) != AUDIT_NOQUIET && | ||
164 | AUDIT_MODE(profile) != AUDIT_ALL) | ||
165 | request &= ~perms->quiet; | ||
166 | |||
167 | if (!request) | ||
168 | return error; | ||
169 | } | ||
170 | |||
171 | aad(&sa)->name = name; | ||
172 | aad(&sa)->mnt.src_name = src_name; | ||
173 | aad(&sa)->mnt.type = type; | ||
174 | aad(&sa)->mnt.trans = trans; | ||
175 | aad(&sa)->mnt.flags = flags; | ||
176 | if (data && (perms->audit & AA_AUDIT_DATA)) | ||
177 | aad(&sa)->mnt.data = data; | ||
178 | aad(&sa)->info = info; | ||
179 | aad(&sa)->error = error; | ||
180 | |||
181 | return aa_audit(audit_type, profile, &sa, audit_cb); | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * match_mnt_flags - Do an ordered match on mount flags | ||
186 | * @dfa: dfa to match against | ||
187 | * @state: state to start in | ||
188 | * @flags: mount flags to match against | ||
189 | * | ||
190 | * Mount flags are encoded as an ordered match. This is done instead of | ||
191 | * checking against a simple bitmask, to allow for logical operations | ||
192 | * on the flags. | ||
193 | * | ||
194 | * Returns: next state after flags match | ||
195 | */ | ||
196 | static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, | ||
197 | unsigned long flags) | ||
198 | { | ||
199 | unsigned int i; | ||
200 | |||
201 | for (i = 0; i <= 31 ; ++i) { | ||
202 | if ((1 << i) & flags) | ||
203 | state = aa_dfa_next(dfa, state, i + 1); | ||
204 | } | ||
205 | |||
206 | return state; | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * compute_mnt_perms - compute mount permission associated with @state | ||
211 | * @dfa: dfa to match against (NOT NULL) | ||
212 | * @state: state match finished in | ||
213 | * | ||
214 | * Returns: mount permissions | ||
215 | */ | ||
216 | static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa, | ||
217 | unsigned int state) | ||
218 | { | ||
219 | struct aa_perms perms; | ||
220 | |||
221 | perms.kill = 0; | ||
222 | perms.allow = dfa_user_allow(dfa, state); | ||
223 | perms.audit = dfa_user_audit(dfa, state); | ||
224 | perms.quiet = dfa_user_quiet(dfa, state); | ||
225 | perms.xindex = dfa_user_xindex(dfa, state); | ||
226 | |||
227 | return perms; | ||
228 | } | ||
229 | |||
230 | static const char * const mnt_info_table[] = { | ||
231 | "match succeeded", | ||
232 | "failed mntpnt match", | ||
233 | "failed srcname match", | ||
234 | "failed type match", | ||
235 | "failed flags match", | ||
236 | "failed data match" | ||
237 | }; | ||
238 | |||
239 | /* | ||
240 | * Returns 0 on success else element that match failed in, this is the | ||
241 | * index into the mnt_info_table above | ||
242 | */ | ||
243 | static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, | ||
244 | const char *mntpnt, const char *devname, | ||
245 | const char *type, unsigned long flags, | ||
246 | void *data, bool binary, struct aa_perms *perms) | ||
247 | { | ||
248 | unsigned int state; | ||
249 | |||
250 | AA_BUG(!dfa); | ||
251 | AA_BUG(!perms); | ||
252 | |||
253 | state = aa_dfa_match(dfa, start, mntpnt); | ||
254 | state = aa_dfa_null_transition(dfa, state); | ||
255 | if (!state) | ||
256 | return 1; | ||
257 | |||
258 | if (devname) | ||
259 | state = aa_dfa_match(dfa, state, devname); | ||
260 | state = aa_dfa_null_transition(dfa, state); | ||
261 | if (!state) | ||
262 | return 2; | ||
263 | |||
264 | if (type) | ||
265 | state = aa_dfa_match(dfa, state, type); | ||
266 | state = aa_dfa_null_transition(dfa, state); | ||
267 | if (!state) | ||
268 | return 3; | ||
269 | |||
270 | state = match_mnt_flags(dfa, state, flags); | ||
271 | if (!state) | ||
272 | return 4; | ||
273 | *perms = compute_mnt_perms(dfa, state); | ||
274 | if (perms->allow & AA_MAY_MOUNT) | ||
275 | return 0; | ||
276 | |||
277 | /* only match data if not binary and the DFA flags data is expected */ | ||
278 | if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) { | ||
279 | state = aa_dfa_null_transition(dfa, state); | ||
280 | if (!state) | ||
281 | return 4; | ||
282 | |||
283 | state = aa_dfa_match(dfa, state, data); | ||
284 | if (!state) | ||
285 | return 5; | ||
286 | *perms = compute_mnt_perms(dfa, state); | ||
287 | if (perms->allow & AA_MAY_MOUNT) | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | /* failed at end of flags match */ | ||
292 | return 4; | ||
293 | } | ||
294 | |||
295 | |||
296 | static int path_flags(struct aa_profile *profile, const struct path *path) | ||
297 | { | ||
298 | AA_BUG(!profile); | ||
299 | AA_BUG(!path); | ||
300 | |||
301 | return profile->path_flags | | ||
302 | (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0); | ||
303 | } | ||
304 | |||
305 | /** | ||
306 | * match_mnt_path_str - handle path matching for mount | ||
307 | * @profile: the confining profile | ||
308 | * @mntpath: for the mntpnt (NOT NULL) | ||
309 | * @buffer: buffer to be used to lookup mntpath | ||
310 | * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR) | ||
311 | * @type: string for the dev type (MAYBE NULL) | ||
312 | * @flags: mount flags to match | ||
313 | * @data: fs mount data (MAYBE NULL) | ||
314 | * @binary: whether @data is binary | ||
315 | * @devinfo: error str if (IS_ERR(@devname)) | ||
316 | * | ||
317 | * Returns: 0 on success else error | ||
318 | */ | ||
319 | static int match_mnt_path_str(struct aa_profile *profile, | ||
320 | const struct path *mntpath, char *buffer, | ||
321 | const char *devname, const char *type, | ||
322 | unsigned long flags, void *data, bool binary, | ||
323 | const char *devinfo) | ||
324 | { | ||
325 | struct aa_perms perms = { }; | ||
326 | const char *mntpnt = NULL, *info = NULL; | ||
327 | int pos, error; | ||
328 | |||
329 | AA_BUG(!profile); | ||
330 | AA_BUG(!mntpath); | ||
331 | AA_BUG(!buffer); | ||
332 | |||
333 | error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer, | ||
334 | &mntpnt, &info, profile->disconnected); | ||
335 | if (error) | ||
336 | goto audit; | ||
337 | if (IS_ERR(devname)) { | ||
338 | error = PTR_ERR(devname); | ||
339 | devname = NULL; | ||
340 | info = devinfo; | ||
341 | goto audit; | ||
342 | } | ||
343 | |||
344 | error = -EACCES; | ||
345 | pos = do_match_mnt(profile->policy.dfa, | ||
346 | profile->policy.start[AA_CLASS_MOUNT], | ||
347 | mntpnt, devname, type, flags, data, binary, &perms); | ||
348 | if (pos) { | ||
349 | info = mnt_info_table[pos]; | ||
350 | goto audit; | ||
351 | } | ||
352 | error = 0; | ||
353 | |||
354 | audit: | ||
355 | return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL, | ||
356 | flags, data, AA_MAY_MOUNT, &perms, info, error); | ||
357 | } | ||
358 | |||
359 | /** | ||
360 | * match_mnt - handle path matching for mount | ||
361 | * @profile: the confining profile | ||
362 | * @mntpath: for the mntpnt (NOT NULL) | ||
363 | * @buffer: buffer to be used to lookup mntpath | ||
364 | * @devpath: path devname/src_name (MAYBE NULL) | ||
365 | * @devbuffer: buffer to be used to lookup devname/src_name | ||
366 | * @type: string for the dev type (MAYBE NULL) | ||
367 | * @flags: mount flags to match | ||
368 | * @data: fs mount data (MAYBE NULL) | ||
369 | * @binary: whether @data is binary | ||
370 | * | ||
371 | * Returns: 0 on success else error | ||
372 | */ | ||
373 | static int match_mnt(struct aa_profile *profile, const struct path *path, | ||
374 | char *buffer, struct path *devpath, char *devbuffer, | ||
375 | const char *type, unsigned long flags, void *data, | ||
376 | bool binary) | ||
377 | { | ||
378 | const char *devname = NULL, *info = NULL; | ||
379 | int error = -EACCES; | ||
380 | |||
381 | AA_BUG(!profile); | ||
382 | AA_BUG(devpath && !devbuffer); | ||
383 | |||
384 | if (devpath) { | ||
385 | error = aa_path_name(devpath, path_flags(profile, devpath), | ||
386 | devbuffer, &devname, &info, | ||
387 | profile->disconnected); | ||
388 | if (error) | ||
389 | devname = ERR_PTR(error); | ||
390 | } | ||
391 | |||
392 | return match_mnt_path_str(profile, path, buffer, devname, type, flags, | ||
393 | data, binary, info); | ||
394 | } | ||
395 | |||
396 | int aa_remount(struct aa_label *label, const struct path *path, | ||
397 | unsigned long flags, void *data) | ||
398 | { | ||
399 | struct aa_profile *profile; | ||
400 | char *buffer = NULL; | ||
401 | bool binary; | ||
402 | int error; | ||
403 | |||
404 | AA_BUG(!label); | ||
405 | AA_BUG(!path); | ||
406 | |||
407 | binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; | ||
408 | |||
409 | get_buffers(buffer); | ||
410 | error = fn_for_each_confined(label, profile, | ||
411 | match_mnt(profile, path, buffer, NULL, NULL, NULL, | ||
412 | flags, data, binary)); | ||
413 | put_buffers(buffer); | ||
414 | |||
415 | return error; | ||
416 | } | ||
417 | |||
418 | int aa_bind_mount(struct aa_label *label, const struct path *path, | ||
419 | const char *dev_name, unsigned long flags) | ||
420 | { | ||
421 | struct aa_profile *profile; | ||
422 | char *buffer = NULL, *old_buffer = NULL; | ||
423 | struct path old_path; | ||
424 | int error; | ||
425 | |||
426 | AA_BUG(!label); | ||
427 | AA_BUG(!path); | ||
428 | |||
429 | if (!dev_name || !*dev_name) | ||
430 | return -EINVAL; | ||
431 | |||
432 | flags &= MS_REC | MS_BIND; | ||
433 | |||
434 | error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); | ||
435 | if (error) | ||
436 | return error; | ||
437 | |||
438 | get_buffers(buffer, old_buffer); | ||
439 | error = fn_for_each_confined(label, profile, | ||
440 | match_mnt(profile, path, buffer, &old_path, old_buffer, | ||
441 | NULL, flags, NULL, false)); | ||
442 | put_buffers(buffer, old_buffer); | ||
443 | path_put(&old_path); | ||
444 | |||
445 | return error; | ||
446 | } | ||
447 | |||
448 | int aa_mount_change_type(struct aa_label *label, const struct path *path, | ||
449 | unsigned long flags) | ||
450 | { | ||
451 | struct aa_profile *profile; | ||
452 | char *buffer = NULL; | ||
453 | int error; | ||
454 | |||
455 | AA_BUG(!label); | ||
456 | AA_BUG(!path); | ||
457 | |||
458 | /* These are the flags allowed by do_change_type() */ | ||
459 | flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | | ||
460 | MS_UNBINDABLE); | ||
461 | |||
462 | get_buffers(buffer); | ||
463 | error = fn_for_each_confined(label, profile, | ||
464 | match_mnt(profile, path, buffer, NULL, NULL, NULL, | ||
465 | flags, NULL, false)); | ||
466 | put_buffers(buffer); | ||
467 | |||
468 | return error; | ||
469 | } | ||
470 | |||
471 | int aa_move_mount(struct aa_label *label, const struct path *path, | ||
472 | const char *orig_name) | ||
473 | { | ||
474 | struct aa_profile *profile; | ||
475 | char *buffer = NULL, *old_buffer = NULL; | ||
476 | struct path old_path; | ||
477 | int error; | ||
478 | |||
479 | AA_BUG(!label); | ||
480 | AA_BUG(!path); | ||
481 | |||
482 | if (!orig_name || !*orig_name) | ||
483 | return -EINVAL; | ||
484 | |||
485 | error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); | ||
486 | if (error) | ||
487 | return error; | ||
488 | |||
489 | get_buffers(buffer, old_buffer); | ||
490 | error = fn_for_each_confined(label, profile, | ||
491 | match_mnt(profile, path, buffer, &old_path, old_buffer, | ||
492 | NULL, MS_MOVE, NULL, false)); | ||
493 | put_buffers(buffer, old_buffer); | ||
494 | path_put(&old_path); | ||
495 | |||
496 | return error; | ||
497 | } | ||
498 | |||
499 | int aa_new_mount(struct aa_label *label, const char *dev_name, | ||
500 | const struct path *path, const char *type, unsigned long flags, | ||
501 | void *data) | ||
502 | { | ||
503 | struct aa_profile *profile; | ||
504 | char *buffer = NULL, *dev_buffer = NULL; | ||
505 | bool binary = true; | ||
506 | int error; | ||
507 | int requires_dev = 0; | ||
508 | struct path tmp_path, *dev_path = NULL; | ||
509 | |||
510 | AA_BUG(!label); | ||
511 | AA_BUG(!path); | ||
512 | |||
513 | if (type) { | ||
514 | struct file_system_type *fstype; | ||
515 | |||
516 | fstype = get_fs_type(type); | ||
517 | if (!fstype) | ||
518 | return -ENODEV; | ||
519 | binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; | ||
520 | requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; | ||
521 | put_filesystem(fstype); | ||
522 | |||
523 | if (requires_dev) { | ||
524 | if (!dev_name || !*dev_name) | ||
525 | return -ENOENT; | ||
526 | |||
527 | error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path); | ||
528 | if (error) | ||
529 | return error; | ||
530 | dev_path = &tmp_path; | ||
531 | } | ||
532 | } | ||
533 | |||
534 | get_buffers(buffer, dev_buffer); | ||
535 | if (dev_path) { | ||
536 | error = fn_for_each_confined(label, profile, | ||
537 | match_mnt(profile, path, buffer, dev_path, dev_buffer, | ||
538 | type, flags, data, binary)); | ||
539 | } else { | ||
540 | error = fn_for_each_confined(label, profile, | ||
541 | match_mnt_path_str(profile, path, buffer, dev_name, | ||
542 | type, flags, data, binary, NULL)); | ||
543 | } | ||
544 | put_buffers(buffer, dev_buffer); | ||
545 | if (dev_path) | ||
546 | path_put(dev_path); | ||
547 | |||
548 | return error; | ||
549 | } | ||
550 | |||
551 | static int profile_umount(struct aa_profile *profile, struct path *path, | ||
552 | char *buffer) | ||
553 | { | ||
554 | struct aa_perms perms = { }; | ||
555 | const char *name = NULL, *info = NULL; | ||
556 | unsigned int state; | ||
557 | int error; | ||
558 | |||
559 | AA_BUG(!profile); | ||
560 | AA_BUG(!path); | ||
561 | |||
562 | error = aa_path_name(path, path_flags(profile, path), buffer, &name, | ||
563 | &info, profile->disconnected); | ||
564 | if (error) | ||
565 | goto audit; | ||
566 | |||
567 | state = aa_dfa_match(profile->policy.dfa, | ||
568 | profile->policy.start[AA_CLASS_MOUNT], | ||
569 | name); | ||
570 | perms = compute_mnt_perms(profile->policy.dfa, state); | ||
571 | if (AA_MAY_UMOUNT & ~perms.allow) | ||
572 | error = -EACCES; | ||
573 | |||
574 | audit: | ||
575 | return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL, | ||
576 | AA_MAY_UMOUNT, &perms, info, error); | ||
577 | } | ||
578 | |||
579 | int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags) | ||
580 | { | ||
581 | struct aa_profile *profile; | ||
582 | char *buffer = NULL; | ||
583 | int error; | ||
584 | struct path path = { .mnt = mnt, .dentry = mnt->mnt_root }; | ||
585 | |||
586 | AA_BUG(!label); | ||
587 | AA_BUG(!mnt); | ||
588 | |||
589 | get_buffers(buffer); | ||
590 | error = fn_for_each_confined(label, profile, | ||
591 | profile_umount(profile, &path, buffer)); | ||
592 | put_buffers(buffer); | ||
593 | |||
594 | return error; | ||
595 | } | ||
596 | |||
597 | /* helper fn for transition on pivotroot | ||
598 | * | ||
599 | * Returns: label for transition or ERR_PTR. Does not return NULL | ||
600 | */ | ||
601 | static struct aa_label *build_pivotroot(struct aa_profile *profile, | ||
602 | const struct path *new_path, | ||
603 | char *new_buffer, | ||
604 | const struct path *old_path, | ||
605 | char *old_buffer) | ||
606 | { | ||
607 | const char *old_name, *new_name = NULL, *info = NULL; | ||
608 | const char *trans_name = NULL; | ||
609 | struct aa_perms perms = { }; | ||
610 | unsigned int state; | ||
611 | int error; | ||
612 | |||
613 | AA_BUG(!profile); | ||
614 | AA_BUG(!new_path); | ||
615 | AA_BUG(!old_path); | ||
616 | |||
617 | if (profile_unconfined(profile)) | ||
618 | return aa_get_newest_label(&profile->label); | ||
619 | |||
620 | error = aa_path_name(old_path, path_flags(profile, old_path), | ||
621 | old_buffer, &old_name, &info, | ||
622 | profile->disconnected); | ||
623 | if (error) | ||
624 | goto audit; | ||
625 | error = aa_path_name(new_path, path_flags(profile, new_path), | ||
626 | new_buffer, &new_name, &info, | ||
627 | profile->disconnected); | ||
628 | if (error) | ||
629 | goto audit; | ||
630 | |||
631 | error = -EACCES; | ||
632 | state = aa_dfa_match(profile->policy.dfa, | ||
633 | profile->policy.start[AA_CLASS_MOUNT], | ||
634 | new_name); | ||
635 | state = aa_dfa_null_transition(profile->policy.dfa, state); | ||
636 | state = aa_dfa_match(profile->policy.dfa, state, old_name); | ||
637 | perms = compute_mnt_perms(profile->policy.dfa, state); | ||
638 | |||
639 | if (AA_MAY_PIVOTROOT & perms.allow) | ||
640 | error = 0; | ||
641 | |||
642 | audit: | ||
643 | error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name, | ||
644 | NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT, | ||
645 | &perms, info, error); | ||
646 | if (error) | ||
647 | return ERR_PTR(error); | ||
648 | |||
649 | return aa_get_newest_label(&profile->label); | ||
650 | } | ||
651 | |||
652 | int aa_pivotroot(struct aa_label *label, const struct path *old_path, | ||
653 | const struct path *new_path) | ||
654 | { | ||
655 | struct aa_profile *profile; | ||
656 | struct aa_label *target = NULL; | ||
657 | char *old_buffer = NULL, *new_buffer = NULL, *info = NULL; | ||
658 | int error; | ||
659 | |||
660 | AA_BUG(!label); | ||
661 | AA_BUG(!old_path); | ||
662 | AA_BUG(!new_path); | ||
663 | |||
664 | get_buffers(old_buffer, new_buffer); | ||
665 | target = fn_label_build(label, profile, GFP_ATOMIC, | ||
666 | build_pivotroot(profile, new_path, new_buffer, | ||
667 | old_path, old_buffer)); | ||
668 | if (!target) { | ||
669 | info = "label build failed"; | ||
670 | error = -ENOMEM; | ||
671 | goto fail; | ||
672 | } else if (!IS_ERR(target)) { | ||
673 | error = aa_replace_current_label(target); | ||
674 | if (error) { | ||
675 | /* TODO: audit target */ | ||
676 | aa_put_label(target); | ||
677 | goto out; | ||
678 | } | ||
679 | } else | ||
680 | /* already audited error */ | ||
681 | error = PTR_ERR(target); | ||
682 | out: | ||
683 | put_buffers(old_buffer, new_buffer); | ||
684 | |||
685 | return error; | ||
686 | |||
687 | fail: | ||
688 | /* TODO: add back in auditing of new_name and old_name */ | ||
689 | error = fn_for_each(label, profile, | ||
690 | audit_mount(profile, OP_PIVOTROOT, NULL /*new_name */, | ||
691 | NULL /* old_name */, | ||
692 | NULL, NULL, | ||
693 | 0, NULL, AA_MAY_PIVOTROOT, &nullperms, info, | ||
694 | error)); | ||
695 | goto out; | ||
696 | } | ||
diff --git a/security/apparmor/net.c b/security/apparmor/net.c new file mode 100644 index 000000000000..33d54435f8d6 --- /dev/null +++ b/security/apparmor/net.c | |||
@@ -0,0 +1,184 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor network mediation | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2017 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include "include/apparmor.h" | ||
16 | #include "include/audit.h" | ||
17 | #include "include/context.h" | ||
18 | #include "include/label.h" | ||
19 | #include "include/net.h" | ||
20 | #include "include/policy.h" | ||
21 | |||
22 | #include "net_names.h" | ||
23 | |||
24 | |||
25 | struct aa_sfs_entry aa_sfs_entry_network[] = { | ||
26 | AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), | ||
27 | { } | ||
28 | }; | ||
29 | |||
30 | static const char * const net_mask_names[] = { | ||
31 | "unknown", | ||
32 | "send", | ||
33 | "receive", | ||
34 | "unknown", | ||
35 | |||
36 | "create", | ||
37 | "shutdown", | ||
38 | "connect", | ||
39 | "unknown", | ||
40 | |||
41 | "setattr", | ||
42 | "getattr", | ||
43 | "setcred", | ||
44 | "getcred", | ||
45 | |||
46 | "chmod", | ||
47 | "chown", | ||
48 | "chgrp", | ||
49 | "lock", | ||
50 | |||
51 | "mmap", | ||
52 | "mprot", | ||
53 | "unknown", | ||
54 | "unknown", | ||
55 | |||
56 | "accept", | ||
57 | "bind", | ||
58 | "listen", | ||
59 | "unknown", | ||
60 | |||
61 | "setopt", | ||
62 | "getopt", | ||
63 | "unknown", | ||
64 | "unknown", | ||
65 | |||
66 | "unknown", | ||
67 | "unknown", | ||
68 | "unknown", | ||
69 | "unknown", | ||
70 | }; | ||
71 | |||
72 | |||
73 | /* audit callback for net specific fields */ | ||
74 | void audit_net_cb(struct audit_buffer *ab, void *va) | ||
75 | { | ||
76 | struct common_audit_data *sa = va; | ||
77 | |||
78 | audit_log_format(ab, " family="); | ||
79 | if (address_family_names[sa->u.net->family]) | ||
80 | audit_log_string(ab, address_family_names[sa->u.net->family]); | ||
81 | else | ||
82 | audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); | ||
83 | audit_log_format(ab, " sock_type="); | ||
84 | if (sock_type_names[aad(sa)->net.type]) | ||
85 | audit_log_string(ab, sock_type_names[aad(sa)->net.type]); | ||
86 | else | ||
87 | audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type); | ||
88 | audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol); | ||
89 | |||
90 | if (aad(sa)->request & NET_PERMS_MASK) { | ||
91 | audit_log_format(ab, " requested_mask="); | ||
92 | aa_audit_perm_mask(ab, aad(sa)->request, NULL, 0, | ||
93 | net_mask_names, NET_PERMS_MASK); | ||
94 | |||
95 | if (aad(sa)->denied & NET_PERMS_MASK) { | ||
96 | audit_log_format(ab, " denied_mask="); | ||
97 | aa_audit_perm_mask(ab, aad(sa)->denied, NULL, 0, | ||
98 | net_mask_names, NET_PERMS_MASK); | ||
99 | } | ||
100 | } | ||
101 | if (aad(sa)->peer) { | ||
102 | audit_log_format(ab, " peer="); | ||
103 | aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, | ||
104 | FLAGS_NONE, GFP_ATOMIC); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | |||
109 | /* Generic af perm */ | ||
110 | int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, | ||
111 | u32 request, u16 family, int type) | ||
112 | { | ||
113 | struct aa_perms perms = { }; | ||
114 | |||
115 | AA_BUG(family >= AF_MAX); | ||
116 | AA_BUG(type < 0 || type >= SOCK_MAX); | ||
117 | |||
118 | if (profile_unconfined(profile)) | ||
119 | return 0; | ||
120 | |||
121 | perms.allow = (profile->net.allow[family] & (1 << type)) ? | ||
122 | ALL_PERMS_MASK : 0; | ||
123 | perms.audit = (profile->net.audit[family] & (1 << type)) ? | ||
124 | ALL_PERMS_MASK : 0; | ||
125 | perms.quiet = (profile->net.quiet[family] & (1 << type)) ? | ||
126 | ALL_PERMS_MASK : 0; | ||
127 | aa_apply_modes_to_perms(profile, &perms); | ||
128 | |||
129 | return aa_check_perms(profile, &perms, request, sa, audit_net_cb); | ||
130 | } | ||
131 | |||
132 | int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, | ||
133 | int type, int protocol) | ||
134 | { | ||
135 | struct aa_profile *profile; | ||
136 | DEFINE_AUDIT_NET(sa, op, NULL, family, type, protocol); | ||
137 | |||
138 | return fn_for_each_confined(label, profile, | ||
139 | aa_profile_af_perm(profile, &sa, request, family, | ||
140 | type)); | ||
141 | } | ||
142 | |||
143 | static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request, | ||
144 | struct sock *sk) | ||
145 | { | ||
146 | struct aa_profile *profile; | ||
147 | DEFINE_AUDIT_SK(sa, op, sk); | ||
148 | |||
149 | AA_BUG(!label); | ||
150 | AA_BUG(!sk); | ||
151 | |||
152 | if (unconfined(label)) | ||
153 | return 0; | ||
154 | |||
155 | return fn_for_each_confined(label, profile, | ||
156 | aa_profile_af_sk_perm(profile, &sa, request, sk)); | ||
157 | } | ||
158 | |||
159 | int aa_sk_perm(const char *op, u32 request, struct sock *sk) | ||
160 | { | ||
161 | struct aa_label *label; | ||
162 | int error; | ||
163 | |||
164 | AA_BUG(!sk); | ||
165 | AA_BUG(in_interrupt()); | ||
166 | |||
167 | /* TODO: switch to begin_current_label ???? */ | ||
168 | label = begin_current_label_crit_section(); | ||
169 | error = aa_label_sk_perm(label, op, request, sk); | ||
170 | end_current_label_crit_section(label); | ||
171 | |||
172 | return error; | ||
173 | } | ||
174 | |||
175 | |||
176 | int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, | ||
177 | struct socket *sock) | ||
178 | { | ||
179 | AA_BUG(!label); | ||
180 | AA_BUG(!sock); | ||
181 | AA_BUG(!sock->sk); | ||
182 | |||
183 | return aa_label_sk_perm(label, op, request, sock->sk); | ||
184 | } | ||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 244ea4a4a8f0..4243b0c3f0e4 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c | |||
@@ -289,85 +289,6 @@ fail: | |||
289 | return NULL; | 289 | return NULL; |
290 | } | 290 | } |
291 | 291 | ||
292 | /** | ||
293 | * aa_new_null_profile - create or find a null-X learning profile | ||
294 | * @parent: profile that caused this profile to be created (NOT NULL) | ||
295 | * @hat: true if the null- learning profile is a hat | ||
296 | * @base: name to base the null profile off of | ||
297 | * @gfp: type of allocation | ||
298 | * | ||
299 | * Find/Create a null- complain mode profile used in learning mode. The | ||
300 | * name of the profile is unique and follows the format of parent//null-XXX. | ||
301 | * where XXX is based on the @name or if that fails or is not supplied | ||
302 | * a unique number | ||
303 | * | ||
304 | * null profiles are added to the profile list but the list does not | ||
305 | * hold a count on them so that they are automatically released when | ||
306 | * not in use. | ||
307 | * | ||
308 | * Returns: new refcounted profile else NULL on failure | ||
309 | */ | ||
310 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, | ||
311 | const char *base, gfp_t gfp) | ||
312 | { | ||
313 | struct aa_profile *profile; | ||
314 | char *name; | ||
315 | |||
316 | AA_BUG(!parent); | ||
317 | |||
318 | if (base) { | ||
319 | name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base), | ||
320 | gfp); | ||
321 | if (name) { | ||
322 | sprintf(name, "%s//null-%s", parent->base.hname, base); | ||
323 | goto name; | ||
324 | } | ||
325 | /* fall through to try shorter uniq */ | ||
326 | } | ||
327 | |||
328 | name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp); | ||
329 | if (!name) | ||
330 | return NULL; | ||
331 | sprintf(name, "%s//null-%x", parent->base.hname, | ||
332 | atomic_inc_return(&parent->ns->uniq_null)); | ||
333 | |||
334 | name: | ||
335 | /* lookup to see if this is a dup creation */ | ||
336 | profile = aa_find_child(parent, basename(name)); | ||
337 | if (profile) | ||
338 | goto out; | ||
339 | |||
340 | profile = aa_alloc_profile(name, NULL, gfp); | ||
341 | if (!profile) | ||
342 | goto fail; | ||
343 | |||
344 | profile->mode = APPARMOR_COMPLAIN; | ||
345 | profile->label.flags |= FLAG_NULL; | ||
346 | if (hat) | ||
347 | profile->label.flags |= FLAG_HAT; | ||
348 | profile->path_flags = parent->path_flags; | ||
349 | |||
350 | /* released on free_profile */ | ||
351 | rcu_assign_pointer(profile->parent, aa_get_profile(parent)); | ||
352 | profile->ns = aa_get_ns(parent->ns); | ||
353 | profile->file.dfa = aa_get_dfa(nulldfa); | ||
354 | profile->policy.dfa = aa_get_dfa(nulldfa); | ||
355 | |||
356 | mutex_lock(&profile->ns->lock); | ||
357 | __add_profile(&parent->base.profiles, profile); | ||
358 | mutex_unlock(&profile->ns->lock); | ||
359 | |||
360 | /* refcount released by caller */ | ||
361 | out: | ||
362 | kfree(name); | ||
363 | |||
364 | return profile; | ||
365 | |||
366 | fail: | ||
367 | aa_free_profile(profile); | ||
368 | return NULL; | ||
369 | } | ||
370 | |||
371 | /* TODO: profile accounting - setup in remove */ | 292 | /* TODO: profile accounting - setup in remove */ |
372 | 293 | ||
373 | /** | 294 | /** |
@@ -559,6 +480,93 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, | |||
559 | } | 480 | } |
560 | 481 | ||
561 | /** | 482 | /** |
483 | * aa_new_null_profile - create or find a null-X learning profile | ||
484 | * @parent: profile that caused this profile to be created (NOT NULL) | ||
485 | * @hat: true if the null- learning profile is a hat | ||
486 | * @base: name to base the null profile off of | ||
487 | * @gfp: type of allocation | ||
488 | * | ||
489 | * Find/Create a null- complain mode profile used in learning mode. The | ||
490 | * name of the profile is unique and follows the format of parent//null-XXX. | ||
491 | * where XXX is based on the @name or if that fails or is not supplied | ||
492 | * a unique number | ||
493 | * | ||
494 | * null profiles are added to the profile list but the list does not | ||
495 | * hold a count on them so that they are automatically released when | ||
496 | * not in use. | ||
497 | * | ||
498 | * Returns: new refcounted profile else NULL on failure | ||
499 | */ | ||
500 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, | ||
501 | const char *base, gfp_t gfp) | ||
502 | { | ||
503 | struct aa_profile *p, *profile; | ||
504 | const char *bname; | ||
505 | char *name; | ||
506 | |||
507 | AA_BUG(!parent); | ||
508 | |||
509 | if (base) { | ||
510 | name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base), | ||
511 | gfp); | ||
512 | if (name) { | ||
513 | sprintf(name, "%s//null-%s", parent->base.hname, base); | ||
514 | goto name; | ||
515 | } | ||
516 | /* fall through to try shorter uniq */ | ||
517 | } | ||
518 | |||
519 | name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp); | ||
520 | if (!name) | ||
521 | return NULL; | ||
522 | sprintf(name, "%s//null-%x", parent->base.hname, | ||
523 | atomic_inc_return(&parent->ns->uniq_null)); | ||
524 | |||
525 | name: | ||
526 | /* lookup to see if this is a dup creation */ | ||
527 | bname = basename(name); | ||
528 | profile = aa_find_child(parent, bname); | ||
529 | if (profile) | ||
530 | goto out; | ||
531 | |||
532 | profile = aa_alloc_profile(name, NULL, gfp); | ||
533 | if (!profile) | ||
534 | goto fail; | ||
535 | |||
536 | profile->mode = APPARMOR_COMPLAIN; | ||
537 | profile->label.flags |= FLAG_NULL; | ||
538 | if (hat) | ||
539 | profile->label.flags |= FLAG_HAT; | ||
540 | profile->path_flags = parent->path_flags; | ||
541 | |||
542 | /* released on free_profile */ | ||
543 | rcu_assign_pointer(profile->parent, aa_get_profile(parent)); | ||
544 | profile->ns = aa_get_ns(parent->ns); | ||
545 | profile->file.dfa = aa_get_dfa(nulldfa); | ||
546 | profile->policy.dfa = aa_get_dfa(nulldfa); | ||
547 | |||
548 | mutex_lock(&profile->ns->lock); | ||
549 | p = __find_child(&parent->base.profiles, bname); | ||
550 | if (p) { | ||
551 | aa_free_profile(profile); | ||
552 | profile = aa_get_profile(p); | ||
553 | } else { | ||
554 | __add_profile(&parent->base.profiles, profile); | ||
555 | } | ||
556 | mutex_unlock(&profile->ns->lock); | ||
557 | |||
558 | /* refcount released by caller */ | ||
559 | out: | ||
560 | kfree(name); | ||
561 | |||
562 | return profile; | ||
563 | |||
564 | fail: | ||
565 | aa_free_profile(profile); | ||
566 | return NULL; | ||
567 | } | ||
568 | |||
569 | /** | ||
562 | * replacement_allowed - test to see if replacement is allowed | 570 | * replacement_allowed - test to see if replacement is allowed |
563 | * @profile: profile to test if it can be replaced (MAYBE NULL) | 571 | * @profile: profile to test if it can be replaced (MAYBE NULL) |
564 | * @noreplace: true if replacement shouldn't be allowed but addition is okay | 572 | * @noreplace: true if replacement shouldn't be allowed but addition is okay |
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 351d3bab3a3d..62a3589c62ab 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c | |||
@@ -112,6 +112,8 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) | |||
112 | ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR | | 112 | ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR | |
113 | FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED; | 113 | FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED; |
114 | ns->unconfined->mode = APPARMOR_UNCONFINED; | 114 | ns->unconfined->mode = APPARMOR_UNCONFINED; |
115 | ns->unconfined->file.dfa = aa_get_dfa(nulldfa); | ||
116 | ns->unconfined->policy.dfa = aa_get_dfa(nulldfa); | ||
115 | 117 | ||
116 | /* ns and ns->unconfined share ns->unconfined refcount */ | 118 | /* ns and ns->unconfined share ns->unconfined refcount */ |
117 | ns->unconfined->ns = ns; | 119 | ns->unconfined->ns = ns; |
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index c600f4dd1783..5a2aec358322 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c | |||
@@ -85,9 +85,9 @@ static void audit_cb(struct audit_buffer *ab, void *va) | |||
85 | audit_log_format(ab, " ns="); | 85 | audit_log_format(ab, " ns="); |
86 | audit_log_untrustedstring(ab, aad(sa)->iface.ns); | 86 | audit_log_untrustedstring(ab, aad(sa)->iface.ns); |
87 | } | 87 | } |
88 | if (aad(sa)->iface.name) { | 88 | if (aad(sa)->name) { |
89 | audit_log_format(ab, " name="); | 89 | audit_log_format(ab, " name="); |
90 | audit_log_untrustedstring(ab, aad(sa)->iface.name); | 90 | audit_log_untrustedstring(ab, aad(sa)->name); |
91 | } | 91 | } |
92 | if (aad(sa)->iface.pos) | 92 | if (aad(sa)->iface.pos) |
93 | audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos); | 93 | audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos); |
@@ -114,9 +114,9 @@ static int audit_iface(struct aa_profile *new, const char *ns_name, | |||
114 | aad(&sa)->iface.pos = e->pos - e->start; | 114 | aad(&sa)->iface.pos = e->pos - e->start; |
115 | aad(&sa)->iface.ns = ns_name; | 115 | aad(&sa)->iface.ns = ns_name; |
116 | if (new) | 116 | if (new) |
117 | aad(&sa)->iface.name = new->base.hname; | 117 | aad(&sa)->name = new->base.hname; |
118 | else | 118 | else |
119 | aad(&sa)->iface.name = name; | 119 | aad(&sa)->name = name; |
120 | aad(&sa)->info = info; | 120 | aad(&sa)->info = info; |
121 | aad(&sa)->error = error; | 121 | aad(&sa)->error = error; |
122 | 122 | ||
@@ -275,6 +275,19 @@ fail: | |||
275 | return 0; | 275 | return 0; |
276 | } | 276 | } |
277 | 277 | ||
278 | static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) | ||
279 | { | ||
280 | if (unpack_nameX(e, AA_U16, name)) { | ||
281 | if (!inbounds(e, sizeof(u16))) | ||
282 | return 0; | ||
283 | if (data) | ||
284 | *data = le16_to_cpu(get_unaligned((__le16 *) e->pos)); | ||
285 | e->pos += sizeof(u16); | ||
286 | return 1; | ||
287 | } | ||
288 | return 0; | ||
289 | } | ||
290 | |||
278 | static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) | 291 | static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) |
279 | { | 292 | { |
280 | if (unpack_nameX(e, AA_U32, name)) { | 293 | if (unpack_nameX(e, AA_U32, name)) { |
@@ -448,7 +461,7 @@ fail: | |||
448 | */ | 461 | */ |
449 | static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) | 462 | static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) |
450 | { | 463 | { |
451 | void *pos = e->pos; | 464 | void *saved_pos = e->pos; |
452 | 465 | ||
453 | /* exec table is optional */ | 466 | /* exec table is optional */ |
454 | if (unpack_nameX(e, AA_STRUCT, "xtable")) { | 467 | if (unpack_nameX(e, AA_STRUCT, "xtable")) { |
@@ -511,7 +524,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) | |||
511 | 524 | ||
512 | fail: | 525 | fail: |
513 | aa_free_domain_entries(&profile->file.trans); | 526 | aa_free_domain_entries(&profile->file.trans); |
514 | e->pos = pos; | 527 | e->pos = saved_pos; |
515 | return 0; | 528 | return 0; |
516 | } | 529 | } |
517 | 530 | ||
@@ -583,7 +596,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
583 | { | 596 | { |
584 | struct aa_profile *profile = NULL; | 597 | struct aa_profile *profile = NULL; |
585 | const char *tmpname, *tmpns = NULL, *name = NULL; | 598 | const char *tmpname, *tmpns = NULL, *name = NULL; |
586 | size_t ns_len; | 599 | const char *info = "failed to unpack profile"; |
600 | size_t size = 0, ns_len; | ||
587 | struct rhashtable_params params = { 0 }; | 601 | struct rhashtable_params params = { 0 }; |
588 | char *key = NULL; | 602 | char *key = NULL; |
589 | struct aa_data *data; | 603 | struct aa_data *data; |
@@ -604,8 +618,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
604 | tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len); | 618 | tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len); |
605 | if (tmpns) { | 619 | if (tmpns) { |
606 | *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); | 620 | *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); |
607 | if (!*ns_name) | 621 | if (!*ns_name) { |
622 | info = "out of memory"; | ||
608 | goto fail; | 623 | goto fail; |
624 | } | ||
609 | name = tmpname; | 625 | name = tmpname; |
610 | } | 626 | } |
611 | 627 | ||
@@ -624,12 +640,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
624 | if (IS_ERR(profile->xmatch)) { | 640 | if (IS_ERR(profile->xmatch)) { |
625 | error = PTR_ERR(profile->xmatch); | 641 | error = PTR_ERR(profile->xmatch); |
626 | profile->xmatch = NULL; | 642 | profile->xmatch = NULL; |
643 | info = "bad xmatch"; | ||
627 | goto fail; | 644 | goto fail; |
628 | } | 645 | } |
629 | /* xmatch_len is not optional if xmatch is set */ | 646 | /* xmatch_len is not optional if xmatch is set */ |
630 | if (profile->xmatch) { | 647 | if (profile->xmatch) { |
631 | if (!unpack_u32(e, &tmp, NULL)) | 648 | if (!unpack_u32(e, &tmp, NULL)) { |
649 | info = "missing xmatch len"; | ||
632 | goto fail; | 650 | goto fail; |
651 | } | ||
633 | profile->xmatch_len = tmp; | 652 | profile->xmatch_len = tmp; |
634 | } | 653 | } |
635 | 654 | ||
@@ -637,8 +656,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
637 | (void) unpack_str(e, &profile->disconnected, "disconnected"); | 656 | (void) unpack_str(e, &profile->disconnected, "disconnected"); |
638 | 657 | ||
639 | /* per profile debug flags (complain, audit) */ | 658 | /* per profile debug flags (complain, audit) */ |
640 | if (!unpack_nameX(e, AA_STRUCT, "flags")) | 659 | if (!unpack_nameX(e, AA_STRUCT, "flags")) { |
660 | info = "profile missing flags"; | ||
641 | goto fail; | 661 | goto fail; |
662 | } | ||
663 | info = "failed to unpack profile flags"; | ||
642 | if (!unpack_u32(e, &tmp, NULL)) | 664 | if (!unpack_u32(e, &tmp, NULL)) |
643 | goto fail; | 665 | goto fail; |
644 | if (tmp & PACKED_FLAG_HAT) | 666 | if (tmp & PACKED_FLAG_HAT) |
@@ -667,6 +689,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
667 | /* set a default value if path_flags field is not present */ | 689 | /* set a default value if path_flags field is not present */ |
668 | profile->path_flags = PATH_MEDIATE_DELETED; | 690 | profile->path_flags = PATH_MEDIATE_DELETED; |
669 | 691 | ||
692 | info = "failed to unpack profile capabilities"; | ||
670 | if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) | 693 | if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) |
671 | goto fail; | 694 | goto fail; |
672 | if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) | 695 | if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) |
@@ -676,6 +699,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
676 | if (!unpack_u32(e, &tmpcap.cap[0], NULL)) | 699 | if (!unpack_u32(e, &tmpcap.cap[0], NULL)) |
677 | goto fail; | 700 | goto fail; |
678 | 701 | ||
702 | info = "failed to unpack upper profile capabilities"; | ||
679 | if (unpack_nameX(e, AA_STRUCT, "caps64")) { | 703 | if (unpack_nameX(e, AA_STRUCT, "caps64")) { |
680 | /* optional upper half of 64 bit caps */ | 704 | /* optional upper half of 64 bit caps */ |
681 | if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL)) | 705 | if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL)) |
@@ -690,6 +714,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
690 | goto fail; | 714 | goto fail; |
691 | } | 715 | } |
692 | 716 | ||
717 | info = "failed to unpack extended profile capabilities"; | ||
693 | if (unpack_nameX(e, AA_STRUCT, "capsx")) { | 718 | if (unpack_nameX(e, AA_STRUCT, "capsx")) { |
694 | /* optional extended caps mediation mask */ | 719 | /* optional extended caps mediation mask */ |
695 | if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL)) | 720 | if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL)) |
@@ -700,11 +725,46 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
700 | goto fail; | 725 | goto fail; |
701 | } | 726 | } |
702 | 727 | ||
703 | if (!unpack_rlimits(e, profile)) | 728 | if (!unpack_rlimits(e, profile)) { |
729 | info = "failed to unpack profile rlimits"; | ||
704 | goto fail; | 730 | goto fail; |
731 | } | ||
732 | |||
733 | size = unpack_array(e, "net_allowed_af"); | ||
734 | if (size) { | ||
735 | |||
736 | for (i = 0; i < size; i++) { | ||
737 | /* discard extraneous rules that this kernel will | ||
738 | * never request | ||
739 | */ | ||
740 | if (i >= AF_MAX) { | ||
741 | u16 tmp; | ||
742 | |||
743 | if (!unpack_u16(e, &tmp, NULL) || | ||
744 | !unpack_u16(e, &tmp, NULL) || | ||
745 | !unpack_u16(e, &tmp, NULL)) | ||
746 | goto fail; | ||
747 | continue; | ||
748 | } | ||
749 | if (!unpack_u16(e, &profile->net.allow[i], NULL)) | ||
750 | goto fail; | ||
751 | if (!unpack_u16(e, &profile->net.audit[i], NULL)) | ||
752 | goto fail; | ||
753 | if (!unpack_u16(e, &profile->net.quiet[i], NULL)) | ||
754 | goto fail; | ||
755 | } | ||
756 | if (!unpack_nameX(e, AA_ARRAYEND, NULL)) | ||
757 | goto fail; | ||
758 | } | ||
759 | if (VERSION_LT(e->version, v7)) { | ||
760 | /* pre v7 policy always allowed these */ | ||
761 | profile->net.allow[AF_UNIX] = 0xffff; | ||
762 | profile->net.allow[AF_NETLINK] = 0xffff; | ||
763 | } | ||
705 | 764 | ||
706 | if (unpack_nameX(e, AA_STRUCT, "policydb")) { | 765 | if (unpack_nameX(e, AA_STRUCT, "policydb")) { |
707 | /* generic policy dfa - optional and may be NULL */ | 766 | /* generic policy dfa - optional and may be NULL */ |
767 | info = "failed to unpack policydb"; | ||
708 | profile->policy.dfa = unpack_dfa(e); | 768 | profile->policy.dfa = unpack_dfa(e); |
709 | if (IS_ERR(profile->policy.dfa)) { | 769 | if (IS_ERR(profile->policy.dfa)) { |
710 | error = PTR_ERR(profile->policy.dfa); | 770 | error = PTR_ERR(profile->policy.dfa); |
@@ -734,6 +794,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
734 | if (IS_ERR(profile->file.dfa)) { | 794 | if (IS_ERR(profile->file.dfa)) { |
735 | error = PTR_ERR(profile->file.dfa); | 795 | error = PTR_ERR(profile->file.dfa); |
736 | profile->file.dfa = NULL; | 796 | profile->file.dfa = NULL; |
797 | info = "failed to unpack profile file rules"; | ||
737 | goto fail; | 798 | goto fail; |
738 | } else if (profile->file.dfa) { | 799 | } else if (profile->file.dfa) { |
739 | if (!unpack_u32(e, &profile->file.start, "dfa_start")) | 800 | if (!unpack_u32(e, &profile->file.start, "dfa_start")) |
@@ -746,10 +807,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
746 | } else | 807 | } else |
747 | profile->file.dfa = aa_get_dfa(nulldfa); | 808 | profile->file.dfa = aa_get_dfa(nulldfa); |
748 | 809 | ||
749 | if (!unpack_trans_table(e, profile)) | 810 | if (!unpack_trans_table(e, profile)) { |
811 | info = "failed to unpack profile transition table"; | ||
750 | goto fail; | 812 | goto fail; |
813 | } | ||
751 | 814 | ||
752 | if (unpack_nameX(e, AA_STRUCT, "data")) { | 815 | if (unpack_nameX(e, AA_STRUCT, "data")) { |
816 | info = "out of memory"; | ||
753 | profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL); | 817 | profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL); |
754 | if (!profile->data) | 818 | if (!profile->data) |
755 | goto fail; | 819 | goto fail; |
@@ -761,8 +825,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
761 | params.hashfn = strhash; | 825 | params.hashfn = strhash; |
762 | params.obj_cmpfn = datacmp; | 826 | params.obj_cmpfn = datacmp; |
763 | 827 | ||
764 | if (rhashtable_init(profile->data, ¶ms)) | 828 | if (rhashtable_init(profile->data, ¶ms)) { |
829 | info = "failed to init key, value hash table"; | ||
765 | goto fail; | 830 | goto fail; |
831 | } | ||
766 | 832 | ||
767 | while (unpack_strdup(e, &key, NULL)) { | 833 | while (unpack_strdup(e, &key, NULL)) { |
768 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 834 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
@@ -784,12 +850,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) | |||
784 | profile->data->p); | 850 | profile->data->p); |
785 | } | 851 | } |
786 | 852 | ||
787 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | 853 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) { |
854 | info = "failed to unpack end of key, value data table"; | ||
788 | goto fail; | 855 | goto fail; |
856 | } | ||
789 | } | 857 | } |
790 | 858 | ||
791 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | 859 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) { |
860 | info = "failed to unpack end of profile"; | ||
792 | goto fail; | 861 | goto fail; |
862 | } | ||
793 | 863 | ||
794 | return profile; | 864 | return profile; |
795 | 865 | ||
@@ -798,8 +868,7 @@ fail: | |||
798 | name = NULL; | 868 | name = NULL; |
799 | else if (!name) | 869 | else if (!name) |
800 | name = "unknown"; | 870 | name = "unknown"; |
801 | audit_iface(profile, NULL, name, "failed to unpack profile", e, | 871 | audit_iface(profile, NULL, name, info, e, error); |
802 | error); | ||
803 | aa_free_profile(profile); | 872 | aa_free_profile(profile); |
804 | 873 | ||
805 | return ERR_PTR(error); | 874 | return ERR_PTR(error); |
@@ -832,7 +901,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) | |||
832 | * if not specified use previous version | 901 | * if not specified use previous version |
833 | * Mask off everything that is not kernel abi version | 902 | * Mask off everything that is not kernel abi version |
834 | */ | 903 | */ |
835 | if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) { | 904 | if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) { |
836 | audit_iface(NULL, NULL, NULL, "unsupported interface version", | 905 | audit_iface(NULL, NULL, NULL, "unsupported interface version", |
837 | e, error); | 906 | e, error); |
838 | return error; | 907 | return error; |
diff --git a/security/commoncap.c b/security/commoncap.c index 6bf72b175b49..c25e0d27537f 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
@@ -294,10 +294,10 @@ int cap_capset(struct cred *new, | |||
294 | * | 294 | * |
295 | * Determine if an inode having a change applied that's marked ATTR_KILL_PRIV | 295 | * Determine if an inode having a change applied that's marked ATTR_KILL_PRIV |
296 | * affects the security markings on that inode, and if it is, should | 296 | * affects the security markings on that inode, and if it is, should |
297 | * inode_killpriv() be invoked or the change rejected? | 297 | * inode_killpriv() be invoked or the change rejected. |
298 | * | 298 | * |
299 | * Returns 0 if granted; +ve if granted, but inode_killpriv() is required; and | 299 | * Returns 1 if security.capability has a value, meaning inode_killpriv() |
300 | * -ve to deny the change. | 300 | * is required, 0 otherwise, meaning inode_killpriv() is not required. |
301 | */ | 301 | */ |
302 | int cap_inode_need_killpriv(struct dentry *dentry) | 302 | int cap_inode_need_killpriv(struct dentry *dentry) |
303 | { | 303 | { |
diff --git a/security/keys/Kconfig b/security/keys/Kconfig index a7a23b5541f8..91eafada3164 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig | |||
@@ -45,10 +45,8 @@ config BIG_KEYS | |||
45 | bool "Large payload keys" | 45 | bool "Large payload keys" |
46 | depends on KEYS | 46 | depends on KEYS |
47 | depends on TMPFS | 47 | depends on TMPFS |
48 | depends on (CRYPTO_ANSI_CPRNG = y || CRYPTO_DRBG = y) | ||
49 | select CRYPTO_AES | 48 | select CRYPTO_AES |
50 | select CRYPTO_ECB | 49 | select CRYPTO_GCM |
51 | select CRYPTO_RNG | ||
52 | help | 50 | help |
53 | This option provides support for holding large keys within the kernel | 51 | This option provides support for holding large keys within the kernel |
54 | (for example Kerberos ticket caches). The data may be stored out to | 52 | (for example Kerberos ticket caches). The data may be stored out to |
diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 6acb00f6f22c..e607830b6154 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* Large capacity key type | 1 | /* Large capacity key type |
2 | * | 2 | * |
3 | * Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. | ||
3 | * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. | 4 | * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. |
4 | * Written by David Howells (dhowells@redhat.com) | 5 | * Written by David Howells (dhowells@redhat.com) |
5 | * | 6 | * |
@@ -16,10 +17,10 @@ | |||
16 | #include <linux/shmem_fs.h> | 17 | #include <linux/shmem_fs.h> |
17 | #include <linux/err.h> | 18 | #include <linux/err.h> |
18 | #include <linux/scatterlist.h> | 19 | #include <linux/scatterlist.h> |
20 | #include <linux/random.h> | ||
19 | #include <keys/user-type.h> | 21 | #include <keys/user-type.h> |
20 | #include <keys/big_key-type.h> | 22 | #include <keys/big_key-type.h> |
21 | #include <crypto/rng.h> | 23 | #include <crypto/aead.h> |
22 | #include <crypto/skcipher.h> | ||
23 | 24 | ||
24 | /* | 25 | /* |
25 | * Layout of key payload words. | 26 | * Layout of key payload words. |
@@ -49,7 +50,12 @@ enum big_key_op { | |||
49 | /* | 50 | /* |
50 | * Key size for big_key data encryption | 51 | * Key size for big_key data encryption |
51 | */ | 52 | */ |
52 | #define ENC_KEY_SIZE 16 | 53 | #define ENC_KEY_SIZE 32 |
54 | |||
55 | /* | ||
56 | * Authentication tag length | ||
57 | */ | ||
58 | #define ENC_AUTHTAG_SIZE 16 | ||
53 | 59 | ||
54 | /* | 60 | /* |
55 | * big_key defined keys take an arbitrary string as the description and an | 61 | * big_key defined keys take an arbitrary string as the description and an |
@@ -64,57 +70,62 @@ struct key_type key_type_big_key = { | |||
64 | .destroy = big_key_destroy, | 70 | .destroy = big_key_destroy, |
65 | .describe = big_key_describe, | 71 | .describe = big_key_describe, |
66 | .read = big_key_read, | 72 | .read = big_key_read, |
73 | /* no ->update(); don't add it without changing big_key_crypt() nonce */ | ||
67 | }; | 74 | }; |
68 | 75 | ||
69 | /* | 76 | /* |
70 | * Crypto names for big_key data encryption | 77 | * Crypto names for big_key data authenticated encryption |
71 | */ | 78 | */ |
72 | static const char big_key_rng_name[] = "stdrng"; | 79 | static const char big_key_alg_name[] = "gcm(aes)"; |
73 | static const char big_key_alg_name[] = "ecb(aes)"; | ||
74 | 80 | ||
75 | /* | 81 | /* |
76 | * Crypto algorithms for big_key data encryption | 82 | * Crypto algorithms for big_key data authenticated encryption |
77 | */ | 83 | */ |
78 | static struct crypto_rng *big_key_rng; | 84 | static struct crypto_aead *big_key_aead; |
79 | static struct crypto_skcipher *big_key_skcipher; | ||
80 | 85 | ||
81 | /* | 86 | /* |
82 | * Generate random key to encrypt big_key data | 87 | * Since changing the key affects the entire object, we need a mutex. |
83 | */ | 88 | */ |
84 | static inline int big_key_gen_enckey(u8 *key) | 89 | static DEFINE_MUTEX(big_key_aead_lock); |
85 | { | ||
86 | return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE); | ||
87 | } | ||
88 | 90 | ||
89 | /* | 91 | /* |
90 | * Encrypt/decrypt big_key data | 92 | * Encrypt/decrypt big_key data |
91 | */ | 93 | */ |
92 | static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key) | 94 | static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key) |
93 | { | 95 | { |
94 | int ret = -EINVAL; | 96 | int ret; |
95 | struct scatterlist sgio; | 97 | struct scatterlist sgio; |
96 | SKCIPHER_REQUEST_ON_STACK(req, big_key_skcipher); | 98 | struct aead_request *aead_req; |
97 | 99 | /* We always use a zero nonce. The reason we can get away with this is | |
98 | if (crypto_skcipher_setkey(big_key_skcipher, key, ENC_KEY_SIZE)) { | 100 | * because we're using a different randomly generated key for every |
101 | * different encryption. Notably, too, key_type_big_key doesn't define | ||
102 | * an .update function, so there's no chance we'll wind up reusing the | ||
103 | * key to encrypt updated data. Simply put: one key, one encryption. | ||
104 | */ | ||
105 | u8 zero_nonce[crypto_aead_ivsize(big_key_aead)]; | ||
106 | |||
107 | aead_req = aead_request_alloc(big_key_aead, GFP_KERNEL); | ||
108 | if (!aead_req) | ||
109 | return -ENOMEM; | ||
110 | |||
111 | memset(zero_nonce, 0, sizeof(zero_nonce)); | ||
112 | sg_init_one(&sgio, data, datalen + (op == BIG_KEY_ENC ? ENC_AUTHTAG_SIZE : 0)); | ||
113 | aead_request_set_crypt(aead_req, &sgio, &sgio, datalen, zero_nonce); | ||
114 | aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); | ||
115 | aead_request_set_ad(aead_req, 0); | ||
116 | |||
117 | mutex_lock(&big_key_aead_lock); | ||
118 | if (crypto_aead_setkey(big_key_aead, key, ENC_KEY_SIZE)) { | ||
99 | ret = -EAGAIN; | 119 | ret = -EAGAIN; |
100 | goto error; | 120 | goto error; |
101 | } | 121 | } |
102 | |||
103 | skcipher_request_set_tfm(req, big_key_skcipher); | ||
104 | skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, | ||
105 | NULL, NULL); | ||
106 | |||
107 | sg_init_one(&sgio, data, datalen); | ||
108 | skcipher_request_set_crypt(req, &sgio, &sgio, datalen, NULL); | ||
109 | |||
110 | if (op == BIG_KEY_ENC) | 122 | if (op == BIG_KEY_ENC) |
111 | ret = crypto_skcipher_encrypt(req); | 123 | ret = crypto_aead_encrypt(aead_req); |
112 | else | 124 | else |
113 | ret = crypto_skcipher_decrypt(req); | 125 | ret = crypto_aead_decrypt(aead_req); |
114 | |||
115 | skcipher_request_zero(req); | ||
116 | |||
117 | error: | 126 | error: |
127 | mutex_unlock(&big_key_aead_lock); | ||
128 | aead_request_free(aead_req); | ||
118 | return ret; | 129 | return ret; |
119 | } | 130 | } |
120 | 131 | ||
@@ -146,16 +157,13 @@ int big_key_preparse(struct key_preparsed_payload *prep) | |||
146 | * | 157 | * |
147 | * File content is stored encrypted with randomly generated key. | 158 | * File content is stored encrypted with randomly generated key. |
148 | */ | 159 | */ |
149 | size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher)); | 160 | size_t enclen = datalen + ENC_AUTHTAG_SIZE; |
150 | loff_t pos = 0; | 161 | loff_t pos = 0; |
151 | 162 | ||
152 | /* prepare aligned data to encrypt */ | ||
153 | data = kmalloc(enclen, GFP_KERNEL); | 163 | data = kmalloc(enclen, GFP_KERNEL); |
154 | if (!data) | 164 | if (!data) |
155 | return -ENOMEM; | 165 | return -ENOMEM; |
156 | |||
157 | memcpy(data, prep->data, datalen); | 166 | memcpy(data, prep->data, datalen); |
158 | memset(data + datalen, 0x00, enclen - datalen); | ||
159 | 167 | ||
160 | /* generate random key */ | 168 | /* generate random key */ |
161 | enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); | 169 | enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); |
@@ -163,13 +171,12 @@ int big_key_preparse(struct key_preparsed_payload *prep) | |||
163 | ret = -ENOMEM; | 171 | ret = -ENOMEM; |
164 | goto error; | 172 | goto error; |
165 | } | 173 | } |
166 | 174 | ret = get_random_bytes_wait(enckey, ENC_KEY_SIZE); | |
167 | ret = big_key_gen_enckey(enckey); | 175 | if (unlikely(ret)) |
168 | if (ret) | ||
169 | goto err_enckey; | 176 | goto err_enckey; |
170 | 177 | ||
171 | /* encrypt aligned data */ | 178 | /* encrypt aligned data */ |
172 | ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey); | 179 | ret = big_key_crypt(BIG_KEY_ENC, data, datalen, enckey); |
173 | if (ret) | 180 | if (ret) |
174 | goto err_enckey; | 181 | goto err_enckey; |
175 | 182 | ||
@@ -195,7 +202,7 @@ int big_key_preparse(struct key_preparsed_payload *prep) | |||
195 | *path = file->f_path; | 202 | *path = file->f_path; |
196 | path_get(path); | 203 | path_get(path); |
197 | fput(file); | 204 | fput(file); |
198 | kfree(data); | 205 | kzfree(data); |
199 | } else { | 206 | } else { |
200 | /* Just store the data in a buffer */ | 207 | /* Just store the data in a buffer */ |
201 | void *data = kmalloc(datalen, GFP_KERNEL); | 208 | void *data = kmalloc(datalen, GFP_KERNEL); |
@@ -211,9 +218,9 @@ int big_key_preparse(struct key_preparsed_payload *prep) | |||
211 | err_fput: | 218 | err_fput: |
212 | fput(file); | 219 | fput(file); |
213 | err_enckey: | 220 | err_enckey: |
214 | kfree(enckey); | 221 | kzfree(enckey); |
215 | error: | 222 | error: |
216 | kfree(data); | 223 | kzfree(data); |
217 | return ret; | 224 | return ret; |
218 | } | 225 | } |
219 | 226 | ||
@@ -227,7 +234,7 @@ void big_key_free_preparse(struct key_preparsed_payload *prep) | |||
227 | 234 | ||
228 | path_put(path); | 235 | path_put(path); |
229 | } | 236 | } |
230 | kfree(prep->payload.data[big_key_data]); | 237 | kzfree(prep->payload.data[big_key_data]); |
231 | } | 238 | } |
232 | 239 | ||
233 | /* | 240 | /* |
@@ -259,7 +266,7 @@ void big_key_destroy(struct key *key) | |||
259 | path->mnt = NULL; | 266 | path->mnt = NULL; |
260 | path->dentry = NULL; | 267 | path->dentry = NULL; |
261 | } | 268 | } |
262 | kfree(key->payload.data[big_key_data]); | 269 | kzfree(key->payload.data[big_key_data]); |
263 | key->payload.data[big_key_data] = NULL; | 270 | key->payload.data[big_key_data] = NULL; |
264 | } | 271 | } |
265 | 272 | ||
@@ -295,7 +302,7 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) | |||
295 | struct file *file; | 302 | struct file *file; |
296 | u8 *data; | 303 | u8 *data; |
297 | u8 *enckey = (u8 *)key->payload.data[big_key_data]; | 304 | u8 *enckey = (u8 *)key->payload.data[big_key_data]; |
298 | size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher)); | 305 | size_t enclen = datalen + ENC_AUTHTAG_SIZE; |
299 | loff_t pos = 0; | 306 | loff_t pos = 0; |
300 | 307 | ||
301 | data = kmalloc(enclen, GFP_KERNEL); | 308 | data = kmalloc(enclen, GFP_KERNEL); |
@@ -328,7 +335,7 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) | |||
328 | err_fput: | 335 | err_fput: |
329 | fput(file); | 336 | fput(file); |
330 | error: | 337 | error: |
331 | kfree(data); | 338 | kzfree(data); |
332 | } else { | 339 | } else { |
333 | ret = datalen; | 340 | ret = datalen; |
334 | if (copy_to_user(buffer, key->payload.data[big_key_data], | 341 | if (copy_to_user(buffer, key->payload.data[big_key_data], |
@@ -344,47 +351,31 @@ error: | |||
344 | */ | 351 | */ |
345 | static int __init big_key_init(void) | 352 | static int __init big_key_init(void) |
346 | { | 353 | { |
347 | struct crypto_skcipher *cipher; | ||
348 | struct crypto_rng *rng; | ||
349 | int ret; | 354 | int ret; |
350 | 355 | ||
351 | rng = crypto_alloc_rng(big_key_rng_name, 0, 0); | ||
352 | if (IS_ERR(rng)) { | ||
353 | pr_err("Can't alloc rng: %ld\n", PTR_ERR(rng)); | ||
354 | return PTR_ERR(rng); | ||
355 | } | ||
356 | |||
357 | big_key_rng = rng; | ||
358 | |||
359 | /* seed RNG */ | ||
360 | ret = crypto_rng_reset(rng, NULL, crypto_rng_seedsize(rng)); | ||
361 | if (ret) { | ||
362 | pr_err("Can't reset rng: %d\n", ret); | ||
363 | goto error_rng; | ||
364 | } | ||
365 | |||
366 | /* init block cipher */ | 356 | /* init block cipher */ |
367 | cipher = crypto_alloc_skcipher(big_key_alg_name, 0, CRYPTO_ALG_ASYNC); | 357 | big_key_aead = crypto_alloc_aead(big_key_alg_name, 0, CRYPTO_ALG_ASYNC); |
368 | if (IS_ERR(cipher)) { | 358 | if (IS_ERR(big_key_aead)) { |
369 | ret = PTR_ERR(cipher); | 359 | ret = PTR_ERR(big_key_aead); |
370 | pr_err("Can't alloc crypto: %d\n", ret); | 360 | pr_err("Can't alloc crypto: %d\n", ret); |
371 | goto error_rng; | 361 | return ret; |
362 | } | ||
363 | ret = crypto_aead_setauthsize(big_key_aead, ENC_AUTHTAG_SIZE); | ||
364 | if (ret < 0) { | ||
365 | pr_err("Can't set crypto auth tag len: %d\n", ret); | ||
366 | goto free_aead; | ||
372 | } | 367 | } |
373 | |||
374 | big_key_skcipher = cipher; | ||
375 | 368 | ||
376 | ret = register_key_type(&key_type_big_key); | 369 | ret = register_key_type(&key_type_big_key); |
377 | if (ret < 0) { | 370 | if (ret < 0) { |
378 | pr_err("Can't register type: %d\n", ret); | 371 | pr_err("Can't register type: %d\n", ret); |
379 | goto error_cipher; | 372 | goto free_aead; |
380 | } | 373 | } |
381 | 374 | ||
382 | return 0; | 375 | return 0; |
383 | 376 | ||
384 | error_cipher: | 377 | free_aead: |
385 | crypto_free_skcipher(big_key_skcipher); | 378 | crypto_free_aead(big_key_aead); |
386 | error_rng: | ||
387 | crypto_free_rng(big_key_rng); | ||
388 | return ret; | 379 | return ret; |
389 | } | 380 | } |
390 | 381 | ||
diff --git a/security/keys/internal.h b/security/keys/internal.h index 1c02c6547038..503adbae7b0d 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h | |||
@@ -141,7 +141,7 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, | |||
141 | extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx); | 141 | extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx); |
142 | extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx); | 142 | extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx); |
143 | 143 | ||
144 | extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check); | 144 | extern struct key *find_keyring_by_name(const char *name, bool uid_keyring); |
145 | 145 | ||
146 | extern int install_user_keyrings(void); | 146 | extern int install_user_keyrings(void); |
147 | extern int install_thread_keyring_to_cred(struct cred *); | 147 | extern int install_thread_keyring_to_cred(struct cred *); |
diff --git a/security/keys/key.c b/security/keys/key.c index 83da68d98b40..eb914a838840 100644 --- a/security/keys/key.c +++ b/security/keys/key.c | |||
@@ -54,10 +54,10 @@ void __key_check(const struct key *key) | |||
54 | struct key_user *key_user_lookup(kuid_t uid) | 54 | struct key_user *key_user_lookup(kuid_t uid) |
55 | { | 55 | { |
56 | struct key_user *candidate = NULL, *user; | 56 | struct key_user *candidate = NULL, *user; |
57 | struct rb_node *parent = NULL; | 57 | struct rb_node *parent, **p; |
58 | struct rb_node **p; | ||
59 | 58 | ||
60 | try_again: | 59 | try_again: |
60 | parent = NULL; | ||
61 | p = &key_user_tree.rb_node; | 61 | p = &key_user_tree.rb_node; |
62 | spin_lock(&key_user_lock); | 62 | spin_lock(&key_user_lock); |
63 | 63 | ||
@@ -302,6 +302,8 @@ struct key *key_alloc(struct key_type *type, const char *desc, | |||
302 | key->flags |= 1 << KEY_FLAG_IN_QUOTA; | 302 | key->flags |= 1 << KEY_FLAG_IN_QUOTA; |
303 | if (flags & KEY_ALLOC_BUILT_IN) | 303 | if (flags & KEY_ALLOC_BUILT_IN) |
304 | key->flags |= 1 << KEY_FLAG_BUILTIN; | 304 | key->flags |= 1 << KEY_FLAG_BUILTIN; |
305 | if (flags & KEY_ALLOC_UID_KEYRING) | ||
306 | key->flags |= 1 << KEY_FLAG_UID_KEYRING; | ||
305 | 307 | ||
306 | #ifdef KEY_DEBUGGING | 308 | #ifdef KEY_DEBUGGING |
307 | key->magic = KEY_DEBUG_MAGIC; | 309 | key->magic = KEY_DEBUG_MAGIC; |
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ab0b337c84b4..365ff85d7e27 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c | |||
@@ -766,12 +766,17 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) | |||
766 | 766 | ||
767 | key = key_ref_to_ptr(key_ref); | 767 | key = key_ref_to_ptr(key_ref); |
768 | 768 | ||
769 | if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { | ||
770 | ret = -ENOKEY; | ||
771 | goto error2; | ||
772 | } | ||
773 | |||
769 | /* see if we can read it directly */ | 774 | /* see if we can read it directly */ |
770 | ret = key_permission(key_ref, KEY_NEED_READ); | 775 | ret = key_permission(key_ref, KEY_NEED_READ); |
771 | if (ret == 0) | 776 | if (ret == 0) |
772 | goto can_read_key; | 777 | goto can_read_key; |
773 | if (ret != -EACCES) | 778 | if (ret != -EACCES) |
774 | goto error; | 779 | goto error2; |
775 | 780 | ||
776 | /* we can't; see if it's searchable from this process's keyrings | 781 | /* we can't; see if it's searchable from this process's keyrings |
777 | * - we automatically take account of the fact that it may be | 782 | * - we automatically take account of the fact that it may be |
@@ -1406,11 +1411,9 @@ long keyctl_assume_authority(key_serial_t id) | |||
1406 | } | 1411 | } |
1407 | 1412 | ||
1408 | ret = keyctl_change_reqkey_auth(authkey); | 1413 | ret = keyctl_change_reqkey_auth(authkey); |
1409 | if (ret < 0) | 1414 | if (ret == 0) |
1410 | goto error; | 1415 | ret = authkey->serial; |
1411 | key_put(authkey); | 1416 | key_put(authkey); |
1412 | |||
1413 | ret = authkey->serial; | ||
1414 | error: | 1417 | error: |
1415 | return ret; | 1418 | return ret; |
1416 | } | 1419 | } |
diff --git a/security/keys/keyring.c b/security/keys/keyring.c index de81793f9920..4fa82a8a9c0e 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c | |||
@@ -423,7 +423,7 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m) | |||
423 | } | 423 | } |
424 | 424 | ||
425 | struct keyring_read_iterator_context { | 425 | struct keyring_read_iterator_context { |
426 | size_t qty; | 426 | size_t buflen; |
427 | size_t count; | 427 | size_t count; |
428 | key_serial_t __user *buffer; | 428 | key_serial_t __user *buffer; |
429 | }; | 429 | }; |
@@ -435,9 +435,9 @@ static int keyring_read_iterator(const void *object, void *data) | |||
435 | int ret; | 435 | int ret; |
436 | 436 | ||
437 | kenter("{%s,%d},,{%zu/%zu}", | 437 | kenter("{%s,%d},,{%zu/%zu}", |
438 | key->type->name, key->serial, ctx->count, ctx->qty); | 438 | key->type->name, key->serial, ctx->count, ctx->buflen); |
439 | 439 | ||
440 | if (ctx->count >= ctx->qty) | 440 | if (ctx->count >= ctx->buflen) |
441 | return 1; | 441 | return 1; |
442 | 442 | ||
443 | ret = put_user(key->serial, ctx->buffer); | 443 | ret = put_user(key->serial, ctx->buffer); |
@@ -472,16 +472,12 @@ static long keyring_read(const struct key *keyring, | |||
472 | return 0; | 472 | return 0; |
473 | 473 | ||
474 | /* Calculate how much data we could return */ | 474 | /* Calculate how much data we could return */ |
475 | ctx.qty = nr_keys * sizeof(key_serial_t); | ||
476 | |||
477 | if (!buffer || !buflen) | 475 | if (!buffer || !buflen) |
478 | return ctx.qty; | 476 | return nr_keys * sizeof(key_serial_t); |
479 | |||
480 | if (buflen > ctx.qty) | ||
481 | ctx.qty = buflen; | ||
482 | 477 | ||
483 | /* Copy the IDs of the subscribed keys into the buffer */ | 478 | /* Copy the IDs of the subscribed keys into the buffer */ |
484 | ctx.buffer = (key_serial_t __user *)buffer; | 479 | ctx.buffer = (key_serial_t __user *)buffer; |
480 | ctx.buflen = buflen; | ||
485 | ctx.count = 0; | 481 | ctx.count = 0; |
486 | ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx); | 482 | ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx); |
487 | if (ret < 0) { | 483 | if (ret < 0) { |
@@ -1101,15 +1097,15 @@ found: | |||
1101 | /* | 1097 | /* |
1102 | * Find a keyring with the specified name. | 1098 | * Find a keyring with the specified name. |
1103 | * | 1099 | * |
1104 | * All named keyrings in the current user namespace are searched, provided they | 1100 | * Only keyrings that have nonzero refcount, are not revoked, and are owned by a |
1105 | * grant Search permission directly to the caller (unless this check is | 1101 | * user in the current user namespace are considered. If @uid_keyring is %true, |
1106 | * skipped). Keyrings whose usage points have reached zero or who have been | 1102 | * the keyring additionally must have been allocated as a user or user session |
1107 | * revoked are skipped. | 1103 | * keyring; otherwise, it must grant Search permission directly to the caller. |
1108 | * | 1104 | * |
1109 | * Returns a pointer to the keyring with the keyring's refcount having being | 1105 | * Returns a pointer to the keyring with the keyring's refcount having being |
1110 | * incremented on success. -ENOKEY is returned if a key could not be found. | 1106 | * incremented on success. -ENOKEY is returned if a key could not be found. |
1111 | */ | 1107 | */ |
1112 | struct key *find_keyring_by_name(const char *name, bool skip_perm_check) | 1108 | struct key *find_keyring_by_name(const char *name, bool uid_keyring) |
1113 | { | 1109 | { |
1114 | struct key *keyring; | 1110 | struct key *keyring; |
1115 | int bucket; | 1111 | int bucket; |
@@ -1137,10 +1133,15 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) | |||
1137 | if (strcmp(keyring->description, name) != 0) | 1133 | if (strcmp(keyring->description, name) != 0) |
1138 | continue; | 1134 | continue; |
1139 | 1135 | ||
1140 | if (!skip_perm_check && | 1136 | if (uid_keyring) { |
1141 | key_permission(make_key_ref(keyring, 0), | 1137 | if (!test_bit(KEY_FLAG_UID_KEYRING, |
1142 | KEY_NEED_SEARCH) < 0) | 1138 | &keyring->flags)) |
1143 | continue; | 1139 | continue; |
1140 | } else { | ||
1141 | if (key_permission(make_key_ref(keyring, 0), | ||
1142 | KEY_NEED_SEARCH) < 0) | ||
1143 | continue; | ||
1144 | } | ||
1144 | 1145 | ||
1145 | /* we've got a match but we might end up racing with | 1146 | /* we've got a match but we might end up racing with |
1146 | * key_cleanup() if the keyring is currently 'dead' | 1147 | * key_cleanup() if the keyring is currently 'dead' |
diff --git a/security/keys/proc.c b/security/keys/proc.c index bf08d02b6646..de834309d100 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c | |||
@@ -187,7 +187,7 @@ static int proc_keys_show(struct seq_file *m, void *v) | |||
187 | struct keyring_search_context ctx = { | 187 | struct keyring_search_context ctx = { |
188 | .index_key.type = key->type, | 188 | .index_key.type = key->type, |
189 | .index_key.description = key->description, | 189 | .index_key.description = key->description, |
190 | .cred = current_cred(), | 190 | .cred = m->file->f_cred, |
191 | .match_data.cmp = lookup_user_key_possessed, | 191 | .match_data.cmp = lookup_user_key_possessed, |
192 | .match_data.raw_data = key, | 192 | .match_data.raw_data = key, |
193 | .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, | 193 | .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, |
@@ -207,11 +207,7 @@ static int proc_keys_show(struct seq_file *m, void *v) | |||
207 | } | 207 | } |
208 | } | 208 | } |
209 | 209 | ||
210 | /* check whether the current task is allowed to view the key (assuming | 210 | /* check whether the current task is allowed to view the key */ |
211 | * non-possession) | ||
212 | * - the caller holds a spinlock, and thus the RCU read lock, making our | ||
213 | * access to __current_cred() safe | ||
214 | */ | ||
215 | rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); | 211 | rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW); |
216 | if (rc < 0) | 212 | if (rc < 0) |
217 | return 0; | 213 | return 0; |
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 86bced9fdbdf..293d3598153b 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c | |||
@@ -77,7 +77,8 @@ int install_user_keyrings(void) | |||
77 | if (IS_ERR(uid_keyring)) { | 77 | if (IS_ERR(uid_keyring)) { |
78 | uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, | 78 | uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, |
79 | cred, user_keyring_perm, | 79 | cred, user_keyring_perm, |
80 | KEY_ALLOC_IN_QUOTA, | 80 | KEY_ALLOC_UID_KEYRING | |
81 | KEY_ALLOC_IN_QUOTA, | ||
81 | NULL, NULL); | 82 | NULL, NULL); |
82 | if (IS_ERR(uid_keyring)) { | 83 | if (IS_ERR(uid_keyring)) { |
83 | ret = PTR_ERR(uid_keyring); | 84 | ret = PTR_ERR(uid_keyring); |
@@ -94,7 +95,8 @@ int install_user_keyrings(void) | |||
94 | session_keyring = | 95 | session_keyring = |
95 | keyring_alloc(buf, user->uid, INVALID_GID, | 96 | keyring_alloc(buf, user->uid, INVALID_GID, |
96 | cred, user_keyring_perm, | 97 | cred, user_keyring_perm, |
97 | KEY_ALLOC_IN_QUOTA, | 98 | KEY_ALLOC_UID_KEYRING | |
99 | KEY_ALLOC_IN_QUOTA, | ||
98 | NULL, NULL); | 100 | NULL, NULL); |
99 | if (IS_ERR(session_keyring)) { | 101 | if (IS_ERR(session_keyring)) { |
100 | ret = PTR_ERR(session_keyring); | 102 | ret = PTR_ERR(session_keyring); |
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index afe9d22ab361..6ebf1af8fce9 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c | |||
@@ -120,6 +120,18 @@ static void request_key_auth_revoke(struct key *key) | |||
120 | } | 120 | } |
121 | } | 121 | } |
122 | 122 | ||
123 | static void free_request_key_auth(struct request_key_auth *rka) | ||
124 | { | ||
125 | if (!rka) | ||
126 | return; | ||
127 | key_put(rka->target_key); | ||
128 | key_put(rka->dest_keyring); | ||
129 | if (rka->cred) | ||
130 | put_cred(rka->cred); | ||
131 | kfree(rka->callout_info); | ||
132 | kfree(rka); | ||
133 | } | ||
134 | |||
123 | /* | 135 | /* |
124 | * Destroy an instantiation authorisation token key. | 136 | * Destroy an instantiation authorisation token key. |
125 | */ | 137 | */ |
@@ -129,15 +141,7 @@ static void request_key_auth_destroy(struct key *key) | |||
129 | 141 | ||
130 | kenter("{%d}", key->serial); | 142 | kenter("{%d}", key->serial); |
131 | 143 | ||
132 | if (rka->cred) { | 144 | free_request_key_auth(rka); |
133 | put_cred(rka->cred); | ||
134 | rka->cred = NULL; | ||
135 | } | ||
136 | |||
137 | key_put(rka->target_key); | ||
138 | key_put(rka->dest_keyring); | ||
139 | kfree(rka->callout_info); | ||
140 | kfree(rka); | ||
141 | } | 145 | } |
142 | 146 | ||
143 | /* | 147 | /* |
@@ -151,22 +155,18 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, | |||
151 | const struct cred *cred = current->cred; | 155 | const struct cred *cred = current->cred; |
152 | struct key *authkey = NULL; | 156 | struct key *authkey = NULL; |
153 | char desc[20]; | 157 | char desc[20]; |
154 | int ret; | 158 | int ret = -ENOMEM; |
155 | 159 | ||
156 | kenter("%d,", target->serial); | 160 | kenter("%d,", target->serial); |
157 | 161 | ||
158 | /* allocate a auth record */ | 162 | /* allocate a auth record */ |
159 | rka = kmalloc(sizeof(*rka), GFP_KERNEL); | 163 | rka = kzalloc(sizeof(*rka), GFP_KERNEL); |
160 | if (!rka) { | 164 | if (!rka) |
161 | kleave(" = -ENOMEM"); | 165 | goto error; |
162 | return ERR_PTR(-ENOMEM); | 166 | rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL); |
163 | } | 167 | if (!rka->callout_info) |
164 | rka->callout_info = kmalloc(callout_len, GFP_KERNEL); | 168 | goto error_free_rka; |
165 | if (!rka->callout_info) { | 169 | rka->callout_len = callout_len; |
166 | kleave(" = -ENOMEM"); | ||
167 | kfree(rka); | ||
168 | return ERR_PTR(-ENOMEM); | ||
169 | } | ||
170 | 170 | ||
171 | /* see if the calling process is already servicing the key request of | 171 | /* see if the calling process is already servicing the key request of |
172 | * another process */ | 172 | * another process */ |
@@ -176,8 +176,12 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, | |||
176 | 176 | ||
177 | /* if the auth key has been revoked, then the key we're | 177 | /* if the auth key has been revoked, then the key we're |
178 | * servicing is already instantiated */ | 178 | * servicing is already instantiated */ |
179 | if (test_bit(KEY_FLAG_REVOKED, &cred->request_key_auth->flags)) | 179 | if (test_bit(KEY_FLAG_REVOKED, |
180 | goto auth_key_revoked; | 180 | &cred->request_key_auth->flags)) { |
181 | up_read(&cred->request_key_auth->sem); | ||
182 | ret = -EKEYREVOKED; | ||
183 | goto error_free_rka; | ||
184 | } | ||
181 | 185 | ||
182 | irka = cred->request_key_auth->payload.data[0]; | 186 | irka = cred->request_key_auth->payload.data[0]; |
183 | rka->cred = get_cred(irka->cred); | 187 | rka->cred = get_cred(irka->cred); |
@@ -193,8 +197,6 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, | |||
193 | 197 | ||
194 | rka->target_key = key_get(target); | 198 | rka->target_key = key_get(target); |
195 | rka->dest_keyring = key_get(dest_keyring); | 199 | rka->dest_keyring = key_get(dest_keyring); |
196 | memcpy(rka->callout_info, callout_info, callout_len); | ||
197 | rka->callout_len = callout_len; | ||
198 | 200 | ||
199 | /* allocate the auth key */ | 201 | /* allocate the auth key */ |
200 | sprintf(desc, "%x", target->serial); | 202 | sprintf(desc, "%x", target->serial); |
@@ -205,32 +207,22 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, | |||
205 | KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL); | 207 | KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL); |
206 | if (IS_ERR(authkey)) { | 208 | if (IS_ERR(authkey)) { |
207 | ret = PTR_ERR(authkey); | 209 | ret = PTR_ERR(authkey); |
208 | goto error_alloc; | 210 | goto error_free_rka; |
209 | } | 211 | } |
210 | 212 | ||
211 | /* construct the auth key */ | 213 | /* construct the auth key */ |
212 | ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL); | 214 | ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL); |
213 | if (ret < 0) | 215 | if (ret < 0) |
214 | goto error_inst; | 216 | goto error_put_authkey; |
215 | 217 | ||
216 | kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage)); | 218 | kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage)); |
217 | return authkey; | 219 | return authkey; |
218 | 220 | ||
219 | auth_key_revoked: | 221 | error_put_authkey: |
220 | up_read(&cred->request_key_auth->sem); | ||
221 | kfree(rka->callout_info); | ||
222 | kfree(rka); | ||
223 | kleave("= -EKEYREVOKED"); | ||
224 | return ERR_PTR(-EKEYREVOKED); | ||
225 | |||
226 | error_inst: | ||
227 | key_revoke(authkey); | ||
228 | key_put(authkey); | 222 | key_put(authkey); |
229 | error_alloc: | 223 | error_free_rka: |
230 | key_put(rka->target_key); | 224 | free_request_key_auth(rka); |
231 | key_put(rka->dest_keyring); | 225 | error: |
232 | kfree(rka->callout_info); | ||
233 | kfree(rka); | ||
234 | kleave("= %d", ret); | 226 | kleave("= %d", ret); |
235 | return ERR_PTR(ret); | 227 | return ERR_PTR(ret); |
236 | } | 228 | } |
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 319add31b4a4..286171a16ed2 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
@@ -1473,7 +1473,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) | |||
1473 | * @inode: the object | 1473 | * @inode: the object |
1474 | * @name: attribute name | 1474 | * @name: attribute name |
1475 | * @buffer: where to put the result | 1475 | * @buffer: where to put the result |
1476 | * @alloc: unused | 1476 | * @alloc: duplicate memory |
1477 | * | 1477 | * |
1478 | * Returns the size of the attribute or an error code | 1478 | * Returns the size of the attribute or an error code |
1479 | */ | 1479 | */ |
@@ -1486,43 +1486,38 @@ static int smack_inode_getsecurity(struct inode *inode, | |||
1486 | struct super_block *sbp; | 1486 | struct super_block *sbp; |
1487 | struct inode *ip = (struct inode *)inode; | 1487 | struct inode *ip = (struct inode *)inode; |
1488 | struct smack_known *isp; | 1488 | struct smack_known *isp; |
1489 | int ilen; | ||
1490 | int rc = 0; | ||
1491 | 1489 | ||
1492 | if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { | 1490 | if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) |
1493 | isp = smk_of_inode(inode); | 1491 | isp = smk_of_inode(inode); |
1494 | ilen = strlen(isp->smk_known); | 1492 | else { |
1495 | *buffer = isp->smk_known; | 1493 | /* |
1496 | return ilen; | 1494 | * The rest of the Smack xattrs are only on sockets. |
1497 | } | 1495 | */ |
1496 | sbp = ip->i_sb; | ||
1497 | if (sbp->s_magic != SOCKFS_MAGIC) | ||
1498 | return -EOPNOTSUPP; | ||
1498 | 1499 | ||
1499 | /* | 1500 | sock = SOCKET_I(ip); |
1500 | * The rest of the Smack xattrs are only on sockets. | 1501 | if (sock == NULL || sock->sk == NULL) |
1501 | */ | 1502 | return -EOPNOTSUPP; |
1502 | sbp = ip->i_sb; | ||
1503 | if (sbp->s_magic != SOCKFS_MAGIC) | ||
1504 | return -EOPNOTSUPP; | ||
1505 | 1503 | ||
1506 | sock = SOCKET_I(ip); | 1504 | ssp = sock->sk->sk_security; |
1507 | if (sock == NULL || sock->sk == NULL) | ||
1508 | return -EOPNOTSUPP; | ||
1509 | |||
1510 | ssp = sock->sk->sk_security; | ||
1511 | 1505 | ||
1512 | if (strcmp(name, XATTR_SMACK_IPIN) == 0) | 1506 | if (strcmp(name, XATTR_SMACK_IPIN) == 0) |
1513 | isp = ssp->smk_in; | 1507 | isp = ssp->smk_in; |
1514 | else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) | 1508 | else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) |
1515 | isp = ssp->smk_out; | 1509 | isp = ssp->smk_out; |
1516 | else | 1510 | else |
1517 | return -EOPNOTSUPP; | 1511 | return -EOPNOTSUPP; |
1512 | } | ||
1518 | 1513 | ||
1519 | ilen = strlen(isp->smk_known); | 1514 | if (alloc) { |
1520 | if (rc == 0) { | 1515 | *buffer = kstrdup(isp->smk_known, GFP_KERNEL); |
1521 | *buffer = isp->smk_known; | 1516 | if (*buffer == NULL) |
1522 | rc = ilen; | 1517 | return -ENOMEM; |
1523 | } | 1518 | } |
1524 | 1519 | ||
1525 | return rc; | 1520 | return strlen(isp->smk_known); |
1526 | } | 1521 | } |
1527 | 1522 | ||
1528 | 1523 | ||