diff options
Diffstat (limited to 'security/apparmor')
-rw-r--r-- | security/apparmor/include/resource.h | 4 | ||||
-rw-r--r-- | security/apparmor/lib.c | 2 | ||||
-rw-r--r-- | security/apparmor/lsm.c | 2 | ||||
-rw-r--r-- | security/apparmor/path.c | 38 | ||||
-rw-r--r-- | security/apparmor/policy.c | 6 | ||||
-rw-r--r-- | security/apparmor/resource.c | 20 |
6 files changed, 31 insertions, 41 deletions
diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h index 3c88be946494..02baec732bb5 100644 --- a/security/apparmor/include/resource.h +++ b/security/apparmor/include/resource.h | |||
@@ -33,8 +33,8 @@ struct aa_rlimit { | |||
33 | }; | 33 | }; |
34 | 34 | ||
35 | int aa_map_resource(int resource); | 35 | int aa_map_resource(int resource); |
36 | int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, | 36 | int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *, |
37 | struct rlimit *new_rlim); | 37 | unsigned int resource, struct rlimit *new_rlim); |
38 | 38 | ||
39 | void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new); | 39 | void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new); |
40 | 40 | ||
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 6e85cdb4303f..506d2baf6147 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c | |||
@@ -40,6 +40,7 @@ char *aa_split_fqname(char *fqname, char **ns_name) | |||
40 | *ns_name = NULL; | 40 | *ns_name = NULL; |
41 | if (name[0] == ':') { | 41 | if (name[0] == ':') { |
42 | char *split = strchr(&name[1], ':'); | 42 | char *split = strchr(&name[1], ':'); |
43 | *ns_name = skip_spaces(&name[1]); | ||
43 | if (split) { | 44 | if (split) { |
44 | /* overwrite ':' with \0 */ | 45 | /* overwrite ':' with \0 */ |
45 | *split = 0; | 46 | *split = 0; |
@@ -47,7 +48,6 @@ char *aa_split_fqname(char *fqname, char **ns_name) | |||
47 | } else | 48 | } else |
48 | /* a ns name without a following profile is allowed */ | 49 | /* a ns name without a following profile is allowed */ |
49 | name = NULL; | 50 | name = NULL; |
50 | *ns_name = &name[1]; | ||
51 | } | 51 | } |
52 | if (name && *name == 0) | 52 | if (name && *name == 0) |
53 | name = NULL; | 53 | name = NULL; |
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f73e2c204218..cf1de4462ccd 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c | |||
@@ -614,7 +614,7 @@ static int apparmor_task_setrlimit(struct task_struct *task, | |||
614 | int error = 0; | 614 | int error = 0; |
615 | 615 | ||
616 | if (!unconfined(profile)) | 616 | if (!unconfined(profile)) |
617 | error = aa_task_setrlimit(profile, resource, new_rlim); | 617 | error = aa_task_setrlimit(profile, task, resource, new_rlim); |
618 | 618 | ||
619 | return error; | 619 | return error; |
620 | } | 620 | } |
diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 19358dc14605..82396050f186 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c | |||
@@ -59,8 +59,7 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, | |||
59 | { | 59 | { |
60 | struct path root, tmp; | 60 | struct path root, tmp; |
61 | char *res; | 61 | char *res; |
62 | int deleted, connected; | 62 | int connected, error = 0; |
63 | int error = 0; | ||
64 | 63 | ||
65 | /* Get the root we want to resolve too, released below */ | 64 | /* Get the root we want to resolve too, released below */ |
66 | if (flags & PATH_CHROOT_REL) { | 65 | if (flags & PATH_CHROOT_REL) { |
@@ -74,19 +73,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, | |||
74 | } | 73 | } |
75 | 74 | ||
76 | spin_lock(&dcache_lock); | 75 | spin_lock(&dcache_lock); |
77 | /* There is a race window between path lookup here and the | 76 | tmp = root; |
78 | * need to strip the " (deleted) string that __d_path applies | 77 | res = __d_path(path, &tmp, buf, buflen); |
79 | * Detect the race and relookup the path | ||
80 | * | ||
81 | * The stripping of (deleted) is a hack that could be removed | ||
82 | * with an updated __d_path | ||
83 | */ | ||
84 | do { | ||
85 | tmp = root; | ||
86 | deleted = d_unlinked(path->dentry); | ||
87 | res = __d_path(path, &tmp, buf, buflen); | ||
88 | |||
89 | } while (deleted != d_unlinked(path->dentry)); | ||
90 | spin_unlock(&dcache_lock); | 78 | spin_unlock(&dcache_lock); |
91 | 79 | ||
92 | *name = res; | 80 | *name = res; |
@@ -98,21 +86,17 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, | |||
98 | *name = buf; | 86 | *name = buf; |
99 | goto out; | 87 | goto out; |
100 | } | 88 | } |
101 | if (deleted) { | ||
102 | /* On some filesystems, newly allocated dentries appear to the | ||
103 | * security_path hooks as a deleted dentry except without an | ||
104 | * inode allocated. | ||
105 | * | ||
106 | * Remove the appended deleted text and return as string for | ||
107 | * normal mediation, or auditing. The (deleted) string is | ||
108 | * guaranteed to be added in this case, so just strip it. | ||
109 | */ | ||
110 | buf[buflen - 11] = 0; /* - (len(" (deleted)") +\0) */ | ||
111 | 89 | ||
112 | if (path->dentry->d_inode && !(flags & PATH_MEDIATE_DELETED)) { | 90 | /* Handle two cases: |
91 | * 1. A deleted dentry && profile is not allowing mediation of deleted | ||
92 | * 2. On some filesystems, newly allocated dentries appear to the | ||
93 | * security_path hooks as a deleted dentry except without an inode | ||
94 | * allocated. | ||
95 | */ | ||
96 | if (d_unlinked(path->dentry) && path->dentry->d_inode && | ||
97 | !(flags & PATH_MEDIATE_DELETED)) { | ||
113 | error = -ENOENT; | 98 | error = -ENOENT; |
114 | goto out; | 99 | goto out; |
115 | } | ||
116 | } | 100 | } |
117 | 101 | ||
118 | /* Determine if the path is connected to the expected root */ | 102 | /* Determine if the path is connected to the expected root */ |
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 3cdc1ad0787e..52cc865f1464 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c | |||
@@ -1151,12 +1151,14 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
1151 | /* released below */ | 1151 | /* released below */ |
1152 | ns = aa_get_namespace(root); | 1152 | ns = aa_get_namespace(root); |
1153 | 1153 | ||
1154 | write_lock(&ns->lock); | ||
1155 | if (!name) { | 1154 | if (!name) { |
1156 | /* remove namespace - can only happen if fqname[0] == ':' */ | 1155 | /* remove namespace - can only happen if fqname[0] == ':' */ |
1156 | write_lock(&ns->parent->lock); | ||
1157 | __remove_namespace(ns); | 1157 | __remove_namespace(ns); |
1158 | write_unlock(&ns->parent->lock); | ||
1158 | } else { | 1159 | } else { |
1159 | /* remove profile */ | 1160 | /* remove profile */ |
1161 | write_lock(&ns->lock); | ||
1160 | profile = aa_get_profile(__lookup_profile(&ns->base, name)); | 1162 | profile = aa_get_profile(__lookup_profile(&ns->base, name)); |
1161 | if (!profile) { | 1163 | if (!profile) { |
1162 | error = -ENOENT; | 1164 | error = -ENOENT; |
@@ -1165,8 +1167,8 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
1165 | } | 1167 | } |
1166 | name = profile->base.hname; | 1168 | name = profile->base.hname; |
1167 | __remove_profile(profile); | 1169 | __remove_profile(profile); |
1170 | write_unlock(&ns->lock); | ||
1168 | } | 1171 | } |
1169 | write_unlock(&ns->lock); | ||
1170 | 1172 | ||
1171 | /* don't fail removal if audit fails */ | 1173 | /* don't fail removal if audit fails */ |
1172 | (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); | 1174 | (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); |
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 4a368f1fd36d..a4136c10b1c6 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c | |||
@@ -72,6 +72,7 @@ int aa_map_resource(int resource) | |||
72 | /** | 72 | /** |
73 | * aa_task_setrlimit - test permission to set an rlimit | 73 | * aa_task_setrlimit - test permission to set an rlimit |
74 | * @profile - profile confining the task (NOT NULL) | 74 | * @profile - profile confining the task (NOT NULL) |
75 | * @task - task the resource is being set on | ||
75 | * @resource - the resource being set | 76 | * @resource - the resource being set |
76 | * @new_rlim - the new resource limit (NOT NULL) | 77 | * @new_rlim - the new resource limit (NOT NULL) |
77 | * | 78 | * |
@@ -79,18 +80,21 @@ int aa_map_resource(int resource) | |||
79 | * | 80 | * |
80 | * Returns: 0 or error code if setting resource failed | 81 | * Returns: 0 or error code if setting resource failed |
81 | */ | 82 | */ |
82 | int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, | 83 | int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, |
83 | struct rlimit *new_rlim) | 84 | unsigned int resource, struct rlimit *new_rlim) |
84 | { | 85 | { |
85 | int error = 0; | 86 | int error = 0; |
86 | 87 | ||
87 | if (profile->rlimits.mask & (1 << resource) && | 88 | /* TODO: extend resource control to handle other (non current) |
88 | new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) | 89 | * processes. AppArmor rules currently have the implicit assumption |
89 | 90 | * that the task is setting the resource of the current process | |
90 | error = audit_resource(profile, resource, new_rlim->rlim_max, | 91 | */ |
91 | -EACCES); | 92 | if ((task != current->group_leader) || |
93 | (profile->rlimits.mask & (1 << resource) && | ||
94 | new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) | ||
95 | error = -EACCES; | ||
92 | 96 | ||
93 | return error; | 97 | return audit_resource(profile, resource, new_rlim->rlim_max, error); |
94 | } | 98 | } |
95 | 99 | ||
96 | /** | 100 | /** |