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