summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorderaadt <>2019-06-17 03:13:17 +0000
committerderaadt <>2019-06-17 03:13:17 +0000
commitc7305ad941e6223f20a1f38219e82ac0fb4f5f77 (patch)
tree5f8dca5ba23bb3d02f17ad2365d8b4dfedf1bf0c /src
parent953e1a43933a52f4ce64bb01978dc5b353984020 (diff)
downloadopenbsd-c7305ad941e6223f20a1f38219e82ac0fb4f5f77.tar.gz
openbsd-c7305ad941e6223f20a1f38219e82ac0fb4f5f77.tar.bz2
openbsd-c7305ad941e6223f20a1f38219e82ac0fb4f5f77.zip
Remove old realpath(3), and the userland-vs-kernel realpath verification
code. We now user the simple userland wrapper on top of __realpath(2). The non-POSIX behaviour still remains, that is the next component to fix. From a diff by beck, but I managed to chainsaw it a bit further. Tested in snaps for a couple of days. ok beck
Diffstat (limited to 'src')
-rw-r--r--src/lib/libc/stdlib/realpath.c378
1 files changed, 44 insertions, 334 deletions
diff --git a/src/lib/libc/stdlib/realpath.c b/src/lib/libc/stdlib/realpath.c
index 8f600741a1..0d44430d05 100644
--- a/src/lib/libc/stdlib/realpath.c
+++ b/src/lib/libc/stdlib/realpath.c
@@ -1,242 +1,7 @@
1/* $OpenBSD: realpath.c,v 1.25 2019/05/30 13:22:48 deraadt Exp $ */ 1/* $OpenBSD: realpath.c,v 1.26 2019/06/17 03:13:17 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 */
47static char *
48urealpath(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
232err:
233 if (mem_allocated)
234 free(resolved);
235 return (NULL);
236}
237
238/* 2/*
239 * Copyright (c) 2019 Bob Beck <beck@openbsd.org> 3 * Copyright (c) 2019 Bob Beck <beck@openbsd.org>
4 * Copyright (c) 2019 Theo de Raadt <deraadt@openbsd.org>
240 * 5 *
241 * Permission to use, copy, modify, and/or distribute this software for any 6 * 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 7 * purpose with or without fee is hereby granted, provided that the above
@@ -251,6 +16,14 @@ err:
251 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
252 */ 17 */
253 18
19#include <errno.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23#include <limits.h>
24#include <syslog.h>
25#include <stdarg.h>
26
254int __realpath(const char *pathname, char *resolved); 27int __realpath(const char *pathname, char *resolved);
255PROTO_NORMAL(__realpath); 28PROTO_NORMAL(__realpath);
256 29
@@ -261,106 +34,43 @@ PROTO_NORMAL(__realpath);
261char * 34char *
262realpath(const char *path, char *resolved) 35realpath(const char *path, char *resolved)
263{ 36{
264 char pbuf[PATH_MAX], rbuf[PATH_MAX], expected[PATH_MAX]; 37 char rbuf[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 38
272 if (!kernelonly) { 39 if (__realpath(path, rbuf) == -1) {
273 memset(expected, 0, sizeof(expected)); 40 /*
274 if (urealpath(path, expected) == NULL) { 41 * XXX XXX XXX
275 usererrno = errno; 42 *
276 expected[0] = '\0'; 43 * The old userland implementation strips trailing slashes.
277 } 44 * According to Dr. POSIX, realpathing "/bsd" should be fine,
278 } 45 * realpathing "/bsd/" should return ENOTDIR.
279 46 *
280 if (path == NULL) { 47 * Similar, but *different* to the above, The old userland
281 kernelerrno = EINVAL; 48 * implementation allows for realpathing "/nonexistent" but
282 goto out; 49 * not "/nonexistent/", Both those should return ENOENT
283 } 50 * according to POSIX.
284 if (path[0] == '\0') { 51 *
285 kernelerrno = ENOENT; 52 * This hack should go away once we decide to match POSIX.
286 goto out; 53 * which we should as soon as is convenient.
287 } 54 */
288 if (strlcat(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf)) { 55 if (errno == ENOTDIR) {
289 kernelerrno = ENAMETOOLONG; 56 char pbuf[PATH_MAX];
290 goto out; 57 ssize_t i;
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
324out:
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 58
350 } else { 59 if (strlcpy(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf)) {
351 /* use kernel result */ 60 errno = ENAMETOOLONG;
352 if (kernelerrno) { 61 return NULL;
353 errno = kernelerrno; 62 }
354 return NULL; 63 /* Try again without the trailing slashes. */
355 } 64 for (i = strlen(pbuf); i > 1 && pbuf[i - 1] == '/'; i--)
356 else 65 pbuf[i - 1] = '\0';
357 errno = 0; 66 if (__realpath(pbuf, rbuf) == -1)
358 if (resolved == NULL) 67 return NULL;
359 resolved = strdup(rbuf); 68 } else
360 else if (strlcpy(resolved, rbuf, PATH_MAX) >= PATH_MAX) {
361 errno = ENAMETOOLONG;
362 return NULL; 69 return NULL;
363 }
364 } 70 }
71
72 if (resolved == NULL)
73 return (strdup(rbuf));
74 strlcpy(resolved, rbuf, PATH_MAX);
365 return (resolved); 75 return (resolved);
366} 76}