1 /*	$OpenBSD: realpathtest.c,v 1.13 2019/08/06 11:38:16 bluhm Exp $ */
2 
3 /*
4  * Copyright (c) 2019 Bob Beck <beck@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 
22 #include <stdio.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <err.h>
28 #include <errno.h>
29 
30 /*
31  * Reference copy of userland realpath(3) implementation.
32  * Assumed to be correct.
33  */
34 extern char *realpath3(const char *pathname, char *resolved);
35 
36 struct rp_compare {
37 	char * resolv2;
38 	char * resolv3;
39 	char * r2;
40 	char * r3;
41 	int e2;
42 	int e3;
43 };
44 
45 static struct rp_compare
46 rpcompare(const char *pathname, char *resolv2,
47     char *resolv3) {
48 	struct rp_compare ret = {0};
49 
50 	errno = 0;
51 	ret.r2 = realpath(pathname, resolv2);
52 	ret.e2 = errno;
53 	ret.resolv2 = resolv2;
54 	errno = 0;
55 	ret.r3 = realpath3(pathname, resolv3);
56 	ret.e3 = errno;
57 	ret.resolv3 = resolv3;
58 	errno = 0;
59 	return ret;
60 }
61 
62 #define RP_SHOULD_SUCCEED(A, B, C) do {					\
63 	struct rp_compare rc = rpcompare(A, B, C);			\
64 	if (rc.r2 == NULL)  {						\
65 		errno = rc.e2;						\
66 		err(1, "%s:%d - realpath of '%s' failed", __FILE__,	\
67 		    __LINE__, (A));					\
68 	}								\
69 	if (rc.r3 == NULL)  {						\
70 		errno = rc.e3;						\
71 		err(1, "%s:%d - realpath3 of '%s' failed", __FILE__,	\
72 		    __LINE__, (A));					\
73 	}								\
74 	if (strcmp(rc.r2, rc.r3) != 0)					\
75 		errx(1, "%s:%d - realpath of '%s' result '%s', "	\
76 		    "expected '%s", __FILE__, __LINE__, (A), rc.r2,	\
77 		    rc.r3);						\
78 } while(0);
79 
80 #define RP_SHOULD_FAIL(A, B, C) do {					\
81 	struct rp_compare rc = rpcompare(A, B, C);			\
82 	if (rc.r2 != NULL)						\
83 		errx(1, "%s:%d - realpath of '%s' should have failed, "	\
84 		    "returned '%s'", __FILE__, __LINE__, (A), rc.r2);	\
85 	if (rc.r3 != NULL)						\
86 		errx(1, "%s:%d - realpath3 of '%s' should have failed, "\
87 		    "returned '%s'", __FILE__, __LINE__, (A), rc.r3);	\
88 	if (rc.e2 != rc.e3)						\
89 		errx(1, "%s:%d - realpath of '%s' errno %d does not "	\
90 		    "match realpath3 errno %d", __FILE__, __LINE__, (A),\
91 		    rc.e2, rc.e3 );					\
92 } while(0);
93 
94 int
95 main(int argc, char *argv[])
96 {
97 	int i, j;
98 	char big[PATH_MAX+PATH_MAX];
99 	char r2[PATH_MAX];
100 	char r3[PATH_MAX];
101 
102 	/* some basics */
103 	RP_SHOULD_FAIL(NULL, NULL, NULL);
104 	RP_SHOULD_FAIL("", NULL, NULL);
105 	RP_SHOULD_SUCCEED("/", NULL, NULL);
106 	RP_SHOULD_SUCCEED("//", NULL, NULL);
107 	RP_SHOULD_SUCCEED("/./", NULL, NULL);
108 	RP_SHOULD_SUCCEED("/./.", NULL, NULL);
109 	RP_SHOULD_SUCCEED("/./..", NULL, NULL);
110 	RP_SHOULD_SUCCEED("/../../", NULL, NULL);
111 	RP_SHOULD_SUCCEED("/tmp", NULL, NULL);
112 	RP_SHOULD_FAIL("/tmp/noreallydoesntexist", NULL, NULL);
113 	RP_SHOULD_FAIL("/tmp/noreallydoesntexist/stillnope", NULL, NULL);
114 	RP_SHOULD_SUCCEED("/bin", NULL, NULL);
115 	RP_SHOULD_FAIL("/bin/herp", NULL, NULL);
116 	RP_SHOULD_SUCCEED("////usr/bin", NULL, NULL);
117 	RP_SHOULD_FAIL("//.//usr/bin/.././../herp", r2, r3);
118 	RP_SHOULD_SUCCEED("/usr/include/machine/setjmp.h", r2, r3);
119 	RP_SHOULD_FAIL("//.//usr/bin/.././../herp/derp", r2, r3);
120 	RP_SHOULD_FAIL("/../.../usr/bin", r2, r3);
121 	RP_SHOULD_FAIL("/bsd/herp", r2, r3);
122 
123 	/* relative paths */
124 	if (mkdir("hoobla", 0755) == -1) {
125 		if (errno != EEXIST)
126 			err(1, "mkdir");
127 	}
128 	RP_SHOULD_SUCCEED("hoobla", r2, r3);
129 	RP_SHOULD_FAIL("hoobla/porkrind", r2, r3);
130 	RP_SHOULD_FAIL("hoobla/porkrind/peepee", r2, r3);
131 
132 	/* total size */
133 	memset(big, '/', PATH_MAX + 1);
134 	RP_SHOULD_FAIL(big, r2, r3);
135 
136 	/* component size */
137 	memset(big, 'a', PATH_MAX + 1);
138 	big[0] = '/';
139 	big[NAME_MAX+1] = '\0';
140 	RP_SHOULD_FAIL(big, r2, r3);
141 	memset(big, 'a', PATH_MAX + 1);
142 	big[0] = '/';
143 	big[NAME_MAX+2] = '\0';
144 	RP_SHOULD_FAIL(big, r2, r3);
145 
146 	/* long relatives back to root */
147 	for (i = 0; i < (PATH_MAX - 4); i += 3) {
148 		big[i] = '.';
149 		big[i+1] = '.';
150 		big[i+2] = '/';
151 	}
152 	i-= 3;
153 	strlcpy(big+i, "bsd", 4);
154 	RP_SHOULD_SUCCEED(big, r2, r3);
155 
156 	for (i = 0; i < (PATH_MAX - 5); i += 3) {
157 		big[i] = '.';
158 		big[i+1] = '.';
159 		big[i+2] = '/';
160 	}
161 	i-= 3;
162 	strlcpy(big+i, "bsd/", 5);
163 	RP_SHOULD_FAIL(big, r2, r3);
164 
165 	for (i = 0; i < (PATH_MAX - 5); i += 3) {
166 		big[i] = '.';
167 		big[i+1] = '.';
168 		big[i+2] = '/';
169 	}
170 	i-= 3;
171 	strlcpy(big+i, "derp", 5);
172 	RP_SHOULD_FAIL(big, r2, r3);
173 
174 	for (i = 0; i < (PATH_MAX - 6); i += 3) {
175 		big[i] = '.';
176 		big[i+1] = '.';
177 		big[i+2] = '/';
178 	}
179 	i-= 3;
180 	strlcpy(big+i, "derp/", 6);
181 	RP_SHOULD_FAIL(big, r2, r3);
182 
183 	for (i = 0; i < (PATH_MAX - 4); i += 3) {
184 		big[i] = '.';
185 		big[i+1] = '.';
186 		big[i+2] = '/';
187 	}
188 	i-= 3;
189 	strlcpy(big+i, "xxx", 4);
190 	RP_SHOULD_FAIL(big, r2, r3);
191 
192 	for (i = 0; i < (PATH_MAX - 8); i += 3) {
193 		big[i] = '.';
194 		big[i+1] = '.';
195 		big[i+2] = '/';
196 	}
197 	i-= 3;
198 	strlcpy(big+i, "xxx/../", 8);
199 	RP_SHOULD_FAIL(big, r2, r3);
200 
201 	return (0);
202 }
203