diff options
Diffstat (limited to 'security/apparmor/path.c')
| -rw-r--r-- | security/apparmor/path.c | 102 | 
1 files changed, 63 insertions, 39 deletions
diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 36cc0cc39e7..35b394a75d7 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -13,7 +13,6 @@   */  #include <linux/magic.h> -#include <linux/mnt_namespace.h>  #include <linux/mount.h>  #include <linux/namei.h>  #include <linux/nsproxy.h> @@ -57,33 +56,57 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen)  static int d_namespace_path(struct path *path, char *buf, int buflen,  			    char **name, int flags)  { -	struct path root, tmp;  	char *res; -	int connected, error = 0; +	int error = 0; +	int connected = 1; + +	if (path->mnt->mnt_flags & MNT_INTERNAL) { +		/* it's not mounted anywhere */ +		res = dentry_path(path->dentry, buf, buflen); +		*name = res; +		if (IS_ERR(res)) { +			*name = buf; +			return PTR_ERR(res); +		} +		if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC && +		    strncmp(*name, "/sys/", 5) == 0) { +			/* TODO: convert over to using a per namespace +			 * control instead of hard coded /proc +			 */ +			return prepend(name, *name - buf, "/proc", 5); +		} +		return 0; +	} -	/* Get the root we want to resolve too, released below */ +	/* resolve paths relative to chroot?*/  	if (flags & PATH_CHROOT_REL) { -		/* resolve paths relative to chroot */ +		struct path root;  		get_fs_root(current->fs, &root); +		res = __d_path(path, &root, buf, buflen); +		path_put(&root);  	} else { -		/* resolve paths relative to namespace */ -		root.mnt = current->nsproxy->mnt_ns->root; -		root.dentry = root.mnt->mnt_root; -		path_get(&root); +		res = d_absolute_path(path, buf, buflen); +		if (!our_mnt(path->mnt)) +			connected = 0;  	} -	tmp = root; -	res = __d_path(path, &tmp, buf, buflen); - -	*name = res;  	/* handle error conditions - and still allow a partial path to  	 * be returned.  	 */ -	if (IS_ERR(res)) { -		error = PTR_ERR(res); -		*name = buf; -		goto out; -	} +	if (!res || IS_ERR(res)) { +		if (PTR_ERR(res) == -ENAMETOOLONG) +			return -ENAMETOOLONG; +		connected = 0; +		res = dentry_path_raw(path->dentry, buf, buflen); +		if (IS_ERR(res)) { +			error = PTR_ERR(res); +			*name = buf; +			goto out; +		}; +	} else if (!our_mnt(path->mnt)) +		connected = 0; + +	*name = res;  	/* Handle two cases:  	 * 1. A deleted dentry && profile is not allowing mediation of deleted @@ -97,10 +120,7 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,  			goto out;  	} -	/* Determine if the path is connected to the expected root */ -	connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; - -	/* If the path is not connected, +	/* If the path is not connected to the expected root,  	 * check if it is a sysctl and handle specially else remove any  	 * leading / that __d_path may have returned.  	 * Unless @@ -112,29 +132,19 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,  	 *     namespace root.  	 */  	if (!connected) { -		/* is the disconnect path a sysctl? */ -		if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && -		    strncmp(*name, "/sys/", 5) == 0) { -			/* TODO: convert over to using a per namespace -			 * control instead of hard coded /proc -			 */ -			error = prepend(name, *name - buf, "/proc", 5); -		} else if (!(flags & PATH_CONNECT_PATH) && +		if (!(flags & PATH_CONNECT_PATH) &&  			   !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && -			     (tmp.mnt == current->nsproxy->mnt_ns->root && -			      tmp.dentry == tmp.mnt->mnt_root))) { +			     our_mnt(path->mnt))) {  			/* disconnected path, don't return pathname starting  			 * with '/'  			 */ -			error = -ESTALE; +			error = -EACCES;  			if (*res == '/')  				*name = res + 1;  		}  	}  out: -	path_put(&root); -  	return error;  } @@ -149,7 +159,7 @@ out:   * Returns: %0 else error on failure   */  static int get_name_to_buffer(struct path *path, int flags, char *buffer, -			      int size, char **name) +			      int size, char **name, const char **info)  {  	int adjust = (flags & PATH_IS_DIR) ? 1 : 0;  	int error = d_namespace_path(path, buffer, size - adjust, name, flags); @@ -161,15 +171,27 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer,  		 */  		strcpy(&buffer[size - 2], "/"); +	if (info && error) { +		if (error == -ENOENT) +			*info = "Failed name lookup - deleted entry"; +		else if (error == -EACCES) +			*info = "Failed name lookup - disconnected path"; +		else if (error == -ENAMETOOLONG) +			*info = "Failed name lookup - name too long"; +		else +			*info = "Failed name lookup"; +	} +  	return error;  }  /** - * aa_get_name - compute the pathname of a file + * aa_path_name - compute the pathname of a file   * @path: path the file  (NOT NULL)   * @flags: flags controlling path name generation   * @buffer: buffer that aa_get_name() allocated  (NOT NULL)   * @name: Returns - the generated path name if !error (NOT NULL) + * @info: Returns - information on why the path lookup failed (MAYBE NULL)   *   * @name is a pointer to the beginning of the pathname (which usually differs   * from the beginning of the buffer), or NULL.  If there is an error @name @@ -182,7 +204,8 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer,   *   * Returns: %0 else error code if could retrieve name   */ -int aa_get_name(struct path *path, int flags, char **buffer, const char **name) +int aa_path_name(struct path *path, int flags, char **buffer, const char **name, +		 const char **info)  {  	char *buf, *str = NULL;  	int size = 256; @@ -196,7 +219,7 @@ int aa_get_name(struct path *path, int flags, char **buffer, const char **name)  		if (!buf)  			return -ENOMEM; -		error = get_name_to_buffer(path, flags, buf, size, &str); +		error = get_name_to_buffer(path, flags, buf, size, &str, info);  		if (error != -ENAMETOOLONG)  			break; @@ -204,6 +227,7 @@ int aa_get_name(struct path *path, int flags, char **buffer, const char **name)  		size <<= 1;  		if (size > aa_g_path_max)  			return -ENAMETOOLONG; +		*info = NULL;  	}  	*buffer = buf;  	*name = str;  | 
