root/security/apparmor/path.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. prepend
  2. disconnect
  3. d_namespace_path
  4. aa_path_name

   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         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 /* 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 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                 /* disconnected path, don't return pathname starting
  57                  * with '/'
  58                  */
  59                 error = -EACCES;
  60                 if (**name == '/')
  61                         *name = *name + 1;
  62         } else {
  63                 if (**name != '/')
  64                         /* CONNECT_PATH with missing root */
  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  *          to 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                 /* it's not mounted anywhere */
  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                         /* TODO: convert over to using a per namespace
 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         /* resolve paths relative to chroot?*/
 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         /* handle error conditions - and still allow a partial path to
 131          * be returned.
 132          */
 133         if (!res || IS_ERR(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         /* 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         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          * Append "/" to the pathname.  The root directory is a special
 169          * case; it already ends in slash.
 170          */
 171         if (!error && isdir && ((*name)[1] != '\0' || (*name)[0] != '/'))
 172                 strcpy(&buf[aa_g_path_max - 2], "/");
 173 
 174         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 int aa_path_name(const struct path *path, int flags, char *buffer,
 198                  const char **name, const char **info, const char *disconnected)
 199 {
 200         char *str = NULL;
 201         int error = d_namespace_path(path, buffer, &str, flags, disconnected);
 202 
 203         if (info && error) {
 204                 if (error == -ENOENT)
 205                         *info = "Failed name lookup - deleted entry";
 206                 else if (error == -EACCES)
 207                         *info = "Failed name lookup - disconnected path";
 208                 else if (error == -ENAMETOOLONG)
 209                         *info = "Failed name lookup - name too long";
 210                 else
 211                         *info = "Failed name lookup";
 212         }
 213 
 214         *name = str;
 215 
 216         return error;
 217 }

/* [<][>][^][v][top][bottom][index][help] */