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/mnt_namespace.h> 17cdff2642SJohn Johansen #include <linux/mount.h> 18cdff2642SJohn Johansen #include <linux/namei.h> 19cdff2642SJohn Johansen #include <linux/nsproxy.h> 20cdff2642SJohn Johansen #include <linux/path.h> 21cdff2642SJohn Johansen #include <linux/sched.h> 22cdff2642SJohn Johansen #include <linux/slab.h> 23cdff2642SJohn Johansen #include <linux/fs_struct.h> 24cdff2642SJohn Johansen 25cdff2642SJohn Johansen #include "include/apparmor.h" 26cdff2642SJohn Johansen #include "include/path.h" 27cdff2642SJohn Johansen #include "include/policy.h" 28cdff2642SJohn Johansen 29cdff2642SJohn Johansen 30cdff2642SJohn Johansen /* modified from dcache.c */ 31cdff2642SJohn Johansen static int prepend(char **buffer, int buflen, const char *str, int namelen) 32cdff2642SJohn Johansen { 33cdff2642SJohn Johansen buflen -= namelen; 34cdff2642SJohn Johansen if (buflen < 0) 35cdff2642SJohn Johansen return -ENAMETOOLONG; 36cdff2642SJohn Johansen *buffer -= namelen; 37cdff2642SJohn Johansen memcpy(*buffer, str, namelen); 38cdff2642SJohn Johansen return 0; 39cdff2642SJohn Johansen } 40cdff2642SJohn Johansen 41cdff2642SJohn Johansen #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) 42cdff2642SJohn Johansen 43cdff2642SJohn Johansen /** 44cdff2642SJohn Johansen * d_namespace_path - lookup a name associated with a given path 45cdff2642SJohn Johansen * @path: path to lookup (NOT NULL) 46cdff2642SJohn Johansen * @buf: buffer to store path to (NOT NULL) 47cdff2642SJohn Johansen * @buflen: length of @buf 48cdff2642SJohn Johansen * @name: Returns - pointer for start of path name with in @buf (NOT NULL) 49cdff2642SJohn Johansen * @flags: flags controlling path lookup 50cdff2642SJohn Johansen * 51cdff2642SJohn Johansen * Handle path name lookup. 52cdff2642SJohn Johansen * 53cdff2642SJohn Johansen * Returns: %0 else error code if path lookup fails 54cdff2642SJohn Johansen * When no error the path name is returned in @name which points to 55cdff2642SJohn Johansen * to a position in @buf 56cdff2642SJohn Johansen */ 57cdff2642SJohn Johansen static int d_namespace_path(struct path *path, char *buf, int buflen, 58cdff2642SJohn Johansen char **name, int flags) 59cdff2642SJohn Johansen { 60cdff2642SJohn Johansen struct path root, tmp; 61cdff2642SJohn Johansen char *res; 62cdff2642SJohn Johansen int deleted, connected; 63cdff2642SJohn Johansen int error = 0; 64cdff2642SJohn Johansen 65*44672e4fSNick Piggin /* Get the root we want to resolve too, released below */ 66cdff2642SJohn Johansen if (flags & PATH_CHROOT_REL) { 67cdff2642SJohn Johansen /* resolve paths relative to chroot */ 68*44672e4fSNick Piggin get_fs_root(current->fs, &root); 69cdff2642SJohn Johansen } else { 70cdff2642SJohn Johansen /* resolve paths relative to namespace */ 71cdff2642SJohn Johansen root.mnt = current->nsproxy->mnt_ns->root; 72cdff2642SJohn Johansen root.dentry = root.mnt->mnt_root; 73cdff2642SJohn Johansen path_get(&root); 74cdff2642SJohn Johansen } 75cdff2642SJohn Johansen 76cdff2642SJohn Johansen spin_lock(&dcache_lock); 77cdff2642SJohn Johansen /* There is a race window between path lookup here and the 78cdff2642SJohn Johansen * need to strip the " (deleted) string that __d_path applies 79cdff2642SJohn Johansen * Detect the race and relookup the path 80cdff2642SJohn Johansen * 81cdff2642SJohn Johansen * The stripping of (deleted) is a hack that could be removed 82cdff2642SJohn Johansen * with an updated __d_path 83cdff2642SJohn Johansen */ 84cdff2642SJohn Johansen do { 85cdff2642SJohn Johansen tmp = root; 86cdff2642SJohn Johansen deleted = d_unlinked(path->dentry); 87cdff2642SJohn Johansen res = __d_path(path, &tmp, buf, buflen); 88cdff2642SJohn Johansen 89cdff2642SJohn Johansen } while (deleted != d_unlinked(path->dentry)); 90cdff2642SJohn Johansen spin_unlock(&dcache_lock); 91cdff2642SJohn Johansen 92cdff2642SJohn Johansen *name = res; 93cdff2642SJohn Johansen /* handle error conditions - and still allow a partial path to 94cdff2642SJohn Johansen * be returned. 95cdff2642SJohn Johansen */ 96cdff2642SJohn Johansen if (IS_ERR(res)) { 97cdff2642SJohn Johansen error = PTR_ERR(res); 98cdff2642SJohn Johansen *name = buf; 99cdff2642SJohn Johansen goto out; 100cdff2642SJohn Johansen } 101cdff2642SJohn Johansen if (deleted) { 102cdff2642SJohn Johansen /* On some filesystems, newly allocated dentries appear to the 103cdff2642SJohn Johansen * security_path hooks as a deleted dentry except without an 104cdff2642SJohn Johansen * inode allocated. 105cdff2642SJohn Johansen * 106cdff2642SJohn Johansen * Remove the appended deleted text and return as string for 107cdff2642SJohn Johansen * normal mediation, or auditing. The (deleted) string is 108cdff2642SJohn Johansen * guaranteed to be added in this case, so just strip it. 109cdff2642SJohn Johansen */ 110cdff2642SJohn Johansen buf[buflen - 11] = 0; /* - (len(" (deleted)") +\0) */ 111cdff2642SJohn Johansen 112cdff2642SJohn Johansen if (path->dentry->d_inode && !(flags & PATH_MEDIATE_DELETED)) { 113cdff2642SJohn Johansen error = -ENOENT; 114cdff2642SJohn Johansen goto out; 115cdff2642SJohn Johansen } 116cdff2642SJohn Johansen } 117cdff2642SJohn Johansen 118cdff2642SJohn Johansen /* Determine if the path is connected to the expected root */ 119cdff2642SJohn Johansen connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; 120cdff2642SJohn Johansen 121cdff2642SJohn Johansen /* If the path is not connected, 122cdff2642SJohn Johansen * check if it is a sysctl and handle specially else remove any 123cdff2642SJohn Johansen * leading / that __d_path may have returned. 124cdff2642SJohn Johansen * Unless 125cdff2642SJohn Johansen * specifically directed to connect the path, 126cdff2642SJohn Johansen * OR 127cdff2642SJohn Johansen * if in a chroot and doing chroot relative paths and the path 128cdff2642SJohn Johansen * resolves to the namespace root (would be connected outside 129cdff2642SJohn Johansen * of chroot) and specifically directed to connect paths to 130cdff2642SJohn Johansen * namespace root. 131cdff2642SJohn Johansen */ 132cdff2642SJohn Johansen if (!connected) { 133cdff2642SJohn Johansen /* is the disconnect path a sysctl? */ 134cdff2642SJohn Johansen if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && 135cdff2642SJohn Johansen strncmp(*name, "/sys/", 5) == 0) { 136cdff2642SJohn Johansen /* TODO: convert over to using a per namespace 137cdff2642SJohn Johansen * control instead of hard coded /proc 138cdff2642SJohn Johansen */ 139cdff2642SJohn Johansen error = prepend(name, *name - buf, "/proc", 5); 140cdff2642SJohn Johansen } else if (!(flags & PATH_CONNECT_PATH) && 141cdff2642SJohn Johansen !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && 142cdff2642SJohn Johansen (tmp.mnt == current->nsproxy->mnt_ns->root && 143cdff2642SJohn Johansen tmp.dentry == tmp.mnt->mnt_root))) { 144cdff2642SJohn Johansen /* disconnected path, don't return pathname starting 145cdff2642SJohn Johansen * with '/' 146cdff2642SJohn Johansen */ 147cdff2642SJohn Johansen error = -ESTALE; 148cdff2642SJohn Johansen if (*res == '/') 149cdff2642SJohn Johansen *name = res + 1; 150cdff2642SJohn Johansen } 151cdff2642SJohn Johansen } 152cdff2642SJohn Johansen 153cdff2642SJohn Johansen out: 154cdff2642SJohn Johansen path_put(&root); 155cdff2642SJohn Johansen 156cdff2642SJohn Johansen return error; 157cdff2642SJohn Johansen } 158cdff2642SJohn Johansen 159cdff2642SJohn Johansen /** 160cdff2642SJohn Johansen * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended 161cdff2642SJohn Johansen * @path: path to get name for (NOT NULL) 162cdff2642SJohn Johansen * @flags: flags controlling path lookup 163cdff2642SJohn Johansen * @buffer: buffer to put name in (NOT NULL) 164cdff2642SJohn Johansen * @size: size of buffer 165cdff2642SJohn Johansen * @name: Returns - contains position of path name in @buffer (NOT NULL) 166cdff2642SJohn Johansen * 167cdff2642SJohn Johansen * Returns: %0 else error on failure 168cdff2642SJohn Johansen */ 169cdff2642SJohn Johansen static int get_name_to_buffer(struct path *path, int flags, char *buffer, 170cdff2642SJohn Johansen int size, char **name) 171cdff2642SJohn Johansen { 172cdff2642SJohn Johansen int adjust = (flags & PATH_IS_DIR) ? 1 : 0; 173cdff2642SJohn Johansen int error = d_namespace_path(path, buffer, size - adjust, name, flags); 174cdff2642SJohn Johansen 175cdff2642SJohn Johansen if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') 176cdff2642SJohn Johansen /* 177cdff2642SJohn Johansen * Append "/" to the pathname. The root directory is a special 178cdff2642SJohn Johansen * case; it already ends in slash. 179cdff2642SJohn Johansen */ 180cdff2642SJohn Johansen strcpy(&buffer[size - 2], "/"); 181cdff2642SJohn Johansen 182cdff2642SJohn Johansen return error; 183cdff2642SJohn Johansen } 184cdff2642SJohn Johansen 185cdff2642SJohn Johansen /** 186cdff2642SJohn Johansen * aa_get_name - compute the pathname of a file 187cdff2642SJohn Johansen * @path: path the file (NOT NULL) 188cdff2642SJohn Johansen * @flags: flags controlling path name generation 189cdff2642SJohn Johansen * @buffer: buffer that aa_get_name() allocated (NOT NULL) 190cdff2642SJohn Johansen * @name: Returns - the generated path name if !error (NOT NULL) 191cdff2642SJohn Johansen * 192cdff2642SJohn Johansen * @name is a pointer to the beginning of the pathname (which usually differs 193cdff2642SJohn Johansen * from the beginning of the buffer), or NULL. If there is an error @name 194cdff2642SJohn Johansen * may contain a partial or invalid name that can be used for audit purposes, 195cdff2642SJohn Johansen * but it can not be used for mediation. 196cdff2642SJohn Johansen * 197cdff2642SJohn Johansen * We need PATH_IS_DIR to indicate whether the file is a directory or not 198cdff2642SJohn Johansen * because the file may not yet exist, and so we cannot check the inode's 199cdff2642SJohn Johansen * file type. 200cdff2642SJohn Johansen * 201cdff2642SJohn Johansen * Returns: %0 else error code if could retrieve name 202cdff2642SJohn Johansen */ 203cdff2642SJohn Johansen int aa_get_name(struct path *path, int flags, char **buffer, const char **name) 204cdff2642SJohn Johansen { 205cdff2642SJohn Johansen char *buf, *str = NULL; 206cdff2642SJohn Johansen int size = 256; 207cdff2642SJohn Johansen int error; 208cdff2642SJohn Johansen 209cdff2642SJohn Johansen *name = NULL; 210cdff2642SJohn Johansen *buffer = NULL; 211cdff2642SJohn Johansen for (;;) { 212cdff2642SJohn Johansen /* freed by caller */ 213cdff2642SJohn Johansen buf = kmalloc(size, GFP_KERNEL); 214cdff2642SJohn Johansen if (!buf) 215cdff2642SJohn Johansen return -ENOMEM; 216cdff2642SJohn Johansen 217cdff2642SJohn Johansen error = get_name_to_buffer(path, flags, buf, size, &str); 218cdff2642SJohn Johansen if (error != -ENAMETOOLONG) 219cdff2642SJohn Johansen break; 220cdff2642SJohn Johansen 221cdff2642SJohn Johansen kfree(buf); 222cdff2642SJohn Johansen size <<= 1; 223cdff2642SJohn Johansen if (size > aa_g_path_max) 224cdff2642SJohn Johansen return -ENAMETOOLONG; 225cdff2642SJohn Johansen } 226cdff2642SJohn Johansen *buffer = buf; 227cdff2642SJohn Johansen *name = str; 228cdff2642SJohn Johansen 229cdff2642SJohn Johansen return error; 230cdff2642SJohn Johansen } 231