Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only 2 : : /* 3 : : * AppArmor security module 4 : : * 5 : : * This file contains AppArmor function for pathnames 6 : : * 7 : : * Copyright (C) 1998-2008 Novell/SUSE 8 : : * Copyright 2009-2010 Canonical Ltd. 9 : : */ 10 : : 11 : : #include <linux/magic.h> 12 : : #include <linux/mount.h> 13 : : #include <linux/namei.h> 14 : : #include <linux/nsproxy.h> 15 : : #include <linux/path.h> 16 : : #include <linux/sched.h> 17 : : #include <linux/slab.h> 18 : : #include <linux/fs_struct.h> 19 : : 20 : : #include "include/apparmor.h" 21 : : #include "include/path.h" 22 : : #include "include/policy.h" 23 : : 24 : : /* modified from dcache.c */ 25 : : static int prepend(char **buffer, int buflen, const char *str, int namelen) 26 : : { 27 : 0 : buflen -= namelen; 28 : 0 : if (buflen < 0) 29 : : return -ENAMETOOLONG; 30 : 0 : *buffer -= namelen; 31 : 0 : memcpy(*buffer, str, namelen); 32 : : return 0; 33 : : } 34 : : 35 : : #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) 36 : : 37 : : /* If the path is not connected to the expected root, 38 : : * check if it is a sysctl and handle specially else remove any 39 : : * leading / that __d_path may have returned. 40 : : * Unless 41 : : * specifically directed to connect the path, 42 : : * OR 43 : : * if in a chroot and doing chroot relative paths and the path 44 : : * resolves to the namespace root (would be connected outside 45 : : * of chroot) and specifically directed to connect paths to 46 : : * namespace root. 47 : : */ 48 : 0 : static int disconnect(const struct path *path, char *buf, char **name, 49 : : int flags, const char *disconnected) 50 : : { 51 : : int error = 0; 52 : : 53 : 0 : if (!(flags & PATH_CONNECT_PATH) && 54 : 0 : !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && 55 : 0 : our_mnt(path->mnt))) { 56 : : /* disconnected path, don't return pathname starting 57 : : * with '/' 58 : : */ 59 : : error = -EACCES; 60 : 0 : if (**name == '/') 61 : 0 : *name = *name + 1; 62 : : } else { 63 : 0 : if (**name != '/') 64 : : /* CONNECT_PATH with missing root */ 65 : 0 : error = prepend(name, *name - buf, "/", 1); 66 : 0 : if (!error && disconnected) 67 : 0 : error = prepend(name, *name - buf, disconnected, 68 : 0 : strlen(disconnected)); 69 : : } 70 : : 71 : 0 : return error; 72 : : } 73 : : 74 : : /** 75 : : * d_namespace_path - lookup a name associated with a given path 76 : : * @path: path to lookup (NOT NULL) 77 : : * @buf: buffer to store path to (NOT NULL) 78 : : * @name: Returns - pointer for start of path name with in @buf (NOT NULL) 79 : : * @flags: flags controlling path lookup 80 : : * @disconnected: string to prefix to disconnected paths 81 : : * 82 : : * Handle path name lookup. 83 : : * 84 : : * Returns: %0 else error code if path lookup fails 85 : : * When no error the path name is returned in @name which points to 86 : : * to a position in @buf 87 : : */ 88 : 0 : static int d_namespace_path(const struct path *path, char *buf, char **name, 89 : : int flags, const char *disconnected) 90 : : { 91 : : char *res; 92 : : int error = 0; 93 : : int connected = 1; 94 : 0 : int isdir = (flags & PATH_IS_DIR) ? 1 : 0; 95 : 0 : int buflen = aa_g_path_max - isdir; 96 : : 97 : 0 : if (path->mnt->mnt_flags & MNT_INTERNAL) { 98 : : /* it's not mounted anywhere */ 99 : 0 : res = dentry_path(path->dentry, buf, buflen); 100 : 0 : *name = res; 101 : 0 : if (IS_ERR(res)) { 102 : 0 : *name = buf; 103 : 0 : return PTR_ERR(res); 104 : : } 105 : 0 : if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC && 106 : 0 : strncmp(*name, "/sys/", 5) == 0) { 107 : : /* TODO: convert over to using a per namespace 108 : : * control instead of hard coded /proc 109 : : */ 110 : 0 : error = prepend(name, *name - buf, "/proc", 5); 111 : 0 : goto out; 112 : : } else 113 : 0 : error = disconnect(path, buf, name, flags, 114 : : disconnected); 115 : 0 : goto out; 116 : : } 117 : : 118 : : /* resolve paths relative to chroot?*/ 119 : 0 : if (flags & PATH_CHROOT_REL) { 120 : : struct path root; 121 : 0 : get_fs_root(current->fs, &root); 122 : 0 : res = __d_path(path, &root, buf, buflen); 123 : 0 : path_put(&root); 124 : : } else { 125 : 0 : res = d_absolute_path(path, buf, buflen); 126 : 0 : if (!our_mnt(path->mnt)) 127 : : connected = 0; 128 : : } 129 : : 130 : : /* handle error conditions - and still allow a partial path to 131 : : * be returned. 132 : : */ 133 : 0 : if (!res || IS_ERR(res)) { 134 : 0 : if (PTR_ERR(res) == -ENAMETOOLONG) { 135 : : error = -ENAMETOOLONG; 136 : 0 : *name = buf; 137 : 0 : goto out; 138 : : } 139 : : connected = 0; 140 : 0 : res = dentry_path_raw(path->dentry, buf, buflen); 141 : 0 : if (IS_ERR(res)) { 142 : : error = PTR_ERR(res); 143 : 0 : *name = buf; 144 : 0 : goto out; 145 : : }; 146 : 0 : } else if (!our_mnt(path->mnt)) 147 : : connected = 0; 148 : : 149 : 0 : *name = res; 150 : : 151 : 0 : if (!connected) 152 : 0 : error = disconnect(path, buf, name, flags, disconnected); 153 : : 154 : : /* Handle two cases: 155 : : * 1. A deleted dentry && profile is not allowing mediation of deleted 156 : : * 2. On some filesystems, newly allocated dentries appear to the 157 : : * security_path hooks as a deleted dentry except without an inode 158 : : * allocated. 159 : : */ 160 : 0 : if (d_unlinked(path->dentry) && d_is_positive(path->dentry) && 161 : 0 : !(flags & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))) { 162 : : error = -ENOENT; 163 : 0 : goto out; 164 : : } 165 : : 166 : : out: 167 : : /* 168 : : * Append "/" to the pathname. The root directory is a special 169 : : * case; it already ends in slash. 170 : : */ 171 : 0 : if (!error && isdir && ((*name)[1] != '\0' || (*name)[0] != '/')) 172 : 0 : strcpy(&buf[aa_g_path_max - 2], "/"); 173 : : 174 : 0 : return error; 175 : : } 176 : : 177 : : /** 178 : : * aa_path_name - get the pathname to a buffer ensure dir / is appended 179 : : * @path: path the file (NOT NULL) 180 : : * @flags: flags controlling path name generation 181 : : * @buffer: buffer to put name in (NOT NULL) 182 : : * @name: Returns - the generated path name if !error (NOT NULL) 183 : : * @info: Returns - information on why the path lookup failed (MAYBE NULL) 184 : : * @disconnected: string to prepend to disconnected paths 185 : : * 186 : : * @name is a pointer to the beginning of the pathname (which usually differs 187 : : * from the beginning of the buffer), or NULL. If there is an error @name 188 : : * may contain a partial or invalid name that can be used for audit purposes, 189 : : * but it can not be used for mediation. 190 : : * 191 : : * We need PATH_IS_DIR to indicate whether the file is a directory or not 192 : : * because the file may not yet exist, and so we cannot check the inode's 193 : : * file type. 194 : : * 195 : : * Returns: %0 else error code if could retrieve name 196 : : */ 197 : 0 : int aa_path_name(const struct path *path, int flags, char *buffer, 198 : : const char **name, const char **info, const char *disconnected) 199 : : { 200 : 0 : char *str = NULL; 201 : 0 : int error = d_namespace_path(path, buffer, &str, flags, disconnected); 202 : : 203 : 0 : if (info && error) { 204 : 0 : if (error == -ENOENT) 205 : 0 : *info = "Failed name lookup - deleted entry"; 206 : 0 : else if (error == -EACCES) 207 : 0 : *info = "Failed name lookup - disconnected path"; 208 : 0 : else if (error == -ENAMETOOLONG) 209 : 0 : *info = "Failed name lookup - name too long"; 210 : : else 211 : 0 : *info = "Failed name lookup"; 212 : : } 213 : : 214 : 0 : *name = str; 215 : : 216 : 0 : return error; 217 : : }