diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:09:22 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:09:22 -0700 |
| commit | f4cdf7ca9a1fdcca413157df19753f388a5a224e (patch) | |
| tree | 60a31895b2446cd7fa27514e8c2843534a0de0da /security/apparmor/path.c | |
| download | linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.tar.gz linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.zip | |
Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-mediagrafted
Pull media updates from Mauro Carvalho Chehab:
- v4l2-core: added ISP statistics support and per-block validation
- v4l2-core: Allow unknown HDR10 white point and luminance
- New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor
- New codec: Milos: VPU v2.0 codec support
- isp driver: gained support for Dreamchip RPPX1 ISP framework
- vsp1 driver: gained support for RZ/T2H and RZ/N2H
- Novalake driver: gained CVS support for new NVL hardware
- dvb-core: fix feed leak on failed DMX_ADD_PID
- several driver fixes, cleanups and minor improvements
* tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits)
media: ipu-bridge: check all DMI entries when overriding sensor rotation
media: v4l2-async: avoid deleting unlinked ASC entry on link error
media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement
media: intel/ipu6: fix async notifier cleanup leak on parse error
media: staging/ipu7: fix async notifier UAF on probe error path
media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path
media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem()
media: rcar-isp: Fix VSPX reference leaks
media: rcar-isp: Release ISPCORE resources
media: i2c: imx415: Release runtime PM reference on VBLANK error
media: i2c: imx415: Return test pattern write errors
media: renesas: vsp1: Declare index variables in for loop statement
media: renesas: vsp1: Make reset control optional to support platforms without a reset line
media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs
media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs
media: nxp: imx8-isi: Add additional 32-bit RGB format support
media: nxp: imx8-isi: Add 16-bit raw Bayer format support
media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams
media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks
media: nxp: imx8-isi: Correct color map between V4L2 and ISI
...
Diffstat (limited to 'security/apparmor/path.c')
| -rw-r--r-- | security/apparmor/path.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/security/apparmor/path.c b/security/apparmor/path.c new file mode 100644 index 000000000..2494e8101 --- /dev/null +++ b/security/apparmor/path.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AppArmor security module + * + * This file contains AppArmor function for pathnames + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#include <linux/magic.h> +#include <linux/mount.h> +#include <linux/namei.h> +#include <linux/nsproxy.h> +#include <linux/path.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/fs_struct.h> + +#include "include/apparmor.h" +#include "include/path.h" +#include "include/policy.h" + +/* modified from dcache.c */ +static int prepend(char **buffer, int buflen, const char *str, int namelen) +{ + buflen -= namelen; + if (buflen < 0) + return -ENAMETOOLONG; + *buffer -= namelen; + memcpy(*buffer, str, namelen); + return 0; +} + +#define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) + +/* If the path is not connected to the expected root, + * check if it is a sysctl and handle specially else remove any + * leading / that __d_path may have returned. + * Unless + * specifically directed to connect the path, + * OR + * if in a chroot and doing chroot relative paths and the path + * resolves to the namespace root (would be connected outside + * of chroot) and specifically directed to connect paths to + * namespace root. + */ +static int disconnect(const struct path *path, char *buf, char **name, + int flags, const char *disconnected) +{ + int error = 0; + + if (!(flags & PATH_CONNECT_PATH) && + !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && + our_mnt(path->mnt))) { + /* disconnected path, don't return pathname starting + * with '/' + */ + error = -EACCES; + if (**name == '/') + *name = *name + 1; + } else { + if (**name != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + if (!error && disconnected) + error = prepend(name, *name - buf, disconnected, + strlen(disconnected)); + } + + return error; +} + +/** + * d_namespace_path - lookup a name associated with a given path + * @path: path to lookup (NOT NULL) + * @buf: buffer to store path to (NOT NULL) + * @name: Returns - pointer for start of path name with in @buf (NOT NULL) + * @flags: flags controlling path lookup + * @disconnected: string to prefix to disconnected paths + * + * Handle path name lookup. + * + * Returns: %0 else error code if path lookup fails + * When no error the path name is returned in @name which points to + * a position in @buf + */ +static int d_namespace_path(const struct path *path, char *buf, char **name, + int flags, const char *disconnected) +{ + char *res; + int error = 0; + int connected = 1; + int isdir = (flags & PATH_IS_DIR) ? 1 : 0; + int buflen = aa_g_path_max - isdir; + + if (path->mnt->mnt_flags & MNT_INTERNAL) { + /* it's not mounted anywhere */ + res = dentry_path(path->dentry, buf, buflen); + *name = res; + if (IS_ERR(res)) { + *name = buf; + return PTR_ERR(res); + } + if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC && + strncmp(*name, "/sys/", 5) == 0) { + /* TODO: convert over to using a per namespace + * control instead of hard coded /proc + */ + error = prepend(name, *name - buf, "/proc", 5); + goto out; + } else + error = disconnect(path, buf, name, flags, + disconnected); + goto out; + } + + /* resolve paths relative to chroot?*/ + if (flags & PATH_CHROOT_REL) { + struct path root; + get_fs_root(current->fs, &root); + res = __d_path(path, &root, buf, buflen); + path_put(&root); + } else { + res = d_absolute_path(path, buf, buflen); + if (!our_mnt(path->mnt)) + connected = 0; + } + + /* handle error conditions - and still allow a partial path to + * be returned. + */ + if (IS_ERR_OR_NULL(res)) { + if (PTR_ERR(res) == -ENAMETOOLONG) { + error = -ENAMETOOLONG; + *name = buf; + goto out; + } + connected = 0; + res = dentry_path_raw(path->dentry, buf, buflen); + if (IS_ERR(res)) { + error = PTR_ERR(res); + *name = buf; + goto out; + } + } else if (!our_mnt(path->mnt)) + connected = 0; + + *name = res; + + if (!connected) + error = disconnect(path, buf, name, flags, disconnected); + + /* Handle two cases: + * 1. A deleted dentry && profile is not allowing mediation of deleted + * 2. On some filesystems, newly allocated dentries appear to the + * security_path hooks as a deleted dentry except without an inode + * allocated. + */ + if (d_unlinked(path->dentry) && d_is_positive(path->dentry) && + !(flags & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))) { + error = -ENOENT; + goto out; + } + +out: + /* Append "/" to directory paths and reterminate string, except for + * root "/" which already ends in a slash. + */ + if (!error && isdir) { + bool is_root = (*name)[0] == '/' && (*name)[1] == '\0'; + + if (!is_root) { + buf[aa_g_path_max - 2] = '/'; + buf[aa_g_path_max - 1] = '\0'; + } + } + + return error; +} + +/** + * aa_path_name - get the pathname to a buffer ensure dir / is appended + * @path: path the file (NOT NULL) + * @flags: flags controlling path name generation + * @buffer: buffer to put name in (NOT NULL) + * @name: Returns - the generated path name if !error (NOT NULL) + * @info: Returns - information on why the path lookup failed (MAYBE NULL) + * @disconnected: string to prepend to disconnected paths + * + * @name is a pointer to the beginning of the pathname (which usually differs + * from the beginning of the buffer), or NULL. If there is an error @name + * may contain a partial or invalid name that can be used for audit purposes, + * but it can not be used for mediation. + * + * We need PATH_IS_DIR to indicate whether the file is a directory or not + * because the file may not yet exist, and so we cannot check the inode's + * file type. + * + * Returns: %0 else error code if could retrieve name + */ +int aa_path_name(const struct path *path, int flags, char *buffer, + const char **name, const char **info, const char *disconnected) +{ + char *str = NULL; + int error = d_namespace_path(path, buffer, &str, flags, disconnected); + + if (info && error) { + if (error == -ENOENT) + *info = "Failed name lookup - deleted entry"; + else if (error == -EACCES) + *info = "Failed name lookup - disconnected path"; + else if (error == -ENAMETOOLONG) + *info = "Failed name lookup - name too long"; + else + *info = "Failed name lookup"; + } + + *name = str; + + return error; +} |
