realpath.c revision 1418a3e5ad4d01b1d4abf2c479c50b0cedd59e3f
1/* 2 * security/tomoyo/realpath.c 3 * 4 * Copyright (C) 2005-2011 NTT DATA CORPORATION 5 */ 6 7#include <linux/types.h> 8#include <linux/mount.h> 9#include <linux/mnt_namespace.h> 10#include <linux/fs_struct.h> 11#include <linux/magic.h> 12#include <linux/slab.h> 13#include <net/sock.h> 14#include "common.h" 15#include "../../fs/internal.h" 16 17/** 18 * tomoyo_encode2 - Encode binary string to ascii string. 19 * 20 * @str: String in binary format. 21 * @str_len: Size of @str in byte. 22 * 23 * Returns pointer to @str in ascii format on success, NULL otherwise. 24 * 25 * This function uses kzalloc(), so caller must kfree() if this function 26 * didn't return NULL. 27 */ 28char *tomoyo_encode2(const char *str, int str_len) 29{ 30 int i; 31 int len = 0; 32 const char *p = str; 33 char *cp; 34 char *cp0; 35 36 if (!p) 37 return NULL; 38 for (i = 0; i < str_len; i++) { 39 const unsigned char c = p[i]; 40 41 if (c == '\\') 42 len += 2; 43 else if (c > ' ' && c < 127) 44 len++; 45 else 46 len += 4; 47 } 48 len++; 49 /* Reserve space for appending "/". */ 50 cp = kzalloc(len + 10, GFP_NOFS); 51 if (!cp) 52 return NULL; 53 cp0 = cp; 54 p = str; 55 for (i = 0; i < str_len; i++) { 56 const unsigned char c = p[i]; 57 58 if (c == '\\') { 59 *cp++ = '\\'; 60 *cp++ = '\\'; 61 } else if (c > ' ' && c < 127) { 62 *cp++ = c; 63 } else { 64 *cp++ = '\\'; 65 *cp++ = (c >> 6) + '0'; 66 *cp++ = ((c >> 3) & 7) + '0'; 67 *cp++ = (c & 7) + '0'; 68 } 69 } 70 return cp0; 71} 72 73/** 74 * tomoyo_encode - Encode binary string to ascii string. 75 * 76 * @str: String in binary format. 77 * 78 * Returns pointer to @str in ascii format on success, NULL otherwise. 79 * 80 * This function uses kzalloc(), so caller must kfree() if this function 81 * didn't return NULL. 82 */ 83char *tomoyo_encode(const char *str) 84{ 85 return str ? tomoyo_encode2(str, strlen(str)) : NULL; 86} 87 88/** 89 * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. 90 * 91 * @path: Pointer to "struct path". 92 * @buffer: Pointer to buffer to return value in. 93 * @buflen: Sizeof @buffer. 94 * 95 * Returns the buffer on success, an error code otherwise. 96 * 97 * If dentry is a directory, trailing '/' is appended. 98 */ 99static char *tomoyo_get_absolute_path(struct path *path, char * const buffer, 100 const int buflen) 101{ 102 char *pos = ERR_PTR(-ENOMEM); 103 if (buflen >= 256) { 104 /* go to whatever namespace root we are under */ 105 pos = d_absolute_path(path, buffer, buflen - 1); 106 if (!IS_ERR(pos) && *pos == '/' && pos[1]) { 107 struct inode *inode = path->dentry->d_inode; 108 if (inode && S_ISDIR(inode->i_mode)) { 109 buffer[buflen - 2] = '/'; 110 buffer[buflen - 1] = '\0'; 111 } 112 } 113 } 114 return pos; 115} 116 117/** 118 * tomoyo_get_dentry_path - Get the path of a dentry. 119 * 120 * @dentry: Pointer to "struct dentry". 121 * @buffer: Pointer to buffer to return value in. 122 * @buflen: Sizeof @buffer. 123 * 124 * Returns the buffer on success, an error code otherwise. 125 * 126 * If dentry is a directory, trailing '/' is appended. 127 */ 128static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer, 129 const int buflen) 130{ 131 char *pos = ERR_PTR(-ENOMEM); 132 if (buflen >= 256) { 133 pos = dentry_path_raw(dentry, buffer, buflen - 1); 134 if (!IS_ERR(pos) && *pos == '/' && pos[1]) { 135 struct inode *inode = dentry->d_inode; 136 if (inode && S_ISDIR(inode->i_mode)) { 137 buffer[buflen - 2] = '/'; 138 buffer[buflen - 1] = '\0'; 139 } 140 } 141 } 142 return pos; 143} 144 145/** 146 * tomoyo_get_local_path - Get the path of a dentry. 147 * 148 * @dentry: Pointer to "struct dentry". 149 * @buffer: Pointer to buffer to return value in. 150 * @buflen: Sizeof @buffer. 151 * 152 * Returns the buffer on success, an error code otherwise. 153 */ 154static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer, 155 const int buflen) 156{ 157 struct super_block *sb = dentry->d_sb; 158 char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen); 159 if (IS_ERR(pos)) 160 return pos; 161 /* Convert from $PID to self if $PID is current thread. */ 162 if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { 163 char *ep; 164 const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); 165 if (*ep == '/' && pid && pid == 166 task_tgid_nr_ns(current, sb->s_fs_info)) { 167 pos = ep - 5; 168 if (pos < buffer) 169 goto out; 170 memmove(pos, "/self", 5); 171 } 172 goto prepend_filesystem_name; 173 } 174 /* Use filesystem name for unnamed devices. */ 175 if (!MAJOR(sb->s_dev)) 176 goto prepend_filesystem_name; 177 { 178 struct inode *inode = sb->s_root->d_inode; 179 /* 180 * Use filesystem name if filesystem does not support rename() 181 * operation. 182 */ 183 if (inode->i_op && !inode->i_op->rename) 184 goto prepend_filesystem_name; 185 } 186 /* Prepend device name. */ 187 { 188 char name[64]; 189 int name_len; 190 const dev_t dev = sb->s_dev; 191 name[sizeof(name) - 1] = '\0'; 192 snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), 193 MINOR(dev)); 194 name_len = strlen(name); 195 pos -= name_len; 196 if (pos < buffer) 197 goto out; 198 memmove(pos, name, name_len); 199 return pos; 200 } 201 /* Prepend filesystem name. */ 202prepend_filesystem_name: 203 { 204 const char *name = sb->s_type->name; 205 const int name_len = strlen(name); 206 pos -= name_len + 1; 207 if (pos < buffer) 208 goto out; 209 memmove(pos, name, name_len); 210 pos[name_len] = ':'; 211 } 212 return pos; 213out: 214 return ERR_PTR(-ENOMEM); 215} 216 217/** 218 * tomoyo_get_socket_name - Get the name of a socket. 219 * 220 * @path: Pointer to "struct path". 221 * @buffer: Pointer to buffer to return value in. 222 * @buflen: Sizeof @buffer. 223 * 224 * Returns the buffer. 225 */ 226static char *tomoyo_get_socket_name(struct path *path, char * const buffer, 227 const int buflen) 228{ 229 struct inode *inode = path->dentry->d_inode; 230 struct socket *sock = inode ? SOCKET_I(inode) : NULL; 231 struct sock *sk = sock ? sock->sk : NULL; 232 if (sk) { 233 snprintf(buffer, buflen, "socket:[family=%u:type=%u:" 234 "protocol=%u]", sk->sk_family, sk->sk_type, 235 sk->sk_protocol); 236 } else { 237 snprintf(buffer, buflen, "socket:[unknown]"); 238 } 239 return buffer; 240} 241 242/** 243 * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. 244 * 245 * @path: Pointer to "struct path". 246 * 247 * Returns the realpath of the given @path on success, NULL otherwise. 248 * 249 * If dentry is a directory, trailing '/' is appended. 250 * Characters out of 0x20 < c < 0x7F range are converted to 251 * \ooo style octal string. 252 * Character \ is converted to \\ string. 253 * 254 * These functions use kzalloc(), so the caller must call kfree() 255 * if these functions didn't return NULL. 256 */ 257char *tomoyo_realpath_from_path(struct path *path) 258{ 259 char *buf = NULL; 260 char *name = NULL; 261 unsigned int buf_len = PAGE_SIZE / 2; 262 struct dentry *dentry = path->dentry; 263 struct super_block *sb; 264 if (!dentry) 265 return NULL; 266 sb = dentry->d_sb; 267 while (1) { 268 char *pos; 269 struct inode *inode; 270 buf_len <<= 1; 271 kfree(buf); 272 buf = kmalloc(buf_len, GFP_NOFS); 273 if (!buf) 274 break; 275 /* To make sure that pos is '\0' terminated. */ 276 buf[buf_len - 1] = '\0'; 277 /* Get better name for socket. */ 278 if (sb->s_magic == SOCKFS_MAGIC) { 279 pos = tomoyo_get_socket_name(path, buf, buf_len - 1); 280 goto encode; 281 } 282 /* For "pipe:[\$]". */ 283 if (dentry->d_op && dentry->d_op->d_dname) { 284 pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); 285 goto encode; 286 } 287 inode = sb->s_root->d_inode; 288 /* 289 * Get local name for filesystems without rename() operation 290 * or dentry without vfsmount. 291 */ 292 if (!path->mnt || (inode->i_op && !inode->i_op->rename)) 293 pos = tomoyo_get_local_path(path->dentry, buf, 294 buf_len - 1); 295 /* Get absolute name for the rest. */ 296 else { 297 pos = tomoyo_get_absolute_path(path, buf, buf_len - 1); 298 /* 299 * Fall back to local name if absolute name is not 300 * available. 301 */ 302 if (pos == ERR_PTR(-EINVAL)) 303 pos = tomoyo_get_local_path(path->dentry, buf, 304 buf_len - 1); 305 } 306encode: 307 if (IS_ERR(pos)) 308 continue; 309 name = tomoyo_encode(pos); 310 break; 311 } 312 kfree(buf); 313 if (!name) 314 tomoyo_warn_oom(__func__); 315 return name; 316} 317 318/** 319 * tomoyo_realpath_nofollow - Get realpath of a pathname. 320 * 321 * @pathname: The pathname to solve. 322 * 323 * Returns the realpath of @pathname on success, NULL otherwise. 324 */ 325char *tomoyo_realpath_nofollow(const char *pathname) 326{ 327 struct path path; 328 329 if (pathname && kern_path(pathname, 0, &path) == 0) { 330 char *buf = tomoyo_realpath_from_path(&path); 331 path_put(&path); 332 return buf; 333 } 334 return NULL; 335} 336