1 /* $OpenBSD: misc.c,v 1.190 2024/03/04 02:16:11 djm Exp $ */
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>
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.
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.
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
26 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <netinet/in_systm.h>
55 #include <netinet/ip.h>
56 #include <netinet/tcp.h>
57 #include <arpa/inet.h>
68 #ifdef SSH_TUN_OPENBSD
80 /* remove newline at end of string */
86 if (*t
== '\n' || *t
== '\r') {
96 /* remove whitespace from end of string */
102 if ((i
= strlen(s
)) == 0)
104 for (i
--; i
> 0; i
--) {
105 if (isspace((unsigned char)s
[i
]))
110 /* set/unset filedescriptor to non-blocking */
116 val
= fcntl(fd
, F_GETFL
);
118 error("fcntl(%d, F_GETFL): %s", fd
, strerror(errno
));
121 if (val
& O_NONBLOCK
) {
122 debug3("fd %d is O_NONBLOCK", fd
);
125 debug2("fd %d setting O_NONBLOCK", fd
);
127 if (fcntl(fd
, F_SETFL
, val
) == -1) {
128 debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd
,
136 unset_nonblock(int fd
)
140 val
= fcntl(fd
, F_GETFL
);
142 error("fcntl(%d, F_GETFL): %s", fd
, strerror(errno
));
145 if (!(val
& O_NONBLOCK
)) {
146 debug3("fd %d is not O_NONBLOCK", fd
);
149 debug("fd %d clearing O_NONBLOCK", fd
);
151 if (fcntl(fd
, F_SETFL
, val
) == -1) {
152 debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s",
153 fd
, strerror(errno
));
160 ssh_gai_strerror(int gaierr
)
162 if (gaierr
== EAI_SYSTEM
&& errno
!= 0)
163 return strerror(errno
);
164 return gai_strerror(gaierr
);
167 /* disable nagle on socket */
175 if (getsockopt(fd
, IPPROTO_TCP
, TCP_NODELAY
, &opt
, &optlen
) == -1) {
176 debug("getsockopt TCP_NODELAY: %.100s", strerror(errno
));
180 debug2("fd %d is TCP_NODELAY", fd
);
184 debug2("fd %d setting TCP_NODELAY", fd
);
185 if (setsockopt(fd
, IPPROTO_TCP
, TCP_NODELAY
, &opt
, sizeof opt
) == -1)
186 error("setsockopt TCP_NODELAY: %.100s", strerror(errno
));
189 /* Allow local port reuse in TIME_WAIT */
191 set_reuseaddr(int fd
)
195 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
196 error("setsockopt SO_REUSEADDR fd %d: %s", fd
, strerror(errno
));
202 /* Get/set routing domain */
206 #if defined(HAVE_SYS_GET_RDOMAIN)
207 return sys_get_rdomain(fd
);
208 #elif defined(__OpenBSD__)
211 socklen_t len
= sizeof(rtable
);
213 if (getsockopt(fd
, SOL_SOCKET
, SO_RTABLE
, &rtable
, &len
) == -1) {
214 error("Failed to get routing domain for fd %d: %s",
215 fd
, strerror(errno
));
218 xasprintf(&ret
, "%d", rtable
);
220 #else /* defined(__OpenBSD__) */
226 set_rdomain(int fd
, const char *name
)
228 #if defined(HAVE_SYS_SET_RDOMAIN)
229 return sys_set_rdomain(fd
, name
);
230 #elif defined(__OpenBSD__)
235 return 0; /* default table */
237 rtable
= (int)strtonum(name
, 0, 255, &errstr
);
238 if (errstr
!= NULL
) {
239 /* Shouldn't happen */
240 error("Invalid routing domain \"%s\": %s", name
, errstr
);
243 if (setsockopt(fd
, SOL_SOCKET
, SO_RTABLE
,
244 &rtable
, sizeof(rtable
)) == -1) {
245 error("Failed to set routing domain %d on fd %d: %s",
246 rtable
, fd
, strerror(errno
));
250 #else /* defined(__OpenBSD__) */
251 error("Setting routing domain is not supported on this platform");
259 struct sockaddr_storage to
;
260 socklen_t tolen
= sizeof(to
);
262 memset(&to
, 0, sizeof(to
));
263 if (getsockname(fd
, (struct sockaddr
*)&to
, &tolen
) == -1)
266 if (to
.ss_family
== AF_INET6
&&
267 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6
*)&to
)->sin6_addr
))
274 set_sock_tos(int fd
, int tos
)
276 #ifndef IP_TOS_IS_BROKEN
279 switch ((af
= get_sock_af(fd
))) {
281 /* assume not a socket */
285 debug3_f("set socket %d IP_TOS 0x%02x", fd
, tos
);
286 if (setsockopt(fd
, IPPROTO_IP
, IP_TOS
,
287 &tos
, sizeof(tos
)) == -1) {
288 error("setsockopt socket %d IP_TOS %d: %s",
289 fd
, tos
, strerror(errno
));
295 debug3_f("set socket %d IPV6_TCLASS 0x%02x", fd
, tos
);
296 if (setsockopt(fd
, IPPROTO_IPV6
, IPV6_TCLASS
,
297 &tos
, sizeof(tos
)) == -1) {
298 error("setsockopt socket %d IPV6_TCLASS %d: %s",
299 fd
, tos
, strerror(errno
));
301 # endif /* IPV6_TCLASS */
304 debug2_f("unsupported socket family %d", af
);
307 #endif /* IP_TOS_IS_BROKEN */
311 * Wait up to *timeoutp milliseconds for events on fd. Updates
312 * *timeoutp with time remaining.
313 * Returns 0 if fd ready or -1 on timeout or error (see errno).
316 waitfd(int fd
, int *timeoutp
, short events
, volatile sig_atomic_t *stop
)
319 struct timespec timeout
;
321 sigset_t nsigset
, osigset
;
323 if (timeoutp
&& *timeoutp
== -1)
327 ptimeout_init(&timeout
);
328 if (timeoutp
!= NULL
)
329 ptimeout_deadline_ms(&timeout
, *timeoutp
);
331 sigfillset(&nsigset
);
332 for (; timeoutp
== NULL
|| *timeoutp
>= 0;) {
334 sigprocmask(SIG_BLOCK
, &nsigset
, &osigset
);
336 sigprocmask(SIG_SETMASK
, &osigset
, NULL
);
341 r
= ppoll(&pfd
, 1, ptimeout_get_tsp(&timeout
),
342 stop
!= NULL
? &osigset
: NULL
);
345 sigprocmask(SIG_SETMASK
, &osigset
, NULL
);
347 *timeoutp
= ptimeout_get_ms(&timeout
);
351 else if (r
== -1 && errno
!= EAGAIN
&& errno
!= EINTR
)
362 * Wait up to *timeoutp milliseconds for fd to be readable. Updates
363 * *timeoutp with time remaining.
364 * Returns 0 if fd ready or -1 on timeout or error (see errno).
367 waitrfd(int fd
, int *timeoutp
, volatile sig_atomic_t *stop
) {
368 return waitfd(fd
, timeoutp
, POLLIN
, stop
);
372 * Attempt a non-blocking connect(2) to the specified address, waiting up to
373 * *timeoutp milliseconds for the connection to complete. If the timeout is
374 * <=0, then wait indefinitely.
376 * Returns 0 on success or -1 on failure.
379 timeout_connect(int sockfd
, const struct sockaddr
*serv_addr
,
380 socklen_t addrlen
, int *timeoutp
)
383 socklen_t optlen
= sizeof(optval
);
385 /* No timeout: just do a blocking connect() */
386 if (timeoutp
== NULL
|| *timeoutp
<= 0)
387 return connect(sockfd
, serv_addr
, addrlen
);
389 set_nonblock(sockfd
);
391 if (connect(sockfd
, serv_addr
, addrlen
) == 0) {
392 /* Succeeded already? */
393 unset_nonblock(sockfd
);
395 } else if (errno
== EINTR
)
397 else if (errno
!= EINPROGRESS
)
402 if (waitfd(sockfd
, timeoutp
, POLLIN
| POLLOUT
, NULL
) == -1)
405 /* Completed or failed */
406 if (getsockopt(sockfd
, SOL_SOCKET
, SO_ERROR
, &optval
, &optlen
) == -1) {
407 debug("getsockopt: %s", strerror(errno
));
414 unset_nonblock(sockfd
);
418 /* Characters considered whitespace in strsep calls. */
419 #define WHITESPACE " \t\r\n"
422 /* return next token in configuration line */
424 strdelim_internal(char **s
, int split_equals
)
435 split_equals
? WHITESPACE QUOTE
"=" : WHITESPACE QUOTE
);
440 memmove(*s
, *s
+ 1, strlen(*s
)); /* move nul too */
441 /* Find matching quote */
442 if ((*s
= strpbrk(*s
, QUOTE
)) == NULL
) {
443 return (NULL
); /* no matching quote */
446 *s
+= strspn(*s
+ 1, WHITESPACE
) + 1;
451 /* Allow only one '=' to be skipped */
452 if (split_equals
&& *s
[0] == '=')
456 /* Skip any extra whitespace after first token */
457 *s
+= strspn(*s
+ 1, WHITESPACE
) + 1;
458 if (split_equals
&& *s
[0] == '=' && !wspace
)
459 *s
+= strspn(*s
+ 1, WHITESPACE
) + 1;
465 * Return next token in configuration line; splts on whitespace or a
466 * single '=' character.
471 return strdelim_internal(s
, 1);
475 * Return next token in configuration line; splts on whitespace only.
480 return strdelim_internal(s
, 0);
484 pwcopy(struct passwd
*pw
)
486 struct passwd
*copy
= xcalloc(1, sizeof(*copy
));
488 copy
->pw_name
= xstrdup(pw
->pw_name
);
489 copy
->pw_passwd
= xstrdup(pw
->pw_passwd
== NULL
? "*" : pw
->pw_passwd
);
490 #ifdef HAVE_STRUCT_PASSWD_PW_GECOS
491 copy
->pw_gecos
= xstrdup(pw
->pw_gecos
);
493 copy
->pw_uid
= pw
->pw_uid
;
494 copy
->pw_gid
= pw
->pw_gid
;
495 #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
496 copy
->pw_expire
= pw
->pw_expire
;
498 #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE
499 copy
->pw_change
= pw
->pw_change
;
501 #ifdef HAVE_STRUCT_PASSWD_PW_CLASS
502 copy
->pw_class
= xstrdup(pw
->pw_class
);
504 copy
->pw_dir
= xstrdup(pw
->pw_dir
);
505 copy
->pw_shell
= xstrdup(pw
->pw_shell
);
510 * Convert ASCII string to TCP/IP port number.
511 * Port must be >=0 and <=65535.
512 * Return -1 if invalid.
515 a2port(const char *s
)
521 port
= strtonum(s
, 0, 65535, &errstr
);
524 if ((se
= getservbyname(s
, "tcp")) != NULL
)
525 return ntohs(se
->s_port
);
530 a2tun(const char *s
, int *remote
)
532 const char *errstr
= NULL
;
536 if (remote
!= NULL
) {
537 *remote
= SSH_TUNID_ANY
;
539 if ((ep
= strchr(sp
, ':')) == NULL
) {
541 return (a2tun(s
, NULL
));
544 *remote
= a2tun(ep
, NULL
);
545 tun
= a2tun(sp
, NULL
);
547 return (*remote
== SSH_TUNID_ERR
? *remote
: tun
);
550 if (strcasecmp(s
, "any") == 0)
551 return (SSH_TUNID_ANY
);
553 tun
= strtonum(s
, 0, SSH_TUNID_MAX
, &errstr
);
555 return (SSH_TUNID_ERR
);
561 #define MINUTES (SECONDS * 60)
562 #define HOURS (MINUTES * 60)
563 #define DAYS (HOURS * 24)
564 #define WEEKS (DAYS * 7)
567 * Convert a time string into seconds; format is
571 * Valid time qualifiers are:
585 * Return -1 if time string is invalid.
588 convtime(const char *s
)
590 long total
, secs
, multiplier
;
598 if (p
== NULL
|| *p
== '\0')
602 secs
= strtol(p
, &endp
, 10);
604 (errno
== ERANGE
&& (secs
== INT_MIN
|| secs
== INT_MAX
)) ||
618 multiplier
= MINUTES
;
635 if (secs
> INT_MAX
/ multiplier
)
638 if (total
> INT_MAX
- secs
)
653 fmt_timeframe(time_t t
)
656 static char tfbuf
[TF_BUFS
][TF_LEN
]; /* ring buffer */
658 unsigned int sec
, min
, hrs
, day
;
659 unsigned long long week
;
677 snprintf(buf
, TF_LEN
, "%02lluw%01ud%02uh", week
, day
, hrs
);
679 snprintf(buf
, TF_LEN
, "%01ud%02uh%02um", day
, hrs
, min
);
681 snprintf(buf
, TF_LEN
, "%02u:%02u:%02u", hrs
, min
, sec
);
687 * Returns a standardized host+port identifier string.
688 * Caller must free returned string.
691 put_host_port(const char *host
, u_short port
)
695 if (port
== 0 || port
== SSH_DEFAULT_PORT
)
696 return(xstrdup(host
));
697 if (asprintf(&hoststr
, "[%s]:%d", host
, (int)port
) == -1)
698 fatal("put_host_port: asprintf: %s", strerror(errno
));
699 debug3("put_host_port: %s", hoststr
);
704 * Search for next delimiter between hostnames/addresses and ports.
705 * Argument may be modified (for termination).
706 * Returns *cp if parsing succeeds.
707 * *cp is set to the start of the next field, if one was found.
708 * The delimiter char, if present, is stored in delim.
709 * If this is the last field, *cp is set to NULL.
712 hpdelim2(char **cp
, char *delim
)
716 if (cp
== NULL
|| *cp
== NULL
)
721 if ((s
= strchr(s
, ']')) == NULL
)
725 } else if ((s
= strpbrk(s
, ":/")) == NULL
)
726 s
= *cp
+ strlen(*cp
); /* skip to end (see first case below) */
730 *cp
= NULL
; /* no more fields*/
737 *s
= '\0'; /* terminate */
748 /* The common case: only accept colon as delimiter. */
752 char *r
, delim
= '\0';
754 r
= hpdelim2(cp
, &delim
);
761 cleanhostname(char *host
)
763 if (*host
== '[' && host
[strlen(host
) - 1] == ']') {
764 host
[strlen(host
) - 1] = '\0';
775 if (*cp
== ':') /* Leading colon is part of file name. */
781 if (*cp
== '@' && *(cp
+1) == '[')
783 if (*cp
== ']' && *(cp
+1) == ':' && flag
)
785 if (*cp
== ':' && !flag
)
794 * Parse a [user@]host:[path] string.
795 * Caller must free returned user, host and path.
796 * Any of the pointer return arguments may be NULL (useful for syntax checking).
797 * If user was not specified then *userp will be set to NULL.
798 * If host was not specified then *hostp will be set to NULL.
799 * If path was not specified then *pathp will be set to ".".
800 * Returns 0 on success, -1 on failure.
803 parse_user_host_path(const char *s
, char **userp
, char **hostp
, char **pathp
)
805 char *user
= NULL
, *host
= NULL
, *path
= NULL
;
818 /* Check for remote syntax: [user@]host:[path] */
819 if ((tmp
= colon(sdup
)) == NULL
)
822 /* Extract optional path */
828 /* Extract optional user and mandatory host */
829 tmp
= strrchr(sdup
, '@');
832 host
= xstrdup(cleanhostname(tmp
));
834 user
= xstrdup(sdup
);
836 host
= xstrdup(cleanhostname(sdup
));
863 * Parse a [user@]host[:port] string.
864 * Caller must free returned user and host.
865 * Any of the pointer return arguments may be NULL (useful for syntax checking).
866 * If user was not specified then *userp will be set to NULL.
867 * If port was not specified then *portp will be -1.
868 * Returns 0 on success, -1 on failure.
871 parse_user_host_port(const char *s
, char **userp
, char **hostp
, int *portp
)
873 char *sdup
, *cp
, *tmp
;
874 char *user
= NULL
, *host
= NULL
;
875 int port
= -1, ret
= -1;
884 if ((sdup
= tmp
= strdup(s
)) == NULL
)
886 /* Extract optional username */
887 if ((cp
= strrchr(tmp
, '@')) != NULL
) {
891 if ((user
= strdup(tmp
)) == NULL
)
895 /* Extract mandatory hostname */
896 if ((cp
= hpdelim(&tmp
)) == NULL
|| *cp
== '\0')
898 host
= xstrdup(cleanhostname(cp
));
899 /* Convert and verify optional port */
900 if (tmp
!= NULL
&& *tmp
!= '\0') {
901 if ((port
= a2port(tmp
)) <= 0)
924 * Converts a two-byte hex string to decimal.
925 * Returns the decimal value or -1 for invalid input.
928 hexchar(const char *s
)
930 unsigned char result
[2];
933 for (i
= 0; i
< 2; i
++) {
934 if (s
[i
] >= '0' && s
[i
] <= '9')
935 result
[i
] = (unsigned char)(s
[i
] - '0');
936 else if (s
[i
] >= 'a' && s
[i
] <= 'f')
937 result
[i
] = (unsigned char)(s
[i
] - 'a') + 10;
938 else if (s
[i
] >= 'A' && s
[i
] <= 'F')
939 result
[i
] = (unsigned char)(s
[i
] - 'A') + 10;
943 return (result
[0] << 4) | result
[1];
947 * Decode an url-encoded string.
948 * Returns a newly allocated string on success or NULL on failure.
951 urldecode(const char *src
)
957 if ((srclen
= strlen(src
)) >= SIZE_MAX
)
958 fatal_f("input too large");
959 ret
= xmalloc(srclen
+ 1);
960 for (dst
= ret
; *src
!= '\0'; src
++) {
966 if (!isxdigit((unsigned char)src
[1]) ||
967 !isxdigit((unsigned char)src
[2]) ||
968 (ch
= hexchar(src
+ 1)) == -1) {
986 * Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI.
987 * See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04
988 * Either user or path may be url-encoded (but not host or port).
989 * Caller must free returned user, host and path.
990 * Any of the pointer return arguments may be NULL (useful for syntax checking)
991 * but the scheme must always be specified.
992 * If user was not specified then *userp will be set to NULL.
993 * If port was not specified then *portp will be -1.
994 * If path was not specified then *pathp will be set to NULL.
995 * Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri.
998 parse_uri(const char *scheme
, const char *uri
, char **userp
, char **hostp
,
999 int *portp
, char **pathp
)
1001 char *uridup
, *cp
, *tmp
, ch
;
1002 char *user
= NULL
, *host
= NULL
, *path
= NULL
;
1003 int port
= -1, ret
= -1;
1006 len
= strlen(scheme
);
1007 if (strncmp(uri
, scheme
, len
) != 0 || strncmp(uri
+ len
, "://", 3) != 0)
1020 uridup
= tmp
= xstrdup(uri
);
1022 /* Extract optional ssh-info (username + connection params) */
1023 if ((cp
= strchr(tmp
, '@')) != NULL
) {
1027 /* Extract username and connection params */
1028 if ((delim
= strchr(tmp
, ';')) != NULL
) {
1029 /* Just ignore connection params for now */
1033 /* Empty username */
1036 if ((user
= urldecode(tmp
)) == NULL
)
1041 /* Extract mandatory hostname */
1042 if ((cp
= hpdelim2(&tmp
, &ch
)) == NULL
|| *cp
== '\0')
1044 host
= xstrdup(cleanhostname(cp
));
1045 if (!valid_domain(host
, 0, NULL
))
1048 if (tmp
!= NULL
&& *tmp
!= '\0') {
1050 /* Convert and verify port. */
1051 if ((cp
= strchr(tmp
, '/')) != NULL
)
1053 if ((port
= a2port(tmp
)) <= 0)
1055 tmp
= cp
? cp
+ 1 : NULL
;
1057 if (tmp
!= NULL
&& *tmp
!= '\0') {
1058 /* Extract optional path */
1059 if ((path
= urldecode(tmp
)) == NULL
)
1065 if (userp
!= NULL
) {
1069 if (hostp
!= NULL
) {
1075 if (pathp
!= NULL
) {
1088 /* function to assist building execv() arguments */
1090 addargs(arglist
*args
, char *fmt
, ...)
1098 r
= vasprintf(&cp
, fmt
, ap
);
1101 fatal_f("argument too long");
1103 nalloc
= args
->nalloc
;
1104 if (args
->list
== NULL
) {
1107 } else if (args
->num
> (256 * 1024))
1108 fatal_f("too many arguments");
1109 else if (args
->num
>= args
->nalloc
)
1110 fatal_f("arglist corrupt");
1111 else if (args
->num
+2 >= nalloc
)
1114 args
->list
= xrecallocarray(args
->list
, args
->nalloc
,
1115 nalloc
, sizeof(char *));
1116 args
->nalloc
= nalloc
;
1117 args
->list
[args
->num
++] = cp
;
1118 args
->list
[args
->num
] = NULL
;
1122 replacearg(arglist
*args
, u_int which
, char *fmt
, ...)
1129 r
= vasprintf(&cp
, fmt
, ap
);
1132 fatal_f("argument too long");
1133 if (args
->list
== NULL
|| args
->num
>= args
->nalloc
)
1134 fatal_f("arglist corrupt");
1136 if (which
>= args
->num
)
1137 fatal_f("tried to replace invalid arg %d >= %d",
1139 free(args
->list
[which
]);
1140 args
->list
[which
] = cp
;
1144 freeargs(arglist
*args
)
1150 if (args
->list
!= NULL
&& args
->num
< args
->nalloc
) {
1151 for (i
= 0; i
< args
->num
; i
++)
1152 free(args
->list
[i
]);
1155 args
->nalloc
= args
->num
= 0;
1160 * Expands tildes in the file name. Returns data allocated by xmalloc.
1161 * Warning: this calls getpw*.
1164 tilde_expand(const char *filename
, uid_t uid
, char **retp
)
1166 char *ocopy
= NULL
, *copy
, *s
= NULL
;
1167 const char *path
= NULL
, *user
= NULL
;
1170 int ret
= -1, r
, slash
;
1173 if (*filename
!= '~') {
1174 *retp
= xstrdup(filename
);
1177 ocopy
= copy
= xstrdup(filename
+ 1);
1179 if (*copy
== '\0') /* ~ */
1181 else if (*copy
== '/') {
1182 copy
+= strspn(copy
, "/");
1184 path
= NULL
; /* ~/ */
1186 path
= copy
; /* ~/path */
1189 if ((path
= strchr(copy
, '/')) != NULL
) {
1190 copy
[path
- copy
] = '\0';
1192 path
+= strspn(path
, "/");
1193 if (*path
== '\0') /* ~user/ */
1195 /* else ~user/path */
1200 if ((pw
= getpwnam(user
)) == NULL
) {
1201 error_f("No such user %s", user
);
1204 } else if ((pw
= getpwuid(uid
)) == NULL
) {
1205 error_f("No such uid %ld", (long)uid
);
1209 /* Make sure directory has a trailing '/' */
1210 slash
= (len
= strlen(pw
->pw_dir
)) == 0 || pw
->pw_dir
[len
- 1] != '/';
1212 if ((r
= xasprintf(&s
, "%s%s%s", pw
->pw_dir
,
1213 slash
? "/" : "", path
!= NULL
? path
: "")) <= 0) {
1214 error_f("xasprintf failed");
1217 if (r
>= PATH_MAX
) {
1218 error_f("Path too long");
1232 tilde_expand_filename(const char *filename
, uid_t uid
)
1236 if (tilde_expand(filename
, uid
, &ret
) != 0)
1242 * Expand a string with a set of %[char] escapes and/or ${ENVIRONMENT}
1243 * substitutions. A number of escapes may be specified as
1244 * (char *escape_chars, char *replacement) pairs. The list must be terminated
1245 * by a NULL escape_char. Returns replaced string in memory allocated by
1246 * xmalloc which the caller must free.
1249 vdollar_percent_expand(int *parseerror
, int dollar
, int percent
,
1250 const char *string
, va_list ap
)
1252 #define EXPAND_MAX_KEYS 64
1253 u_int num_keys
= 0, i
;
1257 } keys
[EXPAND_MAX_KEYS
];
1259 int r
, missingvar
= 0;
1260 char *ret
= NULL
, *var
, *varend
, *val
;
1263 if ((buf
= sshbuf_new()) == NULL
)
1264 fatal_f("sshbuf_new failed");
1265 if (parseerror
== NULL
)
1266 fatal_f("null parseerror arg");
1269 /* Gather keys if we're doing percent expansion. */
1271 for (num_keys
= 0; num_keys
< EXPAND_MAX_KEYS
; num_keys
++) {
1272 keys
[num_keys
].key
= va_arg(ap
, char *);
1273 if (keys
[num_keys
].key
== NULL
)
1275 keys
[num_keys
].repl
= va_arg(ap
, char *);
1276 if (keys
[num_keys
].repl
== NULL
) {
1277 fatal_f("NULL replacement for token %s",
1278 keys
[num_keys
].key
);
1281 if (num_keys
== EXPAND_MAX_KEYS
&& va_arg(ap
, char *) != NULL
)
1282 fatal_f("too many keys");
1284 fatal_f("percent expansion without token list");
1288 for (i
= 0; *string
!= '\0'; string
++) {
1289 /* Optionally process ${ENVIRONMENT} expansions. */
1290 if (dollar
&& string
[0] == '$' && string
[1] == '{') {
1291 string
+= 2; /* skip over '${' */
1292 if ((varend
= strchr(string
, '}')) == NULL
) {
1293 error_f("environment variable '%s' missing "
1294 "closing '}'", string
);
1297 len
= varend
- string
;
1299 error_f("zero-length environment variable");
1302 var
= xmalloc(len
+ 1);
1303 (void)strlcpy(var
, string
, len
+ 1);
1304 if ((val
= getenv(var
)) == NULL
) {
1305 error_f("env var ${%s} has no value", var
);
1308 debug3_f("expand ${%s} -> '%s'", var
, val
);
1309 if ((r
= sshbuf_put(buf
, val
, strlen(val
))) !=0)
1310 fatal_fr(r
, "sshbuf_put ${}");
1318 * Process percent expansions if we have a list of TOKENs.
1319 * If we're not doing percent expansion everything just gets
1322 if (*string
!= '%' || !percent
) {
1324 if ((r
= sshbuf_put_u8(buf
, *string
)) != 0)
1325 fatal_fr(r
, "sshbuf_put_u8 %%");
1332 if (*string
== '\0') {
1333 error_f("invalid format");
1336 for (i
= 0; i
< num_keys
; i
++) {
1337 if (strchr(keys
[i
].key
, *string
) != NULL
) {
1338 if ((r
= sshbuf_put(buf
, keys
[i
].repl
,
1339 strlen(keys
[i
].repl
))) != 0)
1340 fatal_fr(r
, "sshbuf_put %%-repl");
1344 if (i
>= num_keys
) {
1345 error_f("unknown key %%%c", *string
);
1349 if (!missingvar
&& (ret
= sshbuf_dup_string(buf
)) == NULL
)
1350 fatal_f("sshbuf_dup_string failed");
1354 return *parseerror
? NULL
: ret
;
1355 #undef EXPAND_MAX_KEYS
1359 * Expand only environment variables.
1360 * Note that although this function is variadic like the other similar
1361 * functions, any such arguments will be unused.
1365 dollar_expand(int *parseerr
, const char *string
, ...)
1371 va_start(ap
, string
);
1372 ret
= vdollar_percent_expand(&err
, 1, 0, string
, ap
);
1374 if (parseerr
!= NULL
)
1380 * Returns expanded string or NULL if a specified environment variable is
1381 * not defined, or calls fatal if the string is invalid.
1384 percent_expand(const char *string
, ...)
1390 va_start(ap
, string
);
1391 ret
= vdollar_percent_expand(&err
, 0, 1, string
, ap
);
1399 * Returns expanded string or NULL if a specified environment variable is
1400 * not defined, or calls fatal if the string is invalid.
1403 percent_dollar_expand(const char *string
, ...)
1409 va_start(ap
, string
);
1410 ret
= vdollar_percent_expand(&err
, 1, 1, string
, ap
);
1418 tun_open(int tun
, int mode
, char **ifname
)
1420 #if defined(CUSTOM_SYS_TUN_OPEN)
1421 return (sys_tun_open(tun
, mode
, ifname
));
1422 #elif defined(SSH_TUN_OPENBSD)
1426 const char *tunbase
= "tun";
1431 if (mode
== SSH_TUNMODE_ETHERNET
)
1434 /* Open the tunnel device */
1435 if (tun
<= SSH_TUNID_MAX
) {
1436 snprintf(name
, sizeof(name
), "/dev/%s%d", tunbase
, tun
);
1437 fd
= open(name
, O_RDWR
);
1438 } else if (tun
== SSH_TUNID_ANY
) {
1439 for (tun
= 100; tun
>= 0; tun
--) {
1440 snprintf(name
, sizeof(name
), "/dev/%s%d",
1442 if ((fd
= open(name
, O_RDWR
)) >= 0)
1446 debug_f("invalid tunnel %u", tun
);
1451 debug_f("%s open: %s", name
, strerror(errno
));
1455 debug_f("%s mode %d fd %d", name
, mode
, fd
);
1457 /* Bring interface up if it is not already */
1458 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s%d", tunbase
, tun
);
1459 if ((sock
= socket(PF_UNIX
, SOCK_STREAM
, 0)) == -1)
1462 if (ioctl(sock
, SIOCGIFFLAGS
, &ifr
) == -1) {
1463 debug_f("get interface %s flags: %s", ifr
.ifr_name
,
1468 if (!(ifr
.ifr_flags
& IFF_UP
)) {
1469 ifr
.ifr_flags
|= IFF_UP
;
1470 if (ioctl(sock
, SIOCSIFFLAGS
, &ifr
) == -1) {
1471 debug_f("activate interface %s: %s", ifr
.ifr_name
,
1478 *ifname
= xstrdup(ifr
.ifr_name
);
1490 error("Tunnel interfaces are not supported on this platform");
1496 sanitise_stdfd(void)
1500 if ((nullfd
= dupfd
= open(_PATH_DEVNULL
, O_RDWR
)) == -1) {
1501 fprintf(stderr
, "Couldn't open /dev/null: %s\n",
1505 while (++dupfd
<= STDERR_FILENO
) {
1506 /* Only populate closed fds. */
1507 if (fcntl(dupfd
, F_GETFL
) == -1 && errno
== EBADF
) {
1508 if (dup2(nullfd
, dupfd
) == -1) {
1509 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
1514 if (nullfd
> STDERR_FILENO
)
1519 tohex(const void *vp
, size_t l
)
1521 const u_char
*p
= (const u_char
*)vp
;
1526 return xstrdup("tohex: length > 65536");
1530 for (i
= 0; i
< l
; i
++) {
1531 snprintf(b
, sizeof(b
), "%02x", p
[i
]);
1538 * Extend string *sp by the specified format. If *sp is not NULL (or empty),
1539 * then the separator 'sep' will be prepended before the formatted arguments.
1540 * Extended strings are heap allocated.
1543 xextendf(char **sp
, const char *sep
, const char *fmt
, ...)
1549 xvasprintf(&tmp1
, fmt
, ap
);
1552 if (*sp
== NULL
|| **sp
== '\0') {
1557 xasprintf(&tmp2
, "%s%s%s", *sp
, sep
== NULL
? "" : sep
, tmp1
);
1565 get_u64(const void *vp
)
1567 const u_char
*p
= (const u_char
*)vp
;
1570 v
= (u_int64_t
)p
[0] << 56;
1571 v
|= (u_int64_t
)p
[1] << 48;
1572 v
|= (u_int64_t
)p
[2] << 40;
1573 v
|= (u_int64_t
)p
[3] << 32;
1574 v
|= (u_int64_t
)p
[4] << 24;
1575 v
|= (u_int64_t
)p
[5] << 16;
1576 v
|= (u_int64_t
)p
[6] << 8;
1577 v
|= (u_int64_t
)p
[7];
1583 get_u32(const void *vp
)
1585 const u_char
*p
= (const u_char
*)vp
;
1588 v
= (u_int32_t
)p
[0] << 24;
1589 v
|= (u_int32_t
)p
[1] << 16;
1590 v
|= (u_int32_t
)p
[2] << 8;
1591 v
|= (u_int32_t
)p
[3];
1597 get_u32_le(const void *vp
)
1599 const u_char
*p
= (const u_char
*)vp
;
1602 v
= (u_int32_t
)p
[0];
1603 v
|= (u_int32_t
)p
[1] << 8;
1604 v
|= (u_int32_t
)p
[2] << 16;
1605 v
|= (u_int32_t
)p
[3] << 24;
1611 get_u16(const void *vp
)
1613 const u_char
*p
= (const u_char
*)vp
;
1616 v
= (u_int16_t
)p
[0] << 8;
1617 v
|= (u_int16_t
)p
[1];
1623 put_u64(void *vp
, u_int64_t v
)
1625 u_char
*p
= (u_char
*)vp
;
1627 p
[0] = (u_char
)(v
>> 56) & 0xff;
1628 p
[1] = (u_char
)(v
>> 48) & 0xff;
1629 p
[2] = (u_char
)(v
>> 40) & 0xff;
1630 p
[3] = (u_char
)(v
>> 32) & 0xff;
1631 p
[4] = (u_char
)(v
>> 24) & 0xff;
1632 p
[5] = (u_char
)(v
>> 16) & 0xff;
1633 p
[6] = (u_char
)(v
>> 8) & 0xff;
1634 p
[7] = (u_char
)v
& 0xff;
1638 put_u32(void *vp
, u_int32_t v
)
1640 u_char
*p
= (u_char
*)vp
;
1642 p
[0] = (u_char
)(v
>> 24) & 0xff;
1643 p
[1] = (u_char
)(v
>> 16) & 0xff;
1644 p
[2] = (u_char
)(v
>> 8) & 0xff;
1645 p
[3] = (u_char
)v
& 0xff;
1649 put_u32_le(void *vp
, u_int32_t v
)
1651 u_char
*p
= (u_char
*)vp
;
1653 p
[0] = (u_char
)v
& 0xff;
1654 p
[1] = (u_char
)(v
>> 8) & 0xff;
1655 p
[2] = (u_char
)(v
>> 16) & 0xff;
1656 p
[3] = (u_char
)(v
>> 24) & 0xff;
1660 put_u16(void *vp
, u_int16_t v
)
1662 u_char
*p
= (u_char
*)vp
;
1664 p
[0] = (u_char
)(v
>> 8) & 0xff;
1665 p
[1] = (u_char
)v
& 0xff;
1669 ms_subtract_diff(struct timeval
*start
, int *ms
)
1671 struct timeval diff
, finish
;
1673 monotime_tv(&finish
);
1674 timersub(&finish
, start
, &diff
);
1675 *ms
-= (diff
.tv_sec
* 1000) + (diff
.tv_usec
/ 1000);
1679 ms_to_timespec(struct timespec
*ts
, int ms
)
1683 ts
->tv_sec
= ms
/ 1000;
1684 ts
->tv_nsec
= (ms
% 1000) * 1000 * 1000;
1688 monotime_ts(struct timespec
*ts
)
1691 #if defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_BOOTTIME) || \
1692 defined(CLOCK_MONOTONIC) || defined(CLOCK_REALTIME))
1693 static int gettime_failed
= 0;
1695 if (!gettime_failed
) {
1696 # ifdef CLOCK_BOOTTIME
1697 if (clock_gettime(CLOCK_BOOTTIME
, ts
) == 0)
1699 # endif /* CLOCK_BOOTTIME */
1700 # ifdef CLOCK_MONOTONIC
1701 if (clock_gettime(CLOCK_MONOTONIC
, ts
) == 0)
1703 # endif /* CLOCK_MONOTONIC */
1704 # ifdef CLOCK_REALTIME
1705 /* Not monotonic, but we're almost out of options here. */
1706 if (clock_gettime(CLOCK_REALTIME
, ts
) == 0)
1708 # endif /* CLOCK_REALTIME */
1709 debug3("clock_gettime: %s", strerror(errno
));
1712 #endif /* HAVE_CLOCK_GETTIME && (BOOTTIME || MONOTONIC || REALTIME) */
1713 gettimeofday(&tv
, NULL
);
1714 ts
->tv_sec
= tv
.tv_sec
;
1715 ts
->tv_nsec
= (long)tv
.tv_usec
* 1000;
1719 monotime_tv(struct timeval
*tv
)
1724 tv
->tv_sec
= ts
.tv_sec
;
1725 tv
->tv_usec
= ts
.tv_nsec
/ 1000;
1738 monotime_double(void)
1743 return ts
.tv_sec
+ ((double)ts
.tv_nsec
/ 1000000000);
1747 bandwidth_limit_init(struct bwlimit
*bw
, u_int64_t kbps
, size_t buflen
)
1749 bw
->buflen
= buflen
;
1751 bw
->thresh
= buflen
;
1753 timerclear(&bw
->bwstart
);
1754 timerclear(&bw
->bwend
);
1757 /* Callback from read/write loop to insert bandwidth-limiting delays */
1759 bandwidth_limit(struct bwlimit
*bw
, size_t read_len
)
1762 struct timespec ts
, rm
;
1764 bw
->lamt
+= read_len
;
1765 if (!timerisset(&bw
->bwstart
)) {
1766 monotime_tv(&bw
->bwstart
);
1769 if (bw
->lamt
< bw
->thresh
)
1772 monotime_tv(&bw
->bwend
);
1773 timersub(&bw
->bwend
, &bw
->bwstart
, &bw
->bwend
);
1774 if (!timerisset(&bw
->bwend
))
1778 waitlen
= (double)1000000L * bw
->lamt
/ bw
->rate
;
1780 bw
->bwstart
.tv_sec
= waitlen
/ 1000000L;
1781 bw
->bwstart
.tv_usec
= waitlen
% 1000000L;
1783 if (timercmp(&bw
->bwstart
, &bw
->bwend
, >)) {
1784 timersub(&bw
->bwstart
, &bw
->bwend
, &bw
->bwend
);
1786 /* Adjust the wait time */
1787 if (bw
->bwend
.tv_sec
) {
1789 if (bw
->thresh
< bw
->buflen
/ 4)
1790 bw
->thresh
= bw
->buflen
/ 4;
1791 } else if (bw
->bwend
.tv_usec
< 10000) {
1793 if (bw
->thresh
> bw
->buflen
* 8)
1794 bw
->thresh
= bw
->buflen
* 8;
1797 TIMEVAL_TO_TIMESPEC(&bw
->bwend
, &ts
);
1798 while (nanosleep(&ts
, &rm
) == -1) {
1806 monotime_tv(&bw
->bwstart
);
1809 /* Make a template filename for mk[sd]temp() */
1811 mktemp_proto(char *s
, size_t len
)
1816 if ((tmpdir
= getenv("TMPDIR")) != NULL
) {
1817 r
= snprintf(s
, len
, "%s/ssh-XXXXXXXXXXXX", tmpdir
);
1818 if (r
> 0 && (size_t)r
< len
)
1821 r
= snprintf(s
, len
, "/tmp/ssh-XXXXXXXXXXXX");
1822 if (r
< 0 || (size_t)r
>= len
)
1823 fatal_f("template string too short");
1826 static const struct {
1830 { "none", INT_MAX
}, /* can't use 0 here; that's CS0 */
1831 { "af11", IPTOS_DSCP_AF11
},
1832 { "af12", IPTOS_DSCP_AF12
},
1833 { "af13", IPTOS_DSCP_AF13
},
1834 { "af21", IPTOS_DSCP_AF21
},
1835 { "af22", IPTOS_DSCP_AF22
},
1836 { "af23", IPTOS_DSCP_AF23
},
1837 { "af31", IPTOS_DSCP_AF31
},
1838 { "af32", IPTOS_DSCP_AF32
},
1839 { "af33", IPTOS_DSCP_AF33
},
1840 { "af41", IPTOS_DSCP_AF41
},
1841 { "af42", IPTOS_DSCP_AF42
},
1842 { "af43", IPTOS_DSCP_AF43
},
1843 { "cs0", IPTOS_DSCP_CS0
},
1844 { "cs1", IPTOS_DSCP_CS1
},
1845 { "cs2", IPTOS_DSCP_CS2
},
1846 { "cs3", IPTOS_DSCP_CS3
},
1847 { "cs4", IPTOS_DSCP_CS4
},
1848 { "cs5", IPTOS_DSCP_CS5
},
1849 { "cs6", IPTOS_DSCP_CS6
},
1850 { "cs7", IPTOS_DSCP_CS7
},
1851 { "ef", IPTOS_DSCP_EF
},
1852 { "le", IPTOS_DSCP_LE
},
1853 { "lowdelay", IPTOS_LOWDELAY
},
1854 { "throughput", IPTOS_THROUGHPUT
},
1855 { "reliability", IPTOS_RELIABILITY
},
1860 parse_ipqos(const char *cp
)
1868 for (i
= 0; ipqos
[i
].name
!= NULL
; i
++) {
1869 if (strcasecmp(cp
, ipqos
[i
].name
) == 0)
1870 return ipqos
[i
].value
;
1872 /* Try parsing as an integer */
1873 val
= strtol(cp
, &ep
, 0);
1874 if (*cp
== '\0' || *ep
!= '\0' || val
< 0 || val
> 255)
1880 iptos2str(int iptos
)
1883 static char iptos_str
[sizeof "0xff"];
1885 for (i
= 0; ipqos
[i
].name
!= NULL
; i
++) {
1886 if (ipqos
[i
].value
== iptos
)
1887 return ipqos
[i
].name
;
1889 snprintf(iptos_str
, sizeof iptos_str
, "0x%02x", iptos
);
1897 *s
= tolower((u_char
)*s
);
1901 unix_listener(const char *path
, int backlog
, int unlink_first
)
1903 struct sockaddr_un sunaddr
;
1904 int saved_errno
, sock
;
1906 memset(&sunaddr
, 0, sizeof(sunaddr
));
1907 sunaddr
.sun_family
= AF_UNIX
;
1908 if (strlcpy(sunaddr
.sun_path
, path
,
1909 sizeof(sunaddr
.sun_path
)) >= sizeof(sunaddr
.sun_path
)) {
1910 error_f("path \"%s\" too long for Unix domain socket", path
);
1911 errno
= ENAMETOOLONG
;
1915 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1917 saved_errno
= errno
;
1918 error_f("socket: %.100s", strerror(errno
));
1919 errno
= saved_errno
;
1922 if (unlink_first
== 1) {
1923 if (unlink(path
) != 0 && errno
!= ENOENT
)
1924 error("unlink(%s): %.100s", path
, strerror(errno
));
1926 if (bind(sock
, (struct sockaddr
*)&sunaddr
, sizeof(sunaddr
)) == -1) {
1927 saved_errno
= errno
;
1928 error_f("cannot bind to path %s: %s", path
, strerror(errno
));
1930 errno
= saved_errno
;
1933 if (listen(sock
, backlog
) == -1) {
1934 saved_errno
= errno
;
1935 error_f("cannot listen on path %s: %s", path
, strerror(errno
));
1938 errno
= saved_errno
;
1945 sock_set_v6only(int s
)
1947 #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
1950 debug3("%s: set socket %d IPV6_V6ONLY", __func__
, s
);
1951 if (setsockopt(s
, IPPROTO_IPV6
, IPV6_V6ONLY
, &on
, sizeof(on
)) == -1)
1952 error("setsockopt IPV6_V6ONLY: %s", strerror(errno
));
1957 * Compares two strings that maybe be NULL. Returns non-zero if strings
1958 * are both NULL or are identical, returns zero otherwise.
1961 strcmp_maybe_null(const char *a
, const char *b
)
1963 if ((a
== NULL
&& b
!= NULL
) || (a
!= NULL
&& b
== NULL
))
1965 if (a
!= NULL
&& strcmp(a
, b
) != 0)
1971 * Compare two forwards, returning non-zero if they are identical or
1975 forward_equals(const struct Forward
*a
, const struct Forward
*b
)
1977 if (strcmp_maybe_null(a
->listen_host
, b
->listen_host
) == 0)
1979 if (a
->listen_port
!= b
->listen_port
)
1981 if (strcmp_maybe_null(a
->listen_path
, b
->listen_path
) == 0)
1983 if (strcmp_maybe_null(a
->connect_host
, b
->connect_host
) == 0)
1985 if (a
->connect_port
!= b
->connect_port
)
1987 if (strcmp_maybe_null(a
->connect_path
, b
->connect_path
) == 0)
1989 /* allocated_port and handle are not checked */
1993 /* returns 1 if process is already daemonized, 0 otherwise */
1999 if ((fd
= open(_PATH_TTY
, O_RDONLY
| O_NOCTTY
)) >= 0) {
2001 return 0; /* have controlling terminal */
2004 return 0; /* parent is not init */
2005 if (getsid(0) != getpid())
2006 return 0; /* not session leader */
2007 debug3("already daemonized");
2012 * Splits 's' into an argument vector. Handles quoted string and basic
2013 * escape characters (\\, \", \'). Caller must free the argument vector
2017 argv_split(const char *s
, int *argcp
, char ***argvp
, int terminate_on_comment
)
2019 int r
= SSH_ERR_INTERNAL_ERROR
;
2020 int argc
= 0, quote
, i
, j
;
2021 char *arg
, **argv
= xcalloc(1, sizeof(*argv
));
2026 for (i
= 0; s
[i
] != '\0'; i
++) {
2027 /* Skip leading whitespace */
2028 if (s
[i
] == ' ' || s
[i
] == '\t')
2030 if (terminate_on_comment
&& s
[i
] == '#')
2032 /* Start of a token */
2035 argv
= xreallocarray(argv
, (argc
+ 2), sizeof(*argv
));
2036 arg
= argv
[argc
++] = xcalloc(1, strlen(s
+ i
) + 1);
2039 /* Copy the token in, removing escapes */
2040 for (j
= 0; s
[i
] != '\0'; i
++) {
2042 if (s
[i
+ 1] == '\'' ||
2045 (quote
== 0 && s
[i
+ 1] == ' ')) {
2049 /* Unrecognised escape */
2052 } else if (quote
== 0 && (s
[i
] == ' ' || s
[i
] == '\t'))
2054 else if (quote
== 0 && (s
[i
] == '\"' || s
[i
] == '\''))
2055 quote
= s
[i
]; /* quote start */
2056 else if (quote
!= 0 && s
[i
] == quote
)
2057 quote
= 0; /* quote end */
2063 /* Ran out of string looking for close quote */
2064 r
= SSH_ERR_INVALID_FORMAT
;
2077 if (argc
!= 0 && argv
!= NULL
) {
2078 for (i
= 0; i
< argc
; i
++)
2086 * Reassemble an argument vector into a string, quoting and escaping as
2087 * necessary. Caller must free returned string.
2090 argv_assemble(int argc
, char **argv
)
2094 struct sshbuf
*buf
, *arg
;
2096 if ((buf
= sshbuf_new()) == NULL
|| (arg
= sshbuf_new()) == NULL
)
2097 fatal_f("sshbuf_new failed");
2099 for (i
= 0; i
< argc
; i
++) {
2102 for (j
= 0; argv
[i
][j
] != '\0'; j
++) {
2109 r
= sshbuf_put_u8(arg
, c
);
2114 if ((r
= sshbuf_put_u8(arg
, '\\')) != 0)
2118 r
= sshbuf_put_u8(arg
, c
);
2122 fatal_fr(r
, "sshbuf_put_u8");
2124 if ((i
!= 0 && (r
= sshbuf_put_u8(buf
, ' ')) != 0) ||
2125 (ws
!= 0 && (r
= sshbuf_put_u8(buf
, '"')) != 0) ||
2126 (r
= sshbuf_putb(buf
, arg
)) != 0 ||
2127 (ws
!= 0 && (r
= sshbuf_put_u8(buf
, '"')) != 0))
2128 fatal_fr(r
, "assemble");
2130 if ((ret
= malloc(sshbuf_len(buf
) + 1)) == NULL
)
2131 fatal_f("malloc failed");
2132 memcpy(ret
, sshbuf_ptr(buf
), sshbuf_len(buf
));
2133 ret
[sshbuf_len(buf
)] = '\0';
2140 argv_next(int *argcp
, char ***argvp
)
2142 char *ret
= (*argvp
)[0];
2144 if (*argcp
> 0 && ret
!= NULL
) {
2152 argv_consume(int *argcp
)
2158 argv_free(char **av
, int ac
)
2164 for (i
= 0; i
< ac
; i
++)
2169 /* Returns 0 if pid exited cleanly, non-zero otherwise */
2171 exited_cleanly(pid_t pid
, const char *tag
, const char *cmd
, int quiet
)
2175 while (waitpid(pid
, &status
, 0) == -1) {
2176 if (errno
!= EINTR
) {
2177 error("%s waitpid: %s", tag
, strerror(errno
));
2181 if (WIFSIGNALED(status
)) {
2182 error("%s %s exited on signal %d", tag
, cmd
, WTERMSIG(status
));
2184 } else if (WEXITSTATUS(status
) != 0) {
2185 do_log2(quiet
? SYSLOG_LEVEL_DEBUG1
: SYSLOG_LEVEL_INFO
,
2186 "%s %s failed, status %d", tag
, cmd
, WEXITSTATUS(status
));
2193 * Check a given path for security. This is defined as all components
2194 * of the path to the file must be owned by either the owner of
2195 * of the file or root and no directories must be group or world writable.
2197 * XXX Should any specific check be done for sym links ?
2199 * Takes a file name, its stat information (preferably from fstat() to
2200 * avoid races), the uid of the expected owner, their home directory and an
2201 * error buffer plus max size as arguments.
2203 * Returns 0 on success and -1 on failure
2206 safe_path(const char *name
, struct stat
*stp
, const char *pw_dir
,
2207 uid_t uid
, char *err
, size_t errlen
)
2209 char buf
[PATH_MAX
], homedir
[PATH_MAX
];
2211 int comparehome
= 0;
2214 if (realpath(name
, buf
) == NULL
) {
2215 snprintf(err
, errlen
, "realpath %s failed: %s", name
,
2219 if (pw_dir
!= NULL
&& realpath(pw_dir
, homedir
) != NULL
)
2222 if (!S_ISREG(stp
->st_mode
)) {
2223 snprintf(err
, errlen
, "%s is not a regular file", buf
);
2226 if ((!platform_sys_dir_uid(stp
->st_uid
) && stp
->st_uid
!= uid
) ||
2227 (stp
->st_mode
& 022) != 0) {
2228 snprintf(err
, errlen
, "bad ownership or modes for file %s",
2233 /* for each component of the canonical path, walking upwards */
2235 if ((cp
= dirname(buf
)) == NULL
) {
2236 snprintf(err
, errlen
, "dirname() failed");
2239 strlcpy(buf
, cp
, sizeof(buf
));
2241 if (stat(buf
, &st
) == -1 ||
2242 (!platform_sys_dir_uid(st
.st_uid
) && st
.st_uid
!= uid
) ||
2243 (st
.st_mode
& 022) != 0) {
2244 snprintf(err
, errlen
,
2245 "bad ownership or modes for directory %s", buf
);
2249 /* If are past the homedir then we can stop */
2250 if (comparehome
&& strcmp(homedir
, buf
) == 0)
2254 * dirname should always complete with a "/" path,
2255 * but we can be paranoid and check for "." too
2257 if ((strcmp("/", buf
) == 0) || (strcmp(".", buf
) == 0))
2264 * Version of safe_path() that accepts an open file descriptor to
2267 * Returns 0 on success and -1 on failure
2270 safe_path_fd(int fd
, const char *file
, struct passwd
*pw
,
2271 char *err
, size_t errlen
)
2275 /* check the open file to avoid races */
2276 if (fstat(fd
, &st
) == -1) {
2277 snprintf(err
, errlen
, "cannot stat file %s: %s",
2278 file
, strerror(errno
));
2281 return safe_path(file
, &st
, pw
->pw_dir
, pw
->pw_uid
, err
, errlen
);
2285 * Sets the value of the given variable in the environment. If the variable
2286 * already exists, its value is overridden.
2289 child_set_env(char ***envp
, u_int
*envsizep
, const char *name
,
2296 if (strchr(name
, '=') != NULL
) {
2297 error("Invalid environment variable \"%.100s\"", name
);
2302 * If we're passed an uninitialized list, allocate a single null
2303 * entry before continuing.
2305 if ((*envp
== NULL
) != (*envsizep
== 0))
2306 fatal_f("environment size mismatch");
2307 if (*envp
== NULL
&& *envsizep
== 0) {
2308 *envp
= xmalloc(sizeof(char *));
2314 * Find the slot where the value should be stored. If the variable
2315 * already exists, we reuse the slot; otherwise we append a new slot
2316 * at the end of the array, expanding if necessary.
2319 namelen
= strlen(name
);
2320 for (i
= 0; env
[i
]; i
++)
2321 if (strncmp(env
[i
], name
, namelen
) == 0 && env
[i
][namelen
] == '=')
2324 /* Reuse the slot. */
2327 /* New variable. Expand if necessary. */
2328 envsize
= *envsizep
;
2329 if (i
>= envsize
- 1) {
2330 if (envsize
>= 1000)
2331 fatal("child_set_env: too many env vars");
2333 env
= (*envp
) = xreallocarray(env
, envsize
, sizeof(char *));
2334 *envsizep
= envsize
;
2336 /* Need to set the NULL pointer at end of array beyond the new slot. */
2340 /* Allocate space and format the variable in the appropriate slot. */
2342 env
[i
] = xmalloc(strlen(name
) + 1 + strlen(value
) + 1);
2343 snprintf(env
[i
], strlen(name
) + 1 + strlen(value
) + 1, "%s=%s", name
, value
);
2347 * Check and optionally lowercase a domain name, also removes trailing '.'
2348 * Returns 1 on success and 0 on failure, storing an error message in errstr.
2351 valid_domain(char *name
, int makelower
, const char **errstr
)
2353 size_t i
, l
= strlen(name
);
2354 u_char c
, last
= '\0';
2355 static char errbuf
[256];
2358 strlcpy(errbuf
, "empty domain name", sizeof(errbuf
));
2361 if (!isalpha((u_char
)name
[0]) && !isdigit((u_char
)name
[0])) {
2362 snprintf(errbuf
, sizeof(errbuf
), "domain name \"%.100s\" "
2363 "starts with invalid character", name
);
2366 for (i
= 0; i
< l
; i
++) {
2367 c
= tolower((u_char
)name
[i
]);
2370 if (last
== '.' && c
== '.') {
2371 snprintf(errbuf
, sizeof(errbuf
), "domain name "
2372 "\"%.100s\" contains consecutive separators", name
);
2375 if (c
!= '.' && c
!= '-' && !isalnum(c
) &&
2376 c
!= '_') /* technically invalid, but common */ {
2377 snprintf(errbuf
, sizeof(errbuf
), "domain name "
2378 "\"%.100s\" contains invalid characters", name
);
2383 if (name
[l
- 1] == '.')
2395 * Verify that a environment variable name (not including initial '$') is
2396 * valid; consisting of one or more alphanumeric or underscore characters only.
2397 * Returns 1 on valid, 0 otherwise.
2400 valid_env_name(const char *name
)
2404 if (name
[0] == '\0')
2406 for (cp
= name
; *cp
!= '\0'; cp
++) {
2407 if (!isalnum((u_char
)*cp
) && *cp
!= '_')
2414 atoi_err(const char *nptr
, int *val
)
2416 const char *errstr
= NULL
;
2419 if (nptr
== NULL
|| *nptr
== '\0')
2421 num
= strtonum(nptr
, 0, INT_MAX
, &errstr
);
2428 parse_absolute_time(const char *s
, uint64_t *tp
)
2440 if (l
> 1 && strcasecmp(s
+ l
- 1, "Z") == 0) {
2443 } else if (l
> 3 && strcasecmp(s
+ l
- 3, "UTC") == 0) {
2448 * POSIX strptime says "The application shall ensure that there
2449 * is white-space or other non-alphanumeric characters between
2450 * any two conversion specifications" so arrange things this way.
2453 case 8: /* YYYYMMDD */
2455 snprintf(buf
, sizeof(buf
), "%.4s-%.2s-%.2s", s
, s
+ 4, s
+ 6);
2457 case 12: /* YYYYMMDDHHMM */
2458 fmt
= "%Y-%m-%dT%H:%M";
2459 snprintf(buf
, sizeof(buf
), "%.4s-%.2s-%.2sT%.2s:%.2s",
2460 s
, s
+ 4, s
+ 6, s
+ 8, s
+ 10);
2462 case 14: /* YYYYMMDDHHMMSS */
2463 fmt
= "%Y-%m-%dT%H:%M:%S";
2464 snprintf(buf
, sizeof(buf
), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",
2465 s
, s
+ 4, s
+ 6, s
+ 8, s
+ 10, s
+ 12);
2468 return SSH_ERR_INVALID_FORMAT
;
2471 memset(&tm
, 0, sizeof(tm
));
2472 if ((cp
= strptime(buf
, fmt
, &tm
)) == NULL
|| *cp
!= '\0')
2473 return SSH_ERR_INVALID_FORMAT
;
2475 if ((tt
= timegm(&tm
)) < 0)
2476 return SSH_ERR_INVALID_FORMAT
;
2478 if ((tt
= mktime(&tm
)) < 0)
2479 return SSH_ERR_INVALID_FORMAT
;
2487 format_absolute_time(uint64_t t
, char *buf
, size_t len
)
2489 time_t tt
= t
> SSH_TIME_T_MAX
? SSH_TIME_T_MAX
: t
;
2492 localtime_r(&tt
, &tm
);
2493 strftime(buf
, len
, "%Y-%m-%dT%H:%M:%S", &tm
);
2497 * Parse a "pattern=interval" clause (e.g. a ChannelTimeout).
2498 * Returns 0 on success or non-zero on failure.
2499 * Caller must free *typep.
2502 parse_pattern_interval(const char *s
, char **typep
, int *secsp
)
2515 if ((cp
= strchr(sdup
, '=')) == NULL
|| cp
== sdup
) {
2520 if ((secs
= convtime(cp
)) < 0) {
2526 *typep
= xstrdup(sdup
);
2533 /* check if path is absolute */
2535 path_absolute(const char *path
)
2537 return (*path
== '/') ? 1 : 0;
2541 skip_space(char **cpp
)
2545 for (cp
= *cpp
; *cp
== ' ' || *cp
== '\t'; cp
++)
2550 /* authorized_key-style options parsing helpers */
2553 * Match flag 'opt' in *optsp, and if allow_negate is set then also match
2554 * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
2555 * if negated option matches.
2556 * If the option or negated option matches, then *optsp is updated to
2557 * point to the first character after the option.
2560 opt_flag(const char *opt
, int allow_negate
, const char **optsp
)
2562 size_t opt_len
= strlen(opt
);
2563 const char *opts
= *optsp
;
2566 if (allow_negate
&& strncasecmp(opts
, "no-", 3) == 0) {
2570 if (strncasecmp(opts
, opt
, opt_len
) == 0) {
2571 *optsp
= opts
+ opt_len
;
2572 return negate
? 0 : 1;
2578 opt_dequote(const char **sp
, const char **errstrp
)
2580 const char *s
= *sp
;
2586 *errstrp
= "missing start quote";
2590 if ((ret
= malloc(strlen((s
)) + 1)) == NULL
) {
2591 *errstrp
= "memory allocation failed";
2594 for (i
= 0; *s
!= '\0' && *s
!= '"';) {
2595 if (s
[0] == '\\' && s
[1] == '"')
2600 *errstrp
= "missing end quote";
2611 opt_match(const char **opts
, const char *term
)
2613 if (strncasecmp((*opts
), term
, strlen(term
)) == 0 &&
2614 (*opts
)[strlen(term
)] == '=') {
2615 *opts
+= strlen(term
) + 1;
2622 opt_array_append2(const char *file
, const int line
, const char *directive
,
2623 char ***array
, int **iarray
, u_int
*lp
, const char *s
, int i
)
2627 fatal("%s line %d: Too many %s entries", file
, line
, directive
);
2629 if (iarray
!= NULL
) {
2630 *iarray
= xrecallocarray(*iarray
, *lp
, *lp
+ 1,
2635 *array
= xrecallocarray(*array
, *lp
, *lp
+ 1, sizeof(**array
));
2636 (*array
)[*lp
] = xstrdup(s
);
2641 opt_array_append(const char *file
, const int line
, const char *directive
,
2642 char ***array
, u_int
*lp
, const char *s
)
2644 opt_array_append2(file
, line
, directive
, array
, NULL
, lp
, s
, 0);
2648 opt_array_free2(char **array
, int **iarray
, u_int l
)
2652 if (array
== NULL
|| l
== 0)
2654 for (i
= 0; i
< l
; i
++)
2661 ssh_signal(int signum
, sshsig_t handler
)
2663 struct sigaction sa
, osa
;
2665 /* mask all other signals while in handler */
2666 memset(&sa
, 0, sizeof(sa
));
2667 sa
.sa_handler
= handler
;
2668 sigfillset(&sa
.sa_mask
);
2669 #if defined(SA_RESTART) && !defined(NO_SA_RESTART)
2670 if (signum
!= SIGALRM
)
2671 sa
.sa_flags
= SA_RESTART
;
2673 if (sigaction(signum
, &sa
, &osa
) == -1) {
2674 debug3("sigaction(%s): %s", strsignal(signum
), strerror(errno
));
2677 return osa
.sa_handler
;
2681 stdfd_devnull(int do_stdin
, int do_stdout
, int do_stderr
)
2683 int devnull
, ret
= 0;
2685 if ((devnull
= open(_PATH_DEVNULL
, O_RDWR
)) == -1) {
2686 error_f("open %s: %s", _PATH_DEVNULL
,
2690 if ((do_stdin
&& dup2(devnull
, STDIN_FILENO
) == -1) ||
2691 (do_stdout
&& dup2(devnull
, STDOUT_FILENO
) == -1) ||
2692 (do_stderr
&& dup2(devnull
, STDERR_FILENO
) == -1)) {
2693 error_f("dup2: %s", strerror(errno
));
2696 if (devnull
> STDERR_FILENO
)
2702 * Runs command in a subprocess with a minimal environment.
2703 * Returns pid on success, 0 on failure.
2704 * The child stdout and stderr maybe captured, left attached or sent to
2705 * /dev/null depending on the contents of flags.
2706 * "tag" is prepended to log messages.
2707 * NB. "command" is only used for logging; the actual command executed is
2711 subprocess(const char *tag
, const char *command
,
2712 int ac
, char **av
, FILE **child
, u_int flags
,
2713 struct passwd
*pw
, privdrop_fn
*drop_privs
, privrestore_fn
*restore_privs
)
2717 int fd
, devnull
, p
[2], i
;
2719 char *cp
, errmsg
[512];
2723 /* If dropping privs, then must specify user and restore function */
2724 if (drop_privs
!= NULL
&& (pw
== NULL
|| restore_privs
== NULL
)) {
2725 error("%s: inconsistent arguments", tag
); /* XXX fatal? */
2728 if (pw
== NULL
&& (pw
= getpwuid(getuid())) == NULL
) {
2729 error("%s: no user for current uid", tag
);
2735 debug3_f("%s command \"%s\" running as %s (flags 0x%x)",
2736 tag
, command
, pw
->pw_name
, flags
);
2738 /* Check consistency */
2739 if ((flags
& SSH_SUBPROCESS_STDOUT_DISCARD
) != 0 &&
2740 (flags
& SSH_SUBPROCESS_STDOUT_CAPTURE
) != 0) {
2741 error_f("inconsistent flags");
2744 if (((flags
& SSH_SUBPROCESS_STDOUT_CAPTURE
) == 0) != (child
== NULL
)) {
2745 error_f("inconsistent flags/output");
2750 * If executing an explicit binary, then verify the it exists
2751 * and appears safe-ish to execute
2753 if (!path_absolute(av
[0])) {
2754 error("%s path is not absolute", tag
);
2757 if (drop_privs
!= NULL
)
2759 if (stat(av
[0], &st
) == -1) {
2760 error("Could not stat %s \"%s\": %s", tag
,
2761 av
[0], strerror(errno
));
2762 goto restore_return
;
2764 if ((flags
& SSH_SUBPROCESS_UNSAFE_PATH
) == 0 &&
2765 safe_path(av
[0], &st
, NULL
, 0, errmsg
, sizeof(errmsg
)) != 0) {
2766 error("Unsafe %s \"%s\": %s", tag
, av
[0], errmsg
);
2767 goto restore_return
;
2769 /* Prepare to keep the child's stdout if requested */
2770 if (pipe(p
) == -1) {
2771 error("%s: pipe: %s", tag
, strerror(errno
));
2773 if (restore_privs
!= NULL
)
2777 if (restore_privs
!= NULL
)
2780 switch ((pid
= fork())) {
2781 case -1: /* error */
2782 error("%s: fork: %s", tag
, strerror(errno
));
2787 /* Prepare a minimal environment for the child. */
2788 if ((flags
& SSH_SUBPROCESS_PRESERVE_ENV
) == 0) {
2790 env
= xcalloc(sizeof(*env
), nenv
);
2791 child_set_env(&env
, &nenv
, "PATH", _PATH_STDPATH
);
2792 child_set_env(&env
, &nenv
, "USER", pw
->pw_name
);
2793 child_set_env(&env
, &nenv
, "LOGNAME", pw
->pw_name
);
2794 child_set_env(&env
, &nenv
, "HOME", pw
->pw_dir
);
2795 if ((cp
= getenv("LANG")) != NULL
)
2796 child_set_env(&env
, &nenv
, "LANG", cp
);
2799 for (i
= 1; i
< NSIG
; i
++)
2800 ssh_signal(i
, SIG_DFL
);
2802 if ((devnull
= open(_PATH_DEVNULL
, O_RDWR
)) == -1) {
2803 error("%s: open %s: %s", tag
, _PATH_DEVNULL
,
2807 if (dup2(devnull
, STDIN_FILENO
) == -1) {
2808 error("%s: dup2: %s", tag
, strerror(errno
));
2812 /* Set up stdout as requested; leave stderr in place for now. */
2814 if ((flags
& SSH_SUBPROCESS_STDOUT_CAPTURE
) != 0)
2816 else if ((flags
& SSH_SUBPROCESS_STDOUT_DISCARD
) != 0)
2818 if (fd
!= -1 && dup2(fd
, STDOUT_FILENO
) == -1) {
2819 error("%s: dup2: %s", tag
, strerror(errno
));
2822 closefrom(STDERR_FILENO
+ 1);
2824 if (geteuid() == 0 &&
2825 initgroups(pw
->pw_name
, pw
->pw_gid
) == -1) {
2826 error("%s: initgroups(%s, %u): %s", tag
,
2827 pw
->pw_name
, (u_int
)pw
->pw_gid
, strerror(errno
));
2830 if (setresgid(pw
->pw_gid
, pw
->pw_gid
, pw
->pw_gid
) == -1) {
2831 error("%s: setresgid %u: %s", tag
, (u_int
)pw
->pw_gid
,
2835 if (setresuid(pw
->pw_uid
, pw
->pw_uid
, pw
->pw_uid
) == -1) {
2836 error("%s: setresuid %u: %s", tag
, (u_int
)pw
->pw_uid
,
2840 /* stdin is pointed to /dev/null at this point */
2841 if ((flags
& SSH_SUBPROCESS_STDOUT_DISCARD
) != 0 &&
2842 dup2(STDIN_FILENO
, STDERR_FILENO
) == -1) {
2843 error("%s: dup2: %s", tag
, strerror(errno
));
2847 execve(av
[0], av
, env
);
2850 error("%s %s \"%s\": %s", tag
, env
== NULL
? "execv" : "execve",
2851 command
, strerror(errno
));
2853 default: /* parent */
2858 if ((flags
& SSH_SUBPROCESS_STDOUT_CAPTURE
) == 0)
2860 else if ((f
= fdopen(p
[0], "r")) == NULL
) {
2861 error("%s: fdopen: %s", tag
, strerror(errno
));
2863 /* Don't leave zombie child */
2865 while (waitpid(pid
, NULL
, 0) == -1 && errno
== EINTR
)
2870 debug3_f("%s pid %ld", tag
, (long)pid
);
2877 lookup_env_in_list(const char *env
, char * const *envs
, size_t nenvs
)
2881 envlen
= strlen(env
);
2882 for (i
= 0; i
< nenvs
; i
++) {
2883 if (strncmp(envs
[i
], env
, envlen
) == 0 &&
2884 envs
[i
][envlen
] == '=') {
2885 return envs
[i
] + envlen
+ 1;
2892 lookup_setenv_in_list(const char *env
, char * const *envs
, size_t nenvs
)
2897 name
= xstrdup(env
);
2898 if ((cp
= strchr(name
, '=')) == NULL
) {
2900 return NULL
; /* not env=val */
2903 ret
= lookup_env_in_list(name
, envs
, nenvs
);
2909 * Helpers for managing poll(2)/ppoll(2) timeouts
2910 * Will remember the earliest deadline and return it for use in poll/ppoll.
2913 /* Initialise a poll/ppoll timeout with an indefinite deadline */
2915 ptimeout_init(struct timespec
*pt
)
2918 * Deliberately invalid for ppoll(2).
2919 * Will be converted to NULL in ptimeout_get_tspec() later.
2925 /* Specify a poll/ppoll deadline of at most 'sec' seconds */
2927 ptimeout_deadline_sec(struct timespec
*pt
, long sec
)
2929 if (pt
->tv_sec
== -1 || pt
->tv_sec
>= sec
) {
2935 /* Specify a poll/ppoll deadline of at most 'p' (timespec) */
2937 ptimeout_deadline_tsp(struct timespec
*pt
, struct timespec
*p
)
2939 if (pt
->tv_sec
== -1 || timespeccmp(pt
, p
, >=))
2943 /* Specify a poll/ppoll deadline of at most 'ms' milliseconds */
2945 ptimeout_deadline_ms(struct timespec
*pt
, long ms
)
2949 p
.tv_sec
= ms
/ 1000;
2950 p
.tv_nsec
= (ms
% 1000) * 1000000;
2951 ptimeout_deadline_tsp(pt
, &p
);
2954 /* Specify a poll/ppoll deadline at wall clock monotime 'when' (timespec) */
2956 ptimeout_deadline_monotime_tsp(struct timespec
*pt
, struct timespec
*when
)
2958 struct timespec now
, t
;
2962 if (timespeccmp(&now
, when
, >=)) {
2963 /* 'when' is now or in the past. Timeout ASAP */
2967 timespecsub(when
, &now
, &t
);
2968 ptimeout_deadline_tsp(pt
, &t
);
2972 /* Specify a poll/ppoll deadline at wall clock monotime 'when' */
2974 ptimeout_deadline_monotime(struct timespec
*pt
, time_t when
)
2980 ptimeout_deadline_monotime_tsp(pt
, &t
);
2983 /* Get a poll(2) timeout value in milliseconds */
2985 ptimeout_get_ms(struct timespec
*pt
)
2987 if (pt
->tv_sec
== -1)
2989 if (pt
->tv_sec
>= (INT_MAX
- (pt
->tv_nsec
/ 1000000)) / 1000)
2991 return (pt
->tv_sec
* 1000) + (pt
->tv_nsec
/ 1000000);
2994 /* Get a ppoll(2) timeout value as a timespec pointer */
2996 ptimeout_get_tsp(struct timespec
*pt
)
2998 return pt
->tv_sec
== -1 ? NULL
: pt
;
3001 /* Returns non-zero if a timeout has been set (i.e. is not indefinite) */
3003 ptimeout_isset(struct timespec
*pt
)
3005 return pt
->tv_sec
!= -1;
3009 * Returns zero if the library at 'path' contains symbol 's', nonzero
3013 lib_contains_symbol(const char *path
, const char *s
)
3019 memset(nl
, 0, sizeof(nl
));
3020 nl
[0].n_name
= xstrdup(s
);
3021 nl
[1].n_name
= NULL
;
3022 if ((r
= nlist(path
, nl
)) == -1) {
3023 error_f("nlist failed for %s", path
);
3026 if (r
!= 0 || nl
[0].n_value
== 0 || nl
[0].n_type
== 0) {
3027 error_f("library %s does not contain symbol %s", path
, s
);
3035 #else /* HAVE_NLIST_H */
3041 memset(&st
, 0, sizeof(st
));
3042 if ((fd
= open(path
, O_RDONLY
)) < 0) {
3043 error_f("open %s: %s", path
, strerror(errno
));
3046 if (fstat(fd
, &st
) != 0) {
3047 error_f("fstat %s: %s", path
, strerror(errno
));
3050 if (!S_ISREG(st
.st_mode
)) {
3051 error_f("%s is not a regular file", path
);
3054 if (st
.st_size
< 0 ||
3055 (size_t)st
.st_size
< strlen(s
) ||
3056 st
.st_size
>= INT_MAX
/2) {
3057 error_f("%s bad size %lld", path
, (long long)st
.st_size
);
3060 sz
= (size_t)st
.st_size
;
3061 if ((m
= mmap(NULL
, sz
, PROT_READ
, MAP_PRIVATE
, fd
, 0)) == MAP_FAILED
||
3063 error_f("mmap %s: %s", path
, strerror(errno
));
3066 if (memmem(m
, sz
, s
, strlen(s
)) == NULL
) {
3067 error_f("%s does not contain expected string %s", path
, s
);
3073 if (m
!= NULL
&& m
!= MAP_FAILED
)
3077 #endif /* HAVE_NLIST_H */