Bug Summary

File:src/usr.bin/ssh/sftp-server/../misc.c
Warning:line 1197, column 7
Value stored to 'i' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name misc.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/ssh/sftp-server/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/ssh/sftp-server/.. -D WITH_OPENSSL -D WITH_ZLIB -D ENABLE_PKCS11 -D HAVE_DLOPEN -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -Wno-unused-parameter -fdebug-compilation-dir=/usr/src/usr.bin/ssh/sftp-server/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/ssh/sftp-server/../misc.c
1/* $OpenBSD: misc.c,v 1.172 2022/01/08 07:32:45 djm Exp $ */
2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2005-2020 Damien Miller. All rights reserved.
5 * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20
21#include <sys/types.h>
22#include <sys/ioctl.h>
23#include <sys/socket.h>
24#include <sys/stat.h>
25#include <sys/time.h>
26#include <sys/wait.h>
27#include <sys/un.h>
28
29#include <net/if.h>
30#include <netinet/in.h>
31#include <netinet/ip.h>
32#include <netinet/tcp.h>
33#include <arpa/inet.h>
34
35#include <ctype.h>
36#include <errno(*__errno()).h>
37#include <fcntl.h>
38#include <netdb.h>
39#include <paths.h>
40#include <pwd.h>
41#include <libgen.h>
42#include <limits.h>
43#include <poll.h>
44#include <signal.h>
45#include <stdarg.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50
51#include "xmalloc.h"
52#include "misc.h"
53#include "log.h"
54#include "ssh.h"
55#include "sshbuf.h"
56#include "ssherr.h"
57
58/* remove newline at end of string */
59char *
60chop(char *s)
61{
62 char *t = s;
63 while (*t) {
64 if (*t == '\n' || *t == '\r') {
65 *t = '\0';
66 return s;
67 }
68 t++;
69 }
70 return s;
71
72}
73
74/* remove whitespace from end of string */
75void
76rtrim(char *s)
77{
78 size_t i;
79
80 if ((i = strlen(s)) == 0)
81 return;
82 for (i--; i > 0; i--) {
83 if (isspace((int)s[i]))
84 s[i] = '\0';
85 }
86}
87
88/* set/unset filedescriptor to non-blocking */
89int
90set_nonblock(int fd)
91{
92 int val;
93
94 val = fcntl(fd, F_GETFL3);
95 if (val == -1) {
96 error("fcntl(%d, F_GETFL): %s", fd, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 96, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "fcntl(%d, F_GETFL): %s"
, fd, strerror((*__errno())))
;
97 return (-1);
98 }
99 if (val & O_NONBLOCK0x0004) {
100 debug3("fd %d is O_NONBLOCK", fd)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 100, 0, SYSLOG_LEVEL_DEBUG3, ((void *)0), "fd %d is O_NONBLOCK"
, fd)
;
101 return (0);
102 }
103 debug2("fd %d setting O_NONBLOCK", fd)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 103, 0, SYSLOG_LEVEL_DEBUG2, ((void *)0), "fd %d setting O_NONBLOCK"
, fd)
;
104 val |= O_NONBLOCK0x0004;
105 if (fcntl(fd, F_SETFL4, val) == -1) {
106 debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd,sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 107, 0, SYSLOG_LEVEL_DEBUG1, ((void *)0), "fcntl(%d, F_SETFL, O_NONBLOCK): %s"
, fd, strerror((*__errno())))
107 strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 107, 0, SYSLOG_LEVEL_DEBUG1, ((void *)0), "fcntl(%d, F_SETFL, O_NONBLOCK): %s"
, fd, strerror((*__errno())))
;
108 return (-1);
109 }
110 return (0);
111}
112
113int
114unset_nonblock(int fd)
115{
116 int val;
117
118 val = fcntl(fd, F_GETFL3);
119 if (val == -1) {
120 error("fcntl(%d, F_GETFL): %s", fd, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 120, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "fcntl(%d, F_GETFL): %s"
, fd, strerror((*__errno())))
;
121 return (-1);
122 }
123 if (!(val & O_NONBLOCK0x0004)) {
124 debug3("fd %d is not O_NONBLOCK", fd)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 124, 0, SYSLOG_LEVEL_DEBUG3, ((void *)0), "fd %d is not O_NONBLOCK"
, fd)
;
125 return (0);
126 }
127 debug("fd %d clearing O_NONBLOCK", fd)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 127, 0, SYSLOG_LEVEL_DEBUG1, ((void *)0), "fd %d clearing O_NONBLOCK"
, fd)
;
128 val &= ~O_NONBLOCK0x0004;
129 if (fcntl(fd, F_SETFL4, val) == -1) {
130 debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s",sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 131, 0, SYSLOG_LEVEL_DEBUG1, ((void *)0), "fcntl(%d, F_SETFL, ~O_NONBLOCK): %s"
, fd, strerror((*__errno())))
131 fd, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 131, 0, SYSLOG_LEVEL_DEBUG1, ((void *)0), "fcntl(%d, F_SETFL, ~O_NONBLOCK): %s"
, fd, strerror((*__errno())))
;
132 return (-1);
133 }
134 return (0);
135}
136
137const char *
138ssh_gai_strerror(int gaierr)
139{
140 if (gaierr == EAI_SYSTEM-11 && errno(*__errno()) != 0)
141 return strerror(errno(*__errno()));
142 return gai_strerror(gaierr);
143}
144
145/* disable nagle on socket */
146void
147set_nodelay(int fd)
148{
149 int opt;
150 socklen_t optlen;
151
152 optlen = sizeof opt;
153 if (getsockopt(fd, IPPROTO_TCP6, TCP_NODELAY0x01, &opt, &optlen) == -1) {
154 debug("getsockopt TCP_NODELAY: %.100s", strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 154, 0, SYSLOG_LEVEL_DEBUG1, ((void *)0), "getsockopt TCP_NODELAY: %.100s"
, strerror((*__errno())))
;
155 return;
156 }
157 if (opt == 1) {
158 debug2("fd %d is TCP_NODELAY", fd)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 158, 0, SYSLOG_LEVEL_DEBUG2, ((void *)0), "fd %d is TCP_NODELAY"
, fd)
;
159 return;
160 }
161 opt = 1;
162 debug2("fd %d setting TCP_NODELAY", fd)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 162, 0, SYSLOG_LEVEL_DEBUG2, ((void *)0), "fd %d setting TCP_NODELAY"
, fd)
;
163 if (setsockopt(fd, IPPROTO_TCP6, TCP_NODELAY0x01, &opt, sizeof opt) == -1)
164 error("setsockopt TCP_NODELAY: %.100s", strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 164, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "setsockopt TCP_NODELAY: %.100s"
, strerror((*__errno())))
;
165}
166
167/* Allow local port reuse in TIME_WAIT */
168int
169set_reuseaddr(int fd)
170{
171 int on = 1;
172
173 if (setsockopt(fd, SOL_SOCKET0xffff, SO_REUSEADDR0x0004, &on, sizeof(on)) == -1) {
174 error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 174, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "setsockopt SO_REUSEADDR fd %d: %s"
, fd, strerror((*__errno())))
;
175 return -1;
176 }
177 return 0;
178}
179
180/* Get/set routing domain */
181char *
182get_rdomain(int fd)
183{
184 int rtable;
185 char *ret;
186 socklen_t len = sizeof(rtable);
187
188 if (getsockopt(fd, SOL_SOCKET0xffff, SO_RTABLE0x1021, &rtable, &len) == -1) {
189 error("Failed to get routing domain for fd %d: %s",sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 190, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "Failed to get routing domain for fd %d: %s"
, fd, strerror((*__errno())))
190 fd, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 190, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "Failed to get routing domain for fd %d: %s"
, fd, strerror((*__errno())))
;
191 return NULL((void *)0);
192 }
193 xasprintf(&ret, "%d", rtable);
194 return ret;
195}
196
197int
198set_rdomain(int fd, const char *name)
199{
200 int rtable;
201 const char *errstr;
202
203 if (name == NULL((void *)0))
204 return 0; /* default table */
205
206 rtable = (int)strtonum(name, 0, 255, &errstr);
207 if (errstr != NULL((void *)0)) {
208 /* Shouldn't happen */
209 error("Invalid routing domain \"%s\": %s", name, errstr)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 209, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "Invalid routing domain \"%s\": %s"
, name, errstr)
;
210 return -1;
211 }
212 if (setsockopt(fd, SOL_SOCKET0xffff, SO_RTABLE0x1021,
213 &rtable, sizeof(rtable)) == -1) {
214 error("Failed to set routing domain %d on fd %d: %s",sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 215, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "Failed to set routing domain %d on fd %d: %s"
, rtable, fd, strerror((*__errno())))
215 rtable, fd, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 215, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "Failed to set routing domain %d on fd %d: %s"
, rtable, fd, strerror((*__errno())))
;
216 return -1;
217 }
218 return 0;
219}
220
221int
222get_sock_af(int fd)
223{
224 struct sockaddr_storage to;
225 socklen_t tolen = sizeof(to);
226
227 memset(&to, 0, sizeof(to));
228 if (getsockname(fd, (struct sockaddr *)&to, &tolen) == -1)
229 return -1;
230 return to.ss_family;
231}
232
233void
234set_sock_tos(int fd, int tos)
235{
236 int af;
237
238 switch ((af = get_sock_af(fd))) {
239 case -1:
240 /* assume not a socket */
241 break;
242 case AF_INET2:
243 debug3_f("set socket %d IP_TOS 0x%02x", fd, tos)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 243, 1, SYSLOG_LEVEL_DEBUG3, ((void *)0), "set socket %d IP_TOS 0x%02x"
, fd, tos)
;
244 if (setsockopt(fd, IPPROTO_IP0, IP_TOS3,
245 &tos, sizeof(tos)) == -1) {
246 error("setsockopt socket %d IP_TOS %d: %s:",sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 247, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "setsockopt socket %d IP_TOS %d: %s:"
, fd, tos, strerror((*__errno())))
247 fd, tos, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 247, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "setsockopt socket %d IP_TOS %d: %s:"
, fd, tos, strerror((*__errno())))
;
248 }
249 break;
250 case AF_INET624:
251 debug3_f("set socket %d IPV6_TCLASS 0x%02x", fd, tos)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 251, 1, SYSLOG_LEVEL_DEBUG3, ((void *)0), "set socket %d IPV6_TCLASS 0x%02x"
, fd, tos)
;
252 if (setsockopt(fd, IPPROTO_IPV641, IPV6_TCLASS61,
253 &tos, sizeof(tos)) == -1) {
254 error("setsockopt socket %d IPV6_TCLASS %d: %.100s:",sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 255, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "setsockopt socket %d IPV6_TCLASS %d: %.100s:"
, fd, tos, strerror((*__errno())))
255 fd, tos, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 255, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "setsockopt socket %d IPV6_TCLASS %d: %.100s:"
, fd, tos, strerror((*__errno())))
;
256 }
257 break;
258 default:
259 debug2_f("unsupported socket family %d", af)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 259, 1, SYSLOG_LEVEL_DEBUG2, ((void *)0), "unsupported socket family %d"
, af)
;
260 break;
261 }
262}
263
264/*
265 * Wait up to *timeoutp milliseconds for events on fd. Updates
266 * *timeoutp with time remaining.
267 * Returns 0 if fd ready or -1 on timeout or error (see errno).
268 */
269static int
270waitfd(int fd, int *timeoutp, short events)
271{
272 struct pollfd pfd;
273 struct timeval t_start;
274 int oerrno, r;
275
276 pfd.fd = fd;
277 pfd.events = events;
278 for (; *timeoutp >= 0;) {
279 monotime_tv(&t_start);
280 r = poll(&pfd, 1, *timeoutp);
281 oerrno = errno(*__errno());
282 ms_subtract_diff(&t_start, timeoutp);
283 errno(*__errno()) = oerrno;
284 if (r > 0)
285 return 0;
286 else if (r == -1 && errno(*__errno()) != EAGAIN35 && errno(*__errno()) != EINTR4)
287 return -1;
288 else if (r == 0)
289 break;
290 }
291 /* timeout */
292 errno(*__errno()) = ETIMEDOUT60;
293 return -1;
294}
295
296/*
297 * Wait up to *timeoutp milliseconds for fd to be readable. Updates
298 * *timeoutp with time remaining.
299 * Returns 0 if fd ready or -1 on timeout or error (see errno).
300 */
301int
302waitrfd(int fd, int *timeoutp) {
303 return waitfd(fd, timeoutp, POLLIN0x0001);
304}
305
306/*
307 * Attempt a non-blocking connect(2) to the specified address, waiting up to
308 * *timeoutp milliseconds for the connection to complete. If the timeout is
309 * <=0, then wait indefinitely.
310 *
311 * Returns 0 on success or -1 on failure.
312 */
313int
314timeout_connect(int sockfd, const struct sockaddr *serv_addr,
315 socklen_t addrlen, int *timeoutp)
316{
317 int optval = 0;
318 socklen_t optlen = sizeof(optval);
319
320 /* No timeout: just do a blocking connect() */
321 if (timeoutp == NULL((void *)0) || *timeoutp <= 0)
322 return connect(sockfd, serv_addr, addrlen);
323
324 set_nonblock(sockfd);
325 for (;;) {
326 if (connect(sockfd, serv_addr, addrlen) == 0) {
327 /* Succeeded already? */
328 unset_nonblock(sockfd);
329 return 0;
330 } else if (errno(*__errno()) == EINTR4)
331 continue;
332 else if (errno(*__errno()) != EINPROGRESS36)
333 return -1;
334 break;
335 }
336
337 if (waitfd(sockfd, timeoutp, POLLIN0x0001 | POLLOUT0x0004) == -1)
338 return -1;
339
340 /* Completed or failed */
341 if (getsockopt(sockfd, SOL_SOCKET0xffff, SO_ERROR0x1007, &optval, &optlen) == -1) {
342 debug("getsockopt: %s", strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 342, 0, SYSLOG_LEVEL_DEBUG1, ((void *)0), "getsockopt: %s",
strerror((*__errno())))
;
343 return -1;
344 }
345 if (optval != 0) {
346 errno(*__errno()) = optval;
347 return -1;
348 }
349 unset_nonblock(sockfd);
350 return 0;
351}
352
353/* Characters considered whitespace in strsep calls. */
354#define WHITESPACE" \t\r\n" " \t\r\n"
355#define QUOTE"\"" "\""
356
357/* return next token in configuration line */
358static char *
359strdelim_internal(char **s, int split_equals)
360{
361 char *old;
362 int wspace = 0;
363
364 if (*s == NULL((void *)0))
365 return NULL((void *)0);
366
367 old = *s;
368
369 *s = strpbrk(*s,
370 split_equals ? WHITESPACE" \t\r\n" QUOTE"\"" "=" : WHITESPACE" \t\r\n" QUOTE"\"");
371 if (*s == NULL((void *)0))
372 return (old);
373
374 if (*s[0] == '\"') {
375 memmove(*s, *s + 1, strlen(*s)); /* move nul too */
376 /* Find matching quote */
377 if ((*s = strpbrk(*s, QUOTE"\"")) == NULL((void *)0)) {
378 return (NULL((void *)0)); /* no matching quote */
379 } else {
380 *s[0] = '\0';
381 *s += strspn(*s + 1, WHITESPACE" \t\r\n") + 1;
382 return (old);
383 }
384 }
385
386 /* Allow only one '=' to be skipped */
387 if (split_equals && *s[0] == '=')
388 wspace = 1;
389 *s[0] = '\0';
390
391 /* Skip any extra whitespace after first token */
392 *s += strspn(*s + 1, WHITESPACE" \t\r\n") + 1;
393 if (split_equals && *s[0] == '=' && !wspace)
394 *s += strspn(*s + 1, WHITESPACE" \t\r\n") + 1;
395
396 return (old);
397}
398
399/*
400 * Return next token in configuration line; splts on whitespace or a
401 * single '=' character.
402 */
403char *
404strdelim(char **s)
405{
406 return strdelim_internal(s, 1);
407}
408
409/*
410 * Return next token in configuration line; splts on whitespace only.
411 */
412char *
413strdelimw(char **s)
414{
415 return strdelim_internal(s, 0);
416}
417
418struct passwd *
419pwcopy(struct passwd *pw)
420{
421 struct passwd *copy = xcalloc(1, sizeof(*copy));
422
423 copy->pw_name = xstrdup(pw->pw_name);
424 copy->pw_passwd = xstrdup(pw->pw_passwd);
425 copy->pw_gecos = xstrdup(pw->pw_gecos);
426 copy->pw_uid = pw->pw_uid;
427 copy->pw_gid = pw->pw_gid;
428 copy->pw_expire = pw->pw_expire;
429 copy->pw_change = pw->pw_change;
430 copy->pw_class = xstrdup(pw->pw_class);
431 copy->pw_dir = xstrdup(pw->pw_dir);
432 copy->pw_shell = xstrdup(pw->pw_shell);
433 return copy;
434}
435
436/*
437 * Convert ASCII string to TCP/IP port number.
438 * Port must be >=0 and <=65535.
439 * Return -1 if invalid.
440 */
441int
442a2port(const char *s)
443{
444 struct servent *se;
445 long long port;
446 const char *errstr;
447
448 port = strtonum(s, 0, 65535, &errstr);
449 if (errstr == NULL((void *)0))
450 return (int)port;
451 if ((se = getservbyname(s, "tcp")) != NULL((void *)0))
452 return ntohs(se->s_port)(__uint16_t)(__builtin_constant_p(se->s_port) ? (__uint16_t
)(((__uint16_t)(se->s_port) & 0xffU) << 8 | ((__uint16_t
)(se->s_port) & 0xff00U) >> 8) : __swap16md(se->
s_port))
;
453 return -1;
454}
455
456int
457a2tun(const char *s, int *remote)
458{
459 const char *errstr = NULL((void *)0);
460 char *sp, *ep;
461 int tun;
462
463 if (remote != NULL((void *)0)) {
464 *remote = SSH_TUNID_ANY0x7fffffff;
465 sp = xstrdup(s);
466 if ((ep = strchr(sp, ':')) == NULL((void *)0)) {
467 free(sp);
468 return (a2tun(s, NULL((void *)0)));
469 }
470 ep[0] = '\0'; ep++;
471 *remote = a2tun(ep, NULL((void *)0));
472 tun = a2tun(sp, NULL((void *)0));
473 free(sp);
474 return (*remote == SSH_TUNID_ERR(0x7fffffff - 1) ? *remote : tun);
475 }
476
477 if (strcasecmp(s, "any") == 0)
478 return (SSH_TUNID_ANY0x7fffffff);
479
480 tun = strtonum(s, 0, SSH_TUNID_MAX(0x7fffffff - 2), &errstr);
481 if (errstr != NULL((void *)0))
482 return (SSH_TUNID_ERR(0x7fffffff - 1));
483
484 return (tun);
485}
486
487#define SECONDS1 1
488#define MINUTES(1 * 60) (SECONDS1 * 60)
489#define HOURS((1 * 60) * 60) (MINUTES(1 * 60) * 60)
490#define DAYS(((1 * 60) * 60) * 24) (HOURS((1 * 60) * 60) * 24)
491#define WEEKS((((1 * 60) * 60) * 24) * 7) (DAYS(((1 * 60) * 60) * 24) * 7)
492
493/*
494 * Convert a time string into seconds; format is
495 * a sequence of:
496 * time[qualifier]
497 *
498 * Valid time qualifiers are:
499 * <none> seconds
500 * s|S seconds
501 * m|M minutes
502 * h|H hours
503 * d|D days
504 * w|W weeks
505 *
506 * Examples:
507 * 90m 90 minutes
508 * 1h30m 90 minutes
509 * 2d 2 days
510 * 1w 1 week
511 *
512 * Return -1 if time string is invalid.
513 */
514int
515convtime(const char *s)
516{
517 long total, secs, multiplier;
518 const char *p;
519 char *endp;
520
521 errno(*__errno()) = 0;
522 total = 0;
523 p = s;
524
525 if (p == NULL((void *)0) || *p == '\0')
526 return -1;
527
528 while (*p) {
529 secs = strtol(p, &endp, 10);
530 if (p == endp ||
531 (errno(*__errno()) == ERANGE34 && (secs == INT_MIN(-2147483647 -1) || secs == INT_MAX2147483647)) ||
532 secs < 0)
533 return -1;
534
535 multiplier = 1;
536 switch (*endp++) {
537 case '\0':
538 endp--;
539 break;
540 case 's':
541 case 'S':
542 break;
543 case 'm':
544 case 'M':
545 multiplier = MINUTES(1 * 60);
546 break;
547 case 'h':
548 case 'H':
549 multiplier = HOURS((1 * 60) * 60);
550 break;
551 case 'd':
552 case 'D':
553 multiplier = DAYS(((1 * 60) * 60) * 24);
554 break;
555 case 'w':
556 case 'W':
557 multiplier = WEEKS((((1 * 60) * 60) * 24) * 7);
558 break;
559 default:
560 return -1;
561 }
562 if (secs > INT_MAX2147483647 / multiplier)
563 return -1;
564 secs *= multiplier;
565 if (total > INT_MAX2147483647 - secs)
566 return -1;
567 total += secs;
568 if (total < 0)
569 return -1;
570 p = endp;
571 }
572
573 return total;
574}
575
576#define TF_BUFS8 8
577#define TF_LEN9 9
578
579const char *
580fmt_timeframe(time_t t)
581{
582 char *buf;
583 static char tfbuf[TF_BUFS8][TF_LEN9]; /* ring buffer */
584 static int idx = 0;
585 unsigned int sec, min, hrs, day;
586 unsigned long long week;
587
588 buf = tfbuf[idx++];
589 if (idx == TF_BUFS8)
590 idx = 0;
591
592 week = t;
593
594 sec = week % 60;
595 week /= 60;
596 min = week % 60;
597 week /= 60;
598 hrs = week % 24;
599 week /= 24;
600 day = week % 7;
601 week /= 7;
602
603 if (week > 0)
604 snprintf(buf, TF_LEN9, "%02lluw%01ud%02uh", week, day, hrs);
605 else if (day > 0)
606 snprintf(buf, TF_LEN9, "%01ud%02uh%02um", day, hrs, min);
607 else
608 snprintf(buf, TF_LEN9, "%02u:%02u:%02u", hrs, min, sec);
609
610 return (buf);
611}
612
613/*
614 * Returns a standardized host+port identifier string.
615 * Caller must free returned string.
616 */
617char *
618put_host_port(const char *host, u_short port)
619{
620 char *hoststr;
621
622 if (port == 0 || port == SSH_DEFAULT_PORT22)
623 return(xstrdup(host));
624 if (asprintf(&hoststr, "[%s]:%d", host, (int)port) == -1)
625 fatal("put_host_port: asprintf: %s", strerror(errno))sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 625, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "put_host_port: asprintf: %s"
, strerror((*__errno())))
;
626 debug3("put_host_port: %s", hoststr)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 626, 0, SYSLOG_LEVEL_DEBUG3, ((void *)0), "put_host_port: %s"
, hoststr)
;
627 return hoststr;
628}
629
630/*
631 * Search for next delimiter between hostnames/addresses and ports.
632 * Argument may be modified (for termination).
633 * Returns *cp if parsing succeeds.
634 * *cp is set to the start of the next field, if one was found.
635 * The delimiter char, if present, is stored in delim.
636 * If this is the last field, *cp is set to NULL.
637 */
638char *
639hpdelim2(char **cp, char *delim)
640{
641 char *s, *old;
642
643 if (cp == NULL((void *)0) || *cp == NULL((void *)0))
644 return NULL((void *)0);
645
646 old = s = *cp;
647 if (*s == '[') {
648 if ((s = strchr(s, ']')) == NULL((void *)0))
649 return NULL((void *)0);
650 else
651 s++;
652 } else if ((s = strpbrk(s, ":/")) == NULL((void *)0))
653 s = *cp + strlen(*cp); /* skip to end (see first case below) */
654
655 switch (*s) {
656 case '\0':
657 *cp = NULL((void *)0); /* no more fields*/
658 break;
659
660 case ':':
661 case '/':
662 if (delim != NULL((void *)0))
663 *delim = *s;
664 *s = '\0'; /* terminate */
665 *cp = s + 1;
666 break;
667
668 default:
669 return NULL((void *)0);
670 }
671
672 return old;
673}
674
675char *
676hpdelim(char **cp)
677{
678 return hpdelim2(cp, NULL((void *)0));
679}
680
681char *
682cleanhostname(char *host)
683{
684 if (*host == '[' && host[strlen(host) - 1] == ']') {
685 host[strlen(host) - 1] = '\0';
686 return (host + 1);
687 } else
688 return host;
689}
690
691char *
692colon(char *cp)
693{
694 int flag = 0;
695
696 if (*cp == ':') /* Leading colon is part of file name. */
697 return NULL((void *)0);
698 if (*cp == '[')
699 flag = 1;
700
701 for (; *cp; ++cp) {
702 if (*cp == '@' && *(cp+1) == '[')
703 flag = 1;
704 if (*cp == ']' && *(cp+1) == ':' && flag)
705 return (cp+1);
706 if (*cp == ':' && !flag)
707 return (cp);
708 if (*cp == '/')
709 return NULL((void *)0);
710 }
711 return NULL((void *)0);
712}
713
714/*
715 * Parse a [user@]host:[path] string.
716 * Caller must free returned user, host and path.
717 * Any of the pointer return arguments may be NULL (useful for syntax checking).
718 * If user was not specified then *userp will be set to NULL.
719 * If host was not specified then *hostp will be set to NULL.
720 * If path was not specified then *pathp will be set to ".".
721 * Returns 0 on success, -1 on failure.
722 */
723int
724parse_user_host_path(const char *s, char **userp, char **hostp, char **pathp)
725{
726 char *user = NULL((void *)0), *host = NULL((void *)0), *path = NULL((void *)0);
727 char *sdup, *tmp;
728 int ret = -1;
729
730 if (userp != NULL((void *)0))
731 *userp = NULL((void *)0);
732 if (hostp != NULL((void *)0))
733 *hostp = NULL((void *)0);
734 if (pathp != NULL((void *)0))
735 *pathp = NULL((void *)0);
736
737 sdup = xstrdup(s);
738
739 /* Check for remote syntax: [user@]host:[path] */
740 if ((tmp = colon(sdup)) == NULL((void *)0))
741 goto out;
742
743 /* Extract optional path */
744 *tmp++ = '\0';
745 if (*tmp == '\0')
746 tmp = ".";
747 path = xstrdup(tmp);
748
749 /* Extract optional user and mandatory host */
750 tmp = strrchr(sdup, '@');
751 if (tmp != NULL((void *)0)) {
752 *tmp++ = '\0';
753 host = xstrdup(cleanhostname(tmp));
754 if (*sdup != '\0')
755 user = xstrdup(sdup);
756 } else {
757 host = xstrdup(cleanhostname(sdup));
758 user = NULL((void *)0);
759 }
760
761 /* Success */
762 if (userp != NULL((void *)0)) {
763 *userp = user;
764 user = NULL((void *)0);
765 }
766 if (hostp != NULL((void *)0)) {
767 *hostp = host;
768 host = NULL((void *)0);
769 }
770 if (pathp != NULL((void *)0)) {
771 *pathp = path;
772 path = NULL((void *)0);
773 }
774 ret = 0;
775out:
776 free(sdup);
777 free(user);
778 free(host);
779 free(path);
780 return ret;
781}
782
783/*
784 * Parse a [user@]host[:port] string.
785 * Caller must free returned user and host.
786 * Any of the pointer return arguments may be NULL (useful for syntax checking).
787 * If user was not specified then *userp will be set to NULL.
788 * If port was not specified then *portp will be -1.
789 * Returns 0 on success, -1 on failure.
790 */
791int
792parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
793{
794 char *sdup, *cp, *tmp;
795 char *user = NULL((void *)0), *host = NULL((void *)0);
796 int port = -1, ret = -1;
797
798 if (userp != NULL((void *)0))
799 *userp = NULL((void *)0);
800 if (hostp != NULL((void *)0))
801 *hostp = NULL((void *)0);
802 if (portp != NULL((void *)0))
803 *portp = -1;
804
805 if ((sdup = tmp = strdup(s)) == NULL((void *)0))
806 return -1;
807 /* Extract optional username */
808 if ((cp = strrchr(tmp, '@')) != NULL((void *)0)) {
809 *cp = '\0';
810 if (*tmp == '\0')
811 goto out;
812 if ((user = strdup(tmp)) == NULL((void *)0))
813 goto out;
814 tmp = cp + 1;
815 }
816 /* Extract mandatory hostname */
817 if ((cp = hpdelim(&tmp)) == NULL((void *)0) || *cp == '\0')
818 goto out;
819 host = xstrdup(cleanhostname(cp));
820 /* Convert and verify optional port */
821 if (tmp != NULL((void *)0) && *tmp != '\0') {
822 if ((port = a2port(tmp)) <= 0)
823 goto out;
824 }
825 /* Success */
826 if (userp != NULL((void *)0)) {
827 *userp = user;
828 user = NULL((void *)0);
829 }
830 if (hostp != NULL((void *)0)) {
831 *hostp = host;
832 host = NULL((void *)0);
833 }
834 if (portp != NULL((void *)0))
835 *portp = port;
836 ret = 0;
837 out:
838 free(sdup);
839 free(user);
840 free(host);
841 return ret;
842}
843
844/*
845 * Converts a two-byte hex string to decimal.
846 * Returns the decimal value or -1 for invalid input.
847 */
848static int
849hexchar(const char *s)
850{
851 unsigned char result[2];
852 int i;
853
854 for (i = 0; i < 2; i++) {
855 if (s[i] >= '0' && s[i] <= '9')
856 result[i] = (unsigned char)(s[i] - '0');
857 else if (s[i] >= 'a' && s[i] <= 'f')
858 result[i] = (unsigned char)(s[i] - 'a') + 10;
859 else if (s[i] >= 'A' && s[i] <= 'F')
860 result[i] = (unsigned char)(s[i] - 'A') + 10;
861 else
862 return -1;
863 }
864 return (result[0] << 4) | result[1];
865}
866
867/*
868 * Decode an url-encoded string.
869 * Returns a newly allocated string on success or NULL on failure.
870 */
871static char *
872urldecode(const char *src)
873{
874 char *ret, *dst;
875 int ch;
876
877 ret = xmalloc(strlen(src) + 1);
878 for (dst = ret; *src != '\0'; src++) {
879 switch (*src) {
880 case '+':
881 *dst++ = ' ';
882 break;
883 case '%':
884 if (!isxdigit((unsigned char)src[1]) ||
885 !isxdigit((unsigned char)src[2]) ||
886 (ch = hexchar(src + 1)) == -1) {
887 free(ret);
888 return NULL((void *)0);
889 }
890 *dst++ = ch;
891 src += 2;
892 break;
893 default:
894 *dst++ = *src;
895 break;
896 }
897 }
898 *dst = '\0';
899
900 return ret;
901}
902
903/*
904 * Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI.
905 * See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04
906 * Either user or path may be url-encoded (but not host or port).
907 * Caller must free returned user, host and path.
908 * Any of the pointer return arguments may be NULL (useful for syntax checking)
909 * but the scheme must always be specified.
910 * If user was not specified then *userp will be set to NULL.
911 * If port was not specified then *portp will be -1.
912 * If path was not specified then *pathp will be set to NULL.
913 * Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri.
914 */
915int
916parse_uri(const char *scheme, const char *uri, char **userp, char **hostp,
917 int *portp, char **pathp)
918{
919 char *uridup, *cp, *tmp, ch;
920 char *user = NULL((void *)0), *host = NULL((void *)0), *path = NULL((void *)0);
921 int port = -1, ret = -1;
922 size_t len;
923
924 len = strlen(scheme);
925 if (strncmp(uri, scheme, len) != 0 || strncmp(uri + len, "://", 3) != 0)
926 return 1;
927 uri += len + 3;
928
929 if (userp != NULL((void *)0))
930 *userp = NULL((void *)0);
931 if (hostp != NULL((void *)0))
932 *hostp = NULL((void *)0);
933 if (portp != NULL((void *)0))
934 *portp = -1;
935 if (pathp != NULL((void *)0))
936 *pathp = NULL((void *)0);
937
938 uridup = tmp = xstrdup(uri);
939
940 /* Extract optional ssh-info (username + connection params) */
941 if ((cp = strchr(tmp, '@')) != NULL((void *)0)) {
942 char *delim;
943
944 *cp = '\0';
945 /* Extract username and connection params */
946 if ((delim = strchr(tmp, ';')) != NULL((void *)0)) {
947 /* Just ignore connection params for now */
948 *delim = '\0';
949 }
950 if (*tmp == '\0') {
951 /* Empty username */
952 goto out;
953 }
954 if ((user = urldecode(tmp)) == NULL((void *)0))
955 goto out;
956 tmp = cp + 1;
957 }
958
959 /* Extract mandatory hostname */
960 if ((cp = hpdelim2(&tmp, &ch)) == NULL((void *)0) || *cp == '\0')
961 goto out;
962 host = xstrdup(cleanhostname(cp));
963 if (!valid_domain(host, 0, NULL((void *)0)))
964 goto out;
965
966 if (tmp != NULL((void *)0) && *tmp != '\0') {
967 if (ch == ':') {
968 /* Convert and verify port. */
969 if ((cp = strchr(tmp, '/')) != NULL((void *)0))
970 *cp = '\0';
971 if ((port = a2port(tmp)) <= 0)
972 goto out;
973 tmp = cp ? cp + 1 : NULL((void *)0);
974 }
975 if (tmp != NULL((void *)0) && *tmp != '\0') {
976 /* Extract optional path */
977 if ((path = urldecode(tmp)) == NULL((void *)0))
978 goto out;
979 }
980 }
981
982 /* Success */
983 if (userp != NULL((void *)0)) {
984 *userp = user;
985 user = NULL((void *)0);
986 }
987 if (hostp != NULL((void *)0)) {
988 *hostp = host;
989 host = NULL((void *)0);
990 }
991 if (portp != NULL((void *)0))
992 *portp = port;
993 if (pathp != NULL((void *)0)) {
994 *pathp = path;
995 path = NULL((void *)0);
996 }
997 ret = 0;
998 out:
999 free(uridup);
1000 free(user);
1001 free(host);
1002 free(path);
1003 return ret;
1004}
1005
1006/* function to assist building execv() arguments */
1007void
1008addargs(arglist *args, char *fmt, ...)
1009{
1010 va_list ap;
1011 char *cp;
1012 u_int nalloc;
1013 int r;
1014
1015 va_start(ap, fmt)__builtin_va_start(ap, fmt);
1016 r = vasprintf(&cp, fmt, ap);
1017 va_end(ap)__builtin_va_end(ap);
1018 if (r == -1)
1019 fatal("addargs: argument too long")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1019, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "addargs: argument too long"
)
;
1020
1021 nalloc = args->nalloc;
1022 if (args->list == NULL((void *)0)) {
1023 nalloc = 32;
1024 args->num = 0;
1025 } else if (args->num+2 >= nalloc)
1026 nalloc *= 2;
1027
1028 args->list = xrecallocarray(args->list, args->nalloc, nalloc, sizeof(char *));
1029 args->nalloc = nalloc;
1030 args->list[args->num++] = cp;
1031 args->list[args->num] = NULL((void *)0);
1032}
1033
1034void
1035replacearg(arglist *args, u_int which, char *fmt, ...)
1036{
1037 va_list ap;
1038 char *cp;
1039 int r;
1040
1041 va_start(ap, fmt)__builtin_va_start(ap, fmt);
1042 r = vasprintf(&cp, fmt, ap);
1043 va_end(ap)__builtin_va_end(ap);
1044 if (r == -1)
1045 fatal("replacearg: argument too long")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1045, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "replacearg: argument too long"
)
;
1046
1047 if (which >= args->num)
1048 fatal("replacearg: tried to replace invalid arg %d >= %d",sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1049, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "replacearg: tried to replace invalid arg %d >= %d"
, which, args->num)
1049 which, args->num)sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1049, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "replacearg: tried to replace invalid arg %d >= %d"
, which, args->num)
;
1050 free(args->list[which]);
1051 args->list[which] = cp;
1052}
1053
1054void
1055freeargs(arglist *args)
1056{
1057 u_int i;
1058
1059 if (args->list != NULL((void *)0)) {
1060 for (i = 0; i < args->num; i++)
1061 free(args->list[i]);
1062 free(args->list);
1063 args->nalloc = args->num = 0;
1064 args->list = NULL((void *)0);
1065 }
1066}
1067
1068/*
1069 * Expands tildes in the file name. Returns data allocated by xmalloc.
1070 * Warning: this calls getpw*.
1071 */
1072int
1073tilde_expand(const char *filename, uid_t uid, char **retp)
1074{
1075 char *ocopy = NULL((void *)0), *copy, *s = NULL((void *)0);
1076 const char *path = NULL((void *)0), *user = NULL((void *)0);
1077 struct passwd *pw;
1078 size_t len;
1079 int ret = -1, r, slash;
1080
1081 *retp = NULL((void *)0);
1082 if (*filename != '~') {
1083 *retp = xstrdup(filename);
1084 return 0;
1085 }
1086 ocopy = copy = xstrdup(filename + 1);
1087
1088 if (*copy == '\0') /* ~ */
1089 path = NULL((void *)0);
1090 else if (*copy == '/') {
1091 copy += strspn(copy, "/");
1092 if (*copy == '\0')
1093 path = NULL((void *)0); /* ~/ */
1094 else
1095 path = copy; /* ~/path */
1096 } else {
1097 user = copy;
1098 if ((path = strchr(copy, '/')) != NULL((void *)0)) {
1099 copy[path - copy] = '\0';
1100 path++;
1101 path += strspn(path, "/");
1102 if (*path == '\0') /* ~user/ */
1103 path = NULL((void *)0);
1104 /* else ~user/path */
1105 }
1106 /* else ~user */
1107 }
1108 if (user != NULL((void *)0)) {
1109 if ((pw = getpwnam(user)) == NULL((void *)0)) {
1110 error_f("No such user %s", user)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1110, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "No such user %s"
, user)
;
1111 goto out;
1112 }
1113 } else if ((pw = getpwuid(uid)) == NULL((void *)0)) {
1114 error_f("No such uid %ld", (long)uid)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1114, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "No such uid %ld"
, (long)uid)
;
1115 goto out;
1116 }
1117
1118 /* Make sure directory has a trailing '/' */
1119 slash = (len = strlen(pw->pw_dir)) == 0 || pw->pw_dir[len - 1] != '/';
1120
1121 if ((r = xasprintf(&s, "%s%s%s", pw->pw_dir,
1122 slash ? "/" : "", path != NULL((void *)0) ? path : "")) <= 0) {
1123 error_f("xasprintf failed")sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1123, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "xasprintf failed"
)
;
1124 goto out;
1125 }
1126 if (r >= PATH_MAX1024) {
1127 error_f("Path too long")sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1127, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "Path too long")
;
1128 goto out;
1129 }
1130 /* success */
1131 ret = 0;
1132 *retp = s;
1133 s = NULL((void *)0);
1134 out:
1135 free(s);
1136 free(ocopy);
1137 return ret;
1138}
1139
1140char *
1141tilde_expand_filename(const char *filename, uid_t uid)
1142{
1143 char *ret;
1144
1145 if (tilde_expand(filename, uid, &ret) != 0)
1146 cleanup_exit(255);
1147 return ret;
1148}
1149
1150/*
1151 * Expand a string with a set of %[char] escapes and/or ${ENVIRONMENT}
1152 * substitutions. A number of escapes may be specified as
1153 * (char *escape_chars, char *replacement) pairs. The list must be terminated
1154 * by a NULL escape_char. Returns replaced string in memory allocated by
1155 * xmalloc which the caller must free.
1156 */
1157static char *
1158vdollar_percent_expand(int *parseerror, int dollar, int percent,
1159 const char *string, va_list ap)
1160{
1161#define EXPAND_MAX_KEYS 16
1162 u_int num_keys = 0, i;
1163 struct {
1164 const char *key;
1165 const char *repl;
1166 } keys[EXPAND_MAX_KEYS];
1167 struct sshbuf *buf;
1168 int r, missingvar = 0;
1169 char *ret = NULL((void *)0), *var, *varend, *val;
1170 size_t len;
1171
1172 if ((buf = sshbuf_new()) == NULL((void *)0))
1173 fatal_f("sshbuf_new failed")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1173, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "sshbuf_new failed"
)
;
1174 if (parseerror == NULL((void *)0))
1175 fatal_f("null parseerror arg")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1175, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "null parseerror arg"
)
;
1176 *parseerror = 1;
1177
1178 /* Gather keys if we're doing percent expansion. */
1179 if (percent) {
1180 for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) {
1181 keys[num_keys].key = va_arg(ap, char *)__builtin_va_arg(ap, char *);
1182 if (keys[num_keys].key == NULL((void *)0))
1183 break;
1184 keys[num_keys].repl = va_arg(ap, char *)__builtin_va_arg(ap, char *);
1185 if (keys[num_keys].repl == NULL((void *)0)) {
1186 fatal_f("NULL replacement for token %s",sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1187, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "NULL replacement for token %s"
, keys[num_keys].key)
1187 keys[num_keys].key)sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1187, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "NULL replacement for token %s"
, keys[num_keys].key)
;
1188 }
1189 }
1190 if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *)__builtin_va_arg(ap, char *) != NULL((void *)0))
1191 fatal_f("too many keys")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1191, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "too many keys")
;
1192 if (num_keys == 0)
1193 fatal_f("percent expansion without token list")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1193, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "percent expansion without token list"
)
;
1194 }
1195
1196 /* Expand string */
1197 for (i = 0; *string != '\0'; string++) {
Value stored to 'i' is never read
1198 /* Optionally process ${ENVIRONMENT} expansions. */
1199 if (dollar && string[0] == '$' && string[1] == '{') {
1200 string += 2; /* skip over '${' */
1201 if ((varend = strchr(string, '}')) == NULL((void *)0)) {
1202 error_f("environment variable '%s' missing "sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1203, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "environment variable '%s' missing "
"closing '}'", string)
1203 "closing '}'", string)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1203, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "environment variable '%s' missing "
"closing '}'", string)
;
1204 goto out;
1205 }
1206 len = varend - string;
1207 if (len == 0) {
1208 error_f("zero-length environment variable")sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1208, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "zero-length environment variable"
)
;
1209 goto out;
1210 }
1211 var = xmalloc(len + 1);
1212 (void)strlcpy(var, string, len + 1);
1213 if ((val = getenv(var)) == NULL((void *)0)) {
1214 error_f("env var ${%s} has no value", var)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1214, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "env var ${%s} has no value"
, var)
;
1215 missingvar = 1;
1216 } else {
1217 debug3_f("expand ${%s} -> '%s'", var, val)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1217, 1, SYSLOG_LEVEL_DEBUG3, ((void *)0), "expand ${%s} -> '%s'"
, var, val)
;
1218 if ((r = sshbuf_put(buf, val, strlen(val))) !=0)
1219 fatal_fr(r, "sshbuf_put ${}")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1219, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "sshbuf_put ${}")
;
1220 }
1221 free(var);
1222 string += len;
1223 continue;
1224 }
1225
1226 /*
1227 * Process percent expansions if we have a list of TOKENs.
1228 * If we're not doing percent expansion everything just gets
1229 * appended here.
1230 */
1231 if (*string != '%' || !percent) {
1232 append:
1233 if ((r = sshbuf_put_u8(buf, *string)) != 0)
1234 fatal_fr(r, "sshbuf_put_u8 %%")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1234, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "sshbuf_put_u8 %%"
)
;
1235 continue;
1236 }
1237 string++;
1238 /* %% case */
1239 if (*string == '%')
1240 goto append;
1241 if (*string == '\0') {
1242 error_f("invalid format")sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1242, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "invalid format")
;
1243 goto out;
1244 }
1245 for (i = 0; i < num_keys; i++) {
1246 if (strchr(keys[i].key, *string) != NULL((void *)0)) {
1247 if ((r = sshbuf_put(buf, keys[i].repl,
1248 strlen(keys[i].repl))) != 0)
1249 fatal_fr(r, "sshbuf_put %%-repl")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1249, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "sshbuf_put %%-repl"
)
;
1250 break;
1251 }
1252 }
1253 if (i >= num_keys) {
1254 error_f("unknown key %%%c", *string)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1254, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "unknown key %%%c"
, *string)
;
1255 goto out;
1256 }
1257 }
1258 if (!missingvar && (ret = sshbuf_dup_string(buf)) == NULL((void *)0))
1259 fatal_f("sshbuf_dup_string failed")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1259, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "sshbuf_dup_string failed"
)
;
1260 *parseerror = 0;
1261 out:
1262 sshbuf_free(buf);
1263 return *parseerror ? NULL((void *)0) : ret;
1264#undef EXPAND_MAX_KEYS
1265}
1266
1267/*
1268 * Expand only environment variables.
1269 * Note that although this function is variadic like the other similar
1270 * functions, any such arguments will be unused.
1271 */
1272
1273char *
1274dollar_expand(int *parseerr, const char *string, ...)
1275{
1276 char *ret;
1277 int err;
1278 va_list ap;
1279
1280 va_start(ap, string)__builtin_va_start(ap, string);
1281 ret = vdollar_percent_expand(&err, 1, 0, string, ap);
1282 va_end(ap)__builtin_va_end(ap);
1283 if (parseerr != NULL((void *)0))
1284 *parseerr = err;
1285 return ret;
1286}
1287
1288/*
1289 * Returns expanded string or NULL if a specified environment variable is
1290 * not defined, or calls fatal if the string is invalid.
1291 */
1292char *
1293percent_expand(const char *string, ...)
1294{
1295 char *ret;
1296 int err;
1297 va_list ap;
1298
1299 va_start(ap, string)__builtin_va_start(ap, string);
1300 ret = vdollar_percent_expand(&err, 0, 1, string, ap);
1301 va_end(ap)__builtin_va_end(ap);
1302 if (err)
1303 fatal_f("failed")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1303, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "failed")
;
1304 return ret;
1305}
1306
1307/*
1308 * Returns expanded string or NULL if a specified environment variable is
1309 * not defined, or calls fatal if the string is invalid.
1310 */
1311char *
1312percent_dollar_expand(const char *string, ...)
1313{
1314 char *ret;
1315 int err;
1316 va_list ap;
1317
1318 va_start(ap, string)__builtin_va_start(ap, string);
1319 ret = vdollar_percent_expand(&err, 1, 1, string, ap);
1320 va_end(ap)__builtin_va_end(ap);
1321 if (err)
1322 fatal_f("failed")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1322, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "failed")
;
1323 return ret;
1324}
1325
1326int
1327tun_open(int tun, int mode, char **ifname)
1328{
1329 struct ifreq ifr;
1330 char name[100];
1331 int fd = -1, sock;
1332 const char *tunbase = "tun";
1333
1334 if (ifname != NULL((void *)0))
1335 *ifname = NULL((void *)0);
1336
1337 if (mode == SSH_TUNMODE_ETHERNET0x02)
1338 tunbase = "tap";
1339
1340 /* Open the tunnel device */
1341 if (tun <= SSH_TUNID_MAX(0x7fffffff - 2)) {
1342 snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun);
1343 fd = open(name, O_RDWR0x0002);
1344 } else if (tun == SSH_TUNID_ANY0x7fffffff) {
1345 for (tun = 100; tun >= 0; tun--) {
1346 snprintf(name, sizeof(name), "/dev/%s%d",
1347 tunbase, tun);
1348 if ((fd = open(name, O_RDWR0x0002)) >= 0)
1349 break;
1350 }
1351 } else {
1352 debug_f("invalid tunnel %u", tun)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1352, 1, SYSLOG_LEVEL_DEBUG1, ((void *)0), "invalid tunnel %u"
, tun)
;
1353 return -1;
1354 }
1355
1356 if (fd == -1) {
1357 debug_f("%s open: %s", name, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1357, 1, SYSLOG_LEVEL_DEBUG1, ((void *)0), "%s open: %s", name
, strerror((*__errno())))
;
1358 return -1;
1359 }
1360
1361 debug_f("%s mode %d fd %d", name, mode, fd)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1361, 1, SYSLOG_LEVEL_DEBUG1, ((void *)0), "%s mode %d fd %d"
, name, mode, fd)
;
1362
1363 /* Bring interface up if it is not already */
1364 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun);
1365 if ((sock = socket(PF_UNIX1, SOCK_STREAM1, 0)) == -1)
1366 goto failed;
1367
1368 if (ioctl(sock, SIOCGIFFLAGS(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof
(struct ifreq) & 0x1fff) << 16) | ((('i')) <<
8) | ((17)))
, &ifr) == -1) {
1369 debug_f("get interface %s flags: %s", ifr.ifr_name,sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1370, 1, SYSLOG_LEVEL_DEBUG1, ((void *)0), "get interface %s flags: %s"
, ifr.ifr_name, strerror((*__errno())))
1370 strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1370, 1, SYSLOG_LEVEL_DEBUG1, ((void *)0), "get interface %s flags: %s"
, ifr.ifr_name, strerror((*__errno())))
;
1371 goto failed;
1372 }
1373
1374 if (!(ifr.ifr_flagsifr_ifru.ifru_flags & IFF_UP0x1)) {
1375 ifr.ifr_flagsifr_ifru.ifru_flags |= IFF_UP0x1;
1376 if (ioctl(sock, SIOCSIFFLAGS((unsigned long)0x80000000 | ((sizeof(struct ifreq) & 0x1fff
) << 16) | ((('i')) << 8) | ((16)))
, &ifr) == -1) {
1377 debug_f("activate interface %s: %s", ifr.ifr_name,sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1378, 1, SYSLOG_LEVEL_DEBUG1, ((void *)0), "activate interface %s: %s"
, ifr.ifr_name, strerror((*__errno())))
1378 strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1378, 1, SYSLOG_LEVEL_DEBUG1, ((void *)0), "activate interface %s: %s"
, ifr.ifr_name, strerror((*__errno())))
;
1379 goto failed;
1380 }
1381 }
1382
1383 if (ifname != NULL((void *)0))
1384 *ifname = xstrdup(ifr.ifr_name);
1385
1386 close(sock);
1387 return fd;
1388
1389 failed:
1390 if (fd >= 0)
1391 close(fd);
1392 if (sock >= 0)
1393 close(sock);
1394 return -1;
1395}
1396
1397void
1398sanitise_stdfd(void)
1399{
1400 int nullfd, dupfd;
1401
1402 if ((nullfd = dupfd = open(_PATH_DEVNULL"/dev/null", O_RDWR0x0002)) == -1) {
1403 fprintf(stderr(&__sF[2]), "Couldn't open /dev/null: %s\n",
1404 strerror(errno(*__errno())));
1405 exit(1);
1406 }
1407 while (++dupfd <= STDERR_FILENO2) {
1408 /* Only populate closed fds. */
1409 if (fcntl(dupfd, F_GETFL3) == -1 && errno(*__errno()) == EBADF9) {
1410 if (dup2(nullfd, dupfd) == -1) {
1411 fprintf(stderr(&__sF[2]), "dup2: %s\n", strerror(errno(*__errno())));
1412 exit(1);
1413 }
1414 }
1415 }
1416 if (nullfd > STDERR_FILENO2)
1417 close(nullfd);
1418}
1419
1420char *
1421tohex(const void *vp, size_t l)
1422{
1423 const u_char *p = (const u_char *)vp;
1424 char b[3], *r;
1425 size_t i, hl;
1426
1427 if (l > 65536)
1428 return xstrdup("tohex: length > 65536");
1429
1430 hl = l * 2 + 1;
1431 r = xcalloc(1, hl);
1432 for (i = 0; i < l; i++) {
1433 snprintf(b, sizeof(b), "%02x", p[i]);
1434 strlcat(r, b, hl);
1435 }
1436 return (r);
1437}
1438
1439/*
1440 * Extend string *sp by the specified format. If *sp is not NULL (or empty),
1441 * then the separator 'sep' will be prepended before the formatted arguments.
1442 * Extended strings are heap allocated.
1443 */
1444void
1445xextendf(char **sp, const char *sep, const char *fmt, ...)
1446{
1447 va_list ap;
1448 char *tmp1, *tmp2;
1449
1450 va_start(ap, fmt)__builtin_va_start(ap, fmt);
1451 xvasprintf(&tmp1, fmt, ap);
1452 va_end(ap)__builtin_va_end(ap);
1453
1454 if (*sp == NULL((void *)0) || **sp == '\0') {
1455 free(*sp);
1456 *sp = tmp1;
1457 return;
1458 }
1459 xasprintf(&tmp2, "%s%s%s", *sp, sep == NULL((void *)0) ? "" : sep, tmp1);
1460 free(tmp1);
1461 free(*sp);
1462 *sp = tmp2;
1463}
1464
1465
1466u_int64_t
1467get_u64(const void *vp)
1468{
1469 const u_char *p = (const u_char *)vp;
1470 u_int64_t v;
1471
1472 v = (u_int64_t)p[0] << 56;
1473 v |= (u_int64_t)p[1] << 48;
1474 v |= (u_int64_t)p[2] << 40;
1475 v |= (u_int64_t)p[3] << 32;
1476 v |= (u_int64_t)p[4] << 24;
1477 v |= (u_int64_t)p[5] << 16;
1478 v |= (u_int64_t)p[6] << 8;
1479 v |= (u_int64_t)p[7];
1480
1481 return (v);
1482}
1483
1484u_int32_t
1485get_u32(const void *vp)
1486{
1487 const u_char *p = (const u_char *)vp;
1488 u_int32_t v;
1489
1490 v = (u_int32_t)p[0] << 24;
1491 v |= (u_int32_t)p[1] << 16;
1492 v |= (u_int32_t)p[2] << 8;
1493 v |= (u_int32_t)p[3];
1494
1495 return (v);
1496}
1497
1498u_int32_t
1499get_u32_le(const void *vp)
1500{
1501 const u_char *p = (const u_char *)vp;
1502 u_int32_t v;
1503
1504 v = (u_int32_t)p[0];
1505 v |= (u_int32_t)p[1] << 8;
1506 v |= (u_int32_t)p[2] << 16;
1507 v |= (u_int32_t)p[3] << 24;
1508
1509 return (v);
1510}
1511
1512u_int16_t
1513get_u16(const void *vp)
1514{
1515 const u_char *p = (const u_char *)vp;
1516 u_int16_t v;
1517
1518 v = (u_int16_t)p[0] << 8;
1519 v |= (u_int16_t)p[1];
1520
1521 return (v);
1522}
1523
1524void
1525put_u64(void *vp, u_int64_t v)
1526{
1527 u_char *p = (u_char *)vp;
1528
1529 p[0] = (u_char)(v >> 56) & 0xff;
1530 p[1] = (u_char)(v >> 48) & 0xff;
1531 p[2] = (u_char)(v >> 40) & 0xff;
1532 p[3] = (u_char)(v >> 32) & 0xff;
1533 p[4] = (u_char)(v >> 24) & 0xff;
1534 p[5] = (u_char)(v >> 16) & 0xff;
1535 p[6] = (u_char)(v >> 8) & 0xff;
1536 p[7] = (u_char)v & 0xff;
1537}
1538
1539void
1540put_u32(void *vp, u_int32_t v)
1541{
1542 u_char *p = (u_char *)vp;
1543
1544 p[0] = (u_char)(v >> 24) & 0xff;
1545 p[1] = (u_char)(v >> 16) & 0xff;
1546 p[2] = (u_char)(v >> 8) & 0xff;
1547 p[3] = (u_char)v & 0xff;
1548}
1549
1550void
1551put_u32_le(void *vp, u_int32_t v)
1552{
1553 u_char *p = (u_char *)vp;
1554
1555 p[0] = (u_char)v & 0xff;
1556 p[1] = (u_char)(v >> 8) & 0xff;
1557 p[2] = (u_char)(v >> 16) & 0xff;
1558 p[3] = (u_char)(v >> 24) & 0xff;
1559}
1560
1561void
1562put_u16(void *vp, u_int16_t v)
1563{
1564 u_char *p = (u_char *)vp;
1565
1566 p[0] = (u_char)(v >> 8) & 0xff;
1567 p[1] = (u_char)v & 0xff;
1568}
1569
1570void
1571ms_subtract_diff(struct timeval *start, int *ms)
1572{
1573 struct timeval diff, finish;
1574
1575 monotime_tv(&finish);
1576 timersub(&finish, start, &diff)do { (&diff)->tv_sec = (&finish)->tv_sec - (start
)->tv_sec; (&diff)->tv_usec = (&finish)->tv_usec
- (start)->tv_usec; if ((&diff)->tv_usec < 0) {
(&diff)->tv_sec--; (&diff)->tv_usec += 1000000
; } } while (0)
;
1577 *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000);
1578}
1579
1580void
1581ms_to_timespec(struct timespec *ts, int ms)
1582{
1583 if (ms < 0)
1584 ms = 0;
1585 ts->tv_sec = ms / 1000;
1586 ts->tv_nsec = (ms % 1000) * 1000 * 1000;
1587}
1588
1589void
1590monotime_ts(struct timespec *ts)
1591{
1592 if (clock_gettime(CLOCK_MONOTONIC3, ts) != 0)
1593 fatal("clock_gettime: %s", strerror(errno))sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1593, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "clock_gettime: %s"
, strerror((*__errno())))
;
1594}
1595
1596void
1597monotime_tv(struct timeval *tv)
1598{
1599 struct timespec ts;
1600
1601 monotime_ts(&ts);
1602 tv->tv_sec = ts.tv_sec;
1603 tv->tv_usec = ts.tv_nsec / 1000;
1604}
1605
1606time_t
1607monotime(void)
1608{
1609 struct timespec ts;
1610
1611 monotime_ts(&ts);
1612 return (ts.tv_sec);
1613}
1614
1615double
1616monotime_double(void)
1617{
1618 struct timespec ts;
1619
1620 monotime_ts(&ts);
1621 return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;
1622}
1623
1624void
1625bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen)
1626{
1627 bw->buflen = buflen;
1628 bw->rate = kbps;
1629 bw->thresh = buflen;
1630 bw->lamt = 0;
1631 timerclear(&bw->bwstart)(&bw->bwstart)->tv_sec = (&bw->bwstart)->
tv_usec = 0
;
1632 timerclear(&bw->bwend)(&bw->bwend)->tv_sec = (&bw->bwend)->tv_usec
= 0
;
1633}
1634
1635/* Callback from read/write loop to insert bandwidth-limiting delays */
1636void
1637bandwidth_limit(struct bwlimit *bw, size_t read_len)
1638{
1639 u_int64_t waitlen;
1640 struct timespec ts, rm;
1641
1642 bw->lamt += read_len;
1643 if (!timerisset(&bw->bwstart)((&bw->bwstart)->tv_sec || (&bw->bwstart)->
tv_usec)
) {
1644 monotime_tv(&bw->bwstart);
1645 return;
1646 }
1647 if (bw->lamt < bw->thresh)
1648 return;
1649
1650 monotime_tv(&bw->bwend);
1651 timersub(&bw->bwend, &bw->bwstart, &bw->bwend)do { (&bw->bwend)->tv_sec = (&bw->bwend)->
tv_sec - (&bw->bwstart)->tv_sec; (&bw->bwend
)->tv_usec = (&bw->bwend)->tv_usec - (&bw->
bwstart)->tv_usec; if ((&bw->bwend)->tv_usec <
0) { (&bw->bwend)->tv_sec--; (&bw->bwend)->
tv_usec += 1000000; } } while (0)
;
1652 if (!timerisset(&bw->bwend)((&bw->bwend)->tv_sec || (&bw->bwend)->tv_usec
)
)
1653 return;
1654
1655 bw->lamt *= 8;
1656 waitlen = (double)1000000L * bw->lamt / bw->rate;
1657
1658 bw->bwstart.tv_sec = waitlen / 1000000L;
1659 bw->bwstart.tv_usec = waitlen % 1000000L;
1660
1661 if (timercmp(&bw->bwstart, &bw->bwend, >)(((&bw->bwstart)->tv_sec == (&bw->bwend)->
tv_sec) ? ((&bw->bwstart)->tv_usec > (&bw->
bwend)->tv_usec) : ((&bw->bwstart)->tv_sec > (
&bw->bwend)->tv_sec))
) {
1662 timersub(&bw->bwstart, &bw->bwend, &bw->bwend)do { (&bw->bwend)->tv_sec = (&bw->bwstart)->
tv_sec - (&bw->bwend)->tv_sec; (&bw->bwend)->
tv_usec = (&bw->bwstart)->tv_usec - (&bw->bwend
)->tv_usec; if ((&bw->bwend)->tv_usec < 0) { (
&bw->bwend)->tv_sec--; (&bw->bwend)->tv_usec
+= 1000000; } } while (0)
;
1663
1664 /* Adjust the wait time */
1665 if (bw->bwend.tv_sec) {
1666 bw->thresh /= 2;
1667 if (bw->thresh < bw->buflen / 4)
1668 bw->thresh = bw->buflen / 4;
1669 } else if (bw->bwend.tv_usec < 10000) {
1670 bw->thresh *= 2;
1671 if (bw->thresh > bw->buflen * 8)
1672 bw->thresh = bw->buflen * 8;
1673 }
1674
1675 TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts)do { (&ts)->tv_sec = (&bw->bwend)->tv_sec; (
&ts)->tv_nsec = (&bw->bwend)->tv_usec * 1000
; } while (0)
;
1676 while (nanosleep(&ts, &rm) == -1) {
1677 if (errno(*__errno()) != EINTR4)
1678 break;
1679 ts = rm;
1680 }
1681 }
1682
1683 bw->lamt = 0;
1684 monotime_tv(&bw->bwstart);
1685}
1686
1687/* Make a template filename for mk[sd]temp() */
1688void
1689mktemp_proto(char *s, size_t len)
1690{
1691 const char *tmpdir;
1692 int r;
1693
1694 if ((tmpdir = getenv("TMPDIR")) != NULL((void *)0)) {
1695 r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir);
1696 if (r > 0 && (size_t)r < len)
1697 return;
1698 }
1699 r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX");
1700 if (r < 0 || (size_t)r >= len)
1701 fatal_f("template string too short")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1701, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "template string too short"
)
;
1702}
1703
1704static const struct {
1705 const char *name;
1706 int value;
1707} ipqos[] = {
1708 { "none", INT_MAX2147483647 }, /* can't use 0 here; that's CS0 */
1709 { "af11", IPTOS_DSCP_AF110x28 },
1710 { "af12", IPTOS_DSCP_AF120x30 },
1711 { "af13", IPTOS_DSCP_AF130x38 },
1712 { "af21", IPTOS_DSCP_AF210x48 },
1713 { "af22", IPTOS_DSCP_AF220x50 },
1714 { "af23", IPTOS_DSCP_AF230x58 },
1715 { "af31", IPTOS_DSCP_AF310x68 },
1716 { "af32", IPTOS_DSCP_AF320x70 },
1717 { "af33", IPTOS_DSCP_AF330x78 },
1718 { "af41", IPTOS_DSCP_AF410x88 },
1719 { "af42", IPTOS_DSCP_AF420x90 },
1720 { "af43", IPTOS_DSCP_AF430x98 },
1721 { "cs0", IPTOS_DSCP_CS00x00 },
1722 { "cs1", IPTOS_DSCP_CS10x20 },
1723 { "cs2", IPTOS_DSCP_CS20x40 },
1724 { "cs3", IPTOS_DSCP_CS30x60 },
1725 { "cs4", IPTOS_DSCP_CS40x80 },
1726 { "cs5", IPTOS_DSCP_CS50xa0 },
1727 { "cs6", IPTOS_DSCP_CS60xc0 },
1728 { "cs7", IPTOS_DSCP_CS70xe0 },
1729 { "ef", IPTOS_DSCP_EF0xb8 },
1730 { "le", IPTOS_DSCP_LE0x04 },
1731 { "lowdelay", IPTOS_LOWDELAY0x10 },
1732 { "throughput", IPTOS_THROUGHPUT0x08 },
1733 { "reliability", IPTOS_RELIABILITY0x04 },
1734 { NULL((void *)0), -1 }
1735};
1736
1737int
1738parse_ipqos(const char *cp)
1739{
1740 u_int i;
1741 char *ep;
1742 long val;
1743
1744 if (cp == NULL((void *)0))
1745 return -1;
1746 for (i = 0; ipqos[i].name != NULL((void *)0); i++) {
1747 if (strcasecmp(cp, ipqos[i].name) == 0)
1748 return ipqos[i].value;
1749 }
1750 /* Try parsing as an integer */
1751 val = strtol(cp, &ep, 0);
1752 if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255)
1753 return -1;
1754 return val;
1755}
1756
1757const char *
1758iptos2str(int iptos)
1759{
1760 int i;
1761 static char iptos_str[sizeof "0xff"];
1762
1763 for (i = 0; ipqos[i].name != NULL((void *)0); i++) {
1764 if (ipqos[i].value == iptos)
1765 return ipqos[i].name;
1766 }
1767 snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos);
1768 return iptos_str;
1769}
1770
1771void
1772lowercase(char *s)
1773{
1774 for (; *s; s++)
1775 *s = tolower((u_char)*s);
1776}
1777
1778int
1779unix_listener(const char *path, int backlog, int unlink_first)
1780{
1781 struct sockaddr_un sunaddr;
1782 int saved_errno, sock;
1783
1784 memset(&sunaddr, 0, sizeof(sunaddr));
1785 sunaddr.sun_family = AF_UNIX1;
1786 if (strlcpy(sunaddr.sun_path, path,
1787 sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) {
1788 error_f("path \"%s\" too long for Unix domain socket", path)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1788, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "path \"%s\" too long for Unix domain socket"
, path)
;
1789 errno(*__errno()) = ENAMETOOLONG63;
1790 return -1;
1791 }
1792
1793 sock = socket(PF_UNIX1, SOCK_STREAM1, 0);
1794 if (sock == -1) {
1795 saved_errno = errno(*__errno());
1796 error_f("socket: %.100s", strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1796, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "socket: %.100s",
strerror((*__errno())))
;
1797 errno(*__errno()) = saved_errno;
1798 return -1;
1799 }
1800 if (unlink_first == 1) {
1801 if (unlink(path) != 0 && errno(*__errno()) != ENOENT2)
1802 error("unlink(%s): %.100s", path, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1802, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "unlink(%s): %.100s"
, path, strerror((*__errno())))
;
1803 }
1804 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) {
1805 saved_errno = errno(*__errno());
1806 error_f("cannot bind to path %s: %s", path, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1806, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "cannot bind to path %s: %s"
, path, strerror((*__errno())))
;
1807 close(sock);
1808 errno(*__errno()) = saved_errno;
1809 return -1;
1810 }
1811 if (listen(sock, backlog) == -1) {
1812 saved_errno = errno(*__errno());
1813 error_f("cannot listen on path %s: %s", path, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1813, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "cannot listen on path %s: %s"
, path, strerror((*__errno())))
;
1814 close(sock);
1815 unlink(path);
1816 errno(*__errno()) = saved_errno;
1817 return -1;
1818 }
1819 return sock;
1820}
1821
1822/*
1823 * Compares two strings that maybe be NULL. Returns non-zero if strings
1824 * are both NULL or are identical, returns zero otherwise.
1825 */
1826static int
1827strcmp_maybe_null(const char *a, const char *b)
1828{
1829 if ((a == NULL((void *)0) && b != NULL((void *)0)) || (a != NULL((void *)0) && b == NULL((void *)0)))
1830 return 0;
1831 if (a != NULL((void *)0) && strcmp(a, b) != 0)
1832 return 0;
1833 return 1;
1834}
1835
1836/*
1837 * Compare two forwards, returning non-zero if they are identical or
1838 * zero otherwise.
1839 */
1840int
1841forward_equals(const struct Forward *a, const struct Forward *b)
1842{
1843 if (strcmp_maybe_null(a->listen_host, b->listen_host) == 0)
1844 return 0;
1845 if (a->listen_port != b->listen_port)
1846 return 0;
1847 if (strcmp_maybe_null(a->listen_path, b->listen_path) == 0)
1848 return 0;
1849 if (strcmp_maybe_null(a->connect_host, b->connect_host) == 0)
1850 return 0;
1851 if (a->connect_port != b->connect_port)
1852 return 0;
1853 if (strcmp_maybe_null(a->connect_path, b->connect_path) == 0)
1854 return 0;
1855 /* allocated_port and handle are not checked */
1856 return 1;
1857}
1858
1859/* returns 1 if process is already daemonized, 0 otherwise */
1860int
1861daemonized(void)
1862{
1863 int fd;
1864
1865 if ((fd = open(_PATH_TTY"/dev/tty", O_RDONLY0x0000 | O_NOCTTY0x8000)) >= 0) {
1866 close(fd);
1867 return 0; /* have controlling terminal */
1868 }
1869 if (getppid() != 1)
1870 return 0; /* parent is not init */
1871 if (getsid(0) != getpid())
1872 return 0; /* not session leader */
1873 debug3("already daemonized")sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1873, 0, SYSLOG_LEVEL_DEBUG3, ((void *)0), "already daemonized"
)
;
1874 return 1;
1875}
1876
1877/*
1878 * Splits 's' into an argument vector. Handles quoted string and basic
1879 * escape characters (\\, \", \'). Caller must free the argument vector
1880 * and its members.
1881 */
1882int
1883argv_split(const char *s, int *argcp, char ***argvp, int terminate_on_comment)
1884{
1885 int r = SSH_ERR_INTERNAL_ERROR-1;
1886 int argc = 0, quote, i, j;
1887 char *arg, **argv = xcalloc(1, sizeof(*argv));
1888
1889 *argvp = NULL((void *)0);
1890 *argcp = 0;
1891
1892 for (i = 0; s[i] != '\0'; i++) {
1893 /* Skip leading whitespace */
1894 if (s[i] == ' ' || s[i] == '\t')
1895 continue;
1896 if (terminate_on_comment && s[i] == '#')
1897 break;
1898 /* Start of a token */
1899 quote = 0;
1900
1901 argv = xreallocarray(argv, (argc + 2), sizeof(*argv));
1902 arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1);
1903 argv[argc] = NULL((void *)0);
1904
1905 /* Copy the token in, removing escapes */
1906 for (j = 0; s[i] != '\0'; i++) {
1907 if (s[i] == '\\') {
1908 if (s[i + 1] == '\'' ||
1909 s[i + 1] == '\"' ||
1910 s[i + 1] == '\\' ||
1911 (quote == 0 && s[i + 1] == ' ')) {
1912 i++; /* Skip '\' */
1913 arg[j++] = s[i];
1914 } else {
1915 /* Unrecognised escape */
1916 arg[j++] = s[i];
1917 }
1918 } else if (quote == 0 && (s[i] == ' ' || s[i] == '\t'))
1919 break; /* done */
1920 else if (quote == 0 && (s[i] == '\"' || s[i] == '\''))
1921 quote = s[i]; /* quote start */
1922 else if (quote != 0 && s[i] == quote)
1923 quote = 0; /* quote end */
1924 else
1925 arg[j++] = s[i];
1926 }
1927 if (s[i] == '\0') {
1928 if (quote != 0) {
1929 /* Ran out of string looking for close quote */
1930 r = SSH_ERR_INVALID_FORMAT-4;
1931 goto out;
1932 }
1933 break;
1934 }
1935 }
1936 /* Success */
1937 *argcp = argc;
1938 *argvp = argv;
1939 argc = 0;
1940 argv = NULL((void *)0);
1941 r = 0;
1942 out:
1943 if (argc != 0 && argv != NULL((void *)0)) {
1944 for (i = 0; i < argc; i++)
1945 free(argv[i]);
1946 free(argv);
1947 }
1948 return r;
1949}
1950
1951/*
1952 * Reassemble an argument vector into a string, quoting and escaping as
1953 * necessary. Caller must free returned string.
1954 */
1955char *
1956argv_assemble(int argc, char **argv)
1957{
1958 int i, j, ws, r;
1959 char c, *ret;
1960 struct sshbuf *buf, *arg;
1961
1962 if ((buf = sshbuf_new()) == NULL((void *)0) || (arg = sshbuf_new()) == NULL((void *)0))
1963 fatal_f("sshbuf_new failed")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1963, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "sshbuf_new failed"
)
;
1964
1965 for (i = 0; i < argc; i++) {
1966 ws = 0;
1967 sshbuf_reset(arg);
1968 for (j = 0; argv[i][j] != '\0'; j++) {
1969 r = 0;
1970 c = argv[i][j];
1971 switch (c) {
1972 case ' ':
1973 case '\t':
1974 ws = 1;
1975 r = sshbuf_put_u8(arg, c);
1976 break;
1977 case '\\':
1978 case '\'':
1979 case '"':
1980 if ((r = sshbuf_put_u8(arg, '\\')) != 0)
1981 break;
1982 /* FALLTHROUGH */
1983 default:
1984 r = sshbuf_put_u8(arg, c);
1985 break;
1986 }
1987 if (r != 0)
1988 fatal_fr(r, "sshbuf_put_u8")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1988, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "sshbuf_put_u8")
;
1989 }
1990 if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) ||
1991 (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) ||
1992 (r = sshbuf_putb(buf, arg)) != 0 ||
1993 (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0))
1994 fatal_fr(r, "assemble")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1994, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), "assemble")
;
1995 }
1996 if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL((void *)0))
1997 fatal_f("malloc failed")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 1997, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "malloc failed")
;
1998 memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf));
1999 ret[sshbuf_len(buf)] = '\0';
2000 sshbuf_free(buf);
2001 sshbuf_free(arg);
2002 return ret;
2003}
2004
2005char *
2006argv_next(int *argcp, char ***argvp)
2007{
2008 char *ret = (*argvp)[0];
2009
2010 if (*argcp > 0 && ret != NULL((void *)0)) {
2011 (*argcp)--;
2012 (*argvp)++;
2013 }
2014 return ret;
2015}
2016
2017void
2018argv_consume(int *argcp)
2019{
2020 *argcp = 0;
2021}
2022
2023void
2024argv_free(char **av, int ac)
2025{
2026 int i;
2027
2028 if (av == NULL((void *)0))
2029 return;
2030 for (i = 0; i < ac; i++)
2031 free(av[i]);
2032 free(av);
2033}
2034
2035/* Returns 0 if pid exited cleanly, non-zero otherwise */
2036int
2037exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet)
2038{
2039 int status;
2040
2041 while (waitpid(pid, &status, 0) == -1) {
2042 if (errno(*__errno()) != EINTR4) {
2043 error("%s waitpid: %s", tag, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2043, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s waitpid: %s",
tag, strerror((*__errno())))
;
2044 return -1;
2045 }
2046 }
2047 if (WIFSIGNALED(status)(((status) & 0177) != 0177 && ((status) & 0177
) != 0)
) {
2048 error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2048, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s %s exited on signal %d"
, tag, cmd, (((status) & 0177)))
;
2049 return -1;
2050 } else if (WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) != 0) {
2051 do_log2(quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO,sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2052, 0, quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO, (
(void *)0), "%s %s failed, status %d", tag, cmd, (int)(((unsigned
)(status) >> 8) & 0xff))
2052 "%s %s failed, status %d", tag, cmd, WEXITSTATUS(status))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2052, 0, quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO, (
(void *)0), "%s %s failed, status %d", tag, cmd, (int)(((unsigned
)(status) >> 8) & 0xff))
;
2053 return -1;
2054 }
2055 return 0;
2056}
2057
2058/*
2059 * Check a given path for security. This is defined as all components
2060 * of the path to the file must be owned by either the owner of
2061 * of the file or root and no directories must be group or world writable.
2062 *
2063 * XXX Should any specific check be done for sym links ?
2064 *
2065 * Takes a file name, its stat information (preferably from fstat() to
2066 * avoid races), the uid of the expected owner, their home directory and an
2067 * error buffer plus max size as arguments.
2068 *
2069 * Returns 0 on success and -1 on failure
2070 */
2071int
2072safe_path(const char *name, struct stat *stp, const char *pw_dir,
2073 uid_t uid, char *err, size_t errlen)
2074{
2075 char buf[PATH_MAX1024], homedir[PATH_MAX1024];
2076 char *cp;
2077 int comparehome = 0;
2078 struct stat st;
2079
2080 if (realpath(name, buf) == NULL((void *)0)) {
2081 snprintf(err, errlen, "realpath %s failed: %s", name,
2082 strerror(errno(*__errno())));
2083 return -1;
2084 }
2085 if (pw_dir != NULL((void *)0) && realpath(pw_dir, homedir) != NULL((void *)0))
2086 comparehome = 1;
2087
2088 if (!S_ISREG(stp->st_mode)((stp->st_mode & 0170000) == 0100000)) {
2089 snprintf(err, errlen, "%s is not a regular file", buf);
2090 return -1;
2091 }
2092 if ((stp->st_uid != 0 && stp->st_uid != uid) ||
2093 (stp->st_mode & 022) != 0) {
2094 snprintf(err, errlen, "bad ownership or modes for file %s",
2095 buf);
2096 return -1;
2097 }
2098
2099 /* for each component of the canonical path, walking upwards */
2100 for (;;) {
2101 if ((cp = dirname(buf)) == NULL((void *)0)) {
2102 snprintf(err, errlen, "dirname() failed");
2103 return -1;
2104 }
2105 strlcpy(buf, cp, sizeof(buf));
2106
2107 if (stat(buf, &st) == -1 ||
2108 (st.st_uid != 0 && st.st_uid != uid) ||
2109 (st.st_mode & 022) != 0) {
2110 snprintf(err, errlen,
2111 "bad ownership or modes for directory %s", buf);
2112 return -1;
2113 }
2114
2115 /* If are past the homedir then we can stop */
2116 if (comparehome && strcmp(homedir, buf) == 0)
2117 break;
2118
2119 /*
2120 * dirname should always complete with a "/" path,
2121 * but we can be paranoid and check for "." too
2122 */
2123 if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
2124 break;
2125 }
2126 return 0;
2127}
2128
2129/*
2130 * Version of safe_path() that accepts an open file descriptor to
2131 * avoid races.
2132 *
2133 * Returns 0 on success and -1 on failure
2134 */
2135int
2136safe_path_fd(int fd, const char *file, struct passwd *pw,
2137 char *err, size_t errlen)
2138{
2139 struct stat st;
2140
2141 /* check the open file to avoid races */
2142 if (fstat(fd, &st) == -1) {
2143 snprintf(err, errlen, "cannot stat file %s: %s",
2144 file, strerror(errno(*__errno())));
2145 return -1;
2146 }
2147 return safe_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen);
2148}
2149
2150/*
2151 * Sets the value of the given variable in the environment. If the variable
2152 * already exists, its value is overridden.
2153 */
2154void
2155child_set_env(char ***envp, u_int *envsizep, const char *name,
2156 const char *value)
2157{
2158 char **env;
2159 u_int envsize;
2160 u_int i, namelen;
2161
2162 if (strchr(name, '=') != NULL((void *)0)) {
2163 error("Invalid environment variable \"%.100s\"", name)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2163, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "Invalid environment variable \"%.100s\""
, name)
;
2164 return;
2165 }
2166
2167 /*
2168 * Find the slot where the value should be stored. If the variable
2169 * already exists, we reuse the slot; otherwise we append a new slot
2170 * at the end of the array, expanding if necessary.
2171 */
2172 env = *envp;
2173 namelen = strlen(name);
2174 for (i = 0; env[i]; i++)
2175 if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
2176 break;
2177 if (env[i]) {
2178 /* Reuse the slot. */
2179 free(env[i]);
2180 } else {
2181 /* New variable. Expand if necessary. */
2182 envsize = *envsizep;
2183 if (i >= envsize - 1) {
2184 if (envsize >= 1000)
2185 fatal("child_set_env: too many env vars")sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2185, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "child_set_env: too many env vars"
)
;
2186 envsize += 50;
2187 env = (*envp) = xreallocarray(env, envsize, sizeof(char *));
2188 *envsizep = envsize;
2189 }
2190 /* Need to set the NULL pointer at end of array beyond the new slot. */
2191 env[i + 1] = NULL((void *)0);
2192 }
2193
2194 /* Allocate space and format the variable in the appropriate slot. */
2195 /* XXX xasprintf */
2196 env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
2197 snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
2198}
2199
2200/*
2201 * Check and optionally lowercase a domain name, also removes trailing '.'
2202 * Returns 1 on success and 0 on failure, storing an error message in errstr.
2203 */
2204int
2205valid_domain(char *name, int makelower, const char **errstr)
2206{
2207 size_t i, l = strlen(name);
2208 u_char c, last = '\0';
2209 static char errbuf[256];
2210
2211 if (l == 0) {
2212 strlcpy(errbuf, "empty domain name", sizeof(errbuf));
2213 goto bad;
2214 }
2215 if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) {
2216 snprintf(errbuf, sizeof(errbuf), "domain name \"%.100s\" "
2217 "starts with invalid character", name);
2218 goto bad;
2219 }
2220 for (i = 0; i < l; i++) {
2221 c = tolower((u_char)name[i]);
2222 if (makelower)
2223 name[i] = (char)c;
2224 if (last == '.' && c == '.') {
2225 snprintf(errbuf, sizeof(errbuf), "domain name "
2226 "\"%.100s\" contains consecutive separators", name);
2227 goto bad;
2228 }
2229 if (c != '.' && c != '-' && !isalnum(c) &&
2230 c != '_') /* technically invalid, but common */ {
2231 snprintf(errbuf, sizeof(errbuf), "domain name "
2232 "\"%.100s\" contains invalid characters", name);
2233 goto bad;
2234 }
2235 last = c;
2236 }
2237 if (name[l - 1] == '.')
2238 name[l - 1] = '\0';
2239 if (errstr != NULL((void *)0))
2240 *errstr = NULL((void *)0);
2241 return 1;
2242bad:
2243 if (errstr != NULL((void *)0))
2244 *errstr = errbuf;
2245 return 0;
2246}
2247
2248/*
2249 * Verify that a environment variable name (not including initial '$') is
2250 * valid; consisting of one or more alphanumeric or underscore characters only.
2251 * Returns 1 on valid, 0 otherwise.
2252 */
2253int
2254valid_env_name(const char *name)
2255{
2256 const char *cp;
2257
2258 if (name[0] == '\0')
2259 return 0;
2260 for (cp = name; *cp != '\0'; cp++) {
2261 if (!isalnum((u_char)*cp) && *cp != '_')
2262 return 0;
2263 }
2264 return 1;
2265}
2266
2267const char *
2268atoi_err(const char *nptr, int *val)
2269{
2270 const char *errstr = NULL((void *)0);
2271 long long num;
2272
2273 if (nptr == NULL((void *)0) || *nptr == '\0')
2274 return "missing";
2275 num = strtonum(nptr, 0, INT_MAX2147483647, &errstr);
2276 if (errstr == NULL((void *)0))
2277 *val = (int)num;
2278 return errstr;
2279}
2280
2281int
2282parse_absolute_time(const char *s, uint64_t *tp)
2283{
2284 struct tm tm;
2285 time_t tt;
2286 char buf[32], *fmt;
2287
2288 *tp = 0;
2289
2290 /*
2291 * POSIX strptime says "The application shall ensure that there
2292 * is white-space or other non-alphanumeric characters between
2293 * any two conversion specifications" so arrange things this way.
2294 */
2295 switch (strlen(s)) {
2296 case 8: /* YYYYMMDD */
2297 fmt = "%Y-%m-%d";
2298 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6);
2299 break;
2300 case 12: /* YYYYMMDDHHMM */
2301 fmt = "%Y-%m-%dT%H:%M";
2302 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s",
2303 s, s + 4, s + 6, s + 8, s + 10);
2304 break;
2305 case 14: /* YYYYMMDDHHMMSS */
2306 fmt = "%Y-%m-%dT%H:%M:%S";
2307 snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",
2308 s, s + 4, s + 6, s + 8, s + 10, s + 12);
2309 break;
2310 default:
2311 return SSH_ERR_INVALID_FORMAT-4;
2312 }
2313
2314 memset(&tm, 0, sizeof(tm));
2315 if (strptime(buf, fmt, &tm) == NULL((void *)0))
2316 return SSH_ERR_INVALID_FORMAT-4;
2317 if ((tt = mktime(&tm)) < 0)
2318 return SSH_ERR_INVALID_FORMAT-4;
2319 /* success */
2320 *tp = (uint64_t)tt;
2321 return 0;
2322}
2323
2324/* On OpenBSD time_t is int64_t which is long long. */
2325#define SSH_TIME_T_MAX9223372036854775807LL LLONG_MAX9223372036854775807LL
2326
2327void
2328format_absolute_time(uint64_t t, char *buf, size_t len)
2329{
2330 time_t tt = t > SSH_TIME_T_MAX9223372036854775807LL ? SSH_TIME_T_MAX9223372036854775807LL : t;
2331 struct tm tm;
2332
2333 localtime_r(&tt, &tm);
2334 strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm);
2335}
2336
2337/* check if path is absolute */
2338int
2339path_absolute(const char *path)
2340{
2341 return (*path == '/') ? 1 : 0;
2342}
2343
2344void
2345skip_space(char **cpp)
2346{
2347 char *cp;
2348
2349 for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
2350 ;
2351 *cpp = cp;
2352}
2353
2354/* authorized_key-style options parsing helpers */
2355
2356/*
2357 * Match flag 'opt' in *optsp, and if allow_negate is set then also match
2358 * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
2359 * if negated option matches.
2360 * If the option or negated option matches, then *optsp is updated to
2361 * point to the first character after the option.
2362 */
2363int
2364opt_flag(const char *opt, int allow_negate, const char **optsp)
2365{
2366 size_t opt_len = strlen(opt);
2367 const char *opts = *optsp;
2368 int negate = 0;
2369
2370 if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
2371 opts += 3;
2372 negate = 1;
2373 }
2374 if (strncasecmp(opts, opt, opt_len) == 0) {
2375 *optsp = opts + opt_len;
2376 return negate ? 0 : 1;
2377 }
2378 return -1;
2379}
2380
2381char *
2382opt_dequote(const char **sp, const char **errstrp)
2383{
2384 const char *s = *sp;
2385 char *ret;
2386 size_t i;
2387
2388 *errstrp = NULL((void *)0);
2389 if (*s != '"') {
2390 *errstrp = "missing start quote";
2391 return NULL((void *)0);
2392 }
2393 s++;
2394 if ((ret = malloc(strlen((s)) + 1)) == NULL((void *)0)) {
2395 *errstrp = "memory allocation failed";
2396 return NULL((void *)0);
2397 }
2398 for (i = 0; *s != '\0' && *s != '"';) {
2399 if (s[0] == '\\' && s[1] == '"')
2400 s++;
2401 ret[i++] = *s++;
2402 }
2403 if (*s == '\0') {
2404 *errstrp = "missing end quote";
2405 free(ret);
2406 return NULL((void *)0);
2407 }
2408 ret[i] = '\0';
2409 s++;
2410 *sp = s;
2411 return ret;
2412}
2413
2414int
2415opt_match(const char **opts, const char *term)
2416{
2417 if (strncasecmp((*opts), term, strlen(term)) == 0 &&
2418 (*opts)[strlen(term)] == '=') {
2419 *opts += strlen(term) + 1;
2420 return 1;
2421 }
2422 return 0;
2423}
2424
2425void
2426opt_array_append2(const char *file, const int line, const char *directive,
2427 char ***array, int **iarray, u_int *lp, const char *s, int i)
2428{
2429
2430 if (*lp >= INT_MAX2147483647)
2431 fatal("%s line %d: Too many %s entries", file, line, directive)sshfatal("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2431, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "%s line %d: Too many %s entries"
, file, line, directive)
;
2432
2433 if (iarray != NULL((void *)0)) {
2434 *iarray = xrecallocarray(*iarray, *lp, *lp + 1,
2435 sizeof(**iarray));
2436 (*iarray)[*lp] = i;
2437 }
2438
2439 *array = xrecallocarray(*array, *lp, *lp + 1, sizeof(**array));
2440 (*array)[*lp] = xstrdup(s);
2441 (*lp)++;
2442}
2443
2444void
2445opt_array_append(const char *file, const int line, const char *directive,
2446 char ***array, u_int *lp, const char *s)
2447{
2448 opt_array_append2(file, line, directive, array, NULL((void *)0), lp, s, 0);
2449}
2450
2451sshsig_t
2452ssh_signal(int signum, sshsig_t handler)
2453{
2454 struct sigaction sa, osa;
2455
2456 /* mask all other signals while in handler */
2457 memset(&sa, 0, sizeof(sa));
2458 sa.sa_handler__sigaction_u.__sa_handler = handler;
2459 sigfillset(&sa.sa_mask);
2460 if (signum != SIGALRM14)
2461 sa.sa_flags = SA_RESTART0x0002;
2462 if (sigaction(signum, &sa, &osa) == -1) {
2463 debug3("sigaction(%s): %s", strsignal(signum), strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2463, 0, SYSLOG_LEVEL_DEBUG3, ((void *)0), "sigaction(%s): %s"
, strsignal(signum), strerror((*__errno())))
;
2464 return SIG_ERR(void (*)(int))-1;
2465 }
2466 return osa.sa_handler__sigaction_u.__sa_handler;
2467}
2468
2469int
2470stdfd_devnull(int do_stdin, int do_stdout, int do_stderr)
2471{
2472 int devnull, ret = 0;
2473
2474 if ((devnull = open(_PATH_DEVNULL"/dev/null", O_RDWR0x0002)) == -1) {
2475 error_f("open %s: %s", _PATH_DEVNULL,sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2476, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "open %s: %s", "/dev/null"
, strerror((*__errno())))
2476 strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2476, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "open %s: %s", "/dev/null"
, strerror((*__errno())))
;
2477 return -1;
2478 }
2479 if ((do_stdin && dup2(devnull, STDIN_FILENO0) == -1) ||
2480 (do_stdout && dup2(devnull, STDOUT_FILENO1) == -1) ||
2481 (do_stderr && dup2(devnull, STDERR_FILENO2) == -1)) {
2482 error_f("dup2: %s", strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2482, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "dup2: %s", strerror
((*__errno())))
;
2483 ret = -1;
2484 }
2485 if (devnull > STDERR_FILENO2)
2486 close(devnull);
2487 return ret;
2488}
2489
2490/*
2491 * Runs command in a subprocess with a minimal environment.
2492 * Returns pid on success, 0 on failure.
2493 * The child stdout and stderr maybe captured, left attached or sent to
2494 * /dev/null depending on the contents of flags.
2495 * "tag" is prepended to log messages.
2496 * NB. "command" is only used for logging; the actual command executed is
2497 * av[0].
2498 */
2499pid_t
2500subprocess(const char *tag, const char *command,
2501 int ac, char **av, FILE **child, u_int flags,
2502 struct passwd *pw, privdrop_fn *drop_privs, privrestore_fn *restore_privs)
2503{
2504 FILE *f = NULL((void *)0);
2505 struct stat st;
2506 int fd, devnull, p[2], i;
2507 pid_t pid;
2508 char *cp, errmsg[512];
2509 u_int nenv = 0;
2510 char **env = NULL((void *)0);
2511
2512 /* If dropping privs, then must specify user and restore function */
2513 if (drop_privs != NULL((void *)0) && (pw == NULL((void *)0) || restore_privs == NULL((void *)0))) {
2514 error("%s: inconsistent arguments", tag)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2514, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: inconsistent arguments"
, tag)
; /* XXX fatal? */
2515 return 0;
2516 }
2517 if (pw == NULL((void *)0) && (pw = getpwuid(getuid())) == NULL((void *)0)) {
2518 error("%s: no user for current uid", tag)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2518, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: no user for current uid"
, tag)
;
2519 return 0;
2520 }
2521 if (child != NULL((void *)0))
2522 *child = NULL((void *)0);
2523
2524 debug3_f("%s command \"%s\" running as %s (flags 0x%x)",sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2525, 1, SYSLOG_LEVEL_DEBUG3, ((void *)0), "%s command \"%s\" running as %s (flags 0x%x)"
, tag, command, pw->pw_name, flags)
2525 tag, command, pw->pw_name, flags)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2525, 1, SYSLOG_LEVEL_DEBUG3, ((void *)0), "%s command \"%s\" running as %s (flags 0x%x)"
, tag, command, pw->pw_name, flags)
;
2526
2527 /* Check consistency */
2528 if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD(1)) != 0 &&
2529 (flags & SSH_SUBPROCESS_STDOUT_CAPTURE(1<<1)) != 0) {
2530 error_f("inconsistent flags")sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2530, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "inconsistent flags"
)
;
2531 return 0;
2532 }
2533 if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE(1<<1)) == 0) != (child == NULL((void *)0))) {
2534 error_f("inconsistent flags/output")sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2534, 1, SYSLOG_LEVEL_ERROR, ((void *)0), "inconsistent flags/output"
)
;
2535 return 0;
2536 }
2537
2538 /*
2539 * If executing an explicit binary, then verify the it exists
2540 * and appears safe-ish to execute
2541 */
2542 if (!path_absolute(av[0])) {
2543 error("%s path is not absolute", tag)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2543, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s path is not absolute"
, tag)
;
2544 return 0;
2545 }
2546 if (drop_privs != NULL((void *)0))
2547 drop_privs(pw);
2548 if (stat(av[0], &st) == -1) {
2549 error("Could not stat %s \"%s\": %s", tag,sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2550, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "Could not stat %s \"%s\": %s"
, tag, av[0], strerror((*__errno())))
2550 av[0], strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2550, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "Could not stat %s \"%s\": %s"
, tag, av[0], strerror((*__errno())))
;
2551 goto restore_return;
2552 }
2553 if ((flags & SSH_SUBPROCESS_UNSAFE_PATH(1<<3)) == 0 &&
2554 safe_path(av[0], &st, NULL((void *)0), 0, errmsg, sizeof(errmsg)) != 0) {
2555 error("Unsafe %s \"%s\": %s", tag, av[0], errmsg)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2555, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "Unsafe %s \"%s\": %s"
, tag, av[0], errmsg)
;
2556 goto restore_return;
2557 }
2558 /* Prepare to keep the child's stdout if requested */
2559 if (pipe(p) == -1) {
2560 error("%s: pipe: %s", tag, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2560, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: pipe: %s", tag
, strerror((*__errno())))
;
2561 restore_return:
2562 if (restore_privs != NULL((void *)0))
2563 restore_privs();
2564 return 0;
2565 }
2566 if (restore_privs != NULL((void *)0))
2567 restore_privs();
2568
2569 switch ((pid = fork())) {
2570 case -1: /* error */
2571 error("%s: fork: %s", tag, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2571, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: fork: %s", tag
, strerror((*__errno())))
;
2572 close(p[0]);
2573 close(p[1]);
2574 return 0;
2575 case 0: /* child */
2576 /* Prepare a minimal environment for the child. */
2577 if ((flags & SSH_SUBPROCESS_PRESERVE_ENV(1<<4)) == 0) {
2578 nenv = 5;
2579 env = xcalloc(sizeof(*env), nenv);
2580 child_set_env(&env, &nenv, "PATH", _PATH_STDPATH"/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin");
2581 child_set_env(&env, &nenv, "USER", pw->pw_name);
2582 child_set_env(&env, &nenv, "LOGNAME", pw->pw_name);
2583 child_set_env(&env, &nenv, "HOME", pw->pw_dir);
2584 if ((cp = getenv("LANG")) != NULL((void *)0))
2585 child_set_env(&env, &nenv, "LANG", cp);
2586 }
2587
2588 for (i = 1; i < NSIG33; i++)
2589 ssh_signal(i, SIG_DFL(void (*)(int))0);
2590
2591 if ((devnull = open(_PATH_DEVNULL"/dev/null", O_RDWR0x0002)) == -1) {
2592 error("%s: open %s: %s", tag, _PATH_DEVNULL,sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2593, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: open %s: %s"
, tag, "/dev/null", strerror((*__errno())))
2593 strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2593, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: open %s: %s"
, tag, "/dev/null", strerror((*__errno())))
;
2594 _exit(1);
2595 }
2596 if (dup2(devnull, STDIN_FILENO0) == -1) {
2597 error("%s: dup2: %s", tag, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2597, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: dup2: %s", tag
, strerror((*__errno())))
;
2598 _exit(1);
2599 }
2600
2601 /* Set up stdout as requested; leave stderr in place for now. */
2602 fd = -1;
2603 if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE(1<<1)) != 0)
2604 fd = p[1];
2605 else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD(1)) != 0)
2606 fd = devnull;
2607 if (fd != -1 && dup2(fd, STDOUT_FILENO1) == -1) {
2608 error("%s: dup2: %s", tag, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2608, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: dup2: %s", tag
, strerror((*__errno())))
;
2609 _exit(1);
2610 }
2611 closefrom(STDERR_FILENO2 + 1);
2612
2613 if (geteuid() == 0 &&
2614 initgroups(pw->pw_name, pw->pw_gid) == -1) {
2615 error("%s: initgroups(%s, %u): %s", tag,sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2616, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: initgroups(%s, %u): %s"
, tag, pw->pw_name, (u_int)pw->pw_gid, strerror((*__errno
())))
2616 pw->pw_name, (u_int)pw->pw_gid, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2616, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: initgroups(%s, %u): %s"
, tag, pw->pw_name, (u_int)pw->pw_gid, strerror((*__errno
())))
;
2617 _exit(1);
2618 }
2619 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) {
2620 error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid,sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2621, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: setresgid %u: %s"
, tag, (u_int)pw->pw_gid, strerror((*__errno())))
2621 strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2621, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: setresgid %u: %s"
, tag, (u_int)pw->pw_gid, strerror((*__errno())))
;
2622 _exit(1);
2623 }
2624 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) {
2625 error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid,sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2626, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: setresuid %u: %s"
, tag, (u_int)pw->pw_uid, strerror((*__errno())))
2626 strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2626, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: setresuid %u: %s"
, tag, (u_int)pw->pw_uid, strerror((*__errno())))
;
2627 _exit(1);
2628 }
2629 /* stdin is pointed to /dev/null at this point */
2630 if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD(1)) != 0 &&
2631 dup2(STDIN_FILENO0, STDERR_FILENO2) == -1) {
2632 error("%s: dup2: %s", tag, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2632, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: dup2: %s", tag
, strerror((*__errno())))
;
2633 _exit(1);
2634 }
2635 if (env != NULL((void *)0))
2636 execve(av[0], av, env);
2637 else
2638 execv(av[0], av);
2639 error("%s %s \"%s\": %s", tag, env == NULL ? "execv" : "execve",sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2640, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s %s \"%s\": %s"
, tag, env == ((void *)0) ? "execv" : "execve", command, strerror
((*__errno())))
2640 command, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2640, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s %s \"%s\": %s"
, tag, env == ((void *)0) ? "execv" : "execve", command, strerror
((*__errno())))
;
2641 _exit(127);
2642 default: /* parent */
2643 break;
2644 }
2645
2646 close(p[1]);
2647 if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE(1<<1)) == 0)
2648 close(p[0]);
2649 else if ((f = fdopen(p[0], "r")) == NULL((void *)0)) {
2650 error("%s: fdopen: %s", tag, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2650, 0, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: fdopen: %s",
tag, strerror((*__errno())))
;
2651 close(p[0]);
2652 /* Don't leave zombie child */
2653 kill(pid, SIGTERM15);
2654 while (waitpid(pid, NULL((void *)0), 0) == -1 && errno(*__errno()) == EINTR4)
2655 ;
2656 return 0;
2657 }
2658 /* Success */
2659 debug3_f("%s pid %ld", tag, (long)pid)sshlog("/usr/src/usr.bin/ssh/sftp-server/../misc.c", __func__
, 2659, 1, SYSLOG_LEVEL_DEBUG3, ((void *)0), "%s pid %ld", tag
, (long)pid)
;
2660 if (child != NULL((void *)0))
2661 *child = f;
2662 return pid;
2663}
2664
2665const char *
2666lookup_env_in_list(const char *env, char * const *envs, size_t nenvs)
2667{
2668 size_t i, envlen;
2669
2670 envlen = strlen(env);
2671 for (i = 0; i < nenvs; i++) {
2672 if (strncmp(envs[i], env, envlen) == 0 &&
2673 envs[i][envlen] == '=') {
2674 return envs[i] + envlen + 1;
2675 }
2676 }
2677 return NULL((void *)0);
2678}