diff options
Diffstat (limited to 'security/apparmor/path.c')
| -rw-r--r-- | security/apparmor/path.c | 130 |
1 files changed, 52 insertions, 78 deletions
diff --git a/security/apparmor/path.c b/security/apparmor/path.c index a8fc7d08c144..9d5de1d05be4 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c | |||
| @@ -50,7 +50,7 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) | |||
| 50 | * namespace root. | 50 | * namespace root. |
| 51 | */ | 51 | */ |
| 52 | static int disconnect(const struct path *path, char *buf, char **name, | 52 | static int disconnect(const struct path *path, char *buf, char **name, |
| 53 | int flags) | 53 | int flags, const char *disconnected) |
| 54 | { | 54 | { |
| 55 | int error = 0; | 55 | int error = 0; |
| 56 | 56 | ||
| @@ -63,9 +63,14 @@ static int disconnect(const struct path *path, char *buf, char **name, | |||
| 63 | error = -EACCES; | 63 | error = -EACCES; |
| 64 | if (**name == '/') | 64 | if (**name == '/') |
| 65 | *name = *name + 1; | 65 | *name = *name + 1; |
| 66 | } else if (**name != '/') | 66 | } else { |
| 67 | /* CONNECT_PATH with missing root */ | 67 | if (**name != '/') |
| 68 | error = prepend(name, *name - buf, "/", 1); | 68 | /* CONNECT_PATH with missing root */ |
| 69 | error = prepend(name, *name - buf, "/", 1); | ||
| 70 | if (!error && disconnected) | ||
| 71 | error = prepend(name, *name - buf, disconnected, | ||
| 72 | strlen(disconnected)); | ||
| 73 | } | ||
| 69 | 74 | ||
| 70 | return error; | 75 | return error; |
| 71 | } | 76 | } |
| @@ -74,9 +79,9 @@ static int disconnect(const struct path *path, char *buf, char **name, | |||
| 74 | * d_namespace_path - lookup a name associated with a given path | 79 | * d_namespace_path - lookup a name associated with a given path |
| 75 | * @path: path to lookup (NOT NULL) | 80 | * @path: path to lookup (NOT NULL) |
| 76 | * @buf: buffer to store path to (NOT NULL) | 81 | * @buf: buffer to store path to (NOT NULL) |
| 77 | * @buflen: length of @buf | ||
| 78 | * @name: Returns - pointer for start of path name with in @buf (NOT NULL) | 82 | * @name: Returns - pointer for start of path name with in @buf (NOT NULL) |
| 79 | * @flags: flags controlling path lookup | 83 | * @flags: flags controlling path lookup |
| 84 | * @disconnected: string to prefix to disconnected paths | ||
| 80 | * | 85 | * |
| 81 | * Handle path name lookup. | 86 | * Handle path name lookup. |
| 82 | * | 87 | * |
| @@ -84,12 +89,14 @@ static int disconnect(const struct path *path, char *buf, char **name, | |||
| 84 | * When no error the path name is returned in @name which points to | 89 | * When no error the path name is returned in @name which points to |
| 85 | * to a position in @buf | 90 | * to a position in @buf |
| 86 | */ | 91 | */ |
| 87 | static int d_namespace_path(const struct path *path, char *buf, int buflen, | 92 | static int d_namespace_path(const struct path *path, char *buf, char **name, |
| 88 | char **name, int flags) | 93 | int flags, const char *disconnected) |
| 89 | { | 94 | { |
| 90 | char *res; | 95 | char *res; |
| 91 | int error = 0; | 96 | int error = 0; |
| 92 | int connected = 1; | 97 | int connected = 1; |
| 98 | int isdir = (flags & PATH_IS_DIR) ? 1 : 0; | ||
| 99 | int buflen = aa_g_path_max - isdir; | ||
| 93 | 100 | ||
| 94 | if (path->mnt->mnt_flags & MNT_INTERNAL) { | 101 | if (path->mnt->mnt_flags & MNT_INTERNAL) { |
| 95 | /* it's not mounted anywhere */ | 102 | /* it's not mounted anywhere */ |
| @@ -104,10 +111,12 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |||
| 104 | /* TODO: convert over to using a per namespace | 111 | /* TODO: convert over to using a per namespace |
| 105 | * control instead of hard coded /proc | 112 | * control instead of hard coded /proc |
| 106 | */ | 113 | */ |
| 107 | return prepend(name, *name - buf, "/proc", 5); | 114 | error = prepend(name, *name - buf, "/proc", 5); |
| 115 | goto out; | ||
| 108 | } else | 116 | } else |
| 109 | return disconnect(path, buf, name, flags); | 117 | error = disconnect(path, buf, name, flags, |
| 110 | return 0; | 118 | disconnected); |
| 119 | goto out; | ||
| 111 | } | 120 | } |
| 112 | 121 | ||
| 113 | /* resolve paths relative to chroot?*/ | 122 | /* resolve paths relative to chroot?*/ |
| @@ -126,8 +135,11 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |||
| 126 | * be returned. | 135 | * be returned. |
| 127 | */ | 136 | */ |
| 128 | if (!res || IS_ERR(res)) { | 137 | if (!res || IS_ERR(res)) { |
| 129 | if (PTR_ERR(res) == -ENAMETOOLONG) | 138 | if (PTR_ERR(res) == -ENAMETOOLONG) { |
| 130 | return -ENAMETOOLONG; | 139 | error = -ENAMETOOLONG; |
| 140 | *name = buf; | ||
| 141 | goto out; | ||
| 142 | } | ||
| 131 | connected = 0; | 143 | connected = 0; |
| 132 | res = dentry_path_raw(path->dentry, buf, buflen); | 144 | res = dentry_path_raw(path->dentry, buf, buflen); |
| 133 | if (IS_ERR(res)) { | 145 | if (IS_ERR(res)) { |
| @@ -140,6 +152,9 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |||
| 140 | 152 | ||
| 141 | *name = res; | 153 | *name = res; |
| 142 | 154 | ||
| 155 | if (!connected) | ||
| 156 | error = disconnect(path, buf, name, flags, disconnected); | ||
| 157 | |||
| 143 | /* Handle two cases: | 158 | /* Handle two cases: |
| 144 | * 1. A deleted dentry && profile is not allowing mediation of deleted | 159 | * 1. A deleted dentry && profile is not allowing mediation of deleted |
| 145 | * 2. On some filesystems, newly allocated dentries appear to the | 160 | * 2. On some filesystems, newly allocated dentries appear to the |
| @@ -147,62 +162,30 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |||
| 147 | * allocated. | 162 | * allocated. |
| 148 | */ | 163 | */ |
| 149 | if (d_unlinked(path->dentry) && d_is_positive(path->dentry) && | 164 | if (d_unlinked(path->dentry) && d_is_positive(path->dentry) && |
| 150 | !(flags & PATH_MEDIATE_DELETED)) { | 165 | !(flags & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))) { |
| 151 | error = -ENOENT; | 166 | error = -ENOENT; |
| 152 | goto out; | 167 | goto out; |
| 153 | } | 168 | } |
| 154 | 169 | ||
| 155 | if (!connected) | ||
| 156 | error = disconnect(path, buf, name, flags); | ||
| 157 | |||
| 158 | out: | 170 | out: |
| 159 | return error; | 171 | /* |
| 160 | } | 172 | * Append "/" to the pathname. The root directory is a special |
| 161 | 173 | * case; it already ends in slash. | |
| 162 | /** | 174 | */ |
| 163 | * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended | 175 | if (!error && isdir && ((*name)[1] != '\0' || (*name)[0] != '/')) |
| 164 | * @path: path to get name for (NOT NULL) | 176 | strcpy(&buf[aa_g_path_max - 2], "/"); |
| 165 | * @flags: flags controlling path lookup | ||
| 166 | * @buffer: buffer to put name in (NOT NULL) | ||
| 167 | * @size: size of buffer | ||
| 168 | * @name: Returns - contains position of path name in @buffer (NOT NULL) | ||
| 169 | * | ||
| 170 | * Returns: %0 else error on failure | ||
| 171 | */ | ||
| 172 | static int get_name_to_buffer(const struct path *path, int flags, char *buffer, | ||
| 173 | int size, char **name, const char **info) | ||
| 174 | { | ||
| 175 | int adjust = (flags & PATH_IS_DIR) ? 1 : 0; | ||
| 176 | int error = d_namespace_path(path, buffer, size - adjust, name, flags); | ||
| 177 | |||
| 178 | if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') | ||
| 179 | /* | ||
| 180 | * Append "/" to the pathname. The root directory is a special | ||
| 181 | * case; it already ends in slash. | ||
| 182 | */ | ||
| 183 | strcpy(&buffer[size - 2], "/"); | ||
| 184 | |||
| 185 | if (info && error) { | ||
| 186 | if (error == -ENOENT) | ||
| 187 | *info = "Failed name lookup - deleted entry"; | ||
| 188 | else if (error == -EACCES) | ||
| 189 | *info = "Failed name lookup - disconnected path"; | ||
| 190 | else if (error == -ENAMETOOLONG) | ||
| 191 | *info = "Failed name lookup - name too long"; | ||
| 192 | else | ||
| 193 | *info = "Failed name lookup"; | ||
| 194 | } | ||
| 195 | 177 | ||
| 196 | return error; | 178 | return error; |
| 197 | } | 179 | } |
| 198 | 180 | ||
| 199 | /** | 181 | /** |
| 200 | * aa_path_name - compute the pathname of a file | 182 | * aa_path_name - get the pathname to a buffer ensure dir / is appended |
| 201 | * @path: path the file (NOT NULL) | 183 | * @path: path the file (NOT NULL) |
| 202 | * @flags: flags controlling path name generation | 184 | * @flags: flags controlling path name generation |
| 203 | * @buffer: buffer that aa_get_name() allocated (NOT NULL) | 185 | * @buffer: buffer to put name in (NOT NULL) |
| 204 | * @name: Returns - the generated path name if !error (NOT NULL) | 186 | * @name: Returns - the generated path name if !error (NOT NULL) |
| 205 | * @info: Returns - information on why the path lookup failed (MAYBE NULL) | 187 | * @info: Returns - information on why the path lookup failed (MAYBE NULL) |
| 188 | * @disconnected: string to prepend to disconnected paths | ||
| 206 | * | 189 | * |
| 207 | * @name is a pointer to the beginning of the pathname (which usually differs | 190 | * @name is a pointer to the beginning of the pathname (which usually differs |
| 208 | * from the beginning of the buffer), or NULL. If there is an error @name | 191 | * from the beginning of the buffer), or NULL. If there is an error @name |
| @@ -215,32 +198,23 @@ static int get_name_to_buffer(const struct path *path, int flags, char *buffer, | |||
| 215 | * | 198 | * |
| 216 | * Returns: %0 else error code if could retrieve name | 199 | * Returns: %0 else error code if could retrieve name |
| 217 | */ | 200 | */ |
| 218 | int aa_path_name(const struct path *path, int flags, char **buffer, | 201 | int aa_path_name(const struct path *path, int flags, char *buffer, |
| 219 | const char **name, const char **info) | 202 | const char **name, const char **info, const char *disconnected) |
| 220 | { | 203 | { |
| 221 | char *buf, *str = NULL; | 204 | char *str = NULL; |
| 222 | int size = 256; | 205 | int error = d_namespace_path(path, buffer, &str, flags, disconnected); |
| 223 | int error; | 206 | |
| 224 | 207 | if (info && error) { | |
| 225 | *name = NULL; | 208 | if (error == -ENOENT) |
| 226 | *buffer = NULL; | 209 | *info = "Failed name lookup - deleted entry"; |
| 227 | for (;;) { | 210 | else if (error == -EACCES) |
| 228 | /* freed by caller */ | 211 | *info = "Failed name lookup - disconnected path"; |
| 229 | buf = kmalloc(size, GFP_KERNEL); | 212 | else if (error == -ENAMETOOLONG) |
| 230 | if (!buf) | 213 | *info = "Failed name lookup - name too long"; |
| 231 | return -ENOMEM; | 214 | else |
| 232 | 215 | *info = "Failed name lookup"; | |
| 233 | error = get_name_to_buffer(path, flags, buf, size, &str, info); | ||
| 234 | if (error != -ENAMETOOLONG) | ||
| 235 | break; | ||
| 236 | |||
| 237 | kfree(buf); | ||
| 238 | size <<= 1; | ||
| 239 | if (size > aa_g_path_max) | ||
| 240 | return -ENAMETOOLONG; | ||
| 241 | *info = NULL; | ||
| 242 | } | 216 | } |
| 243 | *buffer = buf; | 217 | |
| 244 | *name = str; | 218 | *name = str; |
| 245 | 219 | ||
| 246 | return error; | 220 | return error; |
