1 /* $OpenBSD: realpath.c,v 1.25 2019/05/30 13:22:48 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The names of the authors may not be used to endorse or promote 14 * products derived from this software without specific prior written 15 * permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <errno.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <limits.h> 35 #include <syslog.h> 36 #include <stdarg.h> 37 38 /* A slightly modified copy of this file exists in libexec/ld.so */ 39 40 /* 41 * char *realpath(const char *path, char resolved[PATH_MAX]); 42 * 43 * Find the real name of path, by removing all ".", ".." and symlink 44 * components. Returns (resolved) on success, or (NULL) on failure, 45 * in which case the path which caused trouble is left in (resolved). 46 */ 47 static char * 48 urealpath(const char *path, char *resolved) 49 { 50 const char *p; 51 char *q; 52 size_t left_len, resolved_len, next_token_len; 53 unsigned symlinks; 54 int serrno, mem_allocated; 55 ssize_t slen; 56 int trailingslash = 0; 57 char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; 58 59 if (path == NULL) { 60 errno = EINVAL; 61 return (NULL); 62 } 63 64 if (path[0] == '\0') { 65 errno = ENOENT; 66 return (NULL); 67 } 68 69 serrno = errno; 70 71 if (resolved == NULL) { 72 resolved = malloc(PATH_MAX); 73 if (resolved == NULL) 74 return (NULL); 75 mem_allocated = 1; 76 } else 77 mem_allocated = 0; 78 79 symlinks = 0; 80 if (path[0] == '/') { 81 resolved[0] = '/'; 82 resolved[1] = '\0'; 83 if (path[1] == '\0') 84 return (resolved); 85 resolved_len = 1; 86 left_len = strlcpy(left, path + 1, sizeof(left)); 87 } else { 88 if (getcwd(resolved, PATH_MAX) == NULL) { 89 if (mem_allocated) 90 free(resolved); 91 else 92 strlcpy(resolved, ".", PATH_MAX); 93 return (NULL); 94 } 95 resolved_len = strlen(resolved); 96 left_len = strlcpy(left, path, sizeof(left)); 97 } 98 if (left_len >= sizeof(left)) { 99 errno = ENAMETOOLONG; 100 goto err; 101 } 102 103 /* 104 * Iterate over path components in `left'. 105 */ 106 while (left_len != 0) { 107 /* 108 * Extract the next path component and adjust `left' 109 * and its length. 110 */ 111 p = strchr(left, '/'); 112 113 next_token_len = p ? (size_t) (p - left) : left_len; 114 memcpy(next_token, left, next_token_len); 115 next_token[next_token_len] = '\0'; 116 117 if (p != NULL) { 118 left_len -= next_token_len + 1; 119 memmove(left, p + 1, left_len + 1); 120 } else { 121 left[0] = '\0'; 122 left_len = 0; 123 } 124 125 if (resolved[resolved_len - 1] != '/') { 126 if (resolved_len + 1 >= PATH_MAX) { 127 errno = ENAMETOOLONG; 128 goto err; 129 } 130 resolved[resolved_len++] = '/'; 131 resolved[resolved_len] = '\0'; 132 } 133 if (next_token[0] == '\0') 134 continue; 135 else if (strcmp(next_token, ".") == 0) 136 continue; 137 else if (strcmp(next_token, "..") == 0) { 138 /* 139 * Strip the last path component except when we have 140 * single "/" 141 */ 142 if (resolved_len > 1) { 143 resolved[resolved_len - 1] = '\0'; 144 q = strrchr(resolved, '/') + 1; 145 *q = '\0'; 146 resolved_len = q - resolved; 147 } 148 continue; 149 } 150 151 /* 152 * Append the next path component and readlink() it. If 153 * readlink() fails we still can return successfully if 154 * it exists but isn't a symlink, or if there are no more 155 * path components left. 156 */ 157 resolved_len = strlcat(resolved, next_token, PATH_MAX); 158 if (resolved_len >= PATH_MAX) { 159 errno = ENAMETOOLONG; 160 goto err; 161 } 162 slen = readlink(resolved, symlink, sizeof(symlink)); 163 if (slen < 0) { 164 switch (errno) { 165 case EINVAL: 166 /* not a symlink, continue to next component */ 167 continue; 168 case ENOENT: 169 if (p == NULL) { 170 errno = serrno; 171 return (resolved); 172 } 173 /* FALLTHROUGH */ 174 default: 175 goto err; 176 } 177 } else if (slen == 0) { 178 errno = EINVAL; 179 goto err; 180 } else if (slen == sizeof(symlink)) { 181 errno = ENAMETOOLONG; 182 goto err; 183 } else { 184 if (symlinks++ > SYMLOOP_MAX) { 185 errno = ELOOP; 186 goto err; 187 } 188 189 symlink[slen] = '\0'; 190 if (symlink[0] == '/') { 191 resolved[1] = 0; 192 resolved_len = 1; 193 } else { 194 /* Strip the last path component. */ 195 q = strrchr(resolved, '/') + 1; 196 *q = '\0'; 197 resolved_len = q - resolved; 198 } 199 200 /* 201 * If there are any path components left, then 202 * append them to symlink. The result is placed 203 * in `left'. 204 */ 205 if (p != NULL) { 206 if (symlink[slen - 1] != '/') { 207 if (slen + 1 >= sizeof(symlink)) { 208 errno = ENAMETOOLONG; 209 goto err; 210 } 211 symlink[slen] = '/'; 212 symlink[slen + 1] = 0; 213 } 214 left_len = strlcat(symlink, left, sizeof(symlink)); 215 if (left_len >= sizeof(symlink)) { 216 errno = ENAMETOOLONG; 217 goto err; 218 } 219 } 220 left_len = strlcpy(left, symlink, sizeof(left)); 221 } 222 } 223 224 /* 225 * Remove trailing slash except when the resolved pathname 226 * is a single "/". 227 */ 228 if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 229 resolved[resolved_len - 1] = '\0'; 230 return (resolved); 231 232 err: 233 if (mem_allocated) 234 free(resolved); 235 return (NULL); 236 } 237 238 /* 239 * Copyright (c) 2019 Bob Beck <beck@openbsd.org> 240 * 241 * Permission to use, copy, modify, and/or distribute this software for any 242 * purpose with or without fee is hereby granted, provided that the above 243 * copyright notice and this permission notice appear in all copies. 244 * 245 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 246 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 247 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 248 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 249 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 250 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 251 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 252 */ 253 254 int __realpath(const char *pathname, char *resolved); 255 PROTO_NORMAL(__realpath); 256 257 /* 258 * wrapper for kernel __realpath 259 */ 260 261 char * 262 realpath(const char *path, char *resolved) 263 { 264 char pbuf[PATH_MAX], rbuf[PATH_MAX], expected[PATH_MAX]; 265 struct syslog_data sdata = SYSLOG_DATA_INIT; 266 int usererrno = 0, kernelerrno = 0, trailingslash = 0, save_errno; 267 int kernelonly = (getenv("USE_KERNEL_REALPATH") != NULL); 268 ssize_t i; 269 270 rbuf[0] = pbuf[0] = expected[0] = '\0'; 271 272 if (!kernelonly) { 273 memset(expected, 0, sizeof(expected)); 274 if (urealpath(path, expected) == NULL) { 275 usererrno = errno; 276 expected[0] = '\0'; 277 } 278 } 279 280 if (path == NULL) { 281 kernelerrno = EINVAL; 282 goto out; 283 } 284 if (path[0] == '\0') { 285 kernelerrno = ENOENT; 286 goto out; 287 } 288 if (strlcat(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf)) { 289 kernelerrno = ENAMETOOLONG; 290 goto out; 291 } 292 293 if (pbuf[strlen(pbuf) - 1] == '/') 294 trailingslash = 1; 295 296 if (__realpath(pbuf, rbuf) == -1) 297 kernelerrno = errno; 298 299 /* 300 * XXX XXX XXX 301 * 302 * The old userland implementation strips trailing slashes. 303 * According to Dr. POSIX, realpathing "/bsd" should be fine, 304 * realpathing "/bsd/" should return ENOTDIR. 305 * 306 * Similar, but *different* to the above, The old userland 307 * implementation allows for realpathing "/nonexistent" but 308 * not "/nonexistent/", Both those should return ENOENT 309 * according to POSIX. 310 * 311 * This hack should go away once we decide to match POSIX. 312 * which we should as soon as is convenient. 313 */ 314 if (kernelerrno == ENOTDIR) { 315 /* Try again without the trailing slash. */ 316 kernelerrno = 0; 317 for (i = strlen(pbuf); i > 1 && pbuf[i - 1] == '/'; i--) 318 pbuf[i - 1] = '\0'; 319 rbuf[0] = '\0'; 320 if (__realpath(pbuf, rbuf) == -1) 321 kernelerrno = errno; 322 } 323 324 out: 325 if (!kernelonly) { 326 /* syslog if kernel and userland are different */ 327 save_errno = errno; 328 if (strcmp(rbuf, expected) != 0 || (usererrno == 0 && 329 kernelerrno != 0)) 330 syslog_r(LOG_CRIT | LOG_CONS, &sdata, 331 "realpath '%s' -> '%s' errno %d, " 332 "expected '%s' errno %d", path, rbuf, 333 kernelerrno, expected, usererrno); 334 errno = save_errno; 335 336 /* use userland result */ 337 if (usererrno) { 338 errno = usererrno; 339 return NULL; 340 } 341 else 342 errno = 0; 343 if (resolved == NULL) 344 resolved = strdup(expected); 345 else if (strlcpy(resolved, expected, PATH_MAX) >= PATH_MAX) { 346 errno = ENAMETOOLONG; 347 return NULL; 348 } 349 350 } else { 351 /* use kernel result */ 352 if (kernelerrno) { 353 errno = kernelerrno; 354 return NULL; 355 } 356 else 357 errno = 0; 358 if (resolved == NULL) 359 resolved = strdup(rbuf); 360 else if (strlcpy(resolved, rbuf, PATH_MAX) >= PATH_MAX) { 361 errno = ENAMETOOLONG; 362 return NULL; 363 } 364 } 365 return (resolved); 366 } 367