| 1 | |
| 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 |
|
| 25 | static int prepend(char **buffer, int buflen, const char *str, int namelen)
|
| 26 | {
|
| 27 | buflen -= namelen;
|
| 28 | if (buflen < 0)
|
| 29 | return -ENAMETOOLONG;
|
| 30 | *buffer -= namelen;
|
| 31 | memcpy(*buffer, str, namelen);
|
| 32 | return 0;
|
| 33 | }
|
| 34 |
|
| 35 | #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT)
|
| 36 |
|
| 37 | |
| 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 | static int disconnect(const struct path *path, char *buf, char **name,
|
| 49 | int flags, const char *disconnected)
|
| 50 | {
|
| 51 | int error = 0;
|
| 52 |
|
| 53 | if (!(flags & PATH_CONNECT_PATH) &&
|
| 54 | !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
|
| 55 | our_mnt(path->mnt))) {
|
| 56 | |
| 57 | * with '/'
|
| 58 |
|
| 59 | error = -EACCES;
|
| 60 | if (**name == '/')
|
| 61 | *name = *name + 1;
|
| 62 | } else {
|
| 63 | if (**name != '/')
|
| 64 |
|
| 65 | error = prepend(name, *name - buf, "/", 1);
|
| 66 | if (!error && disconnected)
|
| 67 | error = prepend(name, *name - buf, disconnected,
|
| 68 | strlen(disconnected));
|
| 69 | }
|
| 70 |
|
| 71 | 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 | * a position in @buf
|
| 87 |
|
| 88 | 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 | int isdir = (flags & PATH_IS_DIR) ? 1 : 0;
|
| 95 | int buflen = aa_g_path_max - isdir;
|
| 96 |
|
| 97 | if (path->mnt->mnt_flags & MNT_INTERNAL) {
|
| 98 |
|
| 99 | res = dentry_path(path->dentry, buf, buflen);
|
| 100 | *name = res;
|
| 101 | if (IS_ERR(res)) {
|
| 102 | *name = buf;
|
| 103 | return PTR_ERR(res);
|
| 104 | }
|
| 105 | if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC &&
|
| 106 | strncmp(*name, "/sys/", 5) == 0) {
|
| 107 | |
| 108 | * control instead of hard coded /proc
|
| 109 |
|
| 110 | error = prepend(name, *name - buf, "/proc", 5);
|
| 111 | goto out;
|
| 112 | } else
|
| 113 | error = disconnect(path, buf, name, flags,
|
| 114 | disconnected);
|
| 115 | goto out;
|
| 116 | }
|
| 117 |
|
| 118 |
|
| 119 | if (flags & PATH_CHROOT_REL) {
|
| 120 | struct path root;
|
| 121 | get_fs_root(current->fs, &root);
|
| 122 | res = __d_path(path, &root, buf, buflen);
|
| 123 | path_put(&root);
|
| 124 | } else {
|
| 125 | res = d_absolute_path(path, buf, buflen);
|
| 126 | if (!our_mnt(path->mnt))
|
| 127 | connected = 0;
|
| 128 | }
|
| 129 |
|
| 130 | |
| 131 | * be returned.
|
| 132 |
|
| 133 | if (IS_ERR_OR_NULL(res)) {
|
| 134 | if (PTR_ERR(res) == -ENAMETOOLONG) {
|
| 135 | error = -ENAMETOOLONG;
|
| 136 | *name = buf;
|
| 137 | goto out;
|
| 138 | }
|
| 139 | connected = 0;
|
| 140 | res = dentry_path_raw(path->dentry, buf, buflen);
|
| 141 | if (IS_ERR(res)) {
|
| 142 | error = PTR_ERR(res);
|
| 143 | *name = buf;
|
| 144 | goto out;
|
| 145 | }
|
| 146 | } else if (!our_mnt(path->mnt))
|
| 147 | connected = 0;
|
| 148 |
|
| 149 | *name = res;
|
| 150 |
|
| 151 | if (!connected)
|
| 152 | error = disconnect(path, buf, name, flags, disconnected);
|
| 153 |
|
| 154 | |
| 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 | if (d_unlinked(path->dentry) && d_is_positive(path->dentry) &&
|
| 161 | !(flags & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))) {
|
| 162 | error = -ENOENT;
|
| 163 | goto out;
|
| 164 | }
|
| 165 |
|
| 166 | out:
|
| 167 | |
| 168 | * root "/" which already ends in a slash.
|
| 169 |
|
| 170 | if (!error && isdir) {
|
| 171 | bool is_root = (*name)[0] == '/' && (*name)[1] == '\0';
|
| 172 |
|
| 173 | if (!is_root) {
|
| 174 | buf[aa_g_path_max - 2] = '/';
|
| 175 | buf[aa_g_path_max - 1] = '\0';
|
| 176 | }
|
| 177 | }
|
| 178 |
|
| 179 | return error;
|
| 180 | }
|
| 181 |
|
| 182 | |
| 183 | * aa_path_name - get the pathname to a buffer ensure dir / is appended
|
| 184 | * @path: path the file (NOT NULL)
|
| 185 | * @flags: flags controlling path name generation
|
| 186 | * @buffer: buffer to put name in (NOT NULL)
|
| 187 | * @name: Returns - the generated path name if !error (NOT NULL)
|
| 188 | * @info: Returns - information on why the path lookup failed (MAYBE NULL)
|
| 189 | * @disconnected: string to prepend to disconnected paths
|
| 190 | *
|
| 191 | * @name is a pointer to the beginning of the pathname (which usually differs
|
| 192 | * from the beginning of the buffer), or NULL. If there is an error @name
|
| 193 | * may contain a partial or invalid name that can be used for audit purposes,
|
| 194 | * but it can not be used for mediation.
|
| 195 | *
|
| 196 | * We need PATH_IS_DIR to indicate whether the file is a directory or not
|
| 197 | * because the file may not yet exist, and so we cannot check the inode's
|
| 198 | * file type.
|
| 199 | *
|
| 200 | * Returns: %0 else error code if could retrieve name
|
| 201 |
|
| 202 | int aa_path_name(const struct path *path, int flags, char *buffer,
|
| 203 | const char **name, const char **info, const char *disconnected)
|
| 204 | {
|
| 205 | char *str = NULL;
|
| 206 | int error = d_namespace_path(path, buffer, &str, flags, disconnected);
|
| 207 |
|
| 208 | if (info && error) {
|
| 209 | if (error == -ENOENT)
|
| 210 | *info = "Failed name lookup - deleted entry";
|
| 211 | else if (error == -EACCES)
|
| 212 | *info = "Failed name lookup - disconnected path";
|
| 213 | else if (error == -ENAMETOOLONG)
|
| 214 | *info = "Failed name lookup - name too long";
|
| 215 | else
|
| 216 | *info = "Failed name lookup";
|
| 217 | }
|
| 218 |
|
| 219 | *name = str;
|
| 220 |
|
| 221 | return error;
|
| 222 | }
|