]> sourceware.org Git - glibc.git/blame - hurd/lookup-retry.c
syslog: Fix integer overflow in __vsyslog_internal (CVE-2023-6780)
[glibc.git] / hurd / lookup-retry.c
CommitLineData
5fe915ee 1/* hairy bits of Hurd file name lookup
dff8da6b 2 Copyright (C) 1992-2024 Free Software Foundation, Inc.
5fe915ee
RM
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
5fe915ee
RM
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 13 Lesser General Public License for more details.
5fe915ee 14
41bdb6e2 15 You should have received a copy of the GNU Lesser General Public
59ba27a6 16 License along with the GNU C Library; if not, see
5a82c748 17 <https://www.gnu.org/licenses/>. */
5fe915ee
RM
18
19#include <hurd.h>
20#include <hurd/lookup.h>
21#include <hurd/term.h>
22#include <hurd/paths.h>
23#include <limits.h>
24#include <fcntl.h>
25#include <string.h>
eb96ffb0 26#include <_itoa.h>
4f75b7a0 27#include <eloop-threshold.h>
065957a3 28#include <unistd.h>
5fe915ee
RM
29
30/* Translate the error from dir_lookup into the error the user sees. */
31static inline error_t
32lookup_error (error_t error)
33{
34 switch (error)
35 {
36 case EOPNOTSUPP:
37 case MIG_BAD_ID:
38 /* These indicate that the server does not understand dir_lookup
39 at all. If it were a directory, it would, by definition. */
40 return ENOTDIR;
41 default:
42 return error;
43 }
44}
45
46error_t
47__hurd_file_name_lookup_retry (error_t (*use_init_port)
10589b4a 48 (int which, error_t (*operate) (file_t)),
5fe915ee
RM
49 file_t (*get_dtable_port) (int fd),
50 error_t (*lookup)
f8bee46c 51 (file_t dir, const char *name,
5fe915ee
RM
52 int flags, mode_t mode,
53 retry_type *do_retry, string_t retry_name,
54 mach_port_t *result),
55 enum retry_type doretry,
56 char retryname[1024],
57 int flags, mode_t mode,
58 file_t *result)
59{
60 error_t err;
61 char *file_name;
62 int nloops;
065957a3 63 file_t lastdir = MACH_PORT_NULL;
5fe915ee
RM
64
65 error_t lookup_op (file_t startdir)
66 {
e42ce0f4
FC
67 if (file_name[0] == '/' && file_name[1] != '\0')
68 {
69 while (file_name[1] == '/')
70 /* Remove double leading slash. */
71 file_name++;
72 if (file_name[1] != '\0')
73 /* Remove leading slash when we have more than the slash. */
74 file_name++;
75 }
5fe915ee
RM
76
77 return lookup_error ((*lookup) (startdir, file_name, flags, mode,
78 &doretry, retryname, result));
79 }
80 error_t reauthenticate (file_t unauth)
81 {
82 error_t err;
83 mach_port_t ref = __mach_reply_port ();
84 error_t reauth (auth_t auth)
85 {
86 return __auth_user_authenticate (auth, ref,
87 MACH_MSG_TYPE_MAKE_SEND,
88 result);
89 }
90 err = __io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
91 if (! err)
92 err = (*use_init_port) (INIT_PORT_AUTH, &reauth);
93 __mach_port_destroy (__mach_task_self (), ref);
94 __mach_port_deallocate (__mach_task_self (), unauth);
95 return err;
96 }
97
98 if (! lookup)
99 lookup = __dir_lookup;
100
101 nloops = 0;
102 err = 0;
103 do
104 {
105 file_t startdir = MACH_PORT_NULL;
106 int dirport = INIT_PORT_CWDIR;
107
108 switch (doretry)
109 {
110 case FS_RETRY_REAUTH:
111 if (err = reauthenticate (*result))
065957a3 112 goto out;
5fe915ee
RM
113 /* Fall through. */
114
115 case FS_RETRY_NORMAL:
4f75b7a0 116 if (nloops++ >= __eloop_threshold ())
5fe915ee
RM
117 {
118 __mach_port_deallocate (__mach_task_self (), *result);
065957a3
JW
119 err = ELOOP;
120 goto out;
5fe915ee
RM
121 }
122
123 /* An empty RETRYNAME indicates we have the final port. */
34a5a146 124 if (retryname[0] == '\0'
5fe915ee
RM
125 /* If reauth'd, we must do one more retry on "" to give the new
126 translator a chance to make a new port for us. */
34a5a146 127 && doretry == FS_RETRY_NORMAL)
5fe915ee
RM
128 {
129 if (flags & O_NOFOLLOW)
130 {
131 /* In Linux, O_NOFOLLOW means to reject symlinks. If we
132 did an O_NOLINK lookup above and io_stat here to check
489999cc 133 for S_IFLNK only, a translator like firmlink could easily
5fe915ee
RM
134 spoof this check by not showing S_IFLNK, but in fact
135 redirecting the lookup to some other name
136 (i.e. opening the very same holes a symlink would).
137
138 Instead we do an O_NOTRANS lookup above, and stat the
139 underlying node: if it has a translator set, and its
140 owner is not root (st_uid 0) then we reject it.
141 Since the motivation for this feature is security, and
142 that security presumes we trust the containing
143 directory, this check approximates the security of
144 refusing symlinks while accepting mount points.
145 Note that we actually permit something Linux doesn't:
146 we follow root-owned symlinks; if that is deemed
7f0d9e61 147 undesirable, we can add a final check for that
5fe915ee 148 one exception to our general translator-based rule. */
337738b7 149 struct stat64 st;
5fe915ee 150 err = __io_stat (*result, &st);
489999cc 151 if (!err)
5fe915ee 152 {
72103e73
ST
153 if (flags & O_DIRECTORY && !S_ISDIR (st.st_mode))
154 err = ENOTDIR;
489999cc
ST
155 if (S_ISLNK (st.st_mode))
156 err = ELOOP;
157 else if (st.st_mode & (S_IPTRANS|S_IATRANS))
5fe915ee 158 {
489999cc
ST
159 if (st.st_uid != 0)
160 err = ELOOP;
161 else if (st.st_mode & S_IPTRANS)
162 {
163 char buf[1024];
164 char *trans = buf;
f4315054 165 mach_msg_type_number_t translen = sizeof buf;
489999cc
ST
166 err = __file_get_translator (*result,
167 &trans, &translen);
168 if (!err
169 && translen > sizeof _HURD_SYMLINK
170 && !memcmp (trans,
171 _HURD_SYMLINK, sizeof _HURD_SYMLINK))
172 err = ELOOP;
173 }
5fe915ee
RM
174 }
175 }
176 }
177
178 /* We got a successful translation. Now apply any open-time
179 action flags we were passed. */
543f0cfc 180#if !IS_IN (rtld)
e095db0b
ST
181 if (!err && (flags & O_TRUNC))
182 {
183 /* Asked to truncate the file. */
184 err = __file_set_size (*result, 0);
185 if (!err)
186 {
187 struct timespec atime = { 0, UTIME_OMIT };
188 struct timespec mtime = { 0, UTIME_NOW };
189 __file_utimens (*result, atime, mtime);
190 }
191 }
543f0cfc 192#endif
5fe915ee
RM
193
194 if (err)
195 __mach_port_deallocate (__mach_task_self (), *result);
065957a3 196 goto out;
5fe915ee
RM
197 }
198
199 startdir = *result;
200 file_name = retryname;
201 break;
202
203 case FS_RETRY_MAGICAL:
204 switch (retryname[0])
205 {
206 case '/':
207 dirport = INIT_PORT_CRDIR;
208 if (*result != MACH_PORT_NULL)
209 __mach_port_deallocate (__mach_task_self (), *result);
4f75b7a0 210 if (nloops++ >= __eloop_threshold ())
065957a3
JW
211 {
212 err = ELOOP;
213 goto out;
214 }
5fe915ee
RM
215 file_name = &retryname[1];
216 break;
217
543f0cfc 218#if !IS_IN (rtld)
5fe915ee
RM
219 case 'f':
220 if (retryname[1] == 'd' && retryname[2] == '/')
221 {
222 int fd;
223 char *end;
224 int save = errno;
225 errno = 0;
10589b4a 226 fd = (int) __strtoul_internal (&retryname[3], &end, 10, 0);
a04549c1 227 if (end == NULL || errno /* Malformed number. */
5fe915ee
RM
228 /* Check for excess text after the number. A slash
229 is valid; it ends the component. Anything else
230 does not name a numeric file descriptor. */
a04549c1 231 || (*end != '/' && *end != '\0'))
5fe915ee
RM
232 {
233 errno = save;
065957a3
JW
234 err = ENOENT;
235 goto out;
5fe915ee
RM
236 }
237 if (! get_dtable_port)
238 err = EGRATUITOUS;
239 else
240 {
241 *result = (*get_dtable_port) (fd);
242 if (*result == MACH_PORT_NULL)
243 {
244 /* If the name was a proper number, but the file
245 descriptor does not exist, we return EBADF instead
246 of ENOENT. */
247 err = errno;
248 errno = save;
249 }
250 }
251 errno = save;
252 if (err)
065957a3 253 goto out;
5fe915ee 254 if (*end == '\0')
065957a3
JW
255 {
256 err = 0;
257 goto out;
258 }
5fe915ee
RM
259 else
260 {
261 /* Do a normal retry on the remaining components. */
262 startdir = *result;
263 file_name = end + 1; /* Skip the slash. */
264 break;
265 }
266 }
267 else
268 goto bad_magic;
269 break;
270
271 case 'm':
34a5a146
JM
272 if (retryname[1] == 'a' && retryname[2] == 'c'
273 && retryname[3] == 'h' && retryname[4] == 't'
274 && retryname[5] == 'y' && retryname[6] == 'p'
275 && retryname[7] == 'e')
5fe915ee
RM
276 {
277 error_t err;
278 struct host_basic_info hostinfo;
279 mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
5fe915ee
RM
280 /* XXX want client's host */
281 if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
8ad684db 282 (integer_t *) &hostinfo,
5fe915ee 283 &hostinfocnt))
065957a3 284 goto out;
5fe915ee 285 if (hostinfocnt != HOST_BASIC_INFO_COUNT)
065957a3
JW
286 {
287 err = EGRATUITOUS;
288 goto out;
289 }
f56ad617
SB
290 file_name = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
291 *--file_name = '/';
292 file_name = _itoa (hostinfo.cpu_type, file_name, 10, 0);
293 if (file_name < retryname)
5fe915ee 294 abort (); /* XXX write this right if this ever happens */
5fe915ee
RM
295 startdir = *result;
296 }
297 else
298 goto bad_magic;
299 break;
300
301 case 't':
302 if (retryname[1] == 't' && retryname[2] == 'y')
303 switch (retryname[3])
304 {
305 error_t opentty (file_t *result)
306 {
307 error_t err;
308 error_t ctty_open (file_t port)
309 {
310 if (port == MACH_PORT_NULL)
311 return ENXIO; /* No controlling terminal. */
312 return __termctty_open_terminal (port,
313 flags,
314 result);
315 }
316 err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
317 if (! err)
318 err = reauthenticate (*result);
319 return err;
320 }
321
322 case '\0':
065957a3
JW
323 err = opentty (result);
324 goto out;
5fe915ee
RM
325 case '/':
326 if (err = opentty (&startdir))
065957a3 327 goto out;
ee11682d 328 memmove (retryname, &retryname[4], strlen(retryname + 4) + 1);
5fe915ee
RM
329 break;
330 default:
331 goto bad_magic;
332 }
333 else
334 goto bad_magic;
335 break;
336
065957a3 337 case 'p':
a04549c1
JM
338 if (retryname[1] == 'i' && retryname[2] == 'd'
339 && (retryname[3] == '/' || retryname[3] == 0))
065957a3
JW
340 {
341 char *p, buf[1024]; /* XXX */
342 size_t len;
343 p = _itoa (__getpid (), &buf[sizeof buf], 10, 0);
344 len = &buf[sizeof buf] - p;
345 memcpy (buf, p, len);
ee11682d
ST
346 strncpy (buf + len, &retryname[3], sizeof buf - len - 1);
347 buf[sizeof buf - 1] = '\0';
065957a3
JW
348 strcpy (retryname, buf);
349
350 /* Do a normal retry on the remaining components. */
351 __mach_port_mod_refs (__mach_task_self (), lastdir,
352 MACH_PORT_RIGHT_SEND, 1);
353 startdir = lastdir;
354 file_name = retryname;
355 }
356 else
357 goto bad_magic;
358 break;
359
5fe915ee 360 bad_magic:
543f0cfc
SB
361#endif /* !IS_IN (rtld) */
362 default:
065957a3
JW
363 err = EGRATUITOUS;
364 goto out;
5fe915ee
RM
365 }
366 break;
367
368 default:
065957a3
JW
369 err = EGRATUITOUS;
370 goto out;
371 }
372
373 if (MACH_PORT_VALID (*result) && *result != lastdir)
374 {
375 if (MACH_PORT_VALID (lastdir))
376 __mach_port_deallocate (__mach_task_self (), lastdir);
377
378 lastdir = *result;
379 __mach_port_mod_refs (__mach_task_self (), lastdir,
380 MACH_PORT_RIGHT_SEND, 1);
5fe915ee
RM
381 }
382
383 if (startdir != MACH_PORT_NULL)
384 {
385 err = lookup_op (startdir);
386 __mach_port_deallocate (__mach_task_self (), startdir);
387 startdir = MACH_PORT_NULL;
388 }
389 else
390 err = (*use_init_port) (dirport, &lookup_op);
391 } while (! err);
392
065957a3
JW
393out:
394 if (MACH_PORT_VALID (lastdir))
395 __mach_port_deallocate (__mach_task_self (), lastdir);
396
5fe915ee
RM
397 return err;
398}
399weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)
This page took 0.484267 seconds and 5 git commands to generate.