summaryrefslogtreecommitdiff
path: root/security/apparmor
diff options
context:
space:
mode:
Diffstat (limited to 'security/apparmor')
-rw-r--r--security/apparmor/.gitignore1
-rw-r--r--security/apparmor/apparmorfs.c13
-rw-r--r--security/apparmor/include/resource.h4
-rw-r--r--security/apparmor/lib.c2
-rw-r--r--security/apparmor/lsm.c6
-rw-r--r--security/apparmor/path.c47
-rw-r--r--security/apparmor/policy.c6
-rw-r--r--security/apparmor/resource.c20
8 files changed, 45 insertions, 54 deletions
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore
index 0a0a99f3b083..4d995aeaebc0 100644
--- a/security/apparmor/.gitignore
+++ b/security/apparmor/.gitignore
@@ -3,3 +3,4 @@
#
af_names.h
capability_names.h
+rlim_names.h
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 7320331b44ab..0848292982a2 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -29,7 +29,7 @@
* aa_simple_write_to_buffer - common routine for getting policy from user
* @op: operation doing the user buffer copy
* @userbuf: user buffer to copy data from (NOT NULL)
- * @alloc_size: size of user buffer
+ * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size)
* @copy_size: size of data to copy from user buffer
* @pos: position write is at in the file (NOT NULL)
*
@@ -42,6 +42,8 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf,
{
char *data;
+ BUG_ON(copy_size > alloc_size);
+
if (*pos != 0)
/* only writes from pos 0, that is complete writes */
return ERR_PTR(-ESPIPE);
@@ -86,7 +88,8 @@ static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
}
static const struct file_operations aa_fs_profile_load = {
- .write = profile_load
+ .write = profile_load,
+ .llseek = default_llseek,
};
/* .replace file hook fn to load and/or replace policy */
@@ -107,7 +110,8 @@ static ssize_t profile_replace(struct file *f, const char __user *buf,
}
static const struct file_operations aa_fs_profile_replace = {
- .write = profile_replace
+ .write = profile_replace,
+ .llseek = default_llseek,
};
/* .remove file hook fn to remove loaded policy */
@@ -134,7 +138,8 @@ static ssize_t profile_remove(struct file *f, const char __user *buf,
}
static const struct file_operations aa_fs_profile_remove = {
- .write = profile_remove
+ .write = profile_remove,
+ .llseek = default_llseek,
};
/** Base file system setup **/
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 {
};
int aa_map_resource(int resource);
-int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
- struct rlimit *new_rlim);
+int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *,
+ unsigned int resource, struct rlimit *new_rlim);
void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new);
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)
*ns_name = NULL;
if (name[0] == ':') {
char *split = strchr(&name[1], ':');
+ *ns_name = skip_spaces(&name[1]);
if (split) {
/* overwrite ':' with \0 */
*split = 0;
@@ -47,7 +48,6 @@ char *aa_split_fqname(char *fqname, char **ns_name)
} else
/* a ns name without a following profile is allowed */
name = NULL;
- *ns_name = &name[1];
}
if (name && *name == 0)
name = NULL;
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index d5666d3cc21b..cf1de4462ccd 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -607,14 +607,14 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
return error;
}
-static int apparmor_task_setrlimit(unsigned int resource,
- struct rlimit *new_rlim)
+static int apparmor_task_setrlimit(struct task_struct *task,
+ unsigned int resource, struct rlimit *new_rlim)
{
struct aa_profile *profile = aa_current_profile();
int error = 0;
if (!unconfined(profile))
- error = aa_task_setrlimit(profile, resource, new_rlim);
+ error = aa_task_setrlimit(profile, task, resource, new_rlim);
return error;
}
diff --git a/security/apparmor/path.c b/security/apparmor/path.c
index 96bab9469d48..82396050f186 100644
--- a/security/apparmor/path.c
+++ b/security/apparmor/path.c
@@ -59,39 +59,22 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
{
struct path root, tmp;
char *res;
- int deleted, connected;
- int error = 0;
+ int connected, error = 0;
- /* Get the root we want to resolve too */
+ /* Get the root we want to resolve too, released below */
if (flags & PATH_CHROOT_REL) {
/* resolve paths relative to chroot */
- read_lock(&current->fs->lock);
- root = current->fs->root;
- /* released below */
- path_get(&root);
- read_unlock(&current->fs->lock);
+ get_fs_root(current->fs, &root);
} else {
/* resolve paths relative to namespace */
root.mnt = current->nsproxy->mnt_ns->root;
root.dentry = root.mnt->mnt_root;
- /* released below */
path_get(&root);
}
spin_lock(&dcache_lock);
- /* There is a race window between path lookup here and the
- * need to strip the " (deleted) string that __d_path applies
- * Detect the race and relookup the path
- *
- * The stripping of (deleted) is a hack that could be removed
- * with an updated __d_path
- */
- do {
- tmp = root;
- deleted = d_unlinked(path->dentry);
- res = __d_path(path, &tmp, buf, buflen);
-
- } while (deleted != d_unlinked(path->dentry));
+ tmp = root;
+ res = __d_path(path, &tmp, buf, buflen);
spin_unlock(&dcache_lock);
*name = res;
@@ -103,21 +86,17 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
*name = buf;
goto out;
}
- if (deleted) {
- /* On some filesystems, newly allocated dentries appear to the
- * security_path hooks as a deleted dentry except without an
- * inode allocated.
- *
- * Remove the appended deleted text and return as string for
- * normal mediation, or auditing. The (deleted) string is
- * guaranteed to be added in this case, so just strip it.
- */
- buf[buflen - 11] = 0; /* - (len(" (deleted)") +\0) */
- if (path->dentry->d_inode && !(flags & PATH_MEDIATE_DELETED)) {
+ /* Handle two cases:
+ * 1. A deleted dentry && profile is not allowing mediation of deleted
+ * 2. On some filesystems, newly allocated dentries appear to the
+ * security_path hooks as a deleted dentry except without an inode
+ * allocated.
+ */
+ if (d_unlinked(path->dentry) && path->dentry->d_inode &&
+ !(flags & PATH_MEDIATE_DELETED)) {
error = -ENOENT;
goto out;
- }
}
/* 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)
/* released below */
ns = aa_get_namespace(root);
- write_lock(&ns->lock);
if (!name) {
/* remove namespace - can only happen if fqname[0] == ':' */
+ write_lock(&ns->parent->lock);
__remove_namespace(ns);
+ write_unlock(&ns->parent->lock);
} else {
/* remove profile */
+ write_lock(&ns->lock);
profile = aa_get_profile(__lookup_profile(&ns->base, name));
if (!profile) {
error = -ENOENT;
@@ -1165,8 +1167,8 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
}
name = profile->base.hname;
__remove_profile(profile);
+ write_unlock(&ns->lock);
}
- write_unlock(&ns->lock);
/* don't fail removal if audit fails */
(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)
/**
* aa_task_setrlimit - test permission to set an rlimit
* @profile - profile confining the task (NOT NULL)
+ * @task - task the resource is being set on
* @resource - the resource being set
* @new_rlim - the new resource limit (NOT NULL)
*
@@ -79,18 +80,21 @@ int aa_map_resource(int resource)
*
* Returns: 0 or error code if setting resource failed
*/
-int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
- struct rlimit *new_rlim)
+int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task,
+ unsigned int resource, struct rlimit *new_rlim)
{
int error = 0;
- if (profile->rlimits.mask & (1 << resource) &&
- new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)
-
- error = audit_resource(profile, resource, new_rlim->rlim_max,
- -EACCES);
+ /* TODO: extend resource control to handle other (non current)
+ * processes. AppArmor rules currently have the implicit assumption
+ * that the task is setting the resource of the current process
+ */
+ if ((task != current->group_leader) ||
+ (profile->rlimits.mask & (1 << resource) &&
+ new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max))
+ error = -EACCES;
- return error;
+ return audit_resource(profile, resource, new_rlim->rlim_max, error);
}
/**