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 41*bd35db8bSJohn Johansen /* If the path is not connected to the expected root, 42*bd35db8bSJohn Johansen * check if it is a sysctl and handle specially else remove any 43*bd35db8bSJohn Johansen * leading / that __d_path may have returned. 44*bd35db8bSJohn Johansen * Unless 45*bd35db8bSJohn Johansen * specifically directed to connect the path, 46*bd35db8bSJohn Johansen * OR 47*bd35db8bSJohn Johansen * if in a chroot and doing chroot relative paths and the path 48*bd35db8bSJohn Johansen * resolves to the namespace root (would be connected outside 49*bd35db8bSJohn Johansen * of chroot) and specifically directed to connect paths to 50*bd35db8bSJohn Johansen * namespace root. 51*bd35db8bSJohn Johansen */ 52*bd35db8bSJohn Johansen static int disconnect(const struct path *path, char *buf, char **name, 53*bd35db8bSJohn Johansen int flags) 54*bd35db8bSJohn Johansen { 55*bd35db8bSJohn Johansen int error = 0; 56*bd35db8bSJohn Johansen 57*bd35db8bSJohn Johansen if (!(flags & PATH_CONNECT_PATH) && 58*bd35db8bSJohn Johansen !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && 59*bd35db8bSJohn Johansen our_mnt(path->mnt))) { 60*bd35db8bSJohn Johansen /* disconnected path, don't return pathname starting 61*bd35db8bSJohn Johansen * with '/' 62*bd35db8bSJohn Johansen */ 63*bd35db8bSJohn Johansen error = -EACCES; 64*bd35db8bSJohn Johansen if (**name == '/') 65*bd35db8bSJohn Johansen *name = *name + 1; 66*bd35db8bSJohn Johansen } else if (**name != '/') 67*bd35db8bSJohn Johansen /* CONNECT_PATH with missing root */ 68*bd35db8bSJohn Johansen error = prepend(name, *name - buf, "/", 1); 69*bd35db8bSJohn Johansen 70*bd35db8bSJohn Johansen return error; 71*bd35db8bSJohn Johansen } 72*bd35db8bSJohn Johansen 73cdff2642SJohn Johansen /** 74cdff2642SJohn Johansen * d_namespace_path - lookup a name associated with a given path 75cdff2642SJohn Johansen * @path: path to lookup (NOT NULL) 76cdff2642SJohn Johansen * @buf: buffer to store path to (NOT NULL) 77cdff2642SJohn Johansen * @buflen: length of @buf 78cdff2642SJohn Johansen * @name: Returns - pointer for start of path name with in @buf (NOT NULL) 79cdff2642SJohn Johansen * @flags: flags controlling path lookup 80cdff2642SJohn Johansen * 81cdff2642SJohn Johansen * Handle path name lookup. 82cdff2642SJohn Johansen * 83cdff2642SJohn Johansen * Returns: %0 else error code if path lookup fails 84cdff2642SJohn Johansen * When no error the path name is returned in @name which points to 85cdff2642SJohn Johansen * to a position in @buf 86cdff2642SJohn Johansen */ 872c7661ffSAl Viro static int d_namespace_path(const struct path *path, char *buf, int buflen, 88cdff2642SJohn Johansen char **name, int flags) 89cdff2642SJohn Johansen { 90cdff2642SJohn Johansen char *res; 9102125a82SAl Viro int error = 0; 9202125a82SAl Viro int connected = 1; 93cdff2642SJohn Johansen 9402125a82SAl Viro if (path->mnt->mnt_flags & MNT_INTERNAL) { 9502125a82SAl Viro /* it's not mounted anywhere */ 9602125a82SAl Viro res = dentry_path(path->dentry, buf, buflen); 9702125a82SAl Viro *name = res; 9802125a82SAl Viro if (IS_ERR(res)) { 9902125a82SAl Viro *name = buf; 10002125a82SAl Viro return PTR_ERR(res); 10102125a82SAl Viro } 10202125a82SAl Viro if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC && 10302125a82SAl Viro strncmp(*name, "/sys/", 5) == 0) { 10402125a82SAl Viro /* TODO: convert over to using a per namespace 10502125a82SAl Viro * control instead of hard coded /proc 10602125a82SAl Viro */ 10702125a82SAl Viro return prepend(name, *name - buf, "/proc", 5); 108*bd35db8bSJohn Johansen } else 109*bd35db8bSJohn Johansen return disconnect(path, buf, name, flags); 11002125a82SAl Viro return 0; 111cdff2642SJohn Johansen } 112cdff2642SJohn Johansen 11302125a82SAl Viro /* resolve paths relative to chroot?*/ 11402125a82SAl Viro if (flags & PATH_CHROOT_REL) { 11502125a82SAl Viro struct path root; 11602125a82SAl Viro get_fs_root(current->fs, &root); 11702125a82SAl Viro res = __d_path(path, &root, buf, buflen); 11802125a82SAl Viro path_put(&root); 1193372b68aSJohn Johansen } else { 12002125a82SAl Viro res = d_absolute_path(path, buf, buflen); 1213372b68aSJohn Johansen if (!our_mnt(path->mnt)) 1223372b68aSJohn Johansen connected = 0; 1233372b68aSJohn Johansen } 124cdff2642SJohn Johansen 125cdff2642SJohn Johansen /* handle error conditions - and still allow a partial path to 126cdff2642SJohn Johansen * be returned. 127cdff2642SJohn Johansen */ 1283372b68aSJohn Johansen if (!res || IS_ERR(res)) { 129cffee16eSJohn Johansen if (PTR_ERR(res) == -ENAMETOOLONG) 130cffee16eSJohn Johansen return -ENAMETOOLONG; 1313372b68aSJohn Johansen connected = 0; 132fbba8d89SJohn Johansen res = dentry_path_raw(path->dentry, buf, buflen); 133fbba8d89SJohn Johansen if (IS_ERR(res)) { 134cdff2642SJohn Johansen error = PTR_ERR(res); 135cdff2642SJohn Johansen *name = buf; 136cdff2642SJohn Johansen goto out; 137fbba8d89SJohn Johansen }; 138fbba8d89SJohn Johansen } else if (!our_mnt(path->mnt)) 13902125a82SAl Viro connected = 0; 140cdff2642SJohn Johansen 141fbba8d89SJohn Johansen *name = res; 142fbba8d89SJohn Johansen 143e819ff51SJohn Johansen /* Handle two cases: 144e819ff51SJohn Johansen * 1. A deleted dentry && profile is not allowing mediation of deleted 145e819ff51SJohn Johansen * 2. On some filesystems, newly allocated dentries appear to the 146e819ff51SJohn Johansen * security_path hooks as a deleted dentry except without an inode 147e819ff51SJohn Johansen * allocated. 148e819ff51SJohn Johansen */ 149729b8a3dSDavid Howells if (d_unlinked(path->dentry) && d_is_positive(path->dentry) && 150e819ff51SJohn Johansen !(flags & PATH_MEDIATE_DELETED)) { 151cdff2642SJohn Johansen error = -ENOENT; 152cdff2642SJohn Johansen goto out; 153cdff2642SJohn Johansen } 154cdff2642SJohn Johansen 155*bd35db8bSJohn Johansen if (!connected) 156*bd35db8bSJohn Johansen error = disconnect(path, buf, name, flags); 157cdff2642SJohn Johansen 158cdff2642SJohn Johansen out: 159cdff2642SJohn Johansen return error; 160cdff2642SJohn Johansen } 161cdff2642SJohn Johansen 162cdff2642SJohn Johansen /** 163cdff2642SJohn Johansen * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended 164cdff2642SJohn Johansen * @path: path to get name for (NOT NULL) 165cdff2642SJohn Johansen * @flags: flags controlling path lookup 166cdff2642SJohn Johansen * @buffer: buffer to put name in (NOT NULL) 167cdff2642SJohn Johansen * @size: size of buffer 168cdff2642SJohn Johansen * @name: Returns - contains position of path name in @buffer (NOT NULL) 169cdff2642SJohn Johansen * 170cdff2642SJohn Johansen * Returns: %0 else error on failure 171cdff2642SJohn Johansen */ 1722c7661ffSAl Viro static int get_name_to_buffer(const struct path *path, int flags, char *buffer, 17357fa1e18SJohn Johansen int size, char **name, const char **info) 174cdff2642SJohn Johansen { 175cdff2642SJohn Johansen int adjust = (flags & PATH_IS_DIR) ? 1 : 0; 176cdff2642SJohn Johansen int error = d_namespace_path(path, buffer, size - adjust, name, flags); 177cdff2642SJohn Johansen 178cdff2642SJohn Johansen if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') 179cdff2642SJohn Johansen /* 180cdff2642SJohn Johansen * Append "/" to the pathname. The root directory is a special 181cdff2642SJohn Johansen * case; it already ends in slash. 182cdff2642SJohn Johansen */ 183cdff2642SJohn Johansen strcpy(&buffer[size - 2], "/"); 184cdff2642SJohn Johansen 18557fa1e18SJohn Johansen if (info && error) { 18657fa1e18SJohn Johansen if (error == -ENOENT) 18757fa1e18SJohn Johansen *info = "Failed name lookup - deleted entry"; 188e573cc30SJohn Johansen else if (error == -EACCES) 18957fa1e18SJohn Johansen *info = "Failed name lookup - disconnected path"; 19057fa1e18SJohn Johansen else if (error == -ENAMETOOLONG) 19157fa1e18SJohn Johansen *info = "Failed name lookup - name too long"; 19257fa1e18SJohn Johansen else 19357fa1e18SJohn Johansen *info = "Failed name lookup"; 19457fa1e18SJohn Johansen } 19557fa1e18SJohn Johansen 196cdff2642SJohn Johansen return error; 197cdff2642SJohn Johansen } 198cdff2642SJohn Johansen 199cdff2642SJohn Johansen /** 20057fa1e18SJohn Johansen * aa_path_name - compute the pathname of a file 201cdff2642SJohn Johansen * @path: path the file (NOT NULL) 202cdff2642SJohn Johansen * @flags: flags controlling path name generation 203cdff2642SJohn Johansen * @buffer: buffer that aa_get_name() allocated (NOT NULL) 204cdff2642SJohn Johansen * @name: Returns - the generated path name if !error (NOT NULL) 20557fa1e18SJohn Johansen * @info: Returns - information on why the path lookup failed (MAYBE NULL) 206cdff2642SJohn Johansen * 207cdff2642SJohn Johansen * @name is a pointer to the beginning of the pathname (which usually differs 208cdff2642SJohn Johansen * from the beginning of the buffer), or NULL. If there is an error @name 209cdff2642SJohn Johansen * may contain a partial or invalid name that can be used for audit purposes, 210cdff2642SJohn Johansen * but it can not be used for mediation. 211cdff2642SJohn Johansen * 212cdff2642SJohn Johansen * We need PATH_IS_DIR to indicate whether the file is a directory or not 213cdff2642SJohn Johansen * because the file may not yet exist, and so we cannot check the inode's 214cdff2642SJohn Johansen * file type. 215cdff2642SJohn Johansen * 216cdff2642SJohn Johansen * Returns: %0 else error code if could retrieve name 217cdff2642SJohn Johansen */ 2182c7661ffSAl Viro int aa_path_name(const struct path *path, int flags, char **buffer, 2192c7661ffSAl Viro const char **name, const char **info) 220cdff2642SJohn Johansen { 221cdff2642SJohn Johansen char *buf, *str = NULL; 222cdff2642SJohn Johansen int size = 256; 223cdff2642SJohn Johansen int error; 224cdff2642SJohn Johansen 225cdff2642SJohn Johansen *name = NULL; 226cdff2642SJohn Johansen *buffer = NULL; 227cdff2642SJohn Johansen for (;;) { 228cdff2642SJohn Johansen /* freed by caller */ 229cdff2642SJohn Johansen buf = kmalloc(size, GFP_KERNEL); 230cdff2642SJohn Johansen if (!buf) 231cdff2642SJohn Johansen return -ENOMEM; 232cdff2642SJohn Johansen 23357fa1e18SJohn Johansen error = get_name_to_buffer(path, flags, buf, size, &str, info); 234cdff2642SJohn Johansen if (error != -ENAMETOOLONG) 235cdff2642SJohn Johansen break; 236cdff2642SJohn Johansen 237cdff2642SJohn Johansen kfree(buf); 238cdff2642SJohn Johansen size <<= 1; 239cdff2642SJohn Johansen if (size > aa_g_path_max) 240cdff2642SJohn Johansen return -ENAMETOOLONG; 24157fa1e18SJohn Johansen *info = NULL; 242cdff2642SJohn Johansen } 243cdff2642SJohn Johansen *buffer = buf; 244cdff2642SJohn Johansen *name = str; 245cdff2642SJohn Johansen 246cdff2642SJohn Johansen return error; 247cdff2642SJohn Johansen } 248