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