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; 62*e819ff51SJohn Johansen int connected, error = 0; 63cdff2642SJohn Johansen 6444672e4fSNick Piggin /* Get the root we want to resolve too, released below */ 65cdff2642SJohn Johansen if (flags & PATH_CHROOT_REL) { 66cdff2642SJohn Johansen /* resolve paths relative to chroot */ 6744672e4fSNick Piggin get_fs_root(current->fs, &root); 68cdff2642SJohn Johansen } else { 69cdff2642SJohn Johansen /* resolve paths relative to namespace */ 70cdff2642SJohn Johansen root.mnt = current->nsproxy->mnt_ns->root; 71cdff2642SJohn Johansen root.dentry = root.mnt->mnt_root; 72cdff2642SJohn Johansen path_get(&root); 73cdff2642SJohn Johansen } 74cdff2642SJohn Johansen 75cdff2642SJohn Johansen spin_lock(&dcache_lock); 76cdff2642SJohn Johansen tmp = root; 77cdff2642SJohn Johansen res = __d_path(path, &tmp, buf, buflen); 78cdff2642SJohn Johansen spin_unlock(&dcache_lock); 79cdff2642SJohn Johansen 80cdff2642SJohn Johansen *name = res; 81cdff2642SJohn Johansen /* handle error conditions - and still allow a partial path to 82cdff2642SJohn Johansen * be returned. 83cdff2642SJohn Johansen */ 84cdff2642SJohn Johansen if (IS_ERR(res)) { 85cdff2642SJohn Johansen error = PTR_ERR(res); 86cdff2642SJohn Johansen *name = buf; 87cdff2642SJohn Johansen goto out; 88cdff2642SJohn Johansen } 89cdff2642SJohn Johansen 90*e819ff51SJohn Johansen /* Handle two cases: 91*e819ff51SJohn Johansen * 1. A deleted dentry && profile is not allowing mediation of deleted 92*e819ff51SJohn Johansen * 2. On some filesystems, newly allocated dentries appear to the 93*e819ff51SJohn Johansen * security_path hooks as a deleted dentry except without an inode 94*e819ff51SJohn Johansen * allocated. 95*e819ff51SJohn Johansen */ 96*e819ff51SJohn Johansen if (d_unlinked(path->dentry) && path->dentry->d_inode && 97*e819ff51SJohn Johansen !(flags & PATH_MEDIATE_DELETED)) { 98cdff2642SJohn Johansen error = -ENOENT; 99cdff2642SJohn Johansen goto out; 100cdff2642SJohn Johansen } 101cdff2642SJohn Johansen 102cdff2642SJohn Johansen /* Determine if the path is connected to the expected root */ 103cdff2642SJohn Johansen connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; 104cdff2642SJohn Johansen 105cdff2642SJohn Johansen /* If the path is not connected, 106cdff2642SJohn Johansen * check if it is a sysctl and handle specially else remove any 107cdff2642SJohn Johansen * leading / that __d_path may have returned. 108cdff2642SJohn Johansen * Unless 109cdff2642SJohn Johansen * specifically directed to connect the path, 110cdff2642SJohn Johansen * OR 111cdff2642SJohn Johansen * if in a chroot and doing chroot relative paths and the path 112cdff2642SJohn Johansen * resolves to the namespace root (would be connected outside 113cdff2642SJohn Johansen * of chroot) and specifically directed to connect paths to 114cdff2642SJohn Johansen * namespace root. 115cdff2642SJohn Johansen */ 116cdff2642SJohn Johansen if (!connected) { 117cdff2642SJohn Johansen /* is the disconnect path a sysctl? */ 118cdff2642SJohn Johansen if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && 119cdff2642SJohn Johansen strncmp(*name, "/sys/", 5) == 0) { 120cdff2642SJohn Johansen /* TODO: convert over to using a per namespace 121cdff2642SJohn Johansen * control instead of hard coded /proc 122cdff2642SJohn Johansen */ 123cdff2642SJohn Johansen error = prepend(name, *name - buf, "/proc", 5); 124cdff2642SJohn Johansen } else if (!(flags & PATH_CONNECT_PATH) && 125cdff2642SJohn Johansen !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && 126cdff2642SJohn Johansen (tmp.mnt == current->nsproxy->mnt_ns->root && 127cdff2642SJohn Johansen tmp.dentry == tmp.mnt->mnt_root))) { 128cdff2642SJohn Johansen /* disconnected path, don't return pathname starting 129cdff2642SJohn Johansen * with '/' 130cdff2642SJohn Johansen */ 131cdff2642SJohn Johansen error = -ESTALE; 132cdff2642SJohn Johansen if (*res == '/') 133cdff2642SJohn Johansen *name = res + 1; 134cdff2642SJohn Johansen } 135cdff2642SJohn Johansen } 136cdff2642SJohn Johansen 137cdff2642SJohn Johansen out: 138cdff2642SJohn Johansen path_put(&root); 139cdff2642SJohn Johansen 140cdff2642SJohn Johansen return error; 141cdff2642SJohn Johansen } 142cdff2642SJohn Johansen 143cdff2642SJohn Johansen /** 144cdff2642SJohn Johansen * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended 145cdff2642SJohn Johansen * @path: path to get name for (NOT NULL) 146cdff2642SJohn Johansen * @flags: flags controlling path lookup 147cdff2642SJohn Johansen * @buffer: buffer to put name in (NOT NULL) 148cdff2642SJohn Johansen * @size: size of buffer 149cdff2642SJohn Johansen * @name: Returns - contains position of path name in @buffer (NOT NULL) 150cdff2642SJohn Johansen * 151cdff2642SJohn Johansen * Returns: %0 else error on failure 152cdff2642SJohn Johansen */ 153cdff2642SJohn Johansen static int get_name_to_buffer(struct path *path, int flags, char *buffer, 154cdff2642SJohn Johansen int size, char **name) 155cdff2642SJohn Johansen { 156cdff2642SJohn Johansen int adjust = (flags & PATH_IS_DIR) ? 1 : 0; 157cdff2642SJohn Johansen int error = d_namespace_path(path, buffer, size - adjust, name, flags); 158cdff2642SJohn Johansen 159cdff2642SJohn Johansen if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') 160cdff2642SJohn Johansen /* 161cdff2642SJohn Johansen * Append "/" to the pathname. The root directory is a special 162cdff2642SJohn Johansen * case; it already ends in slash. 163cdff2642SJohn Johansen */ 164cdff2642SJohn Johansen strcpy(&buffer[size - 2], "/"); 165cdff2642SJohn Johansen 166cdff2642SJohn Johansen return error; 167cdff2642SJohn Johansen } 168cdff2642SJohn Johansen 169cdff2642SJohn Johansen /** 170cdff2642SJohn Johansen * aa_get_name - compute the pathname of a file 171cdff2642SJohn Johansen * @path: path the file (NOT NULL) 172cdff2642SJohn Johansen * @flags: flags controlling path name generation 173cdff2642SJohn Johansen * @buffer: buffer that aa_get_name() allocated (NOT NULL) 174cdff2642SJohn Johansen * @name: Returns - the generated path name if !error (NOT NULL) 175cdff2642SJohn Johansen * 176cdff2642SJohn Johansen * @name is a pointer to the beginning of the pathname (which usually differs 177cdff2642SJohn Johansen * from the beginning of the buffer), or NULL. If there is an error @name 178cdff2642SJohn Johansen * may contain a partial or invalid name that can be used for audit purposes, 179cdff2642SJohn Johansen * but it can not be used for mediation. 180cdff2642SJohn Johansen * 181cdff2642SJohn Johansen * We need PATH_IS_DIR to indicate whether the file is a directory or not 182cdff2642SJohn Johansen * because the file may not yet exist, and so we cannot check the inode's 183cdff2642SJohn Johansen * file type. 184cdff2642SJohn Johansen * 185cdff2642SJohn Johansen * Returns: %0 else error code if could retrieve name 186cdff2642SJohn Johansen */ 187cdff2642SJohn Johansen int aa_get_name(struct path *path, int flags, char **buffer, const char **name) 188cdff2642SJohn Johansen { 189cdff2642SJohn Johansen char *buf, *str = NULL; 190cdff2642SJohn Johansen int size = 256; 191cdff2642SJohn Johansen int error; 192cdff2642SJohn Johansen 193cdff2642SJohn Johansen *name = NULL; 194cdff2642SJohn Johansen *buffer = NULL; 195cdff2642SJohn Johansen for (;;) { 196cdff2642SJohn Johansen /* freed by caller */ 197cdff2642SJohn Johansen buf = kmalloc(size, GFP_KERNEL); 198cdff2642SJohn Johansen if (!buf) 199cdff2642SJohn Johansen return -ENOMEM; 200cdff2642SJohn Johansen 201cdff2642SJohn Johansen error = get_name_to_buffer(path, flags, buf, size, &str); 202cdff2642SJohn Johansen if (error != -ENAMETOOLONG) 203cdff2642SJohn Johansen break; 204cdff2642SJohn Johansen 205cdff2642SJohn Johansen kfree(buf); 206cdff2642SJohn Johansen size <<= 1; 207cdff2642SJohn Johansen if (size > aa_g_path_max) 208cdff2642SJohn Johansen return -ENAMETOOLONG; 209cdff2642SJohn Johansen } 210cdff2642SJohn Johansen *buffer = buf; 211cdff2642SJohn Johansen *name = str; 212cdff2642SJohn Johansen 213cdff2642SJohn Johansen return error; 214cdff2642SJohn Johansen } 215