Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/security/apparmor/path.c
26377 views
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
* 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 (IS_ERR_OR_NULL(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
}
218
219