Branch data Line data Source code
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 : 0 : buflen -= namelen;
28 [ # # # # : 0 : if (buflen < 0)
# # ]
29 : : return -ENAMETOOLONG;
30 : 0 : *buffer -= namelen;
31 : 0 : 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 : 0 : static int disconnect(const struct path *path, char *buf, char **name,
49 : : int flags, const char *disconnected)
50 : : {
51 : : int error = 0;
52 : :
53 [ # # # # ]: 0 : if (!(flags & PATH_CONNECT_PATH) &&
54 [ # # ]: 0 : !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
55 : 0 : our_mnt(path->mnt))) {
56 : : /* disconnected path, don't return pathname starting
57 : : * with '/'
58 : : */
59 : : error = -EACCES;
60 [ # # ]: 0 : if (**name == '/')
61 : 0 : *name = *name + 1;
62 : : } else {
63 [ # # ]: 0 : if (**name != '/')
64 : : /* CONNECT_PATH with missing root */
65 : 0 : error = prepend(name, *name - buf, "/", 1);
66 [ # # ]: 0 : if (!error && disconnected)
67 : 0 : error = prepend(name, *name - buf, disconnected,
68 : 0 : strlen(disconnected));
69 : : }
70 : :
71 : 0 : 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 : 0 : 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 : 0 : int isdir = (flags & PATH_IS_DIR) ? 1 : 0;
95 : 0 : int buflen = aa_g_path_max - isdir;
96 : :
97 [ # # ]: 0 : if (path->mnt->mnt_flags & MNT_INTERNAL) {
98 : : /* it's not mounted anywhere */
99 : 0 : res = dentry_path(path->dentry, buf, buflen);
100 : 0 : *name = res;
101 [ # # ]: 0 : if (IS_ERR(res)) {
102 : 0 : *name = buf;
103 : 0 : return PTR_ERR(res);
104 : : }
105 [ # # # # ]: 0 : if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC &&
106 : 0 : strncmp(*name, "/sys/", 5) == 0) {
107 : : /* TODO: convert over to using a per namespace
108 : : * control instead of hard coded /proc
109 : : */
110 : 0 : error = prepend(name, *name - buf, "/proc", 5);
111 : 0 : goto out;
112 : : } else
113 : 0 : error = disconnect(path, buf, name, flags,
114 : : disconnected);
115 : 0 : goto out;
116 : : }
117 : :
118 : : /* resolve paths relative to chroot?*/
119 [ # # ]: 0 : if (flags & PATH_CHROOT_REL) {
120 : : struct path root;
121 : 0 : get_fs_root(current->fs, &root);
122 : 0 : res = __d_path(path, &root, buf, buflen);
123 : 0 : path_put(&root);
124 : : } else {
125 : 0 : res = d_absolute_path(path, buf, buflen);
126 [ # # ]: 0 : 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 [ # # # # ]: 0 : if (!res || IS_ERR(res)) {
134 [ # # ]: 0 : if (PTR_ERR(res) == -ENAMETOOLONG) {
135 : : error = -ENAMETOOLONG;
136 : 0 : *name = buf;
137 : 0 : goto out;
138 : : }
139 : : connected = 0;
140 : 0 : res = dentry_path_raw(path->dentry, buf, buflen);
141 [ # # ]: 0 : if (IS_ERR(res)) {
142 : : error = PTR_ERR(res);
143 : 0 : *name = buf;
144 : 0 : goto out;
145 : : };
146 [ # # ]: 0 : } else if (!our_mnt(path->mnt))
147 : : connected = 0;
148 : :
149 : 0 : *name = res;
150 : :
151 [ # # ]: 0 : if (!connected)
152 : 0 : 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 [ # # # # : 0 : if (d_unlinked(path->dentry) && d_is_positive(path->dentry) &&
# # ]
161 : 0 : !(flags & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))) {
162 : : error = -ENOENT;
163 : 0 : 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 [ # # # # : 0 : if (!error && isdir && ((*name)[1] != '\0' || (*name)[0] != '/'))
# # ]
172 : 0 : strcpy(&buf[aa_g_path_max - 2], "/");
173 : :
174 : 0 : 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 : 0 : int aa_path_name(const struct path *path, int flags, char *buffer,
198 : : const char **name, const char **info, const char *disconnected)
199 : : {
200 : 0 : char *str = NULL;
201 : 0 : int error = d_namespace_path(path, buffer, &str, flags, disconnected);
202 : :
203 [ # # ]: 0 : if (info && error) {
204 [ # # ]: 0 : if (error == -ENOENT)
205 : 0 : *info = "Failed name lookup - deleted entry";
206 [ # # ]: 0 : else if (error == -EACCES)
207 : 0 : *info = "Failed name lookup - disconnected path";
208 [ # # ]: 0 : else if (error == -ENAMETOOLONG)
209 : 0 : *info = "Failed name lookup - name too long";
210 : : else
211 : 0 : *info = "Failed name lookup";
212 : : }
213 : :
214 : 0 : *name = str;
215 : :
216 : 0 : return error;
217 : : }
|