// SPDX-License-Identifier: GPL-2.0-only1/*2* AppArmor security module3*4* This file contains AppArmor function for pathnames5*6* Copyright (C) 1998-2008 Novell/SUSE7* Copyright 2009-2010 Canonical Ltd.8*/910#include <linux/magic.h>11#include <linux/mount.h>12#include <linux/namei.h>13#include <linux/nsproxy.h>14#include <linux/path.h>15#include <linux/sched.h>16#include <linux/slab.h>17#include <linux/fs_struct.h>1819#include "include/apparmor.h"20#include "include/path.h"21#include "include/policy.h"2223/* modified from dcache.c */24static int prepend(char **buffer, int buflen, const char *str, int namelen)25{26buflen -= namelen;27if (buflen < 0)28return -ENAMETOOLONG;29*buffer -= namelen;30memcpy(*buffer, str, namelen);31return 0;32}3334#define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT)3536/* If the path is not connected to the expected root,37* check if it is a sysctl and handle specially else remove any38* leading / that __d_path may have returned.39* Unless40* specifically directed to connect the path,41* OR42* if in a chroot and doing chroot relative paths and the path43* resolves to the namespace root (would be connected outside44* of chroot) and specifically directed to connect paths to45* namespace root.46*/47static int disconnect(const struct path *path, char *buf, char **name,48int flags, const char *disconnected)49{50int error = 0;5152if (!(flags & PATH_CONNECT_PATH) &&53!(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&54our_mnt(path->mnt))) {55/* disconnected path, don't return pathname starting56* with '/'57*/58error = -EACCES;59if (**name == '/')60*name = *name + 1;61} else {62if (**name != '/')63/* CONNECT_PATH with missing root */64error = prepend(name, *name - buf, "/", 1);65if (!error && disconnected)66error = prepend(name, *name - buf, disconnected,67strlen(disconnected));68}6970return error;71}7273/**74* d_namespace_path - lookup a name associated with a given path75* @path: path to lookup (NOT NULL)76* @buf: buffer to store path to (NOT NULL)77* @name: Returns - pointer for start of path name with in @buf (NOT NULL)78* @flags: flags controlling path lookup79* @disconnected: string to prefix to disconnected paths80*81* Handle path name lookup.82*83* Returns: %0 else error code if path lookup fails84* When no error the path name is returned in @name which points to85* a position in @buf86*/87static int d_namespace_path(const struct path *path, char *buf, char **name,88int flags, const char *disconnected)89{90char *res;91int error = 0;92int connected = 1;93int isdir = (flags & PATH_IS_DIR) ? 1 : 0;94int buflen = aa_g_path_max - isdir;9596if (path->mnt->mnt_flags & MNT_INTERNAL) {97/* it's not mounted anywhere */98res = dentry_path(path->dentry, buf, buflen);99*name = res;100if (IS_ERR(res)) {101*name = buf;102return PTR_ERR(res);103}104if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC &&105strncmp(*name, "/sys/", 5) == 0) {106/* TODO: convert over to using a per namespace107* control instead of hard coded /proc108*/109error = prepend(name, *name - buf, "/proc", 5);110goto out;111} else112error = disconnect(path, buf, name, flags,113disconnected);114goto out;115}116117/* resolve paths relative to chroot?*/118if (flags & PATH_CHROOT_REL) {119struct path root;120get_fs_root(current->fs, &root);121res = __d_path(path, &root, buf, buflen);122path_put(&root);123} else {124res = d_absolute_path(path, buf, buflen);125if (!our_mnt(path->mnt))126connected = 0;127}128129/* handle error conditions - and still allow a partial path to130* be returned.131*/132if (IS_ERR_OR_NULL(res)) {133if (PTR_ERR(res) == -ENAMETOOLONG) {134error = -ENAMETOOLONG;135*name = buf;136goto out;137}138connected = 0;139res = dentry_path_raw(path->dentry, buf, buflen);140if (IS_ERR(res)) {141error = PTR_ERR(res);142*name = buf;143goto out;144}145} else if (!our_mnt(path->mnt))146connected = 0;147148*name = res;149150if (!connected)151error = disconnect(path, buf, name, flags, disconnected);152153/* Handle two cases:154* 1. A deleted dentry && profile is not allowing mediation of deleted155* 2. On some filesystems, newly allocated dentries appear to the156* security_path hooks as a deleted dentry except without an inode157* allocated.158*/159if (d_unlinked(path->dentry) && d_is_positive(path->dentry) &&160!(flags & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))) {161error = -ENOENT;162goto out;163}164165out:166/*167* Append "/" to the pathname. The root directory is a special168* case; it already ends in slash.169*/170if (!error && isdir && ((*name)[1] != '\0' || (*name)[0] != '/'))171strcpy(&buf[aa_g_path_max - 2], "/");172173return error;174}175176/**177* aa_path_name - get the pathname to a buffer ensure dir / is appended178* @path: path the file (NOT NULL)179* @flags: flags controlling path name generation180* @buffer: buffer to put name in (NOT NULL)181* @name: Returns - the generated path name if !error (NOT NULL)182* @info: Returns - information on why the path lookup failed (MAYBE NULL)183* @disconnected: string to prepend to disconnected paths184*185* @name is a pointer to the beginning of the pathname (which usually differs186* from the beginning of the buffer), or NULL. If there is an error @name187* may contain a partial or invalid name that can be used for audit purposes,188* but it can not be used for mediation.189*190* We need PATH_IS_DIR to indicate whether the file is a directory or not191* because the file may not yet exist, and so we cannot check the inode's192* file type.193*194* Returns: %0 else error code if could retrieve name195*/196int aa_path_name(const struct path *path, int flags, char *buffer,197const char **name, const char **info, const char *disconnected)198{199char *str = NULL;200int error = d_namespace_path(path, buffer, &str, flags, disconnected);201202if (info && error) {203if (error == -ENOENT)204*info = "Failed name lookup - deleted entry";205else if (error == -EACCES)206*info = "Failed name lookup - disconnected path";207else if (error == -ENAMETOOLONG)208*info = "Failed name lookup - name too long";209else210*info = "Failed name lookup";211}212213*name = str;214215return error;216}217218219