1 /* $OpenBSD: misc.c,v 1.196 2024/06/06 17:15:25 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)
569 while (isdigit((unsigned char)*s
))
575 * Convert a time string into seconds; format is
579 * Valid time qualifiers are:
593 * Return -1 if time string is invalid.
596 convtime(const char *s
)
598 int secs
, total
= 0, multiplier
;
599 char *p
, *os
, *np
, c
= 0;
602 if (s
== NULL
|| *s
== '\0')
604 p
= os
= strdup(s
); /* deal with const */
614 secs
= (int)strtonum(p
, 0, INT_MAX
, &errstr
);
629 multiplier
= MINUTES
;
646 if (secs
> INT_MAX
/ multiplier
)
649 if (total
> INT_MAX
- secs
)
667 fmt_timeframe(time_t t
)
670 static char tfbuf
[TF_BUFS
][TF_LEN
]; /* ring buffer */
672 unsigned int sec
, min
, hrs
, day
;
673 unsigned long long week
;
691 snprintf(buf
, TF_LEN
, "%02lluw%01ud%02uh", week
, day
, hrs
);
693 snprintf(buf
, TF_LEN
, "%01ud%02uh%02um", day
, hrs
, min
);
695 snprintf(buf
, TF_LEN
, "%02u:%02u:%02u", hrs
, min
, sec
);
701 * Returns a standardized host+port identifier string.
702 * Caller must free returned string.
705 put_host_port(const char *host
, u_short port
)
709 if (port
== 0 || port
== SSH_DEFAULT_PORT
)
710 return(xstrdup(host
));
711 if (asprintf(&hoststr
, "[%s]:%d", host
, (int)port
) == -1)
712 fatal("put_host_port: asprintf: %s", strerror(errno
));
713 debug3("put_host_port: %s", hoststr
);
718 * Search for next delimiter between hostnames/addresses and ports.
719 * Argument may be modified (for termination).
720 * Returns *cp if parsing succeeds.
721 * *cp is set to the start of the next field, if one was found.
722 * The delimiter char, if present, is stored in delim.
723 * If this is the last field, *cp is set to NULL.
726 hpdelim2(char **cp
, char *delim
)
730 if (cp
== NULL
|| *cp
== NULL
)
735 if ((s
= strchr(s
, ']')) == NULL
)
739 } else if ((s
= strpbrk(s
, ":/")) == NULL
)
740 s
= *cp
+ strlen(*cp
); /* skip to end (see first case below) */
744 *cp
= NULL
; /* no more fields*/
751 *s
= '\0'; /* terminate */
762 /* The common case: only accept colon as delimiter. */
766 char *r
, delim
= '\0';
768 r
= hpdelim2(cp
, &delim
);
775 cleanhostname(char *host
)
777 if (*host
== '[' && host
[strlen(host
) - 1] == ']') {
778 host
[strlen(host
) - 1] = '\0';
789 if (*cp
== ':') /* Leading colon is part of file name. */
795 if (*cp
== '@' && *(cp
+1) == '[')
797 if (*cp
== ']' && *(cp
+1) == ':' && flag
)
799 if (*cp
== ':' && !flag
)
808 * Parse a [user@]host:[path] string.
809 * Caller must free returned user, host and path.
810 * Any of the pointer return arguments may be NULL (useful for syntax checking).
811 * If user was not specified then *userp will be set to NULL.
812 * If host was not specified then *hostp will be set to NULL.
813 * If path was not specified then *pathp will be set to ".".
814 * Returns 0 on success, -1 on failure.
817 parse_user_host_path(const char *s
, char **userp
, char **hostp
, char **pathp
)
819 char *user
= NULL
, *host
= NULL
, *path
= NULL
;
832 /* Check for remote syntax: [user@]host:[path] */
833 if ((tmp
= colon(sdup
)) == NULL
)
836 /* Extract optional path */
842 /* Extract optional user and mandatory host */
843 tmp
= strrchr(sdup
, '@');
846 host
= xstrdup(cleanhostname(tmp
));
848 user
= xstrdup(sdup
);
850 host
= xstrdup(cleanhostname(sdup
));
877 * Parse a [user@]host[:port] string.
878 * Caller must free returned user and host.
879 * Any of the pointer return arguments may be NULL (useful for syntax checking).
880 * If user was not specified then *userp will be set to NULL.
881 * If port was not specified then *portp will be -1.
882 * Returns 0 on success, -1 on failure.
885 parse_user_host_port(const char *s
, char **userp
, char **hostp
, int *portp
)
887 char *sdup
, *cp
, *tmp
;
888 char *user
= NULL
, *host
= NULL
;
889 int port
= -1, ret
= -1;
898 if ((sdup
= tmp
= strdup(s
)) == NULL
)
900 /* Extract optional username */
901 if ((cp
= strrchr(tmp
, '@')) != NULL
) {
905 if ((user
= strdup(tmp
)) == NULL
)
909 /* Extract mandatory hostname */
910 if ((cp
= hpdelim(&tmp
)) == NULL
|| *cp
== '\0')
912 host
= xstrdup(cleanhostname(cp
));
913 /* Convert and verify optional port */
914 if (tmp
!= NULL
&& *tmp
!= '\0') {
915 if ((port
= a2port(tmp
)) <= 0)
938 * Converts a two-byte hex string to decimal.
939 * Returns the decimal value or -1 for invalid input.
942 hexchar(const char *s
)
944 unsigned char result
[2];
947 for (i
= 0; i
< 2; i
++) {
948 if (s
[i
] >= '0' && s
[i
] <= '9')
949 result
[i
] = (unsigned char)(s
[i
] - '0');
950 else if (s
[i
] >= 'a' && s
[i
] <= 'f')
951 result
[i
] = (unsigned char)(s
[i
] - 'a') + 10;
952 else if (s
[i
] >= 'A' && s
[i
] <= 'F')
953 result
[i
] = (unsigned char)(s
[i
] - 'A') + 10;
957 return (result
[0] << 4) | result
[1];
961 * Decode an url-encoded string.
962 * Returns a newly allocated string on success or NULL on failure.
965 urldecode(const char *src
)
971 if ((srclen
= strlen(src
)) >= SIZE_MAX
)
972 fatal_f("input too large");
973 ret
= xmalloc(srclen
+ 1);
974 for (dst
= ret
; *src
!= '\0'; src
++) {
980 if (!isxdigit((unsigned char)src
[1]) ||
981 !isxdigit((unsigned char)src
[2]) ||
982 (ch
= hexchar(src
+ 1)) == -1) {
1000 * Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI.
1001 * See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04
1002 * Either user or path may be url-encoded (but not host or port).
1003 * Caller must free returned user, host and path.
1004 * Any of the pointer return arguments may be NULL (useful for syntax checking)
1005 * but the scheme must always be specified.
1006 * If user was not specified then *userp will be set to NULL.
1007 * If port was not specified then *portp will be -1.
1008 * If path was not specified then *pathp will be set to NULL.
1009 * Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri.
1012 parse_uri(const char *scheme
, const char *uri
, char **userp
, char **hostp
,
1013 int *portp
, char **pathp
)
1015 char *uridup
, *cp
, *tmp
, ch
;
1016 char *user
= NULL
, *host
= NULL
, *path
= NULL
;
1017 int port
= -1, ret
= -1;
1020 len
= strlen(scheme
);
1021 if (strncmp(uri
, scheme
, len
) != 0 || strncmp(uri
+ len
, "://", 3) != 0)
1034 uridup
= tmp
= xstrdup(uri
);
1036 /* Extract optional ssh-info (username + connection params) */
1037 if ((cp
= strchr(tmp
, '@')) != NULL
) {
1041 /* Extract username and connection params */
1042 if ((delim
= strchr(tmp
, ';')) != NULL
) {
1043 /* Just ignore connection params for now */
1047 /* Empty username */
1050 if ((user
= urldecode(tmp
)) == NULL
)
1055 /* Extract mandatory hostname */
1056 if ((cp
= hpdelim2(&tmp
, &ch
)) == NULL
|| *cp
== '\0')
1058 host
= xstrdup(cleanhostname(cp
));
1059 if (!valid_domain(host
, 0, NULL
))
1062 if (tmp
!= NULL
&& *tmp
!= '\0') {
1064 /* Convert and verify port. */
1065 if ((cp
= strchr(tmp
, '/')) != NULL
)
1067 if ((port
= a2port(tmp
)) <= 0)
1069 tmp
= cp
? cp
+ 1 : NULL
;
1071 if (tmp
!= NULL
&& *tmp
!= '\0') {
1072 /* Extract optional path */
1073 if ((path
= urldecode(tmp
)) == NULL
)
1079 if (userp
!= NULL
) {
1083 if (hostp
!= NULL
) {
1089 if (pathp
!= NULL
) {
1102 /* function to assist building execv() arguments */
1104 addargs(arglist
*args
, char *fmt
, ...)
1112 r
= vasprintf(&cp
, fmt
, ap
);
1115 fatal_f("argument too long");
1117 nalloc
= args
->nalloc
;
1118 if (args
->list
== NULL
) {
1121 } else if (args
->num
> (256 * 1024))
1122 fatal_f("too many arguments");
1123 else if (args
->num
>= args
->nalloc
)
1124 fatal_f("arglist corrupt");
1125 else if (args
->num
+2 >= nalloc
)
1128 args
->list
= xrecallocarray(args
->list
, args
->nalloc
,
1129 nalloc
, sizeof(char *));
1130 args
->nalloc
= nalloc
;
1131 args
->list
[args
->num
++] = cp
;
1132 args
->list
[args
->num
] = NULL
;
1136 replacearg(arglist
*args
, u_int which
, char *fmt
, ...)
1143 r
= vasprintf(&cp
, fmt
, ap
);
1146 fatal_f("argument too long");
1147 if (args
->list
== NULL
|| args
->num
>= args
->nalloc
)
1148 fatal_f("arglist corrupt");
1150 if (which
>= args
->num
)
1151 fatal_f("tried to replace invalid arg %d >= %d",
1153 free(args
->list
[which
]);
1154 args
->list
[which
] = cp
;
1158 freeargs(arglist
*args
)
1164 if (args
->list
!= NULL
&& args
->num
< args
->nalloc
) {
1165 for (i
= 0; i
< args
->num
; i
++)
1166 free(args
->list
[i
]);
1169 args
->nalloc
= args
->num
= 0;
1174 * Expands tildes in the file name. Returns data allocated by xmalloc.
1175 * Warning: this calls getpw*.
1178 tilde_expand(const char *filename
, uid_t uid
, char **retp
)
1180 char *ocopy
= NULL
, *copy
, *s
= NULL
;
1181 const char *path
= NULL
, *user
= NULL
;
1184 int ret
= -1, r
, slash
;
1187 if (*filename
!= '~') {
1188 *retp
= xstrdup(filename
);
1191 ocopy
= copy
= xstrdup(filename
+ 1);
1193 if (*copy
== '\0') /* ~ */
1195 else if (*copy
== '/') {
1196 copy
+= strspn(copy
, "/");
1198 path
= NULL
; /* ~/ */
1200 path
= copy
; /* ~/path */
1203 if ((path
= strchr(copy
, '/')) != NULL
) {
1204 copy
[path
- copy
] = '\0';
1206 path
+= strspn(path
, "/");
1207 if (*path
== '\0') /* ~user/ */
1209 /* else ~user/path */
1214 if ((pw
= getpwnam(user
)) == NULL
) {
1215 error_f("No such user %s", user
);
1218 } else if ((pw
= getpwuid(uid
)) == NULL
) {
1219 error_f("No such uid %ld", (long)uid
);
1223 /* Make sure directory has a trailing '/' */
1224 slash
= (len
= strlen(pw
->pw_dir
)) == 0 || pw
->pw_dir
[len
- 1] != '/';
1226 if ((r
= xasprintf(&s
, "%s%s%s", pw
->pw_dir
,
1227 slash
? "/" : "", path
!= NULL
? path
: "")) <= 0) {
1228 error_f("xasprintf failed");
1231 if (r
>= PATH_MAX
) {
1232 error_f("Path too long");
1246 tilde_expand_filename(const char *filename
, uid_t uid
)
1250 if (tilde_expand(filename
, uid
, &ret
) != 0)
1256 * Expand a string with a set of %[char] escapes and/or ${ENVIRONMENT}
1257 * substitutions. A number of escapes may be specified as
1258 * (char *escape_chars, char *replacement) pairs. The list must be terminated
1259 * by a NULL escape_char. Returns replaced string in memory allocated by
1260 * xmalloc which the caller must free.
1263 vdollar_percent_expand(int *parseerror
, int dollar
, int percent
,
1264 const char *string
, va_list ap
)
1266 #define EXPAND_MAX_KEYS 64
1267 u_int num_keys
= 0, i
;
1271 } keys
[EXPAND_MAX_KEYS
];
1273 int r
, missingvar
= 0;
1274 char *ret
= NULL
, *var
, *varend
, *val
;
1277 if ((buf
= sshbuf_new()) == NULL
)
1278 fatal_f("sshbuf_new failed");
1279 if (parseerror
== NULL
)
1280 fatal_f("null parseerror arg");
1283 /* Gather keys if we're doing percent expansion. */
1285 for (num_keys
= 0; num_keys
< EXPAND_MAX_KEYS
; num_keys
++) {
1286 keys
[num_keys
].key
= va_arg(ap
, char *);
1287 if (keys
[num_keys
].key
== NULL
)
1289 keys
[num_keys
].repl
= va_arg(ap
, char *);
1290 if (keys
[num_keys
].repl
== NULL
) {
1291 fatal_f("NULL replacement for token %s",
1292 keys
[num_keys
].key
);
1295 if (num_keys
== EXPAND_MAX_KEYS
&& va_arg(ap
, char *) != NULL
)
1296 fatal_f("too many keys");
1298 fatal_f("percent expansion without token list");
1302 for (i
= 0; *string
!= '\0'; string
++) {
1303 /* Optionally process ${ENVIRONMENT} expansions. */
1304 if (dollar
&& string
[0] == '$' && string
[1] == '{') {
1305 string
+= 2; /* skip over '${' */
1306 if ((varend
= strchr(string
, '}')) == NULL
) {
1307 error_f("environment variable '%s' missing "
1308 "closing '}'", string
);
1311 len
= varend
- string
;
1313 error_f("zero-length environment variable");
1316 var
= xmalloc(len
+ 1);
1317 (void)strlcpy(var
, string
, len
+ 1);
1318 if ((val
= getenv(var
)) == NULL
) {
1319 error_f("env var ${%s} has no value", var
);
1322 debug3_f("expand ${%s} -> '%s'", var
, val
);
1323 if ((r
= sshbuf_put(buf
, val
, strlen(val
))) !=0)
1324 fatal_fr(r
, "sshbuf_put ${}");
1332 * Process percent expansions if we have a list of TOKENs.
1333 * If we're not doing percent expansion everything just gets
1336 if (*string
!= '%' || !percent
) {
1338 if ((r
= sshbuf_put_u8(buf
, *string
)) != 0)
1339 fatal_fr(r
, "sshbuf_put_u8 %%");
1346 if (*string
== '\0') {
1347 error_f("invalid format");
1350 for (i
= 0; i
< num_keys
; i
++) {
1351 if (strchr(keys
[i
].key
, *string
) != NULL
) {
1352 if ((r
= sshbuf_put(buf
, keys
[i
].repl
,
1353 strlen(keys
[i
].repl
))) != 0)
1354 fatal_fr(r
, "sshbuf_put %%-repl");
1358 if (i
>= num_keys
) {
1359 error_f("unknown key %%%c", *string
);
1363 if (!missingvar
&& (ret
= sshbuf_dup_string(buf
)) == NULL
)
1364 fatal_f("sshbuf_dup_string failed");
1368 return *parseerror
? NULL
: ret
;
1369 #undef EXPAND_MAX_KEYS
1373 * Expand only environment variables.
1374 * Note that although this function is variadic like the other similar
1375 * functions, any such arguments will be unused.
1379 dollar_expand(int *parseerr
, const char *string
, ...)
1385 va_start(ap
, string
);
1386 ret
= vdollar_percent_expand(&err
, 1, 0, string
, ap
);
1388 if (parseerr
!= NULL
)
1394 * Returns expanded string or NULL if a specified environment variable is
1395 * not defined, or calls fatal if the string is invalid.
1398 percent_expand(const char *string
, ...)
1404 va_start(ap
, string
);
1405 ret
= vdollar_percent_expand(&err
, 0, 1, string
, ap
);
1413 * Returns expanded string or NULL if a specified environment variable is
1414 * not defined, or calls fatal if the string is invalid.
1417 percent_dollar_expand(const char *string
, ...)
1423 va_start(ap
, string
);
1424 ret
= vdollar_percent_expand(&err
, 1, 1, string
, ap
);
1432 tun_open(int tun
, int mode
, char **ifname
)
1434 #if defined(CUSTOM_SYS_TUN_OPEN)
1435 return (sys_tun_open(tun
, mode
, ifname
));
1436 #elif defined(SSH_TUN_OPENBSD)
1440 const char *tunbase
= "tun";
1445 if (mode
== SSH_TUNMODE_ETHERNET
)
1448 /* Open the tunnel device */
1449 if (tun
<= SSH_TUNID_MAX
) {
1450 snprintf(name
, sizeof(name
), "/dev/%s%d", tunbase
, tun
);
1451 fd
= open(name
, O_RDWR
);
1452 } else if (tun
== SSH_TUNID_ANY
) {
1453 for (tun
= 100; tun
>= 0; tun
--) {
1454 snprintf(name
, sizeof(name
), "/dev/%s%d",
1456 if ((fd
= open(name
, O_RDWR
)) >= 0)
1460 debug_f("invalid tunnel %u", tun
);
1465 debug_f("%s open: %s", name
, strerror(errno
));
1469 debug_f("%s mode %d fd %d", name
, mode
, fd
);
1471 /* Bring interface up if it is not already */
1472 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s%d", tunbase
, tun
);
1473 if ((sock
= socket(PF_UNIX
, SOCK_STREAM
, 0)) == -1)
1476 if (ioctl(sock
, SIOCGIFFLAGS
, &ifr
) == -1) {
1477 debug_f("get interface %s flags: %s", ifr
.ifr_name
,
1482 if (!(ifr
.ifr_flags
& IFF_UP
)) {
1483 ifr
.ifr_flags
|= IFF_UP
;
1484 if (ioctl(sock
, SIOCSIFFLAGS
, &ifr
) == -1) {
1485 debug_f("activate interface %s: %s", ifr
.ifr_name
,
1492 *ifname
= xstrdup(ifr
.ifr_name
);
1504 error("Tunnel interfaces are not supported on this platform");
1510 sanitise_stdfd(void)
1514 if ((nullfd
= dupfd
= open(_PATH_DEVNULL
, O_RDWR
)) == -1) {
1515 fprintf(stderr
, "Couldn't open /dev/null: %s\n",
1519 while (++dupfd
<= STDERR_FILENO
) {
1520 /* Only populate closed fds. */
1521 if (fcntl(dupfd
, F_GETFL
) == -1 && errno
== EBADF
) {
1522 if (dup2(nullfd
, dupfd
) == -1) {
1523 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
1528 if (nullfd
> STDERR_FILENO
)
1533 tohex(const void *vp
, size_t l
)
1535 const u_char
*p
= (const u_char
*)vp
;
1540 return xstrdup("tohex: length > 65536");
1544 for (i
= 0; i
< l
; i
++) {
1545 snprintf(b
, sizeof(b
), "%02x", p
[i
]);
1552 * Extend string *sp by the specified format. If *sp is not NULL (or empty),
1553 * then the separator 'sep' will be prepended before the formatted arguments.
1554 * Extended strings are heap allocated.
1557 xextendf(char **sp
, const char *sep
, const char *fmt
, ...)
1563 xvasprintf(&tmp1
, fmt
, ap
);
1566 if (*sp
== NULL
|| **sp
== '\0') {
1571 xasprintf(&tmp2
, "%s%s%s", *sp
, sep
== NULL
? "" : sep
, tmp1
);
1579 get_u64(const void *vp
)
1581 const u_char
*p
= (const u_char
*)vp
;
1584 v
= (u_int64_t
)p
[0] << 56;
1585 v
|= (u_int64_t
)p
[1] << 48;
1586 v
|= (u_int64_t
)p
[2] << 40;
1587 v
|= (u_int64_t
)p
[3] << 32;
1588 v
|= (u_int64_t
)p
[4] << 24;
1589 v
|= (u_int64_t
)p
[5] << 16;
1590 v
|= (u_int64_t
)p
[6] << 8;
1591 v
|= (u_int64_t
)p
[7];
1597 get_u32(const void *vp
)
1599 const u_char
*p
= (const u_char
*)vp
;
1602 v
= (u_int32_t
)p
[0] << 24;
1603 v
|= (u_int32_t
)p
[1] << 16;
1604 v
|= (u_int32_t
)p
[2] << 8;
1605 v
|= (u_int32_t
)p
[3];
1611 get_u32_le(const void *vp
)
1613 const u_char
*p
= (const u_char
*)vp
;
1616 v
= (u_int32_t
)p
[0];
1617 v
|= (u_int32_t
)p
[1] << 8;
1618 v
|= (u_int32_t
)p
[2] << 16;
1619 v
|= (u_int32_t
)p
[3] << 24;
1625 get_u16(const void *vp
)
1627 const u_char
*p
= (const u_char
*)vp
;
1630 v
= (u_int16_t
)p
[0] << 8;
1631 v
|= (u_int16_t
)p
[1];
1637 put_u64(void *vp
, u_int64_t v
)
1639 u_char
*p
= (u_char
*)vp
;
1641 p
[0] = (u_char
)(v
>> 56) & 0xff;
1642 p
[1] = (u_char
)(v
>> 48) & 0xff;
1643 p
[2] = (u_char
)(v
>> 40) & 0xff;
1644 p
[3] = (u_char
)(v
>> 32) & 0xff;
1645 p
[4] = (u_char
)(v
>> 24) & 0xff;
1646 p
[5] = (u_char
)(v
>> 16) & 0xff;
1647 p
[6] = (u_char
)(v
>> 8) & 0xff;
1648 p
[7] = (u_char
)v
& 0xff;
1652 put_u32(void *vp
, u_int32_t v
)
1654 u_char
*p
= (u_char
*)vp
;
1656 p
[0] = (u_char
)(v
>> 24) & 0xff;
1657 p
[1] = (u_char
)(v
>> 16) & 0xff;
1658 p
[2] = (u_char
)(v
>> 8) & 0xff;
1659 p
[3] = (u_char
)v
& 0xff;
1663 put_u32_le(void *vp
, u_int32_t v
)
1665 u_char
*p
= (u_char
*)vp
;
1667 p
[0] = (u_char
)v
& 0xff;
1668 p
[1] = (u_char
)(v
>> 8) & 0xff;
1669 p
[2] = (u_char
)(v
>> 16) & 0xff;
1670 p
[3] = (u_char
)(v
>> 24) & 0xff;
1674 put_u16(void *vp
, u_int16_t v
)
1676 u_char
*p
= (u_char
*)vp
;
1678 p
[0] = (u_char
)(v
>> 8) & 0xff;
1679 p
[1] = (u_char
)v
& 0xff;
1683 ms_subtract_diff(struct timeval
*start
, int *ms
)
1685 struct timeval diff
, finish
;
1687 monotime_tv(&finish
);
1688 timersub(&finish
, start
, &diff
);
1689 *ms
-= (diff
.tv_sec
* 1000) + (diff
.tv_usec
/ 1000);
1693 ms_to_timespec(struct timespec
*ts
, int ms
)
1697 ts
->tv_sec
= ms
/ 1000;
1698 ts
->tv_nsec
= (ms
% 1000) * 1000 * 1000;
1702 monotime_ts(struct timespec
*ts
)
1705 #if defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_BOOTTIME) || \
1706 defined(CLOCK_MONOTONIC) || defined(CLOCK_REALTIME))
1707 static int gettime_failed
= 0;
1709 if (!gettime_failed
) {
1710 # ifdef CLOCK_BOOTTIME
1711 if (clock_gettime(CLOCK_BOOTTIME
, ts
) == 0)
1713 # endif /* CLOCK_BOOTTIME */
1714 # ifdef CLOCK_MONOTONIC
1715 if (clock_gettime(CLOCK_MONOTONIC
, ts
) == 0)
1717 # endif /* CLOCK_MONOTONIC */
1718 # ifdef CLOCK_REALTIME
1719 /* Not monotonic, but we're almost out of options here. */
1720 if (clock_gettime(CLOCK_REALTIME
, ts
) == 0)
1722 # endif /* CLOCK_REALTIME */
1723 debug3("clock_gettime: %s", strerror(errno
));
1726 #endif /* HAVE_CLOCK_GETTIME && (BOOTTIME || MONOTONIC || REALTIME) */
1727 gettimeofday(&tv
, NULL
);
1728 ts
->tv_sec
= tv
.tv_sec
;
1729 ts
->tv_nsec
= (long)tv
.tv_usec
* 1000;
1733 monotime_tv(struct timeval
*tv
)
1738 tv
->tv_sec
= ts
.tv_sec
;
1739 tv
->tv_usec
= ts
.tv_nsec
/ 1000;
1752 monotime_double(void)
1757 return ts
.tv_sec
+ ((double)ts
.tv_nsec
/ 1000000000);
1761 bandwidth_limit_init(struct bwlimit
*bw
, u_int64_t kbps
, size_t buflen
)
1763 bw
->buflen
= buflen
;
1765 bw
->thresh
= buflen
;
1767 timerclear(&bw
->bwstart
);
1768 timerclear(&bw
->bwend
);
1771 /* Callback from read/write loop to insert bandwidth-limiting delays */
1773 bandwidth_limit(struct bwlimit
*bw
, size_t read_len
)
1776 struct timespec ts
, rm
;
1778 bw
->lamt
+= read_len
;
1779 if (!timerisset(&bw
->bwstart
)) {
1780 monotime_tv(&bw
->bwstart
);
1783 if (bw
->lamt
< bw
->thresh
)
1786 monotime_tv(&bw
->bwend
);
1787 timersub(&bw
->bwend
, &bw
->bwstart
, &bw
->bwend
);
1788 if (!timerisset(&bw
->bwend
))
1792 waitlen
= (double)1000000L * bw
->lamt
/ bw
->rate
;
1794 bw
->bwstart
.tv_sec
= waitlen
/ 1000000L;
1795 bw
->bwstart
.tv_usec
= waitlen
% 1000000L;
1797 if (timercmp(&bw
->bwstart
, &bw
->bwend
, >)) {
1798 timersub(&bw
->bwstart
, &bw
->bwend
, &bw
->bwend
);
1800 /* Adjust the wait time */
1801 if (bw
->bwend
.tv_sec
) {
1803 if (bw
->thresh
< bw
->buflen
/ 4)
1804 bw
->thresh
= bw
->buflen
/ 4;
1805 } else if (bw
->bwend
.tv_usec
< 10000) {
1807 if (bw
->thresh
> bw
->buflen
* 8)
1808 bw
->thresh
= bw
->buflen
* 8;
1811 TIMEVAL_TO_TIMESPEC(&bw
->bwend
, &ts
);
1812 while (nanosleep(&ts
, &rm
) == -1) {
1820 monotime_tv(&bw
->bwstart
);
1823 /* Make a template filename for mk[sd]temp() */
1825 mktemp_proto(char *s
, size_t len
)
1830 if ((tmpdir
= getenv("TMPDIR")) != NULL
) {
1831 r
= snprintf(s
, len
, "%s/ssh-XXXXXXXXXXXX", tmpdir
);
1832 if (r
> 0 && (size_t)r
< len
)
1835 r
= snprintf(s
, len
, "/tmp/ssh-XXXXXXXXXXXX");
1836 if (r
< 0 || (size_t)r
>= len
)
1837 fatal_f("template string too short");
1840 static const struct {
1844 { "none", INT_MAX
}, /* can't use 0 here; that's CS0 */
1845 { "af11", IPTOS_DSCP_AF11
},
1846 { "af12", IPTOS_DSCP_AF12
},
1847 { "af13", IPTOS_DSCP_AF13
},
1848 { "af21", IPTOS_DSCP_AF21
},
1849 { "af22", IPTOS_DSCP_AF22
},
1850 { "af23", IPTOS_DSCP_AF23
},
1851 { "af31", IPTOS_DSCP_AF31
},
1852 { "af32", IPTOS_DSCP_AF32
},
1853 { "af33", IPTOS_DSCP_AF33
},
1854 { "af41", IPTOS_DSCP_AF41
},
1855 { "af42", IPTOS_DSCP_AF42
},
1856 { "af43", IPTOS_DSCP_AF43
},
1857 { "cs0", IPTOS_DSCP_CS0
},
1858 { "cs1", IPTOS_DSCP_CS1
},
1859 { "cs2", IPTOS_DSCP_CS2
},
1860 { "cs3", IPTOS_DSCP_CS3
},
1861 { "cs4", IPTOS_DSCP_CS4
},
1862 { "cs5", IPTOS_DSCP_CS5
},
1863 { "cs6", IPTOS_DSCP_CS6
},
1864 { "cs7", IPTOS_DSCP_CS7
},
1865 { "ef", IPTOS_DSCP_EF
},
1866 { "le", IPTOS_DSCP_LE
},
1867 { "lowdelay", IPTOS_LOWDELAY
},
1868 { "throughput", IPTOS_THROUGHPUT
},
1869 { "reliability", IPTOS_RELIABILITY
},
1874 parse_ipqos(const char *cp
)
1882 for (i
= 0; ipqos
[i
].name
!= NULL
; i
++) {
1883 if (strcasecmp(cp
, ipqos
[i
].name
) == 0)
1884 return ipqos
[i
].value
;
1886 /* Try parsing as an integer */
1887 val
= (int)strtonum(cp
, 0, 255, &errstr
);
1894 iptos2str(int iptos
)
1897 static char iptos_str
[sizeof "0xff"];
1899 for (i
= 0; ipqos
[i
].name
!= NULL
; i
++) {
1900 if (ipqos
[i
].value
== iptos
)
1901 return ipqos
[i
].name
;
1903 snprintf(iptos_str
, sizeof iptos_str
, "0x%02x", iptos
);
1911 *s
= tolower((u_char
)*s
);
1915 unix_listener(const char *path
, int backlog
, int unlink_first
)
1917 struct sockaddr_un sunaddr
;
1918 int saved_errno
, sock
;
1920 memset(&sunaddr
, 0, sizeof(sunaddr
));
1921 sunaddr
.sun_family
= AF_UNIX
;
1922 if (strlcpy(sunaddr
.sun_path
, path
,
1923 sizeof(sunaddr
.sun_path
)) >= sizeof(sunaddr
.sun_path
)) {
1924 error_f("path \"%s\" too long for Unix domain socket", path
);
1925 errno
= ENAMETOOLONG
;
1929 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1931 saved_errno
= errno
;
1932 error_f("socket: %.100s", strerror(errno
));
1933 errno
= saved_errno
;
1936 if (unlink_first
== 1) {
1937 if (unlink(path
) != 0 && errno
!= ENOENT
)
1938 error("unlink(%s): %.100s", path
, strerror(errno
));
1940 if (bind(sock
, (struct sockaddr
*)&sunaddr
, sizeof(sunaddr
)) == -1) {
1941 saved_errno
= errno
;
1942 error_f("cannot bind to path %s: %s", path
, strerror(errno
));
1944 errno
= saved_errno
;
1947 if (listen(sock
, backlog
) == -1) {
1948 saved_errno
= errno
;
1949 error_f("cannot listen on path %s: %s", path
, strerror(errno
));
1952 errno
= saved_errno
;
1959 sock_set_v6only(int s
)
1961 #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
1964 debug3("%s: set socket %d IPV6_V6ONLY", __func__
, s
);
1965 if (setsockopt(s
, IPPROTO_IPV6
, IPV6_V6ONLY
, &on
, sizeof(on
)) == -1)
1966 error("setsockopt IPV6_V6ONLY: %s", strerror(errno
));
1971 * Compares two strings that maybe be NULL. Returns non-zero if strings
1972 * are both NULL or are identical, returns zero otherwise.
1975 strcmp_maybe_null(const char *a
, const char *b
)
1977 if ((a
== NULL
&& b
!= NULL
) || (a
!= NULL
&& b
== NULL
))
1979 if (a
!= NULL
&& strcmp(a
, b
) != 0)
1985 * Compare two forwards, returning non-zero if they are identical or
1989 forward_equals(const struct Forward
*a
, const struct Forward
*b
)
1991 if (strcmp_maybe_null(a
->listen_host
, b
->listen_host
) == 0)
1993 if (a
->listen_port
!= b
->listen_port
)
1995 if (strcmp_maybe_null(a
->listen_path
, b
->listen_path
) == 0)
1997 if (strcmp_maybe_null(a
->connect_host
, b
->connect_host
) == 0)
1999 if (a
->connect_port
!= b
->connect_port
)
2001 if (strcmp_maybe_null(a
->connect_path
, b
->connect_path
) == 0)
2003 /* allocated_port and handle are not checked */
2007 /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
2009 permitopen_port(const char *p
)
2013 if (strcmp(p
, "*") == 0)
2014 return FWD_PERMIT_ANY_PORT
;
2015 if ((port
= a2port(p
)) > 0)
2020 /* returns 1 if process is already daemonized, 0 otherwise */
2026 if ((fd
= open(_PATH_TTY
, O_RDONLY
| O_NOCTTY
)) >= 0) {
2028 return 0; /* have controlling terminal */
2031 return 0; /* parent is not init */
2032 if (getsid(0) != getpid())
2033 return 0; /* not session leader */
2034 debug3("already daemonized");
2039 * Splits 's' into an argument vector. Handles quoted string and basic
2040 * escape characters (\\, \", \'). Caller must free the argument vector
2044 argv_split(const char *s
, int *argcp
, char ***argvp
, int terminate_on_comment
)
2046 int r
= SSH_ERR_INTERNAL_ERROR
;
2047 int argc
= 0, quote
, i
, j
;
2048 char *arg
, **argv
= xcalloc(1, sizeof(*argv
));
2053 for (i
= 0; s
[i
] != '\0'; i
++) {
2054 /* Skip leading whitespace */
2055 if (s
[i
] == ' ' || s
[i
] == '\t')
2057 if (terminate_on_comment
&& s
[i
] == '#')
2059 /* Start of a token */
2062 argv
= xreallocarray(argv
, (argc
+ 2), sizeof(*argv
));
2063 arg
= argv
[argc
++] = xcalloc(1, strlen(s
+ i
) + 1);
2066 /* Copy the token in, removing escapes */
2067 for (j
= 0; s
[i
] != '\0'; i
++) {
2069 if (s
[i
+ 1] == '\'' ||
2072 (quote
== 0 && s
[i
+ 1] == ' ')) {
2076 /* Unrecognised escape */
2079 } else if (quote
== 0 && (s
[i
] == ' ' || s
[i
] == '\t'))
2081 else if (quote
== 0 && (s
[i
] == '\"' || s
[i
] == '\''))
2082 quote
= s
[i
]; /* quote start */
2083 else if (quote
!= 0 && s
[i
] == quote
)
2084 quote
= 0; /* quote end */
2090 /* Ran out of string looking for close quote */
2091 r
= SSH_ERR_INVALID_FORMAT
;
2104 if (argc
!= 0 && argv
!= NULL
) {
2105 for (i
= 0; i
< argc
; i
++)
2113 * Reassemble an argument vector into a string, quoting and escaping as
2114 * necessary. Caller must free returned string.
2117 argv_assemble(int argc
, char **argv
)
2121 struct sshbuf
*buf
, *arg
;
2123 if ((buf
= sshbuf_new()) == NULL
|| (arg
= sshbuf_new()) == NULL
)
2124 fatal_f("sshbuf_new failed");
2126 for (i
= 0; i
< argc
; i
++) {
2129 for (j
= 0; argv
[i
][j
] != '\0'; j
++) {
2136 r
= sshbuf_put_u8(arg
, c
);
2141 if ((r
= sshbuf_put_u8(arg
, '\\')) != 0)
2145 r
= sshbuf_put_u8(arg
, c
);
2149 fatal_fr(r
, "sshbuf_put_u8");
2151 if ((i
!= 0 && (r
= sshbuf_put_u8(buf
, ' ')) != 0) ||
2152 (ws
!= 0 && (r
= sshbuf_put_u8(buf
, '"')) != 0) ||
2153 (r
= sshbuf_putb(buf
, arg
)) != 0 ||
2154 (ws
!= 0 && (r
= sshbuf_put_u8(buf
, '"')) != 0))
2155 fatal_fr(r
, "assemble");
2157 if ((ret
= malloc(sshbuf_len(buf
) + 1)) == NULL
)
2158 fatal_f("malloc failed");
2159 memcpy(ret
, sshbuf_ptr(buf
), sshbuf_len(buf
));
2160 ret
[sshbuf_len(buf
)] = '\0';
2167 argv_next(int *argcp
, char ***argvp
)
2169 char *ret
= (*argvp
)[0];
2171 if (*argcp
> 0 && ret
!= NULL
) {
2179 argv_consume(int *argcp
)
2185 argv_free(char **av
, int ac
)
2191 for (i
= 0; i
< ac
; i
++)
2196 /* Returns 0 if pid exited cleanly, non-zero otherwise */
2198 exited_cleanly(pid_t pid
, const char *tag
, const char *cmd
, int quiet
)
2202 while (waitpid(pid
, &status
, 0) == -1) {
2203 if (errno
!= EINTR
) {
2204 error("%s waitpid: %s", tag
, strerror(errno
));
2208 if (WIFSIGNALED(status
)) {
2209 error("%s %s exited on signal %d", tag
, cmd
, WTERMSIG(status
));
2211 } else if (WEXITSTATUS(status
) != 0) {
2212 do_log2(quiet
? SYSLOG_LEVEL_DEBUG1
: SYSLOG_LEVEL_INFO
,
2213 "%s %s failed, status %d", tag
, cmd
, WEXITSTATUS(status
));
2220 * Check a given path for security. This is defined as all components
2221 * of the path to the file must be owned by either the owner of
2222 * of the file or root and no directories must be group or world writable.
2224 * XXX Should any specific check be done for sym links ?
2226 * Takes a file name, its stat information (preferably from fstat() to
2227 * avoid races), the uid of the expected owner, their home directory and an
2228 * error buffer plus max size as arguments.
2230 * Returns 0 on success and -1 on failure
2233 safe_path(const char *name
, struct stat
*stp
, const char *pw_dir
,
2234 uid_t uid
, char *err
, size_t errlen
)
2236 char buf
[PATH_MAX
], homedir
[PATH_MAX
];
2238 int comparehome
= 0;
2241 if (realpath(name
, buf
) == NULL
) {
2242 snprintf(err
, errlen
, "realpath %s failed: %s", name
,
2246 if (pw_dir
!= NULL
&& realpath(pw_dir
, homedir
) != NULL
)
2249 if (!S_ISREG(stp
->st_mode
)) {
2250 snprintf(err
, errlen
, "%s is not a regular file", buf
);
2253 if ((!platform_sys_dir_uid(stp
->st_uid
) && stp
->st_uid
!= uid
) ||
2254 (stp
->st_mode
& 022) != 0) {
2255 snprintf(err
, errlen
, "bad ownership or modes for file %s",
2260 /* for each component of the canonical path, walking upwards */
2262 if ((cp
= dirname(buf
)) == NULL
) {
2263 snprintf(err
, errlen
, "dirname() failed");
2266 strlcpy(buf
, cp
, sizeof(buf
));
2268 if (stat(buf
, &st
) == -1 ||
2269 (!platform_sys_dir_uid(st
.st_uid
) && st
.st_uid
!= uid
) ||
2270 (st
.st_mode
& 022) != 0) {
2271 snprintf(err
, errlen
,
2272 "bad ownership or modes for directory %s", buf
);
2276 /* If are past the homedir then we can stop */
2277 if (comparehome
&& strcmp(homedir
, buf
) == 0)
2281 * dirname should always complete with a "/" path,
2282 * but we can be paranoid and check for "." too
2284 if ((strcmp("/", buf
) == 0) || (strcmp(".", buf
) == 0))
2291 * Version of safe_path() that accepts an open file descriptor to
2294 * Returns 0 on success and -1 on failure
2297 safe_path_fd(int fd
, const char *file
, struct passwd
*pw
,
2298 char *err
, size_t errlen
)
2302 /* check the open file to avoid races */
2303 if (fstat(fd
, &st
) == -1) {
2304 snprintf(err
, errlen
, "cannot stat file %s: %s",
2305 file
, strerror(errno
));
2308 return safe_path(file
, &st
, pw
->pw_dir
, pw
->pw_uid
, err
, errlen
);
2312 * Sets the value of the given variable in the environment. If the variable
2313 * already exists, its value is overridden.
2316 child_set_env(char ***envp
, u_int
*envsizep
, const char *name
,
2323 if (strchr(name
, '=') != NULL
) {
2324 error("Invalid environment variable \"%.100s\"", name
);
2329 * If we're passed an uninitialized list, allocate a single null
2330 * entry before continuing.
2332 if ((*envp
== NULL
) != (*envsizep
== 0))
2333 fatal_f("environment size mismatch");
2334 if (*envp
== NULL
&& *envsizep
== 0) {
2335 *envp
= xmalloc(sizeof(char *));
2341 * Find the slot where the value should be stored. If the variable
2342 * already exists, we reuse the slot; otherwise we append a new slot
2343 * at the end of the array, expanding if necessary.
2346 namelen
= strlen(name
);
2347 for (i
= 0; env
[i
]; i
++)
2348 if (strncmp(env
[i
], name
, namelen
) == 0 && env
[i
][namelen
] == '=')
2351 /* Reuse the slot. */
2354 /* New variable. Expand if necessary. */
2355 envsize
= *envsizep
;
2356 if (i
>= envsize
- 1) {
2357 if (envsize
>= 1000)
2358 fatal("child_set_env: too many env vars");
2360 env
= (*envp
) = xreallocarray(env
, envsize
, sizeof(char *));
2361 *envsizep
= envsize
;
2363 /* Need to set the NULL pointer at end of array beyond the new slot. */
2367 /* Allocate space and format the variable in the appropriate slot. */
2369 env
[i
] = xmalloc(strlen(name
) + 1 + strlen(value
) + 1);
2370 snprintf(env
[i
], strlen(name
) + 1 + strlen(value
) + 1, "%s=%s", name
, value
);
2374 * Check and optionally lowercase a domain name, also removes trailing '.'
2375 * Returns 1 on success and 0 on failure, storing an error message in errstr.
2378 valid_domain(char *name
, int makelower
, const char **errstr
)
2380 size_t i
, l
= strlen(name
);
2381 u_char c
, last
= '\0';
2382 static char errbuf
[256];
2385 strlcpy(errbuf
, "empty domain name", sizeof(errbuf
));
2388 if (!isalpha((u_char
)name
[0]) && !isdigit((u_char
)name
[0])) {
2389 snprintf(errbuf
, sizeof(errbuf
), "domain name \"%.100s\" "
2390 "starts with invalid character", name
);
2393 for (i
= 0; i
< l
; i
++) {
2394 c
= tolower((u_char
)name
[i
]);
2397 if (last
== '.' && c
== '.') {
2398 snprintf(errbuf
, sizeof(errbuf
), "domain name "
2399 "\"%.100s\" contains consecutive separators", name
);
2402 if (c
!= '.' && c
!= '-' && !isalnum(c
) &&
2403 c
!= '_') /* technically invalid, but common */ {
2404 snprintf(errbuf
, sizeof(errbuf
), "domain name "
2405 "\"%.100s\" contains invalid characters", name
);
2410 if (name
[l
- 1] == '.')
2422 * Verify that a environment variable name (not including initial '$') is
2423 * valid; consisting of one or more alphanumeric or underscore characters only.
2424 * Returns 1 on valid, 0 otherwise.
2427 valid_env_name(const char *name
)
2431 if (name
[0] == '\0')
2433 for (cp
= name
; *cp
!= '\0'; cp
++) {
2434 if (!isalnum((u_char
)*cp
) && *cp
!= '_')
2441 atoi_err(const char *nptr
, int *val
)
2443 const char *errstr
= NULL
;
2445 if (nptr
== NULL
|| *nptr
== '\0')
2447 *val
= strtonum(nptr
, 0, INT_MAX
, &errstr
);
2452 parse_absolute_time(const char *s
, uint64_t *tp
)
2464 if (l
> 1 && strcasecmp(s
+ l
- 1, "Z") == 0) {
2467 } else if (l
> 3 && strcasecmp(s
+ l
- 3, "UTC") == 0) {
2472 * POSIX strptime says "The application shall ensure that there
2473 * is white-space or other non-alphanumeric characters between
2474 * any two conversion specifications" so arrange things this way.
2477 case 8: /* YYYYMMDD */
2479 snprintf(buf
, sizeof(buf
), "%.4s-%.2s-%.2s", s
, s
+ 4, s
+ 6);
2481 case 12: /* YYYYMMDDHHMM */
2482 fmt
= "%Y-%m-%dT%H:%M";
2483 snprintf(buf
, sizeof(buf
), "%.4s-%.2s-%.2sT%.2s:%.2s",
2484 s
, s
+ 4, s
+ 6, s
+ 8, s
+ 10);
2486 case 14: /* YYYYMMDDHHMMSS */
2487 fmt
= "%Y-%m-%dT%H:%M:%S";
2488 snprintf(buf
, sizeof(buf
), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",
2489 s
, s
+ 4, s
+ 6, s
+ 8, s
+ 10, s
+ 12);
2492 return SSH_ERR_INVALID_FORMAT
;
2495 memset(&tm
, 0, sizeof(tm
));
2496 if ((cp
= strptime(buf
, fmt
, &tm
)) == NULL
|| *cp
!= '\0')
2497 return SSH_ERR_INVALID_FORMAT
;
2499 if ((tt
= timegm(&tm
)) < 0)
2500 return SSH_ERR_INVALID_FORMAT
;
2502 if ((tt
= mktime(&tm
)) < 0)
2503 return SSH_ERR_INVALID_FORMAT
;
2511 format_absolute_time(uint64_t t
, char *buf
, size_t len
)
2513 time_t tt
= t
> SSH_TIME_T_MAX
? SSH_TIME_T_MAX
: t
;
2516 localtime_r(&tt
, &tm
);
2517 strftime(buf
, len
, "%Y-%m-%dT%H:%M:%S", &tm
);
2521 * Parse a "pattern=interval" clause (e.g. a ChannelTimeout).
2522 * Returns 0 on success or non-zero on failure.
2523 * Caller must free *typep.
2526 parse_pattern_interval(const char *s
, char **typep
, int *secsp
)
2539 if ((cp
= strchr(sdup
, '=')) == NULL
|| cp
== sdup
) {
2544 if ((secs
= convtime(cp
)) < 0) {
2550 *typep
= xstrdup(sdup
);
2557 /* check if path is absolute */
2559 path_absolute(const char *path
)
2561 return (*path
== '/') ? 1 : 0;
2565 skip_space(char **cpp
)
2569 for (cp
= *cpp
; *cp
== ' ' || *cp
== '\t'; cp
++)
2574 /* authorized_key-style options parsing helpers */
2577 * Match flag 'opt' in *optsp, and if allow_negate is set then also match
2578 * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
2579 * if negated option matches.
2580 * If the option or negated option matches, then *optsp is updated to
2581 * point to the first character after the option.
2584 opt_flag(const char *opt
, int allow_negate
, const char **optsp
)
2586 size_t opt_len
= strlen(opt
);
2587 const char *opts
= *optsp
;
2590 if (allow_negate
&& strncasecmp(opts
, "no-", 3) == 0) {
2594 if (strncasecmp(opts
, opt
, opt_len
) == 0) {
2595 *optsp
= opts
+ opt_len
;
2596 return negate
? 0 : 1;
2602 opt_dequote(const char **sp
, const char **errstrp
)
2604 const char *s
= *sp
;
2610 *errstrp
= "missing start quote";
2614 if ((ret
= malloc(strlen((s
)) + 1)) == NULL
) {
2615 *errstrp
= "memory allocation failed";
2618 for (i
= 0; *s
!= '\0' && *s
!= '"';) {
2619 if (s
[0] == '\\' && s
[1] == '"')
2624 *errstrp
= "missing end quote";
2635 opt_match(const char **opts
, const char *term
)
2637 if (strncasecmp((*opts
), term
, strlen(term
)) == 0 &&
2638 (*opts
)[strlen(term
)] == '=') {
2639 *opts
+= strlen(term
) + 1;
2646 opt_array_append2(const char *file
, const int line
, const char *directive
,
2647 char ***array
, int **iarray
, u_int
*lp
, const char *s
, int i
)
2651 fatal("%s line %d: Too many %s entries", file
, line
, directive
);
2653 if (iarray
!= NULL
) {
2654 *iarray
= xrecallocarray(*iarray
, *lp
, *lp
+ 1,
2659 *array
= xrecallocarray(*array
, *lp
, *lp
+ 1, sizeof(**array
));
2660 (*array
)[*lp
] = xstrdup(s
);
2665 opt_array_append(const char *file
, const int line
, const char *directive
,
2666 char ***array
, u_int
*lp
, const char *s
)
2668 opt_array_append2(file
, line
, directive
, array
, NULL
, lp
, s
, 0);
2672 opt_array_free2(char **array
, int **iarray
, u_int l
)
2676 if (array
== NULL
|| l
== 0)
2678 for (i
= 0; i
< l
; i
++)
2685 ssh_signal(int signum
, sshsig_t handler
)
2687 struct sigaction sa
, osa
;
2689 /* mask all other signals while in handler */
2690 memset(&sa
, 0, sizeof(sa
));
2691 sa
.sa_handler
= handler
;
2692 sigfillset(&sa
.sa_mask
);
2693 #if defined(SA_RESTART) && !defined(NO_SA_RESTART)
2694 if (signum
!= SIGALRM
)
2695 sa
.sa_flags
= SA_RESTART
;
2697 if (sigaction(signum
, &sa
, &osa
) == -1) {
2698 debug3("sigaction(%s): %s", strsignal(signum
), strerror(errno
));
2701 return osa
.sa_handler
;
2705 stdfd_devnull(int do_stdin
, int do_stdout
, int do_stderr
)
2707 int devnull
, ret
= 0;
2709 if ((devnull
= open(_PATH_DEVNULL
, O_RDWR
)) == -1) {
2710 error_f("open %s: %s", _PATH_DEVNULL
,
2714 if ((do_stdin
&& dup2(devnull
, STDIN_FILENO
) == -1) ||
2715 (do_stdout
&& dup2(devnull
, STDOUT_FILENO
) == -1) ||
2716 (do_stderr
&& dup2(devnull
, STDERR_FILENO
) == -1)) {
2717 error_f("dup2: %s", strerror(errno
));
2720 if (devnull
> STDERR_FILENO
)
2726 * Runs command in a subprocess with a minimal environment.
2727 * Returns pid on success, 0 on failure.
2728 * The child stdout and stderr maybe captured, left attached or sent to
2729 * /dev/null depending on the contents of flags.
2730 * "tag" is prepended to log messages.
2731 * NB. "command" is only used for logging; the actual command executed is
2735 subprocess(const char *tag
, const char *command
,
2736 int ac
, char **av
, FILE **child
, u_int flags
,
2737 struct passwd
*pw
, privdrop_fn
*drop_privs
, privrestore_fn
*restore_privs
)
2741 int fd
, devnull
, p
[2], i
;
2743 char *cp
, errmsg
[512];
2747 /* If dropping privs, then must specify user and restore function */
2748 if (drop_privs
!= NULL
&& (pw
== NULL
|| restore_privs
== NULL
)) {
2749 error("%s: inconsistent arguments", tag
); /* XXX fatal? */
2752 if (pw
== NULL
&& (pw
= getpwuid(getuid())) == NULL
) {
2753 error("%s: no user for current uid", tag
);
2759 debug3_f("%s command \"%s\" running as %s (flags 0x%x)",
2760 tag
, command
, pw
->pw_name
, flags
);
2762 /* Check consistency */
2763 if ((flags
& SSH_SUBPROCESS_STDOUT_DISCARD
) != 0 &&
2764 (flags
& SSH_SUBPROCESS_STDOUT_CAPTURE
) != 0) {
2765 error_f("inconsistent flags");
2768 if (((flags
& SSH_SUBPROCESS_STDOUT_CAPTURE
) == 0) != (child
== NULL
)) {
2769 error_f("inconsistent flags/output");
2774 * If executing an explicit binary, then verify the it exists
2775 * and appears safe-ish to execute
2777 if (!path_absolute(av
[0])) {
2778 error("%s path is not absolute", tag
);
2781 if (drop_privs
!= NULL
)
2783 if (stat(av
[0], &st
) == -1) {
2784 error("Could not stat %s \"%s\": %s", tag
,
2785 av
[0], strerror(errno
));
2786 goto restore_return
;
2788 if ((flags
& SSH_SUBPROCESS_UNSAFE_PATH
) == 0 &&
2789 safe_path(av
[0], &st
, NULL
, 0, errmsg
, sizeof(errmsg
)) != 0) {
2790 error("Unsafe %s \"%s\": %s", tag
, av
[0], errmsg
);
2791 goto restore_return
;
2793 /* Prepare to keep the child's stdout if requested */
2794 if (pipe(p
) == -1) {
2795 error("%s: pipe: %s", tag
, strerror(errno
));
2797 if (restore_privs
!= NULL
)
2801 if (restore_privs
!= NULL
)
2804 switch ((pid
= fork())) {
2805 case -1: /* error */
2806 error("%s: fork: %s", tag
, strerror(errno
));
2811 /* Prepare a minimal environment for the child. */
2812 if ((flags
& SSH_SUBPROCESS_PRESERVE_ENV
) == 0) {
2814 env
= xcalloc(sizeof(*env
), nenv
);
2815 child_set_env(&env
, &nenv
, "PATH", _PATH_STDPATH
);
2816 child_set_env(&env
, &nenv
, "USER", pw
->pw_name
);
2817 child_set_env(&env
, &nenv
, "LOGNAME", pw
->pw_name
);
2818 child_set_env(&env
, &nenv
, "HOME", pw
->pw_dir
);
2819 if ((cp
= getenv("LANG")) != NULL
)
2820 child_set_env(&env
, &nenv
, "LANG", cp
);
2823 for (i
= 1; i
< NSIG
; i
++)
2824 ssh_signal(i
, SIG_DFL
);
2826 if ((devnull
= open(_PATH_DEVNULL
, O_RDWR
)) == -1) {
2827 error("%s: open %s: %s", tag
, _PATH_DEVNULL
,
2831 if (dup2(devnull
, STDIN_FILENO
) == -1) {
2832 error("%s: dup2: %s", tag
, strerror(errno
));
2836 /* Set up stdout as requested; leave stderr in place for now. */
2838 if ((flags
& SSH_SUBPROCESS_STDOUT_CAPTURE
) != 0)
2840 else if ((flags
& SSH_SUBPROCESS_STDOUT_DISCARD
) != 0)
2842 if (fd
!= -1 && dup2(fd
, STDOUT_FILENO
) == -1) {
2843 error("%s: dup2: %s", tag
, strerror(errno
));
2846 closefrom(STDERR_FILENO
+ 1);
2848 if (geteuid() == 0 &&
2849 initgroups(pw
->pw_name
, pw
->pw_gid
) == -1) {
2850 error("%s: initgroups(%s, %u): %s", tag
,
2851 pw
->pw_name
, (u_int
)pw
->pw_gid
, strerror(errno
));
2854 if (setresgid(pw
->pw_gid
, pw
->pw_gid
, pw
->pw_gid
) == -1) {
2855 error("%s: setresgid %u: %s", tag
, (u_int
)pw
->pw_gid
,
2859 if (setresuid(pw
->pw_uid
, pw
->pw_uid
, pw
->pw_uid
) == -1) {
2860 error("%s: setresuid %u: %s", tag
, (u_int
)pw
->pw_uid
,
2864 /* stdin is pointed to /dev/null at this point */
2865 if ((flags
& SSH_SUBPROCESS_STDOUT_DISCARD
) != 0 &&
2866 dup2(STDIN_FILENO
, STDERR_FILENO
) == -1) {
2867 error("%s: dup2: %s", tag
, strerror(errno
));
2871 execve(av
[0], av
, env
);
2874 error("%s %s \"%s\": %s", tag
, env
== NULL
? "execv" : "execve",
2875 command
, strerror(errno
));
2877 default: /* parent */
2882 if ((flags
& SSH_SUBPROCESS_STDOUT_CAPTURE
) == 0)
2884 else if ((f
= fdopen(p
[0], "r")) == NULL
) {
2885 error("%s: fdopen: %s", tag
, strerror(errno
));
2887 /* Don't leave zombie child */
2889 while (waitpid(pid
, NULL
, 0) == -1 && errno
== EINTR
)
2894 debug3_f("%s pid %ld", tag
, (long)pid
);
2901 lookup_env_in_list(const char *env
, char * const *envs
, size_t nenvs
)
2905 envlen
= strlen(env
);
2906 for (i
= 0; i
< nenvs
; i
++) {
2907 if (strncmp(envs
[i
], env
, envlen
) == 0 &&
2908 envs
[i
][envlen
] == '=') {
2909 return envs
[i
] + envlen
+ 1;
2916 lookup_setenv_in_list(const char *env
, char * const *envs
, size_t nenvs
)
2921 name
= xstrdup(env
);
2922 if ((cp
= strchr(name
, '=')) == NULL
) {
2924 return NULL
; /* not env=val */
2927 ret
= lookup_env_in_list(name
, envs
, nenvs
);
2933 * Helpers for managing poll(2)/ppoll(2) timeouts
2934 * Will remember the earliest deadline and return it for use in poll/ppoll.
2937 /* Initialise a poll/ppoll timeout with an indefinite deadline */
2939 ptimeout_init(struct timespec
*pt
)
2942 * Deliberately invalid for ppoll(2).
2943 * Will be converted to NULL in ptimeout_get_tspec() later.
2949 /* Specify a poll/ppoll deadline of at most 'sec' seconds */
2951 ptimeout_deadline_sec(struct timespec
*pt
, long sec
)
2953 if (pt
->tv_sec
== -1 || pt
->tv_sec
>= sec
) {
2959 /* Specify a poll/ppoll deadline of at most 'p' (timespec) */
2961 ptimeout_deadline_tsp(struct timespec
*pt
, struct timespec
*p
)
2963 if (pt
->tv_sec
== -1 || timespeccmp(pt
, p
, >=))
2967 /* Specify a poll/ppoll deadline of at most 'ms' milliseconds */
2969 ptimeout_deadline_ms(struct timespec
*pt
, long ms
)
2973 p
.tv_sec
= ms
/ 1000;
2974 p
.tv_nsec
= (ms
% 1000) * 1000000;
2975 ptimeout_deadline_tsp(pt
, &p
);
2978 /* Specify a poll/ppoll deadline at wall clock monotime 'when' (timespec) */
2980 ptimeout_deadline_monotime_tsp(struct timespec
*pt
, struct timespec
*when
)
2982 struct timespec now
, t
;
2986 if (timespeccmp(&now
, when
, >=)) {
2987 /* 'when' is now or in the past. Timeout ASAP */
2991 timespecsub(when
, &now
, &t
);
2992 ptimeout_deadline_tsp(pt
, &t
);
2996 /* Specify a poll/ppoll deadline at wall clock monotime 'when' */
2998 ptimeout_deadline_monotime(struct timespec
*pt
, time_t when
)
3004 ptimeout_deadline_monotime_tsp(pt
, &t
);
3007 /* Get a poll(2) timeout value in milliseconds */
3009 ptimeout_get_ms(struct timespec
*pt
)
3011 if (pt
->tv_sec
== -1)
3013 if (pt
->tv_sec
>= (INT_MAX
- (pt
->tv_nsec
/ 1000000)) / 1000)
3015 return (pt
->tv_sec
* 1000) + (pt
->tv_nsec
/ 1000000);
3018 /* Get a ppoll(2) timeout value as a timespec pointer */
3020 ptimeout_get_tsp(struct timespec
*pt
)
3022 return pt
->tv_sec
== -1 ? NULL
: pt
;
3025 /* Returns non-zero if a timeout has been set (i.e. is not indefinite) */
3027 ptimeout_isset(struct timespec
*pt
)
3029 return pt
->tv_sec
!= -1;
3033 * Returns zero if the library at 'path' contains symbol 's', nonzero
3037 lib_contains_symbol(const char *path
, const char *s
)
3043 memset(nl
, 0, sizeof(nl
));
3044 nl
[0].n_name
= xstrdup(s
);
3045 nl
[1].n_name
= NULL
;
3046 if ((r
= nlist(path
, nl
)) == -1) {
3047 error_f("nlist failed for %s", path
);
3050 if (r
!= 0 || nl
[0].n_value
== 0 || nl
[0].n_type
== 0) {
3051 error_f("library %s does not contain symbol %s", path
, s
);
3059 #else /* HAVE_NLIST_H */
3065 memset(&st
, 0, sizeof(st
));
3066 if ((fd
= open(path
, O_RDONLY
)) < 0) {
3067 error_f("open %s: %s", path
, strerror(errno
));
3070 if (fstat(fd
, &st
) != 0) {
3071 error_f("fstat %s: %s", path
, strerror(errno
));
3074 if (!S_ISREG(st
.st_mode
)) {
3075 error_f("%s is not a regular file", path
);
3078 if (st
.st_size
< 0 ||
3079 (size_t)st
.st_size
< strlen(s
) ||
3080 st
.st_size
>= INT_MAX
/2) {
3081 error_f("%s bad size %lld", path
, (long long)st
.st_size
);
3084 sz
= (size_t)st
.st_size
;
3085 if ((m
= mmap(NULL
, sz
, PROT_READ
, MAP_PRIVATE
, fd
, 0)) == MAP_FAILED
||
3087 error_f("mmap %s: %s", path
, strerror(errno
));
3090 if (memmem(m
, sz
, s
, strlen(s
)) == NULL
) {
3091 error_f("%s does not contain expected string %s", path
, s
);
3097 if (m
!= NULL
&& m
!= MAP_FAILED
)
3101 #endif /* HAVE_NLIST_H */
3105 signal_is_crash(int sig
)