1cdff2642SJohn Johansen /* 2cdff2642SJohn Johansen * AppArmor security module 3cdff2642SJohn Johansen * 4cdff2642SJohn Johansen * This file contains AppArmor function for pathnames 5cdff2642SJohn Johansen * 6cdff2642SJohn Johansen * Copyright (C) 1998-2008 Novell/SUSE 7cdff2642SJohn Johansen * Copyright 2009-2010 Canonical Ltd. 8cdff2642SJohn Johansen * 9cdff2642SJohn Johansen * This program is free software; you can redistribute it and/or 10cdff2642SJohn Johansen * modify it under the terms of the GNU General Public License as 11cdff2642SJohn Johansen * published by the Free Software Foundation, version 2 of the 12cdff2642SJohn Johansen * License. 13cdff2642SJohn Johansen */ 14cdff2642SJohn Johansen 15cdff2642SJohn Johansen #include <linux/magic.h> 16cdff2642SJohn Johansen #include <linux/mount.h> 17cdff2642SJohn Johansen #include <linux/namei.h> 18cdff2642SJohn Johansen #include <linux/nsproxy.h> 19cdff2642SJohn Johansen #include <linux/path.h> 20cdff2642SJohn Johansen #include <linux/sched.h> 21cdff2642SJohn Johansen #include <linux/slab.h> 22cdff2642SJohn Johansen #include <linux/fs_struct.h> 23cdff2642SJohn Johansen 24cdff2642SJohn Johansen #include "include/apparmor.h" 25cdff2642SJohn Johansen #include "include/path.h" 26cdff2642SJohn Johansen #include "include/policy.h" 27cdff2642SJohn Johansen 28cdff2642SJohn Johansen /* modified from dcache.c */ 29cdff2642SJohn Johansen static int prepend(char **buffer, int buflen, const char *str, int namelen) 30cdff2642SJohn Johansen { 31cdff2642SJohn Johansen buflen -= namelen; 32cdff2642SJohn Johansen if (buflen < 0) 33cdff2642SJohn Johansen return -ENAMETOOLONG; 34cdff2642SJohn Johansen *buffer -= namelen; 35cdff2642SJohn Johansen memcpy(*buffer, str, namelen); 36cdff2642SJohn Johansen return 0; 37cdff2642SJohn Johansen } 38cdff2642SJohn Johansen 39cdff2642SJohn Johansen #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) 40cdff2642SJohn Johansen 41bd35db8bSJohn Johansen /* If the path is not connected to the expected root, 42bd35db8bSJohn Johansen * check if it is a sysctl and handle specially else remove any 43bd35db8bSJohn Johansen * leading / that __d_path may have returned. 44bd35db8bSJohn Johansen * Unless 45bd35db8bSJohn Johansen * specifically directed to connect the path, 46bd35db8bSJohn Johansen * OR 47bd35db8bSJohn Johansen * if in a chroot and doing chroot relative paths and the path 48bd35db8bSJohn Johansen * resolves to the namespace root (would be connected outside 49bd35db8bSJohn Johansen * of chroot) and specifically directed to connect paths to 50bd35db8bSJohn Johansen * namespace root. 51bd35db8bSJohn Johansen */ 52bd35db8bSJohn Johansen static int disconnect(const struct path *path, char *buf, char **name, 53*72c8a768SJohn Johansen int flags, const char *disconnected) 54bd35db8bSJohn Johansen { 55bd35db8bSJohn Johansen int error = 0; 56bd35db8bSJohn Johansen 57bd35db8bSJohn Johansen if (!(flags & PATH_CONNECT_PATH) && 58bd35db8bSJohn Johansen !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && 59bd35db8bSJohn Johansen our_mnt(path->mnt))) { 60bd35db8bSJohn Johansen /* disconnected path, don't return pathname starting 61bd35db8bSJohn Johansen * with '/' 62bd35db8bSJohn Johansen */ 63bd35db8bSJohn Johansen error = -EACCES; 64bd35db8bSJohn Johansen if (**name == '/') 65bd35db8bSJohn Johansen *name = *name + 1; 66*72c8a768SJohn Johansen } else { 67*72c8a768SJohn Johansen if (**name != '/') 68bd35db8bSJohn Johansen /* CONNECT_PATH with missing root */ 69bd35db8bSJohn Johansen error = prepend(name, *name - buf, "/", 1); 70*72c8a768SJohn Johansen if (!error && disconnected) 71*72c8a768SJohn Johansen error = prepend(name, *name - buf, disconnected, 72*72c8a768SJohn Johansen strlen(disconnected)); 73*72c8a768SJohn Johansen } 74bd35db8bSJohn Johansen 75bd35db8bSJohn Johansen return error; 76bd35db8bSJohn Johansen } 77bd35db8bSJohn Johansen 78cdff2642SJohn Johansen /** 79cdff2642SJohn Johansen * d_namespace_path - lookup a name associated with a given path 80cdff2642SJohn Johansen * @path: path to lookup (NOT NULL) 81cdff2642SJohn Johansen * @buf: buffer to store path to (NOT NULL) 82cdff2642SJohn Johansen * @buflen: length of @buf 83cdff2642SJohn Johansen * @name: Returns - pointer for start of path name with in @buf (NOT NULL) 84cdff2642SJohn Johansen * @flags: flags controlling path lookup 85*72c8a768SJohn Johansen * @disconnected: string to prefix to disconnected paths 86cdff2642SJohn Johansen * 87cdff2642SJohn Johansen * Handle path name lookup. 88cdff2642SJohn Johansen * 89cdff2642SJohn Johansen * Returns: %0 else error code if path lookup fails 90cdff2642SJohn Johansen * When no error the path name is returned in @name which points to 91cdff2642SJohn Johansen * to a position in @buf 92cdff2642SJohn Johansen */ 932c7661ffSAl Viro static int d_namespace_path(const struct path *path, char *buf, int buflen, 94*72c8a768SJohn Johansen char **name, int flags, const char *disconnected) 95cdff2642SJohn Johansen { 96cdff2642SJohn Johansen char *res; 9702125a82SAl Viro int error = 0; 9802125a82SAl Viro int connected = 1; 99cdff2642SJohn Johansen 10002125a82SAl Viro if (path->mnt->mnt_flags & MNT_INTERNAL) { 10102125a82SAl Viro /* it's not mounted anywhere */ 10202125a82SAl Viro res = dentry_path(path->dentry, buf, buflen); 10302125a82SAl Viro *name = res; 10402125a82SAl Viro if (IS_ERR(res)) { 10502125a82SAl Viro *name = buf; 10602125a82SAl Viro return PTR_ERR(res); 10702125a82SAl Viro } 10802125a82SAl Viro if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC && 10902125a82SAl Viro strncmp(*name, "/sys/", 5) == 0) { 11002125a82SAl Viro /* TODO: convert over to using a per namespace 11102125a82SAl Viro * control instead of hard coded /proc 11202125a82SAl Viro */ 11302125a82SAl Viro return prepend(name, *name - buf, "/proc", 5); 114bd35db8bSJohn Johansen } else 115*72c8a768SJohn Johansen return disconnect(path, buf, name, flags, 116*72c8a768SJohn Johansen disconnected); 117cdff2642SJohn Johansen } 118cdff2642SJohn Johansen 11902125a82SAl Viro /* resolve paths relative to chroot?*/ 12002125a82SAl Viro if (flags & PATH_CHROOT_REL) { 12102125a82SAl Viro struct path root; 12202125a82SAl Viro get_fs_root(current->fs, &root); 12302125a82SAl Viro res = __d_path(path, &root, buf, buflen); 12402125a82SAl Viro path_put(&root); 1253372b68aSJohn Johansen } else { 12602125a82SAl Viro res = d_absolute_path(path, buf, buflen); 1273372b68aSJohn Johansen if (!our_mnt(path->mnt)) 1283372b68aSJohn Johansen connected = 0; 1293372b68aSJohn Johansen } 130cdff2642SJohn Johansen 131cdff2642SJohn Johansen /* handle error conditions - and still allow a partial path to 132cdff2642SJohn Johansen * be returned. 133cdff2642SJohn Johansen */ 1343372b68aSJohn Johansen if (!res || IS_ERR(res)) { 135cffee16eSJohn Johansen if (PTR_ERR(res) == -ENAMETOOLONG) 136cffee16eSJohn Johansen return -ENAMETOOLONG; 1373372b68aSJohn Johansen connected = 0; 138fbba8d89SJohn Johansen res = dentry_path_raw(path->dentry, buf, buflen); 139fbba8d89SJohn Johansen if (IS_ERR(res)) { 140cdff2642SJohn Johansen error = PTR_ERR(res); 141cdff2642SJohn Johansen *name = buf; 142cdff2642SJohn Johansen goto out; 143fbba8d89SJohn Johansen }; 144fbba8d89SJohn Johansen } else if (!our_mnt(path->mnt)) 14502125a82SAl Viro connected = 0; 146cdff2642SJohn Johansen 147fbba8d89SJohn Johansen *name = res; 148fbba8d89SJohn Johansen 149e819ff51SJohn Johansen /* Handle two cases: 150e819ff51SJohn Johansen * 1. A deleted dentry && profile is not allowing mediation of deleted 151e819ff51SJohn Johansen * 2. On some filesystems, newly allocated dentries appear to the 152e819ff51SJohn Johansen * security_path hooks as a deleted dentry except without an inode 153e819ff51SJohn Johansen * allocated. 154e819ff51SJohn Johansen */ 155729b8a3dSDavid Howells if (d_unlinked(path->dentry) && d_is_positive(path->dentry) && 156e819ff51SJohn Johansen !(flags & PATH_MEDIATE_DELETED)) { 157cdff2642SJohn Johansen error = -ENOENT; 158cdff2642SJohn Johansen goto out; 159cdff2642SJohn Johansen } 160cdff2642SJohn Johansen 161bd35db8bSJohn Johansen if (!connected) 162*72c8a768SJohn Johansen error = disconnect(path, buf, name, flags, disconnected); 163cdff2642SJohn Johansen 164cdff2642SJohn Johansen out: 165cdff2642SJohn Johansen return error; 166cdff2642SJohn Johansen } 167cdff2642SJohn Johansen 168cdff2642SJohn Johansen /** 169cdff2642SJohn Johansen * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended 170cdff2642SJohn Johansen * @path: path to get name for (NOT NULL) 171cdff2642SJohn Johansen * @flags: flags controlling path lookup 172cdff2642SJohn Johansen * @buffer: buffer to put name in (NOT NULL) 173cdff2642SJohn Johansen * @size: size of buffer 174cdff2642SJohn Johansen * @name: Returns - contains position of path name in @buffer (NOT NULL) 175cdff2642SJohn Johansen * 176cdff2642SJohn Johansen * Returns: %0 else error on failure 177cdff2642SJohn Johansen */ 1782c7661ffSAl Viro static int get_name_to_buffer(const struct path *path, int flags, char *buffer, 179*72c8a768SJohn Johansen int size, char **name, const char **info, 180*72c8a768SJohn Johansen const char *disconnected) 181cdff2642SJohn Johansen { 182cdff2642SJohn Johansen int adjust = (flags & PATH_IS_DIR) ? 1 : 0; 183*72c8a768SJohn Johansen int error = d_namespace_path(path, buffer, size - adjust, name, flags, 184*72c8a768SJohn Johansen disconnected); 185cdff2642SJohn Johansen 186cdff2642SJohn Johansen if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') 187cdff2642SJohn Johansen /* 188cdff2642SJohn Johansen * Append "/" to the pathname. The root directory is a special 189cdff2642SJohn Johansen * case; it already ends in slash. 190cdff2642SJohn Johansen */ 191cdff2642SJohn Johansen strcpy(&buffer[size - 2], "/"); 192cdff2642SJohn Johansen 19357fa1e18SJohn Johansen if (info && error) { 19457fa1e18SJohn Johansen if (error == -ENOENT) 19557fa1e18SJohn Johansen *info = "Failed name lookup - deleted entry"; 196e573cc30SJohn Johansen else if (error == -EACCES) 19757fa1e18SJohn Johansen *info = "Failed name lookup - disconnected path"; 19857fa1e18SJohn Johansen else if (error == -ENAMETOOLONG) 19957fa1e18SJohn Johansen *info = "Failed name lookup - name too long"; 20057fa1e18SJohn Johansen else 20157fa1e18SJohn Johansen *info = "Failed name lookup"; 20257fa1e18SJohn Johansen } 20357fa1e18SJohn Johansen 204cdff2642SJohn Johansen return error; 205cdff2642SJohn Johansen } 206cdff2642SJohn Johansen 207cdff2642SJohn Johansen /** 20857fa1e18SJohn Johansen * aa_path_name - compute the pathname of a file 209cdff2642SJohn Johansen * @path: path the file (NOT NULL) 210cdff2642SJohn Johansen * @flags: flags controlling path name generation 211cdff2642SJohn Johansen * @buffer: buffer that aa_get_name() allocated (NOT NULL) 212cdff2642SJohn Johansen * @name: Returns - the generated path name if !error (NOT NULL) 21357fa1e18SJohn Johansen * @info: Returns - information on why the path lookup failed (MAYBE NULL) 214*72c8a768SJohn Johansen * @disconnected: string to prepend to disconnected paths 215cdff2642SJohn Johansen * 216cdff2642SJohn Johansen * @name is a pointer to the beginning of the pathname (which usually differs 217cdff2642SJohn Johansen * from the beginning of the buffer), or NULL. If there is an error @name 218cdff2642SJohn Johansen * may contain a partial or invalid name that can be used for audit purposes, 219cdff2642SJohn Johansen * but it can not be used for mediation. 220cdff2642SJohn Johansen * 221cdff2642SJohn Johansen * We need PATH_IS_DIR to indicate whether the file is a directory or not 222cdff2642SJohn Johansen * because the file may not yet exist, and so we cannot check the inode's 223cdff2642SJohn Johansen * file type. 224cdff2642SJohn Johansen * 225cdff2642SJohn Johansen * Returns: %0 else error code if could retrieve name 226cdff2642SJohn Johansen */ 2272c7661ffSAl Viro int aa_path_name(const struct path *path, int flags, char **buffer, 228*72c8a768SJohn Johansen const char **name, const char **info, const char *disconnected) 229cdff2642SJohn Johansen { 230cdff2642SJohn Johansen char *buf, *str = NULL; 231cdff2642SJohn Johansen int size = 256; 232cdff2642SJohn Johansen int error; 233cdff2642SJohn Johansen 234cdff2642SJohn Johansen *name = NULL; 235cdff2642SJohn Johansen *buffer = NULL; 236cdff2642SJohn Johansen for (;;) { 237cdff2642SJohn Johansen /* freed by caller */ 238cdff2642SJohn Johansen buf = kmalloc(size, GFP_KERNEL); 239cdff2642SJohn Johansen if (!buf) 240cdff2642SJohn Johansen return -ENOMEM; 241cdff2642SJohn Johansen 242*72c8a768SJohn Johansen error = get_name_to_buffer(path, flags, buf, size, &str, info, 243*72c8a768SJohn Johansen disconnected); 244cdff2642SJohn Johansen if (error != -ENAMETOOLONG) 245cdff2642SJohn Johansen break; 246cdff2642SJohn Johansen 247cdff2642SJohn Johansen kfree(buf); 248cdff2642SJohn Johansen size <<= 1; 249cdff2642SJohn Johansen if (size > aa_g_path_max) 250cdff2642SJohn Johansen return -ENAMETOOLONG; 25157fa1e18SJohn Johansen *info = NULL; 252cdff2642SJohn Johansen } 253cdff2642SJohn Johansen *buffer = buf; 254cdff2642SJohn Johansen *name = str; 255cdff2642SJohn Johansen 256cdff2642SJohn Johansen return error; 257cdff2642SJohn Johansen } 258