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