1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cdff2642SJohn Johansen /*
3cdff2642SJohn Johansen * AppArmor security module
4cdff2642SJohn Johansen *
5cdff2642SJohn Johansen * This file contains AppArmor function for pathnames
6cdff2642SJohn Johansen *
7cdff2642SJohn Johansen * Copyright (C) 1998-2008 Novell/SUSE
8cdff2642SJohn Johansen * Copyright 2009-2010 Canonical Ltd.
9cdff2642SJohn Johansen */
10cdff2642SJohn Johansen
11cdff2642SJohn Johansen #include <linux/magic.h>
12cdff2642SJohn Johansen #include <linux/mount.h>
13cdff2642SJohn Johansen #include <linux/namei.h>
14cdff2642SJohn Johansen #include <linux/nsproxy.h>
15cdff2642SJohn Johansen #include <linux/path.h>
16cdff2642SJohn Johansen #include <linux/sched.h>
17cdff2642SJohn Johansen #include <linux/slab.h>
18cdff2642SJohn Johansen #include <linux/fs_struct.h>
19cdff2642SJohn Johansen
20cdff2642SJohn Johansen #include "include/apparmor.h"
21cdff2642SJohn Johansen #include "include/path.h"
22cdff2642SJohn Johansen #include "include/policy.h"
23cdff2642SJohn Johansen
24cdff2642SJohn Johansen /* modified from dcache.c */
prepend(char ** buffer,int buflen,const char * str,int namelen)25cdff2642SJohn Johansen static int prepend(char **buffer, int buflen, const char *str, int namelen)
26cdff2642SJohn Johansen {
27cdff2642SJohn Johansen buflen -= namelen;
28cdff2642SJohn Johansen if (buflen < 0)
29cdff2642SJohn Johansen return -ENAMETOOLONG;
30cdff2642SJohn Johansen *buffer -= namelen;
31cdff2642SJohn Johansen memcpy(*buffer, str, namelen);
32cdff2642SJohn Johansen return 0;
33cdff2642SJohn Johansen }
34cdff2642SJohn Johansen
35cdff2642SJohn Johansen #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT)
36cdff2642SJohn Johansen
37bd35db8bSJohn Johansen /* If the path is not connected to the expected root,
38bd35db8bSJohn Johansen * check if it is a sysctl and handle specially else remove any
39bd35db8bSJohn Johansen * leading / that __d_path may have returned.
40bd35db8bSJohn Johansen * Unless
41bd35db8bSJohn Johansen * specifically directed to connect the path,
42bd35db8bSJohn Johansen * OR
43bd35db8bSJohn Johansen * if in a chroot and doing chroot relative paths and the path
44bd35db8bSJohn Johansen * resolves to the namespace root (would be connected outside
45bd35db8bSJohn Johansen * of chroot) and specifically directed to connect paths to
46bd35db8bSJohn Johansen * namespace root.
47bd35db8bSJohn Johansen */
disconnect(const struct path * path,char * buf,char ** name,int flags,const char * disconnected)48bd35db8bSJohn Johansen static int disconnect(const struct path *path, char *buf, char **name,
4972c8a768SJohn Johansen int flags, const char *disconnected)
50bd35db8bSJohn Johansen {
51bd35db8bSJohn Johansen int error = 0;
52bd35db8bSJohn Johansen
53bd35db8bSJohn Johansen if (!(flags & PATH_CONNECT_PATH) &&
54bd35db8bSJohn Johansen !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
55bd35db8bSJohn Johansen our_mnt(path->mnt))) {
56bd35db8bSJohn Johansen /* disconnected path, don't return pathname starting
57bd35db8bSJohn Johansen * with '/'
58bd35db8bSJohn Johansen */
59bd35db8bSJohn Johansen error = -EACCES;
60bd35db8bSJohn Johansen if (**name == '/')
61bd35db8bSJohn Johansen *name = *name + 1;
6272c8a768SJohn Johansen } else {
6372c8a768SJohn Johansen if (**name != '/')
64bd35db8bSJohn Johansen /* CONNECT_PATH with missing root */
65bd35db8bSJohn Johansen error = prepend(name, *name - buf, "/", 1);
6672c8a768SJohn Johansen if (!error && disconnected)
6772c8a768SJohn Johansen error = prepend(name, *name - buf, disconnected,
6872c8a768SJohn Johansen strlen(disconnected));
6972c8a768SJohn Johansen }
70bd35db8bSJohn Johansen
71bd35db8bSJohn Johansen return error;
72bd35db8bSJohn Johansen }
73bd35db8bSJohn Johansen
74cdff2642SJohn Johansen /**
75cdff2642SJohn Johansen * d_namespace_path - lookup a name associated with a given path
76cdff2642SJohn Johansen * @path: path to lookup (NOT NULL)
77cdff2642SJohn Johansen * @buf: buffer to store path to (NOT NULL)
78cdff2642SJohn Johansen * @name: Returns - pointer for start of path name with in @buf (NOT NULL)
79cdff2642SJohn Johansen * @flags: flags controlling path lookup
8072c8a768SJohn Johansen * @disconnected: string to prefix to disconnected paths
81cdff2642SJohn Johansen *
82cdff2642SJohn Johansen * Handle path name lookup.
83cdff2642SJohn Johansen *
84cdff2642SJohn Johansen * Returns: %0 else error code if path lookup fails
85cdff2642SJohn Johansen * When no error the path name is returned in @name which points to
86*4af7c863SRandy Dunlap * a position in @buf
87cdff2642SJohn Johansen */
d_namespace_path(const struct path * path,char * buf,char ** name,int flags,const char * disconnected)884227c333SJohn Johansen static int d_namespace_path(const struct path *path, char *buf, char **name,
894227c333SJohn Johansen int flags, const char *disconnected)
90cdff2642SJohn Johansen {
91cdff2642SJohn Johansen char *res;
9202125a82SAl Viro int error = 0;
9302125a82SAl Viro int connected = 1;
944227c333SJohn Johansen int isdir = (flags & PATH_IS_DIR) ? 1 : 0;
954227c333SJohn Johansen int buflen = aa_g_path_max - isdir;
96cdff2642SJohn Johansen
9702125a82SAl Viro if (path->mnt->mnt_flags & MNT_INTERNAL) {
9802125a82SAl Viro /* it's not mounted anywhere */
9902125a82SAl Viro res = dentry_path(path->dentry, buf, buflen);
10002125a82SAl Viro *name = res;
10102125a82SAl Viro if (IS_ERR(res)) {
10202125a82SAl Viro *name = buf;
10302125a82SAl Viro return PTR_ERR(res);
10402125a82SAl Viro }
10502125a82SAl Viro if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC &&
10602125a82SAl Viro strncmp(*name, "/sys/", 5) == 0) {
10702125a82SAl Viro /* TODO: convert over to using a per namespace
10802125a82SAl Viro * control instead of hard coded /proc
10902125a82SAl Viro */
1104227c333SJohn Johansen error = prepend(name, *name - buf, "/proc", 5);
1114227c333SJohn Johansen goto out;
112bd35db8bSJohn Johansen } else
1134227c333SJohn Johansen error = disconnect(path, buf, name, flags,
11472c8a768SJohn Johansen disconnected);
1154227c333SJohn Johansen goto out;
116cdff2642SJohn Johansen }
117cdff2642SJohn Johansen
11802125a82SAl Viro /* resolve paths relative to chroot?*/
11902125a82SAl Viro if (flags & PATH_CHROOT_REL) {
12002125a82SAl Viro struct path root;
12102125a82SAl Viro get_fs_root(current->fs, &root);
12202125a82SAl Viro res = __d_path(path, &root, buf, buflen);
12302125a82SAl Viro path_put(&root);
1243372b68aSJohn Johansen } else {
12502125a82SAl Viro res = d_absolute_path(path, buf, buflen);
1263372b68aSJohn Johansen if (!our_mnt(path->mnt))
1273372b68aSJohn Johansen connected = 0;
1283372b68aSJohn Johansen }
129cdff2642SJohn Johansen
130cdff2642SJohn Johansen /* handle error conditions - and still allow a partial path to
131cdff2642SJohn Johansen * be returned.
132cdff2642SJohn Johansen */
1333372b68aSJohn Johansen if (!res || IS_ERR(res)) {
1344227c333SJohn Johansen if (PTR_ERR(res) == -ENAMETOOLONG) {
1354227c333SJohn Johansen error = -ENAMETOOLONG;
1364227c333SJohn Johansen *name = buf;
1374227c333SJohn Johansen goto out;
1384227c333SJohn Johansen }
1393372b68aSJohn Johansen connected = 0;
140fbba8d89SJohn Johansen res = dentry_path_raw(path->dentry, buf, buflen);
141fbba8d89SJohn Johansen if (IS_ERR(res)) {
142cdff2642SJohn Johansen error = PTR_ERR(res);
143cdff2642SJohn Johansen *name = buf;
144cdff2642SJohn Johansen goto out;
145e4f4e6baSVasyl Gomonovych }
146fbba8d89SJohn Johansen } else if (!our_mnt(path->mnt))
14702125a82SAl Viro connected = 0;
148cdff2642SJohn Johansen
149fbba8d89SJohn Johansen *name = res;
150fbba8d89SJohn Johansen
1514227c333SJohn Johansen if (!connected)
1524227c333SJohn Johansen error = disconnect(path, buf, name, flags, disconnected);
1534227c333SJohn Johansen
154e819ff51SJohn Johansen /* Handle two cases:
155e819ff51SJohn Johansen * 1. A deleted dentry && profile is not allowing mediation of deleted
156e819ff51SJohn Johansen * 2. On some filesystems, newly allocated dentries appear to the
157e819ff51SJohn Johansen * security_path hooks as a deleted dentry except without an inode
158e819ff51SJohn Johansen * allocated.
159e819ff51SJohn Johansen */
160729b8a3dSDavid Howells if (d_unlinked(path->dentry) && d_is_positive(path->dentry) &&
1614227c333SJohn Johansen !(flags & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))) {
162cdff2642SJohn Johansen error = -ENOENT;
163cdff2642SJohn Johansen goto out;
164cdff2642SJohn Johansen }
165cdff2642SJohn Johansen
166cdff2642SJohn Johansen out:
167cdff2642SJohn Johansen /*
168cdff2642SJohn Johansen * Append "/" to the pathname. The root directory is a special
169cdff2642SJohn Johansen * case; it already ends in slash.
170cdff2642SJohn Johansen */
1714227c333SJohn Johansen if (!error && isdir && ((*name)[1] != '\0' || (*name)[0] != '/'))
1724227c333SJohn Johansen strcpy(&buf[aa_g_path_max - 2], "/");
17357fa1e18SJohn Johansen
174cdff2642SJohn Johansen return error;
175cdff2642SJohn Johansen }
176cdff2642SJohn Johansen
177cdff2642SJohn Johansen /**
1784227c333SJohn Johansen * aa_path_name - get the pathname to a buffer ensure dir / is appended
179cdff2642SJohn Johansen * @path: path the file (NOT NULL)
180cdff2642SJohn Johansen * @flags: flags controlling path name generation
1814227c333SJohn Johansen * @buffer: buffer to put name in (NOT NULL)
182cdff2642SJohn Johansen * @name: Returns - the generated path name if !error (NOT NULL)
18357fa1e18SJohn Johansen * @info: Returns - information on why the path lookup failed (MAYBE NULL)
18472c8a768SJohn Johansen * @disconnected: string to prepend to disconnected paths
185cdff2642SJohn Johansen *
186cdff2642SJohn Johansen * @name is a pointer to the beginning of the pathname (which usually differs
187cdff2642SJohn Johansen * from the beginning of the buffer), or NULL. If there is an error @name
188cdff2642SJohn Johansen * may contain a partial or invalid name that can be used for audit purposes,
189cdff2642SJohn Johansen * but it can not be used for mediation.
190cdff2642SJohn Johansen *
191cdff2642SJohn Johansen * We need PATH_IS_DIR to indicate whether the file is a directory or not
192cdff2642SJohn Johansen * because the file may not yet exist, and so we cannot check the inode's
193cdff2642SJohn Johansen * file type.
194cdff2642SJohn Johansen *
195cdff2642SJohn Johansen * Returns: %0 else error code if could retrieve name
196cdff2642SJohn Johansen */
aa_path_name(const struct path * path,int flags,char * buffer,const char ** name,const char ** info,const char * disconnected)1974227c333SJohn Johansen int aa_path_name(const struct path *path, int flags, char *buffer,
19872c8a768SJohn Johansen const char **name, const char **info, const char *disconnected)
199cdff2642SJohn Johansen {
2004227c333SJohn Johansen char *str = NULL;
2014227c333SJohn Johansen int error = d_namespace_path(path, buffer, &str, flags, disconnected);
202cdff2642SJohn Johansen
2034227c333SJohn Johansen if (info && error) {
2044227c333SJohn Johansen if (error == -ENOENT)
2054227c333SJohn Johansen *info = "Failed name lookup - deleted entry";
2064227c333SJohn Johansen else if (error == -EACCES)
2074227c333SJohn Johansen *info = "Failed name lookup - disconnected path";
2084227c333SJohn Johansen else if (error == -ENAMETOOLONG)
2094227c333SJohn Johansen *info = "Failed name lookup - name too long";
2104227c333SJohn Johansen else
2114227c333SJohn Johansen *info = "Failed name lookup";
212cdff2642SJohn Johansen }
2134227c333SJohn Johansen
214cdff2642SJohn Johansen *name = str;
215cdff2642SJohn Johansen
216cdff2642SJohn Johansen return error;
217cdff2642SJohn Johansen }
218