Blame view

security/apparmor/path.c 5.79 KB
cdff26426   John Johansen   AppArmor: misc. b...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * AppArmor security module
   *
   * This file contains AppArmor function for pathnames
   *
   * Copyright (C) 1998-2008 Novell/SUSE
   * Copyright 2009-2010 Canonical Ltd.
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License as
   * published by the Free Software Foundation, version 2 of the
   * License.
   */
  
  #include <linux/magic.h>
cdff26426   John Johansen   AppArmor: misc. b...
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  #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)
  
  /**
   * 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)
   * @buflen: length of @buf
   * @name: Returns - pointer for start of path name with in @buf (NOT NULL)
   * @flags: flags controlling path lookup
   *
   * 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
   *          to a position in @buf
   */
  static int d_namespace_path(struct path *path, char *buf, int buflen,
  			    char **name, int flags)
  {
cdff26426   John Johansen   AppArmor: misc. b...
59
  	char *res;
02125a826   Al Viro   fix apparmor dere...
60
61
  	int error = 0;
  	int connected = 1;
cdff26426   John Johansen   AppArmor: misc. b...
62

02125a826   Al Viro   fix apparmor dere...
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
  	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
  			 */
  			return prepend(name, *name - buf, "/proc", 5);
  		}
  		return 0;
  	}
  
  	/* resolve paths relative to chroot?*/
cdff26426   John Johansen   AppArmor: misc. b...
82
  	if (flags & PATH_CHROOT_REL) {
02125a826   Al Viro   fix apparmor dere...
83
  		struct path root;
44672e4fb   Nick Piggin   apparmor: use tas...
84
  		get_fs_root(current->fs, &root);
02125a826   Al Viro   fix apparmor dere...
85
86
87
88
89
90
91
92
93
  		res = __d_path(path, &root, buf, buflen);
  		if (res && !IS_ERR(res)) {
  			/* everything's fine */
  			*name = res;
  			path_put(&root);
  			goto ok;
  		}
  		path_put(&root);
  		connected = 0;
cdff26426   John Johansen   AppArmor: misc. b...
94
  	}
02125a826   Al Viro   fix apparmor dere...
95
  	res = d_absolute_path(path, buf, buflen);
cdff26426   John Johansen   AppArmor: misc. b...
96
97
98
99
100
101
102
103
104
105
  
  	*name = res;
  	/* handle error conditions - and still allow a partial path to
  	 * be returned.
  	 */
  	if (IS_ERR(res)) {
  		error = PTR_ERR(res);
  		*name = buf;
  		goto out;
  	}
02125a826   Al Viro   fix apparmor dere...
106
107
  	if (!our_mnt(path->mnt))
  		connected = 0;
cdff26426   John Johansen   AppArmor: misc. b...
108

02125a826   Al Viro   fix apparmor dere...
109
  ok:
e819ff519   John Johansen   AppArmor: Drop ha...
110
111
112
113
114
115
116
117
  	/* 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) && path->dentry->d_inode &&
  	    !(flags & PATH_MEDIATE_DELETED)) {
cdff26426   John Johansen   AppArmor: misc. b...
118
119
  			error = -ENOENT;
  			goto out;
cdff26426   John Johansen   AppArmor: misc. b...
120
  	}
02125a826   Al Viro   fix apparmor dere...
121
  	/* If the path is not connected to the expected root,
cdff26426   John Johansen   AppArmor: misc. b...
122
123
124
125
126
127
128
129
130
131
132
  	 * 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.
  	 */
  	if (!connected) {
02125a826   Al Viro   fix apparmor dere...
133
  		if (!(flags & PATH_CONNECT_PATH) &&
cdff26426   John Johansen   AppArmor: misc. b...
134
  			   !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
02125a826   Al Viro   fix apparmor dere...
135
  			     our_mnt(path->mnt))) {
cdff26426   John Johansen   AppArmor: misc. b...
136
137
138
139
140
141
142
143
144
145
  			/* disconnected path, don't return pathname starting
  			 * with '/'
  			 */
  			error = -ESTALE;
  			if (*res == '/')
  				*name = res + 1;
  		}
  	}
  
  out:
cdff26426   John Johansen   AppArmor: misc. b...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
  	return error;
  }
  
  /**
   * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended
   * @path: path to get name for  (NOT NULL)
   * @flags: flags controlling path lookup
   * @buffer: buffer to put name in  (NOT NULL)
   * @size: size of buffer
   * @name: Returns - contains position of path name in @buffer (NOT NULL)
   *
   * Returns: %0 else error on failure
   */
  static int get_name_to_buffer(struct path *path, int flags, char *buffer,
  			      int size, char **name)
  {
  	int adjust = (flags & PATH_IS_DIR) ? 1 : 0;
  	int error = d_namespace_path(path, buffer, size - adjust, name, flags);
  
  	if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0')
  		/*
  		 * Append "/" to the pathname.  The root directory is a special
  		 * case; it already ends in slash.
  		 */
  		strcpy(&buffer[size - 2], "/");
  
  	return error;
  }
  
  /**
   * aa_get_name - compute the pathname of a file
   * @path: path the file  (NOT NULL)
   * @flags: flags controlling path name generation
   * @buffer: buffer that aa_get_name() allocated  (NOT NULL)
   * @name: Returns - the generated path name if !error (NOT NULL)
   *
   * @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_get_name(struct path *path, int flags, char **buffer, const char **name)
  {
  	char *buf, *str = NULL;
  	int size = 256;
  	int error;
  
  	*name = NULL;
  	*buffer = NULL;
  	for (;;) {
  		/* freed by caller */
  		buf = kmalloc(size, GFP_KERNEL);
  		if (!buf)
  			return -ENOMEM;
  
  		error = get_name_to_buffer(path, flags, buf, size, &str);
  		if (error != -ENAMETOOLONG)
  			break;
  
  		kfree(buf);
  		size <<= 1;
  		if (size > aa_g_path_max)
  			return -ENAMETOOLONG;
  	}
  	*buffer = buf;
  	*name = str;
  
  	return error;
  }