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 if (buflen >= 256) { 98 /* go to whatever namespace root we are under */ 99 pos = d_absolute_path(path, buffer, buflen - 1); 100 if (!IS_ERR(pos) && *pos == '/' && pos[1]) { 101 struct inode *inode = d_backing_inode(path->dentry); 102 if (inode && S_ISDIR(inode->i_mode)) { 103 buffer[buflen - 2] = '/'; 104 buffer[buflen - 1] = '\0'; 105 } 106 } 107 } 108 return pos; 109 } 110 111 /** 112 * tomoyo_get_dentry_path - Get the path of a dentry. 113 * 114 * @dentry: Pointer to "struct dentry". 115 * @buffer: Pointer to buffer to return value in. 116 * @buflen: Sizeof @buffer. 117 * 118 * Returns the buffer on success, an error code otherwise. 119 * 120 * If dentry is a directory, trailing '/' is appended. 121 */ 122 static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer, 123 const int buflen) 124 { 125 char *pos = ERR_PTR(-ENOMEM); 126 if (buflen >= 256) { 127 pos = dentry_path_raw(dentry, buffer, buflen - 1); 128 if (!IS_ERR(pos) && *pos == '/' && pos[1]) { 129 struct inode *inode = d_backing_inode(dentry); 130 if (inode && S_ISDIR(inode->i_mode)) { 131 buffer[buflen - 2] = '/'; 132 buffer[buflen - 1] = '\0'; 133 } 134 } 135 } 136 return pos; 137 } 138 139 /** 140 * tomoyo_get_local_path - Get the path of a dentry. 141 * 142 * @dentry: Pointer to "struct dentry". 143 * @buffer: Pointer to buffer to return value in. 144 * @buflen: Sizeof @buffer. 145 * 146 * Returns the buffer on success, an error code otherwise. 147 */ 148 static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer, 149 const int buflen) 150 { 151 struct super_block *sb = dentry->d_sb; 152 char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen); 153 if (IS_ERR(pos)) 154 return pos; 155 /* Convert from $PID to self if $PID is current thread. */ 156 if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { 157 char *ep; 158 const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); 159 if (*ep == '/' && pid && pid == 160 task_tgid_nr_ns(current, sb->s_fs_info)) { 161 pos = ep - 5; 162 if (pos < buffer) 163 goto out; 164 memmove(pos, "/self", 5); 165 } 166 goto prepend_filesystem_name; 167 } 168 /* Use filesystem name for unnamed devices. */ 169 if (!MAJOR(sb->s_dev)) 170 goto prepend_filesystem_name; 171 { 172 struct inode *inode = d_backing_inode(sb->s_root); 173 /* 174 * Use filesystem name if filesystem does not support rename() 175 * operation. 176 */ 177 if (!inode->i_op->rename) 178 goto prepend_filesystem_name; 179 } 180 /* Prepend device name. */ 181 { 182 char name[64]; 183 int name_len; 184 const dev_t dev = sb->s_dev; 185 name[sizeof(name) - 1] = '\0'; 186 snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), 187 MINOR(dev)); 188 name_len = strlen(name); 189 pos -= name_len; 190 if (pos < buffer) 191 goto out; 192 memmove(pos, name, name_len); 193 return pos; 194 } 195 /* Prepend filesystem name. */ 196 prepend_filesystem_name: 197 { 198 const char *name = sb->s_type->name; 199 const int name_len = strlen(name); 200 pos -= name_len + 1; 201 if (pos < buffer) 202 goto out; 203 memmove(pos, name, name_len); 204 pos[name_len] = ':'; 205 } 206 return pos; 207 out: 208 return ERR_PTR(-ENOMEM); 209 } 210 211 /** 212 * tomoyo_get_socket_name - Get the name of a socket. 213 * 214 * @path: Pointer to "struct path". 215 * @buffer: Pointer to buffer to return value in. 216 * @buflen: Sizeof @buffer. 217 * 218 * Returns the buffer. 219 */ 220 static char *tomoyo_get_socket_name(const struct path *path, char * const buffer, 221 const int buflen) 222 { 223 struct inode *inode = d_backing_inode(path->dentry); 224 struct socket *sock = inode ? SOCKET_I(inode) : NULL; 225 struct sock *sk = sock ? sock->sk : NULL; 226 if (sk) { 227 snprintf(buffer, buflen, "socket:[family=%u:type=%u:" 228 "protocol=%u]", sk->sk_family, sk->sk_type, 229 sk->sk_protocol); 230 } else { 231 snprintf(buffer, buflen, "socket:[unknown]"); 232 } 233 return buffer; 234 } 235 236 /** 237 * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. 238 * 239 * @path: Pointer to "struct path". 240 * 241 * Returns the realpath of the given @path on success, NULL otherwise. 242 * 243 * If dentry is a directory, trailing '/' is appended. 244 * Characters out of 0x20 < c < 0x7F range are converted to 245 * \ooo style octal string. 246 * Character \ is converted to \\ string. 247 * 248 * These functions use kzalloc(), so the caller must call kfree() 249 * if these functions didn't return NULL. 250 */ 251 char *tomoyo_realpath_from_path(const struct path *path) 252 { 253 char *buf = NULL; 254 char *name = NULL; 255 unsigned int buf_len = PAGE_SIZE / 2; 256 struct dentry *dentry = path->dentry; 257 struct super_block *sb; 258 if (!dentry) 259 return NULL; 260 sb = dentry->d_sb; 261 while (1) { 262 char *pos; 263 struct inode *inode; 264 buf_len <<= 1; 265 kfree(buf); 266 buf = kmalloc(buf_len, GFP_NOFS); 267 if (!buf) 268 break; 269 /* To make sure that pos is '\0' terminated. */ 270 buf[buf_len - 1] = '\0'; 271 /* Get better name for socket. */ 272 if (sb->s_magic == SOCKFS_MAGIC) { 273 pos = tomoyo_get_socket_name(path, buf, buf_len - 1); 274 goto encode; 275 } 276 /* For "pipe:[\$]". */ 277 if (dentry->d_op && dentry->d_op->d_dname) { 278 pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); 279 goto encode; 280 } 281 inode = d_backing_inode(sb->s_root); 282 /* 283 * Get local name for filesystems without rename() operation 284 * or dentry without vfsmount. 285 */ 286 if (!path->mnt || 287 (!inode->i_op->rename)) 288 pos = tomoyo_get_local_path(path->dentry, buf, 289 buf_len - 1); 290 /* Get absolute name for the rest. */ 291 else { 292 pos = tomoyo_get_absolute_path(path, buf, buf_len - 1); 293 /* 294 * Fall back to local name if absolute name is not 295 * available. 296 */ 297 if (pos == ERR_PTR(-EINVAL)) 298 pos = tomoyo_get_local_path(path->dentry, buf, 299 buf_len - 1); 300 } 301 encode: 302 if (IS_ERR(pos)) 303 continue; 304 name = tomoyo_encode(pos); 305 break; 306 } 307 kfree(buf); 308 if (!name) 309 tomoyo_warn_oom(__func__); 310 return name; 311 } 312 313 /** 314 * tomoyo_realpath_nofollow - Get realpath of a pathname. 315 * 316 * @pathname: The pathname to solve. 317 * 318 * Returns the realpath of @pathname on success, NULL otherwise. 319 */ 320 char *tomoyo_realpath_nofollow(const char *pathname) 321 { 322 struct path path; 323 324 if (pathname && kern_path(pathname, 0, &path) == 0) { 325 char *buf = tomoyo_realpath_from_path(&path); 326 path_put(&path); 327 return buf; 328 } 329 return NULL; 330 } 331