1*ce74bacaSMatthew Dillon /* $OpenBSD: misc.c,v 1.113 2017/08/18 05:48:04 djm Exp $ */ 218de8d7fSPeter Avalos /* 318de8d7fSPeter Avalos * Copyright (c) 2000 Markus Friedl. All rights reserved. 418de8d7fSPeter Avalos * Copyright (c) 2005,2006 Damien Miller. All rights reserved. 518de8d7fSPeter Avalos * 618de8d7fSPeter Avalos * Redistribution and use in source and binary forms, with or without 718de8d7fSPeter Avalos * modification, are permitted provided that the following conditions 818de8d7fSPeter Avalos * are met: 918de8d7fSPeter Avalos * 1. Redistributions of source code must retain the above copyright 1018de8d7fSPeter Avalos * notice, this list of conditions and the following disclaimer. 1118de8d7fSPeter Avalos * 2. Redistributions in binary form must reproduce the above copyright 1218de8d7fSPeter Avalos * notice, this list of conditions and the following disclaimer in the 1318de8d7fSPeter Avalos * documentation and/or other materials provided with the distribution. 1418de8d7fSPeter Avalos * 1518de8d7fSPeter Avalos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1618de8d7fSPeter Avalos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1718de8d7fSPeter Avalos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1818de8d7fSPeter Avalos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1918de8d7fSPeter Avalos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2018de8d7fSPeter Avalos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2118de8d7fSPeter Avalos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2218de8d7fSPeter Avalos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2318de8d7fSPeter Avalos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2418de8d7fSPeter Avalos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2518de8d7fSPeter Avalos */ 2618de8d7fSPeter Avalos 2718de8d7fSPeter Avalos #include "includes.h" 2818de8d7fSPeter Avalos 2918de8d7fSPeter Avalos #include <sys/types.h> 3018de8d7fSPeter Avalos #include <sys/ioctl.h> 3118de8d7fSPeter Avalos #include <sys/socket.h> 32*ce74bacaSMatthew Dillon #include <sys/stat.h> 33e9778795SPeter Avalos #include <sys/time.h> 34*ce74bacaSMatthew Dillon #include <sys/wait.h> 3536e94dc5SPeter Avalos #include <sys/un.h> 3618de8d7fSPeter Avalos 37e9778795SPeter Avalos #include <limits.h> 38*ce74bacaSMatthew Dillon #ifdef HAVE_LIBGEN_H 39*ce74bacaSMatthew Dillon # include <libgen.h> 40*ce74bacaSMatthew Dillon #endif 41*ce74bacaSMatthew Dillon #include <signal.h> 4218de8d7fSPeter Avalos #include <stdarg.h> 4318de8d7fSPeter Avalos #include <stdio.h> 4418de8d7fSPeter Avalos #include <stdlib.h> 4518de8d7fSPeter Avalos #include <string.h> 469f304aafSPeter Avalos #include <time.h> 4718de8d7fSPeter Avalos #include <unistd.h> 4818de8d7fSPeter Avalos 4918de8d7fSPeter Avalos #include <netinet/in.h> 509f304aafSPeter Avalos #include <netinet/in_systm.h> 519f304aafSPeter Avalos #include <netinet/ip.h> 5218de8d7fSPeter Avalos #include <netinet/tcp.h> 5318de8d7fSPeter Avalos 5436e94dc5SPeter Avalos #include <ctype.h> 5518de8d7fSPeter Avalos #include <errno.h> 5618de8d7fSPeter Avalos #include <fcntl.h> 5718de8d7fSPeter Avalos #include <netdb.h> 5818de8d7fSPeter Avalos #ifdef HAVE_PATHS_H 5918de8d7fSPeter Avalos # include <paths.h> 6018de8d7fSPeter Avalos #include <pwd.h> 6118de8d7fSPeter Avalos #endif 6218de8d7fSPeter Avalos #ifdef SSH_TUN_OPENBSD 6318de8d7fSPeter Avalos #include <net/if.h> 6418de8d7fSPeter Avalos #endif 6518de8d7fSPeter Avalos 6618de8d7fSPeter Avalos #include "xmalloc.h" 6718de8d7fSPeter Avalos #include "misc.h" 6818de8d7fSPeter Avalos #include "log.h" 6918de8d7fSPeter Avalos #include "ssh.h" 70*ce74bacaSMatthew Dillon #include "sshbuf.h" 71*ce74bacaSMatthew Dillon #include "ssherr.h" 72*ce74bacaSMatthew Dillon #include "uidswap.h" 73*ce74bacaSMatthew Dillon #include "platform.h" 7418de8d7fSPeter Avalos 7518de8d7fSPeter Avalos /* remove newline at end of string */ 7618de8d7fSPeter Avalos char * 7718de8d7fSPeter Avalos chop(char *s) 7818de8d7fSPeter Avalos { 7918de8d7fSPeter Avalos char *t = s; 8018de8d7fSPeter Avalos while (*t) { 8118de8d7fSPeter Avalos if (*t == '\n' || *t == '\r') { 8218de8d7fSPeter Avalos *t = '\0'; 8318de8d7fSPeter Avalos return s; 8418de8d7fSPeter Avalos } 8518de8d7fSPeter Avalos t++; 8618de8d7fSPeter Avalos } 8718de8d7fSPeter Avalos return s; 8818de8d7fSPeter Avalos 8918de8d7fSPeter Avalos } 9018de8d7fSPeter Avalos 9118de8d7fSPeter Avalos /* set/unset filedescriptor to non-blocking */ 9218de8d7fSPeter Avalos int 9318de8d7fSPeter Avalos set_nonblock(int fd) 9418de8d7fSPeter Avalos { 9518de8d7fSPeter Avalos int val; 9618de8d7fSPeter Avalos 97e9778795SPeter Avalos val = fcntl(fd, F_GETFL); 9818de8d7fSPeter Avalos if (val < 0) { 99e9778795SPeter Avalos error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); 10018de8d7fSPeter Avalos return (-1); 10118de8d7fSPeter Avalos } 10218de8d7fSPeter Avalos if (val & O_NONBLOCK) { 10318de8d7fSPeter Avalos debug3("fd %d is O_NONBLOCK", fd); 10418de8d7fSPeter Avalos return (0); 10518de8d7fSPeter Avalos } 10618de8d7fSPeter Avalos debug2("fd %d setting O_NONBLOCK", fd); 10718de8d7fSPeter Avalos val |= O_NONBLOCK; 10818de8d7fSPeter Avalos if (fcntl(fd, F_SETFL, val) == -1) { 10918de8d7fSPeter Avalos debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, 11018de8d7fSPeter Avalos strerror(errno)); 11118de8d7fSPeter Avalos return (-1); 11218de8d7fSPeter Avalos } 11318de8d7fSPeter Avalos return (0); 11418de8d7fSPeter Avalos } 11518de8d7fSPeter Avalos 11618de8d7fSPeter Avalos int 11718de8d7fSPeter Avalos unset_nonblock(int fd) 11818de8d7fSPeter Avalos { 11918de8d7fSPeter Avalos int val; 12018de8d7fSPeter Avalos 121e9778795SPeter Avalos val = fcntl(fd, F_GETFL); 12218de8d7fSPeter Avalos if (val < 0) { 123e9778795SPeter Avalos error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); 12418de8d7fSPeter Avalos return (-1); 12518de8d7fSPeter Avalos } 12618de8d7fSPeter Avalos if (!(val & O_NONBLOCK)) { 12718de8d7fSPeter Avalos debug3("fd %d is not O_NONBLOCK", fd); 12818de8d7fSPeter Avalos return (0); 12918de8d7fSPeter Avalos } 13018de8d7fSPeter Avalos debug("fd %d clearing O_NONBLOCK", fd); 13118de8d7fSPeter Avalos val &= ~O_NONBLOCK; 13218de8d7fSPeter Avalos if (fcntl(fd, F_SETFL, val) == -1) { 13318de8d7fSPeter Avalos debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", 13418de8d7fSPeter Avalos fd, strerror(errno)); 13518de8d7fSPeter Avalos return (-1); 13618de8d7fSPeter Avalos } 13718de8d7fSPeter Avalos return (0); 13818de8d7fSPeter Avalos } 13918de8d7fSPeter Avalos 14018de8d7fSPeter Avalos const char * 14118de8d7fSPeter Avalos ssh_gai_strerror(int gaierr) 14218de8d7fSPeter Avalos { 14336e94dc5SPeter Avalos if (gaierr == EAI_SYSTEM && errno != 0) 14418de8d7fSPeter Avalos return strerror(errno); 14518de8d7fSPeter Avalos return gai_strerror(gaierr); 14618de8d7fSPeter Avalos } 14718de8d7fSPeter Avalos 14818de8d7fSPeter Avalos /* disable nagle on socket */ 14918de8d7fSPeter Avalos void 15018de8d7fSPeter Avalos set_nodelay(int fd) 15118de8d7fSPeter Avalos { 15218de8d7fSPeter Avalos int opt; 15318de8d7fSPeter Avalos socklen_t optlen; 15418de8d7fSPeter Avalos 15518de8d7fSPeter Avalos optlen = sizeof opt; 15618de8d7fSPeter Avalos if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { 15718de8d7fSPeter Avalos debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); 15818de8d7fSPeter Avalos return; 15918de8d7fSPeter Avalos } 16018de8d7fSPeter Avalos if (opt == 1) { 16118de8d7fSPeter Avalos debug2("fd %d is TCP_NODELAY", fd); 16218de8d7fSPeter Avalos return; 16318de8d7fSPeter Avalos } 16418de8d7fSPeter Avalos opt = 1; 16518de8d7fSPeter Avalos debug2("fd %d setting TCP_NODELAY", fd); 16618de8d7fSPeter Avalos if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) 16718de8d7fSPeter Avalos error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); 16818de8d7fSPeter Avalos } 16918de8d7fSPeter Avalos 17018de8d7fSPeter Avalos /* Characters considered whitespace in strsep calls. */ 17118de8d7fSPeter Avalos #define WHITESPACE " \t\r\n" 17218de8d7fSPeter Avalos #define QUOTE "\"" 17318de8d7fSPeter Avalos 17418de8d7fSPeter Avalos /* return next token in configuration line */ 17518de8d7fSPeter Avalos char * 17618de8d7fSPeter Avalos strdelim(char **s) 17718de8d7fSPeter Avalos { 17818de8d7fSPeter Avalos char *old; 17918de8d7fSPeter Avalos int wspace = 0; 18018de8d7fSPeter Avalos 18118de8d7fSPeter Avalos if (*s == NULL) 18218de8d7fSPeter Avalos return NULL; 18318de8d7fSPeter Avalos 18418de8d7fSPeter Avalos old = *s; 18518de8d7fSPeter Avalos 18618de8d7fSPeter Avalos *s = strpbrk(*s, WHITESPACE QUOTE "="); 18718de8d7fSPeter Avalos if (*s == NULL) 18818de8d7fSPeter Avalos return (old); 18918de8d7fSPeter Avalos 19018de8d7fSPeter Avalos if (*s[0] == '\"') { 19118de8d7fSPeter Avalos memmove(*s, *s + 1, strlen(*s)); /* move nul too */ 19218de8d7fSPeter Avalos /* Find matching quote */ 19318de8d7fSPeter Avalos if ((*s = strpbrk(*s, QUOTE)) == NULL) { 19418de8d7fSPeter Avalos return (NULL); /* no matching quote */ 19518de8d7fSPeter Avalos } else { 19618de8d7fSPeter Avalos *s[0] = '\0'; 197856ea928SPeter Avalos *s += strspn(*s + 1, WHITESPACE) + 1; 19818de8d7fSPeter Avalos return (old); 19918de8d7fSPeter Avalos } 20018de8d7fSPeter Avalos } 20118de8d7fSPeter Avalos 20218de8d7fSPeter Avalos /* Allow only one '=' to be skipped */ 20318de8d7fSPeter Avalos if (*s[0] == '=') 20418de8d7fSPeter Avalos wspace = 1; 20518de8d7fSPeter Avalos *s[0] = '\0'; 20618de8d7fSPeter Avalos 20718de8d7fSPeter Avalos /* Skip any extra whitespace after first token */ 20818de8d7fSPeter Avalos *s += strspn(*s + 1, WHITESPACE) + 1; 20918de8d7fSPeter Avalos if (*s[0] == '=' && !wspace) 21018de8d7fSPeter Avalos *s += strspn(*s + 1, WHITESPACE) + 1; 21118de8d7fSPeter Avalos 21218de8d7fSPeter Avalos return (old); 21318de8d7fSPeter Avalos } 21418de8d7fSPeter Avalos 21518de8d7fSPeter Avalos struct passwd * 21618de8d7fSPeter Avalos pwcopy(struct passwd *pw) 21718de8d7fSPeter Avalos { 21818de8d7fSPeter Avalos struct passwd *copy = xcalloc(1, sizeof(*copy)); 21918de8d7fSPeter Avalos 22018de8d7fSPeter Avalos copy->pw_name = xstrdup(pw->pw_name); 22118de8d7fSPeter Avalos copy->pw_passwd = xstrdup(pw->pw_passwd); 22236e94dc5SPeter Avalos #ifdef HAVE_STRUCT_PASSWD_PW_GECOS 22318de8d7fSPeter Avalos copy->pw_gecos = xstrdup(pw->pw_gecos); 22436e94dc5SPeter Avalos #endif 22518de8d7fSPeter Avalos copy->pw_uid = pw->pw_uid; 22618de8d7fSPeter Avalos copy->pw_gid = pw->pw_gid; 22736e94dc5SPeter Avalos #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE 22818de8d7fSPeter Avalos copy->pw_expire = pw->pw_expire; 22918de8d7fSPeter Avalos #endif 23036e94dc5SPeter Avalos #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE 23118de8d7fSPeter Avalos copy->pw_change = pw->pw_change; 23218de8d7fSPeter Avalos #endif 23336e94dc5SPeter Avalos #ifdef HAVE_STRUCT_PASSWD_PW_CLASS 23418de8d7fSPeter Avalos copy->pw_class = xstrdup(pw->pw_class); 23518de8d7fSPeter Avalos #endif 23618de8d7fSPeter Avalos copy->pw_dir = xstrdup(pw->pw_dir); 23718de8d7fSPeter Avalos copy->pw_shell = xstrdup(pw->pw_shell); 23818de8d7fSPeter Avalos return copy; 23918de8d7fSPeter Avalos } 24018de8d7fSPeter Avalos 24118de8d7fSPeter Avalos /* 24218de8d7fSPeter Avalos * Convert ASCII string to TCP/IP port number. 243cb5eb4f1SPeter Avalos * Port must be >=0 and <=65535. 244cb5eb4f1SPeter Avalos * Return -1 if invalid. 24518de8d7fSPeter Avalos */ 24618de8d7fSPeter Avalos int 24718de8d7fSPeter Avalos a2port(const char *s) 24818de8d7fSPeter Avalos { 249cb5eb4f1SPeter Avalos long long port; 250cb5eb4f1SPeter Avalos const char *errstr; 25118de8d7fSPeter Avalos 252cb5eb4f1SPeter Avalos port = strtonum(s, 0, 65535, &errstr); 253cb5eb4f1SPeter Avalos if (errstr != NULL) 254cb5eb4f1SPeter Avalos return -1; 255cb5eb4f1SPeter Avalos return (int)port; 25618de8d7fSPeter Avalos } 25718de8d7fSPeter Avalos 25818de8d7fSPeter Avalos int 25918de8d7fSPeter Avalos a2tun(const char *s, int *remote) 26018de8d7fSPeter Avalos { 26118de8d7fSPeter Avalos const char *errstr = NULL; 26218de8d7fSPeter Avalos char *sp, *ep; 26318de8d7fSPeter Avalos int tun; 26418de8d7fSPeter Avalos 26518de8d7fSPeter Avalos if (remote != NULL) { 26618de8d7fSPeter Avalos *remote = SSH_TUNID_ANY; 26718de8d7fSPeter Avalos sp = xstrdup(s); 26818de8d7fSPeter Avalos if ((ep = strchr(sp, ':')) == NULL) { 26936e94dc5SPeter Avalos free(sp); 27018de8d7fSPeter Avalos return (a2tun(s, NULL)); 27118de8d7fSPeter Avalos } 27218de8d7fSPeter Avalos ep[0] = '\0'; ep++; 27318de8d7fSPeter Avalos *remote = a2tun(ep, NULL); 27418de8d7fSPeter Avalos tun = a2tun(sp, NULL); 27536e94dc5SPeter Avalos free(sp); 27618de8d7fSPeter Avalos return (*remote == SSH_TUNID_ERR ? *remote : tun); 27718de8d7fSPeter Avalos } 27818de8d7fSPeter Avalos 27918de8d7fSPeter Avalos if (strcasecmp(s, "any") == 0) 28018de8d7fSPeter Avalos return (SSH_TUNID_ANY); 28118de8d7fSPeter Avalos 28218de8d7fSPeter Avalos tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); 28318de8d7fSPeter Avalos if (errstr != NULL) 28418de8d7fSPeter Avalos return (SSH_TUNID_ERR); 28518de8d7fSPeter Avalos 28618de8d7fSPeter Avalos return (tun); 28718de8d7fSPeter Avalos } 28818de8d7fSPeter Avalos 28918de8d7fSPeter Avalos #define SECONDS 1 29018de8d7fSPeter Avalos #define MINUTES (SECONDS * 60) 29118de8d7fSPeter Avalos #define HOURS (MINUTES * 60) 29218de8d7fSPeter Avalos #define DAYS (HOURS * 24) 29318de8d7fSPeter Avalos #define WEEKS (DAYS * 7) 29418de8d7fSPeter Avalos 29518de8d7fSPeter Avalos /* 29618de8d7fSPeter Avalos * Convert a time string into seconds; format is 29718de8d7fSPeter Avalos * a sequence of: 29818de8d7fSPeter Avalos * time[qualifier] 29918de8d7fSPeter Avalos * 30018de8d7fSPeter Avalos * Valid time qualifiers are: 30118de8d7fSPeter Avalos * <none> seconds 30218de8d7fSPeter Avalos * s|S seconds 30318de8d7fSPeter Avalos * m|M minutes 30418de8d7fSPeter Avalos * h|H hours 30518de8d7fSPeter Avalos * d|D days 30618de8d7fSPeter Avalos * w|W weeks 30718de8d7fSPeter Avalos * 30818de8d7fSPeter Avalos * Examples: 30918de8d7fSPeter Avalos * 90m 90 minutes 31018de8d7fSPeter Avalos * 1h30m 90 minutes 31118de8d7fSPeter Avalos * 2d 2 days 31218de8d7fSPeter Avalos * 1w 1 week 31318de8d7fSPeter Avalos * 31418de8d7fSPeter Avalos * Return -1 if time string is invalid. 31518de8d7fSPeter Avalos */ 31618de8d7fSPeter Avalos long 31718de8d7fSPeter Avalos convtime(const char *s) 31818de8d7fSPeter Avalos { 319*ce74bacaSMatthew Dillon long total, secs, multiplier = 1; 32018de8d7fSPeter Avalos const char *p; 32118de8d7fSPeter Avalos char *endp; 32218de8d7fSPeter Avalos 32318de8d7fSPeter Avalos errno = 0; 32418de8d7fSPeter Avalos total = 0; 32518de8d7fSPeter Avalos p = s; 32618de8d7fSPeter Avalos 32718de8d7fSPeter Avalos if (p == NULL || *p == '\0') 32818de8d7fSPeter Avalos return -1; 32918de8d7fSPeter Avalos 33018de8d7fSPeter Avalos while (*p) { 33118de8d7fSPeter Avalos secs = strtol(p, &endp, 10); 33218de8d7fSPeter Avalos if (p == endp || 33318de8d7fSPeter Avalos (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 33418de8d7fSPeter Avalos secs < 0) 33518de8d7fSPeter Avalos return -1; 33618de8d7fSPeter Avalos 33718de8d7fSPeter Avalos switch (*endp++) { 33818de8d7fSPeter Avalos case '\0': 33918de8d7fSPeter Avalos endp--; 34018de8d7fSPeter Avalos break; 34118de8d7fSPeter Avalos case 's': 34218de8d7fSPeter Avalos case 'S': 34318de8d7fSPeter Avalos break; 34418de8d7fSPeter Avalos case 'm': 34518de8d7fSPeter Avalos case 'M': 346*ce74bacaSMatthew Dillon multiplier = MINUTES; 34718de8d7fSPeter Avalos break; 34818de8d7fSPeter Avalos case 'h': 34918de8d7fSPeter Avalos case 'H': 350*ce74bacaSMatthew Dillon multiplier = HOURS; 35118de8d7fSPeter Avalos break; 35218de8d7fSPeter Avalos case 'd': 35318de8d7fSPeter Avalos case 'D': 354*ce74bacaSMatthew Dillon multiplier = DAYS; 35518de8d7fSPeter Avalos break; 35618de8d7fSPeter Avalos case 'w': 35718de8d7fSPeter Avalos case 'W': 358*ce74bacaSMatthew Dillon multiplier = WEEKS; 35918de8d7fSPeter Avalos break; 36018de8d7fSPeter Avalos default: 36118de8d7fSPeter Avalos return -1; 36218de8d7fSPeter Avalos } 363*ce74bacaSMatthew Dillon if (secs >= LONG_MAX / multiplier) 364*ce74bacaSMatthew Dillon return -1; 365*ce74bacaSMatthew Dillon secs *= multiplier; 366*ce74bacaSMatthew Dillon if (total >= LONG_MAX - secs) 367*ce74bacaSMatthew Dillon return -1; 36818de8d7fSPeter Avalos total += secs; 36918de8d7fSPeter Avalos if (total < 0) 37018de8d7fSPeter Avalos return -1; 37118de8d7fSPeter Avalos p = endp; 37218de8d7fSPeter Avalos } 37318de8d7fSPeter Avalos 37418de8d7fSPeter Avalos return total; 37518de8d7fSPeter Avalos } 37618de8d7fSPeter Avalos 37718de8d7fSPeter Avalos /* 37818de8d7fSPeter Avalos * Returns a standardized host+port identifier string. 37918de8d7fSPeter Avalos * Caller must free returned string. 38018de8d7fSPeter Avalos */ 38118de8d7fSPeter Avalos char * 38218de8d7fSPeter Avalos put_host_port(const char *host, u_short port) 38318de8d7fSPeter Avalos { 38418de8d7fSPeter Avalos char *hoststr; 38518de8d7fSPeter Avalos 38618de8d7fSPeter Avalos if (port == 0 || port == SSH_DEFAULT_PORT) 38718de8d7fSPeter Avalos return(xstrdup(host)); 38818de8d7fSPeter Avalos if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0) 38918de8d7fSPeter Avalos fatal("put_host_port: asprintf: %s", strerror(errno)); 39018de8d7fSPeter Avalos debug3("put_host_port: %s", hoststr); 39118de8d7fSPeter Avalos return hoststr; 39218de8d7fSPeter Avalos } 39318de8d7fSPeter Avalos 39418de8d7fSPeter Avalos /* 39518de8d7fSPeter Avalos * Search for next delimiter between hostnames/addresses and ports. 39618de8d7fSPeter Avalos * Argument may be modified (for termination). 39718de8d7fSPeter Avalos * Returns *cp if parsing succeeds. 39818de8d7fSPeter Avalos * *cp is set to the start of the next delimiter, if one was found. 39918de8d7fSPeter Avalos * If this is the last field, *cp is set to NULL. 40018de8d7fSPeter Avalos */ 40118de8d7fSPeter Avalos char * 40218de8d7fSPeter Avalos hpdelim(char **cp) 40318de8d7fSPeter Avalos { 40418de8d7fSPeter Avalos char *s, *old; 40518de8d7fSPeter Avalos 40618de8d7fSPeter Avalos if (cp == NULL || *cp == NULL) 40718de8d7fSPeter Avalos return NULL; 40818de8d7fSPeter Avalos 40918de8d7fSPeter Avalos old = s = *cp; 41018de8d7fSPeter Avalos if (*s == '[') { 41118de8d7fSPeter Avalos if ((s = strchr(s, ']')) == NULL) 41218de8d7fSPeter Avalos return NULL; 41318de8d7fSPeter Avalos else 41418de8d7fSPeter Avalos s++; 41518de8d7fSPeter Avalos } else if ((s = strpbrk(s, ":/")) == NULL) 41618de8d7fSPeter Avalos s = *cp + strlen(*cp); /* skip to end (see first case below) */ 41718de8d7fSPeter Avalos 41818de8d7fSPeter Avalos switch (*s) { 41918de8d7fSPeter Avalos case '\0': 42018de8d7fSPeter Avalos *cp = NULL; /* no more fields*/ 42118de8d7fSPeter Avalos break; 42218de8d7fSPeter Avalos 42318de8d7fSPeter Avalos case ':': 42418de8d7fSPeter Avalos case '/': 42518de8d7fSPeter Avalos *s = '\0'; /* terminate */ 42618de8d7fSPeter Avalos *cp = s + 1; 42718de8d7fSPeter Avalos break; 42818de8d7fSPeter Avalos 42918de8d7fSPeter Avalos default: 43018de8d7fSPeter Avalos return NULL; 43118de8d7fSPeter Avalos } 43218de8d7fSPeter Avalos 43318de8d7fSPeter Avalos return old; 43418de8d7fSPeter Avalos } 43518de8d7fSPeter Avalos 43618de8d7fSPeter Avalos char * 43718de8d7fSPeter Avalos cleanhostname(char *host) 43818de8d7fSPeter Avalos { 43918de8d7fSPeter Avalos if (*host == '[' && host[strlen(host) - 1] == ']') { 44018de8d7fSPeter Avalos host[strlen(host) - 1] = '\0'; 44118de8d7fSPeter Avalos return (host + 1); 44218de8d7fSPeter Avalos } else 44318de8d7fSPeter Avalos return host; 44418de8d7fSPeter Avalos } 44518de8d7fSPeter Avalos 44618de8d7fSPeter Avalos char * 44718de8d7fSPeter Avalos colon(char *cp) 44818de8d7fSPeter Avalos { 44918de8d7fSPeter Avalos int flag = 0; 45018de8d7fSPeter Avalos 45118de8d7fSPeter Avalos if (*cp == ':') /* Leading colon is part of file name. */ 452856ea928SPeter Avalos return NULL; 45318de8d7fSPeter Avalos if (*cp == '[') 45418de8d7fSPeter Avalos flag = 1; 45518de8d7fSPeter Avalos 45618de8d7fSPeter Avalos for (; *cp; ++cp) { 45718de8d7fSPeter Avalos if (*cp == '@' && *(cp+1) == '[') 45818de8d7fSPeter Avalos flag = 1; 45918de8d7fSPeter Avalos if (*cp == ']' && *(cp+1) == ':' && flag) 46018de8d7fSPeter Avalos return (cp+1); 46118de8d7fSPeter Avalos if (*cp == ':' && !flag) 46218de8d7fSPeter Avalos return (cp); 46318de8d7fSPeter Avalos if (*cp == '/') 464856ea928SPeter Avalos return NULL; 46518de8d7fSPeter Avalos } 466856ea928SPeter Avalos return NULL; 46718de8d7fSPeter Avalos } 46818de8d7fSPeter Avalos 469e9778795SPeter Avalos /* 470e9778795SPeter Avalos * Parse a [user@]host[:port] string. 471e9778795SPeter Avalos * Caller must free returned user and host. 472e9778795SPeter Avalos * Any of the pointer return arguments may be NULL (useful for syntax checking). 473e9778795SPeter Avalos * If user was not specified then *userp will be set to NULL. 474e9778795SPeter Avalos * If port was not specified then *portp will be -1. 475e9778795SPeter Avalos * Returns 0 on success, -1 on failure. 476e9778795SPeter Avalos */ 477e9778795SPeter Avalos int 478e9778795SPeter Avalos parse_user_host_port(const char *s, char **userp, char **hostp, int *portp) 479e9778795SPeter Avalos { 480e9778795SPeter Avalos char *sdup, *cp, *tmp; 481e9778795SPeter Avalos char *user = NULL, *host = NULL; 482e9778795SPeter Avalos int port = -1, ret = -1; 483e9778795SPeter Avalos 484e9778795SPeter Avalos if (userp != NULL) 485e9778795SPeter Avalos *userp = NULL; 486e9778795SPeter Avalos if (hostp != NULL) 487e9778795SPeter Avalos *hostp = NULL; 488e9778795SPeter Avalos if (portp != NULL) 489e9778795SPeter Avalos *portp = -1; 490e9778795SPeter Avalos 491e9778795SPeter Avalos if ((sdup = tmp = strdup(s)) == NULL) 492e9778795SPeter Avalos return -1; 493e9778795SPeter Avalos /* Extract optional username */ 494e9778795SPeter Avalos if ((cp = strchr(tmp, '@')) != NULL) { 495e9778795SPeter Avalos *cp = '\0'; 496e9778795SPeter Avalos if (*tmp == '\0') 497e9778795SPeter Avalos goto out; 498e9778795SPeter Avalos if ((user = strdup(tmp)) == NULL) 499e9778795SPeter Avalos goto out; 500e9778795SPeter Avalos tmp = cp + 1; 501e9778795SPeter Avalos } 502e9778795SPeter Avalos /* Extract mandatory hostname */ 503e9778795SPeter Avalos if ((cp = hpdelim(&tmp)) == NULL || *cp == '\0') 504e9778795SPeter Avalos goto out; 505e9778795SPeter Avalos host = xstrdup(cleanhostname(cp)); 506e9778795SPeter Avalos /* Convert and verify optional port */ 507e9778795SPeter Avalos if (tmp != NULL && *tmp != '\0') { 508e9778795SPeter Avalos if ((port = a2port(tmp)) <= 0) 509e9778795SPeter Avalos goto out; 510e9778795SPeter Avalos } 511e9778795SPeter Avalos /* Success */ 512e9778795SPeter Avalos if (userp != NULL) { 513e9778795SPeter Avalos *userp = user; 514e9778795SPeter Avalos user = NULL; 515e9778795SPeter Avalos } 516e9778795SPeter Avalos if (hostp != NULL) { 517e9778795SPeter Avalos *hostp = host; 518e9778795SPeter Avalos host = NULL; 519e9778795SPeter Avalos } 520e9778795SPeter Avalos if (portp != NULL) 521e9778795SPeter Avalos *portp = port; 522e9778795SPeter Avalos ret = 0; 523e9778795SPeter Avalos out: 524e9778795SPeter Avalos free(sdup); 525e9778795SPeter Avalos free(user); 526e9778795SPeter Avalos free(host); 527e9778795SPeter Avalos return ret; 528e9778795SPeter Avalos } 529e9778795SPeter Avalos 53018de8d7fSPeter Avalos /* function to assist building execv() arguments */ 53118de8d7fSPeter Avalos void 53218de8d7fSPeter Avalos addargs(arglist *args, char *fmt, ...) 53318de8d7fSPeter Avalos { 53418de8d7fSPeter Avalos va_list ap; 53518de8d7fSPeter Avalos char *cp; 53618de8d7fSPeter Avalos u_int nalloc; 53718de8d7fSPeter Avalos int r; 53818de8d7fSPeter Avalos 53918de8d7fSPeter Avalos va_start(ap, fmt); 54018de8d7fSPeter Avalos r = vasprintf(&cp, fmt, ap); 54118de8d7fSPeter Avalos va_end(ap); 54218de8d7fSPeter Avalos if (r == -1) 54318de8d7fSPeter Avalos fatal("addargs: argument too long"); 54418de8d7fSPeter Avalos 54518de8d7fSPeter Avalos nalloc = args->nalloc; 54618de8d7fSPeter Avalos if (args->list == NULL) { 54718de8d7fSPeter Avalos nalloc = 32; 54818de8d7fSPeter Avalos args->num = 0; 54918de8d7fSPeter Avalos } else if (args->num+2 >= nalloc) 55018de8d7fSPeter Avalos nalloc *= 2; 55118de8d7fSPeter Avalos 552*ce74bacaSMatthew Dillon args->list = xrecallocarray(args->list, args->nalloc, nalloc, sizeof(char *)); 55318de8d7fSPeter Avalos args->nalloc = nalloc; 55418de8d7fSPeter Avalos args->list[args->num++] = cp; 55518de8d7fSPeter Avalos args->list[args->num] = NULL; 55618de8d7fSPeter Avalos } 55718de8d7fSPeter Avalos 55818de8d7fSPeter Avalos void 55918de8d7fSPeter Avalos replacearg(arglist *args, u_int which, char *fmt, ...) 56018de8d7fSPeter Avalos { 56118de8d7fSPeter Avalos va_list ap; 56218de8d7fSPeter Avalos char *cp; 56318de8d7fSPeter Avalos int r; 56418de8d7fSPeter Avalos 56518de8d7fSPeter Avalos va_start(ap, fmt); 56618de8d7fSPeter Avalos r = vasprintf(&cp, fmt, ap); 56718de8d7fSPeter Avalos va_end(ap); 56818de8d7fSPeter Avalos if (r == -1) 56918de8d7fSPeter Avalos fatal("replacearg: argument too long"); 57018de8d7fSPeter Avalos 57118de8d7fSPeter Avalos if (which >= args->num) 57218de8d7fSPeter Avalos fatal("replacearg: tried to replace invalid arg %d >= %d", 57318de8d7fSPeter Avalos which, args->num); 57436e94dc5SPeter Avalos free(args->list[which]); 57518de8d7fSPeter Avalos args->list[which] = cp; 57618de8d7fSPeter Avalos } 57718de8d7fSPeter Avalos 57818de8d7fSPeter Avalos void 57918de8d7fSPeter Avalos freeargs(arglist *args) 58018de8d7fSPeter Avalos { 58118de8d7fSPeter Avalos u_int i; 58218de8d7fSPeter Avalos 58318de8d7fSPeter Avalos if (args->list != NULL) { 58418de8d7fSPeter Avalos for (i = 0; i < args->num; i++) 58536e94dc5SPeter Avalos free(args->list[i]); 58636e94dc5SPeter Avalos free(args->list); 58718de8d7fSPeter Avalos args->nalloc = args->num = 0; 58818de8d7fSPeter Avalos args->list = NULL; 58918de8d7fSPeter Avalos } 59018de8d7fSPeter Avalos } 59118de8d7fSPeter Avalos 59218de8d7fSPeter Avalos /* 59318de8d7fSPeter Avalos * Expands tildes in the file name. Returns data allocated by xmalloc. 59418de8d7fSPeter Avalos * Warning: this calls getpw*. 59518de8d7fSPeter Avalos */ 59618de8d7fSPeter Avalos char * 59718de8d7fSPeter Avalos tilde_expand_filename(const char *filename, uid_t uid) 59818de8d7fSPeter Avalos { 59936e94dc5SPeter Avalos const char *path, *sep; 60036e94dc5SPeter Avalos char user[128], *ret; 60118de8d7fSPeter Avalos struct passwd *pw; 60218de8d7fSPeter Avalos u_int len, slash; 60318de8d7fSPeter Avalos 60418de8d7fSPeter Avalos if (*filename != '~') 60518de8d7fSPeter Avalos return (xstrdup(filename)); 60618de8d7fSPeter Avalos filename++; 60718de8d7fSPeter Avalos 60818de8d7fSPeter Avalos path = strchr(filename, '/'); 60918de8d7fSPeter Avalos if (path != NULL && path > filename) { /* ~user/path */ 61018de8d7fSPeter Avalos slash = path - filename; 61118de8d7fSPeter Avalos if (slash > sizeof(user) - 1) 61218de8d7fSPeter Avalos fatal("tilde_expand_filename: ~username too long"); 61318de8d7fSPeter Avalos memcpy(user, filename, slash); 61418de8d7fSPeter Avalos user[slash] = '\0'; 61518de8d7fSPeter Avalos if ((pw = getpwnam(user)) == NULL) 61618de8d7fSPeter Avalos fatal("tilde_expand_filename: No such user %s", user); 61718de8d7fSPeter Avalos } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ 61818de8d7fSPeter Avalos fatal("tilde_expand_filename: No such uid %ld", (long)uid); 61918de8d7fSPeter Avalos 62018de8d7fSPeter Avalos /* Make sure directory has a trailing '/' */ 62118de8d7fSPeter Avalos len = strlen(pw->pw_dir); 62236e94dc5SPeter Avalos if (len == 0 || pw->pw_dir[len - 1] != '/') 62336e94dc5SPeter Avalos sep = "/"; 62436e94dc5SPeter Avalos else 62536e94dc5SPeter Avalos sep = ""; 62618de8d7fSPeter Avalos 62718de8d7fSPeter Avalos /* Skip leading '/' from specified path */ 62818de8d7fSPeter Avalos if (path != NULL) 62918de8d7fSPeter Avalos filename = path + 1; 63036e94dc5SPeter Avalos 631e9778795SPeter Avalos if (xasprintf(&ret, "%s%s%s", pw->pw_dir, sep, filename) >= PATH_MAX) 63218de8d7fSPeter Avalos fatal("tilde_expand_filename: Path too long"); 63318de8d7fSPeter Avalos 63436e94dc5SPeter Avalos return (ret); 63518de8d7fSPeter Avalos } 63618de8d7fSPeter Avalos 63718de8d7fSPeter Avalos /* 63818de8d7fSPeter Avalos * Expand a string with a set of %[char] escapes. A number of escapes may be 63918de8d7fSPeter Avalos * specified as (char *escape_chars, char *replacement) pairs. The list must 64018de8d7fSPeter Avalos * be terminated by a NULL escape_char. Returns replaced string in memory 64118de8d7fSPeter Avalos * allocated by xmalloc. 64218de8d7fSPeter Avalos */ 64318de8d7fSPeter Avalos char * 64418de8d7fSPeter Avalos percent_expand(const char *string, ...) 64518de8d7fSPeter Avalos { 64618de8d7fSPeter Avalos #define EXPAND_MAX_KEYS 16 647856ea928SPeter Avalos u_int num_keys, i, j; 64818de8d7fSPeter Avalos struct { 64918de8d7fSPeter Avalos const char *key; 65018de8d7fSPeter Avalos const char *repl; 65118de8d7fSPeter Avalos } keys[EXPAND_MAX_KEYS]; 65218de8d7fSPeter Avalos char buf[4096]; 65318de8d7fSPeter Avalos va_list ap; 65418de8d7fSPeter Avalos 65518de8d7fSPeter Avalos /* Gather keys */ 65618de8d7fSPeter Avalos va_start(ap, string); 65718de8d7fSPeter Avalos for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { 65818de8d7fSPeter Avalos keys[num_keys].key = va_arg(ap, char *); 65918de8d7fSPeter Avalos if (keys[num_keys].key == NULL) 66018de8d7fSPeter Avalos break; 66118de8d7fSPeter Avalos keys[num_keys].repl = va_arg(ap, char *); 66218de8d7fSPeter Avalos if (keys[num_keys].repl == NULL) 663856ea928SPeter Avalos fatal("%s: NULL replacement", __func__); 66418de8d7fSPeter Avalos } 665856ea928SPeter Avalos if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) 666856ea928SPeter Avalos fatal("%s: too many keys", __func__); 66718de8d7fSPeter Avalos va_end(ap); 66818de8d7fSPeter Avalos 66918de8d7fSPeter Avalos /* Expand string */ 67018de8d7fSPeter Avalos *buf = '\0'; 67118de8d7fSPeter Avalos for (i = 0; *string != '\0'; string++) { 67218de8d7fSPeter Avalos if (*string != '%') { 67318de8d7fSPeter Avalos append: 67418de8d7fSPeter Avalos buf[i++] = *string; 67518de8d7fSPeter Avalos if (i >= sizeof(buf)) 676856ea928SPeter Avalos fatal("%s: string too long", __func__); 67718de8d7fSPeter Avalos buf[i] = '\0'; 67818de8d7fSPeter Avalos continue; 67918de8d7fSPeter Avalos } 68018de8d7fSPeter Avalos string++; 681856ea928SPeter Avalos /* %% case */ 68218de8d7fSPeter Avalos if (*string == '%') 68318de8d7fSPeter Avalos goto append; 684e9778795SPeter Avalos if (*string == '\0') 685e9778795SPeter Avalos fatal("%s: invalid format", __func__); 68618de8d7fSPeter Avalos for (j = 0; j < num_keys; j++) { 68718de8d7fSPeter Avalos if (strchr(keys[j].key, *string) != NULL) { 68818de8d7fSPeter Avalos i = strlcat(buf, keys[j].repl, sizeof(buf)); 68918de8d7fSPeter Avalos if (i >= sizeof(buf)) 690856ea928SPeter Avalos fatal("%s: string too long", __func__); 69118de8d7fSPeter Avalos break; 69218de8d7fSPeter Avalos } 69318de8d7fSPeter Avalos } 69418de8d7fSPeter Avalos if (j >= num_keys) 695856ea928SPeter Avalos fatal("%s: unknown key %%%c", __func__, *string); 69618de8d7fSPeter Avalos } 69718de8d7fSPeter Avalos return (xstrdup(buf)); 69818de8d7fSPeter Avalos #undef EXPAND_MAX_KEYS 69918de8d7fSPeter Avalos } 70018de8d7fSPeter Avalos 70118de8d7fSPeter Avalos /* 70218de8d7fSPeter Avalos * Read an entire line from a public key file into a static buffer, discarding 70318de8d7fSPeter Avalos * lines that exceed the buffer size. Returns 0 on success, -1 on failure. 70418de8d7fSPeter Avalos */ 70518de8d7fSPeter Avalos int 70618de8d7fSPeter Avalos read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, 70718de8d7fSPeter Avalos u_long *lineno) 70818de8d7fSPeter Avalos { 70918de8d7fSPeter Avalos while (fgets(buf, bufsz, f) != NULL) { 71018de8d7fSPeter Avalos if (buf[0] == '\0') 71118de8d7fSPeter Avalos continue; 71218de8d7fSPeter Avalos (*lineno)++; 71318de8d7fSPeter Avalos if (buf[strlen(buf) - 1] == '\n' || feof(f)) { 71418de8d7fSPeter Avalos return 0; 71518de8d7fSPeter Avalos } else { 71618de8d7fSPeter Avalos debug("%s: %s line %lu exceeds size limit", __func__, 71718de8d7fSPeter Avalos filename, *lineno); 71818de8d7fSPeter Avalos /* discard remainder of line */ 71918de8d7fSPeter Avalos while (fgetc(f) != '\n' && !feof(f)) 72018de8d7fSPeter Avalos ; /* nothing */ 72118de8d7fSPeter Avalos } 72218de8d7fSPeter Avalos } 72318de8d7fSPeter Avalos return -1; 72418de8d7fSPeter Avalos } 72518de8d7fSPeter Avalos 72618de8d7fSPeter Avalos int 72718de8d7fSPeter Avalos tun_open(int tun, int mode) 72818de8d7fSPeter Avalos { 72918de8d7fSPeter Avalos #if defined(CUSTOM_SYS_TUN_OPEN) 73018de8d7fSPeter Avalos return (sys_tun_open(tun, mode)); 73118de8d7fSPeter Avalos #elif defined(SSH_TUN_OPENBSD) 73218de8d7fSPeter Avalos struct ifreq ifr; 73318de8d7fSPeter Avalos char name[100]; 73418de8d7fSPeter Avalos int fd = -1, sock; 735e9778795SPeter Avalos const char *tunbase = "tun"; 736e9778795SPeter Avalos 737e9778795SPeter Avalos if (mode == SSH_TUNMODE_ETHERNET) 738e9778795SPeter Avalos tunbase = "tap"; 73918de8d7fSPeter Avalos 74018de8d7fSPeter Avalos /* Open the tunnel device */ 74118de8d7fSPeter Avalos if (tun <= SSH_TUNID_MAX) { 742e9778795SPeter Avalos snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); 74318de8d7fSPeter Avalos fd = open(name, O_RDWR); 74418de8d7fSPeter Avalos } else if (tun == SSH_TUNID_ANY) { 74518de8d7fSPeter Avalos for (tun = 100; tun >= 0; tun--) { 746e9778795SPeter Avalos snprintf(name, sizeof(name), "/dev/%s%d", 747e9778795SPeter Avalos tunbase, tun); 74818de8d7fSPeter Avalos if ((fd = open(name, O_RDWR)) >= 0) 74918de8d7fSPeter Avalos break; 75018de8d7fSPeter Avalos } 75118de8d7fSPeter Avalos } else { 75218de8d7fSPeter Avalos debug("%s: invalid tunnel %u", __func__, tun); 753e9778795SPeter Avalos return -1; 75418de8d7fSPeter Avalos } 75518de8d7fSPeter Avalos 75618de8d7fSPeter Avalos if (fd < 0) { 757e9778795SPeter Avalos debug("%s: %s open: %s", __func__, name, strerror(errno)); 758e9778795SPeter Avalos return -1; 75918de8d7fSPeter Avalos } 76018de8d7fSPeter Avalos 76118de8d7fSPeter Avalos debug("%s: %s mode %d fd %d", __func__, name, mode, fd); 76218de8d7fSPeter Avalos 763e9778795SPeter Avalos /* Bring interface up if it is not already */ 764e9778795SPeter Avalos snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun); 76518de8d7fSPeter Avalos if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 76618de8d7fSPeter Avalos goto failed; 76718de8d7fSPeter Avalos 768e9778795SPeter Avalos if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) { 769e9778795SPeter Avalos debug("%s: get interface %s flags: %s", __func__, 770e9778795SPeter Avalos ifr.ifr_name, strerror(errno)); 77118de8d7fSPeter Avalos goto failed; 772e9778795SPeter Avalos } 77318de8d7fSPeter Avalos 774e9778795SPeter Avalos if (!(ifr.ifr_flags & IFF_UP)) { 77518de8d7fSPeter Avalos ifr.ifr_flags |= IFF_UP; 776e9778795SPeter Avalos if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) { 777e9778795SPeter Avalos debug("%s: activate interface %s: %s", __func__, 778e9778795SPeter Avalos ifr.ifr_name, strerror(errno)); 77918de8d7fSPeter Avalos goto failed; 780e9778795SPeter Avalos } 781e9778795SPeter Avalos } 78218de8d7fSPeter Avalos 78318de8d7fSPeter Avalos close(sock); 784e9778795SPeter Avalos return fd; 78518de8d7fSPeter Avalos 78618de8d7fSPeter Avalos failed: 78718de8d7fSPeter Avalos if (fd >= 0) 78818de8d7fSPeter Avalos close(fd); 78918de8d7fSPeter Avalos if (sock >= 0) 79018de8d7fSPeter Avalos close(sock); 791e9778795SPeter Avalos return -1; 79218de8d7fSPeter Avalos #else 79318de8d7fSPeter Avalos error("Tunnel interfaces are not supported on this platform"); 79418de8d7fSPeter Avalos return (-1); 79518de8d7fSPeter Avalos #endif 79618de8d7fSPeter Avalos } 79718de8d7fSPeter Avalos 79818de8d7fSPeter Avalos void 79918de8d7fSPeter Avalos sanitise_stdfd(void) 80018de8d7fSPeter Avalos { 80118de8d7fSPeter Avalos int nullfd, dupfd; 80218de8d7fSPeter Avalos 80318de8d7fSPeter Avalos if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 804cb5eb4f1SPeter Avalos fprintf(stderr, "Couldn't open /dev/null: %s\n", 805cb5eb4f1SPeter Avalos strerror(errno)); 80618de8d7fSPeter Avalos exit(1); 80718de8d7fSPeter Avalos } 808e9778795SPeter Avalos while (++dupfd <= STDERR_FILENO) { 809e9778795SPeter Avalos /* Only populate closed fds. */ 810e9778795SPeter Avalos if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) { 81118de8d7fSPeter Avalos if (dup2(nullfd, dupfd) == -1) { 812cb5eb4f1SPeter Avalos fprintf(stderr, "dup2: %s\n", strerror(errno)); 81318de8d7fSPeter Avalos exit(1); 81418de8d7fSPeter Avalos } 81518de8d7fSPeter Avalos } 816e9778795SPeter Avalos } 817e9778795SPeter Avalos if (nullfd > STDERR_FILENO) 81818de8d7fSPeter Avalos close(nullfd); 81918de8d7fSPeter Avalos } 82018de8d7fSPeter Avalos 82118de8d7fSPeter Avalos char * 82218de8d7fSPeter Avalos tohex(const void *vp, size_t l) 82318de8d7fSPeter Avalos { 82418de8d7fSPeter Avalos const u_char *p = (const u_char *)vp; 82518de8d7fSPeter Avalos char b[3], *r; 82618de8d7fSPeter Avalos size_t i, hl; 82718de8d7fSPeter Avalos 82818de8d7fSPeter Avalos if (l > 65536) 82918de8d7fSPeter Avalos return xstrdup("tohex: length > 65536"); 83018de8d7fSPeter Avalos 83118de8d7fSPeter Avalos hl = l * 2 + 1; 83218de8d7fSPeter Avalos r = xcalloc(1, hl); 83318de8d7fSPeter Avalos for (i = 0; i < l; i++) { 83418de8d7fSPeter Avalos snprintf(b, sizeof(b), "%02x", p[i]); 83518de8d7fSPeter Avalos strlcat(r, b, hl); 83618de8d7fSPeter Avalos } 83718de8d7fSPeter Avalos return (r); 83818de8d7fSPeter Avalos } 83918de8d7fSPeter Avalos 84018de8d7fSPeter Avalos u_int64_t 84118de8d7fSPeter Avalos get_u64(const void *vp) 84218de8d7fSPeter Avalos { 84318de8d7fSPeter Avalos const u_char *p = (const u_char *)vp; 84418de8d7fSPeter Avalos u_int64_t v; 84518de8d7fSPeter Avalos 84618de8d7fSPeter Avalos v = (u_int64_t)p[0] << 56; 84718de8d7fSPeter Avalos v |= (u_int64_t)p[1] << 48; 84818de8d7fSPeter Avalos v |= (u_int64_t)p[2] << 40; 84918de8d7fSPeter Avalos v |= (u_int64_t)p[3] << 32; 85018de8d7fSPeter Avalos v |= (u_int64_t)p[4] << 24; 85118de8d7fSPeter Avalos v |= (u_int64_t)p[5] << 16; 85218de8d7fSPeter Avalos v |= (u_int64_t)p[6] << 8; 85318de8d7fSPeter Avalos v |= (u_int64_t)p[7]; 85418de8d7fSPeter Avalos 85518de8d7fSPeter Avalos return (v); 85618de8d7fSPeter Avalos } 85718de8d7fSPeter Avalos 85818de8d7fSPeter Avalos u_int32_t 85918de8d7fSPeter Avalos get_u32(const void *vp) 86018de8d7fSPeter Avalos { 86118de8d7fSPeter Avalos const u_char *p = (const u_char *)vp; 86218de8d7fSPeter Avalos u_int32_t v; 86318de8d7fSPeter Avalos 86418de8d7fSPeter Avalos v = (u_int32_t)p[0] << 24; 86518de8d7fSPeter Avalos v |= (u_int32_t)p[1] << 16; 86618de8d7fSPeter Avalos v |= (u_int32_t)p[2] << 8; 86718de8d7fSPeter Avalos v |= (u_int32_t)p[3]; 86818de8d7fSPeter Avalos 86918de8d7fSPeter Avalos return (v); 87018de8d7fSPeter Avalos } 87118de8d7fSPeter Avalos 87236e94dc5SPeter Avalos u_int32_t 87336e94dc5SPeter Avalos get_u32_le(const void *vp) 87436e94dc5SPeter Avalos { 87536e94dc5SPeter Avalos const u_char *p = (const u_char *)vp; 87636e94dc5SPeter Avalos u_int32_t v; 87736e94dc5SPeter Avalos 87836e94dc5SPeter Avalos v = (u_int32_t)p[0]; 87936e94dc5SPeter Avalos v |= (u_int32_t)p[1] << 8; 88036e94dc5SPeter Avalos v |= (u_int32_t)p[2] << 16; 88136e94dc5SPeter Avalos v |= (u_int32_t)p[3] << 24; 88236e94dc5SPeter Avalos 88336e94dc5SPeter Avalos return (v); 88436e94dc5SPeter Avalos } 88536e94dc5SPeter Avalos 88618de8d7fSPeter Avalos u_int16_t 88718de8d7fSPeter Avalos get_u16(const void *vp) 88818de8d7fSPeter Avalos { 88918de8d7fSPeter Avalos const u_char *p = (const u_char *)vp; 89018de8d7fSPeter Avalos u_int16_t v; 89118de8d7fSPeter Avalos 89218de8d7fSPeter Avalos v = (u_int16_t)p[0] << 8; 89318de8d7fSPeter Avalos v |= (u_int16_t)p[1]; 89418de8d7fSPeter Avalos 89518de8d7fSPeter Avalos return (v); 89618de8d7fSPeter Avalos } 89718de8d7fSPeter Avalos 89818de8d7fSPeter Avalos void 89918de8d7fSPeter Avalos put_u64(void *vp, u_int64_t v) 90018de8d7fSPeter Avalos { 90118de8d7fSPeter Avalos u_char *p = (u_char *)vp; 90218de8d7fSPeter Avalos 90318de8d7fSPeter Avalos p[0] = (u_char)(v >> 56) & 0xff; 90418de8d7fSPeter Avalos p[1] = (u_char)(v >> 48) & 0xff; 90518de8d7fSPeter Avalos p[2] = (u_char)(v >> 40) & 0xff; 90618de8d7fSPeter Avalos p[3] = (u_char)(v >> 32) & 0xff; 90718de8d7fSPeter Avalos p[4] = (u_char)(v >> 24) & 0xff; 90818de8d7fSPeter Avalos p[5] = (u_char)(v >> 16) & 0xff; 90918de8d7fSPeter Avalos p[6] = (u_char)(v >> 8) & 0xff; 91018de8d7fSPeter Avalos p[7] = (u_char)v & 0xff; 91118de8d7fSPeter Avalos } 91218de8d7fSPeter Avalos 91318de8d7fSPeter Avalos void 91418de8d7fSPeter Avalos put_u32(void *vp, u_int32_t v) 91518de8d7fSPeter Avalos { 91618de8d7fSPeter Avalos u_char *p = (u_char *)vp; 91718de8d7fSPeter Avalos 91818de8d7fSPeter Avalos p[0] = (u_char)(v >> 24) & 0xff; 91918de8d7fSPeter Avalos p[1] = (u_char)(v >> 16) & 0xff; 92018de8d7fSPeter Avalos p[2] = (u_char)(v >> 8) & 0xff; 92118de8d7fSPeter Avalos p[3] = (u_char)v & 0xff; 92218de8d7fSPeter Avalos } 92318de8d7fSPeter Avalos 92436e94dc5SPeter Avalos void 92536e94dc5SPeter Avalos put_u32_le(void *vp, u_int32_t v) 92636e94dc5SPeter Avalos { 92736e94dc5SPeter Avalos u_char *p = (u_char *)vp; 92836e94dc5SPeter Avalos 92936e94dc5SPeter Avalos p[0] = (u_char)v & 0xff; 93036e94dc5SPeter Avalos p[1] = (u_char)(v >> 8) & 0xff; 93136e94dc5SPeter Avalos p[2] = (u_char)(v >> 16) & 0xff; 93236e94dc5SPeter Avalos p[3] = (u_char)(v >> 24) & 0xff; 93336e94dc5SPeter Avalos } 93418de8d7fSPeter Avalos 93518de8d7fSPeter Avalos void 93618de8d7fSPeter Avalos put_u16(void *vp, u_int16_t v) 93718de8d7fSPeter Avalos { 93818de8d7fSPeter Avalos u_char *p = (u_char *)vp; 93918de8d7fSPeter Avalos 94018de8d7fSPeter Avalos p[0] = (u_char)(v >> 8) & 0xff; 94118de8d7fSPeter Avalos p[1] = (u_char)v & 0xff; 94218de8d7fSPeter Avalos } 94318de8d7fSPeter Avalos 94418de8d7fSPeter Avalos void 94518de8d7fSPeter Avalos ms_subtract_diff(struct timeval *start, int *ms) 94618de8d7fSPeter Avalos { 94718de8d7fSPeter Avalos struct timeval diff, finish; 94818de8d7fSPeter Avalos 94918de8d7fSPeter Avalos gettimeofday(&finish, NULL); 95018de8d7fSPeter Avalos timersub(&finish, start, &diff); 95118de8d7fSPeter Avalos *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); 95218de8d7fSPeter Avalos } 95318de8d7fSPeter Avalos 95418de8d7fSPeter Avalos void 95518de8d7fSPeter Avalos ms_to_timeval(struct timeval *tv, int ms) 95618de8d7fSPeter Avalos { 95718de8d7fSPeter Avalos if (ms < 0) 95818de8d7fSPeter Avalos ms = 0; 95918de8d7fSPeter Avalos tv->tv_sec = ms / 1000; 96018de8d7fSPeter Avalos tv->tv_usec = (ms % 1000) * 1000; 96118de8d7fSPeter Avalos } 96218de8d7fSPeter Avalos 96336e94dc5SPeter Avalos time_t 96436e94dc5SPeter Avalos monotime(void) 96536e94dc5SPeter Avalos { 96636e94dc5SPeter Avalos #if defined(HAVE_CLOCK_GETTIME) && \ 96736e94dc5SPeter Avalos (defined(CLOCK_MONOTONIC) || defined(CLOCK_BOOTTIME)) 96836e94dc5SPeter Avalos struct timespec ts; 96936e94dc5SPeter Avalos static int gettime_failed = 0; 97036e94dc5SPeter Avalos 97136e94dc5SPeter Avalos if (!gettime_failed) { 97236e94dc5SPeter Avalos #if defined(CLOCK_BOOTTIME) 97336e94dc5SPeter Avalos if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) 97436e94dc5SPeter Avalos return (ts.tv_sec); 97536e94dc5SPeter Avalos #endif 97636e94dc5SPeter Avalos #if defined(CLOCK_MONOTONIC) 97736e94dc5SPeter Avalos if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) 97836e94dc5SPeter Avalos return (ts.tv_sec); 97936e94dc5SPeter Avalos #endif 98036e94dc5SPeter Avalos debug3("clock_gettime: %s", strerror(errno)); 98136e94dc5SPeter Avalos gettime_failed = 1; 98236e94dc5SPeter Avalos } 98336e94dc5SPeter Avalos #endif /* HAVE_CLOCK_GETTIME && (CLOCK_MONOTONIC || CLOCK_BOOTTIME */ 98436e94dc5SPeter Avalos 98536e94dc5SPeter Avalos return time(NULL); 98636e94dc5SPeter Avalos } 98736e94dc5SPeter Avalos 988e9778795SPeter Avalos double 989e9778795SPeter Avalos monotime_double(void) 990e9778795SPeter Avalos { 991e9778795SPeter Avalos #if defined(HAVE_CLOCK_GETTIME) && \ 992e9778795SPeter Avalos (defined(CLOCK_MONOTONIC) || defined(CLOCK_BOOTTIME)) 993e9778795SPeter Avalos struct timespec ts; 994e9778795SPeter Avalos static int gettime_failed = 0; 995e9778795SPeter Avalos 996e9778795SPeter Avalos if (!gettime_failed) { 997e9778795SPeter Avalos #if defined(CLOCK_BOOTTIME) 998e9778795SPeter Avalos if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) 999e9778795SPeter Avalos return (ts.tv_sec + (double)ts.tv_nsec / 1000000000); 1000e9778795SPeter Avalos #endif 1001e9778795SPeter Avalos #if defined(CLOCK_MONOTONIC) 1002e9778795SPeter Avalos if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) 1003e9778795SPeter Avalos return (ts.tv_sec + (double)ts.tv_nsec / 1000000000); 1004e9778795SPeter Avalos #endif 1005e9778795SPeter Avalos debug3("clock_gettime: %s", strerror(errno)); 1006e9778795SPeter Avalos gettime_failed = 1; 1007e9778795SPeter Avalos } 1008e9778795SPeter Avalos #endif /* HAVE_CLOCK_GETTIME && (CLOCK_MONOTONIC || CLOCK_BOOTTIME */ 1009e9778795SPeter Avalos 1010e9778795SPeter Avalos return (double)time(NULL); 1011e9778795SPeter Avalos } 1012e9778795SPeter Avalos 10139f304aafSPeter Avalos void 10149f304aafSPeter Avalos bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen) 1015856ea928SPeter Avalos { 10169f304aafSPeter Avalos bw->buflen = buflen; 10179f304aafSPeter Avalos bw->rate = kbps; 10189f304aafSPeter Avalos bw->thresh = bw->rate; 10199f304aafSPeter Avalos bw->lamt = 0; 10209f304aafSPeter Avalos timerclear(&bw->bwstart); 10219f304aafSPeter Avalos timerclear(&bw->bwend); 1022856ea928SPeter Avalos } 10239f304aafSPeter Avalos 10249f304aafSPeter Avalos /* Callback from read/write loop to insert bandwidth-limiting delays */ 10259f304aafSPeter Avalos void 10269f304aafSPeter Avalos bandwidth_limit(struct bwlimit *bw, size_t read_len) 10279f304aafSPeter Avalos { 10289f304aafSPeter Avalos u_int64_t waitlen; 10299f304aafSPeter Avalos struct timespec ts, rm; 10309f304aafSPeter Avalos 10319f304aafSPeter Avalos if (!timerisset(&bw->bwstart)) { 10329f304aafSPeter Avalos gettimeofday(&bw->bwstart, NULL); 10339f304aafSPeter Avalos return; 10349f304aafSPeter Avalos } 10359f304aafSPeter Avalos 10369f304aafSPeter Avalos bw->lamt += read_len; 10379f304aafSPeter Avalos if (bw->lamt < bw->thresh) 10389f304aafSPeter Avalos return; 10399f304aafSPeter Avalos 10409f304aafSPeter Avalos gettimeofday(&bw->bwend, NULL); 10419f304aafSPeter Avalos timersub(&bw->bwend, &bw->bwstart, &bw->bwend); 10429f304aafSPeter Avalos if (!timerisset(&bw->bwend)) 10439f304aafSPeter Avalos return; 10449f304aafSPeter Avalos 10459f304aafSPeter Avalos bw->lamt *= 8; 10469f304aafSPeter Avalos waitlen = (double)1000000L * bw->lamt / bw->rate; 10479f304aafSPeter Avalos 10489f304aafSPeter Avalos bw->bwstart.tv_sec = waitlen / 1000000L; 10499f304aafSPeter Avalos bw->bwstart.tv_usec = waitlen % 1000000L; 10509f304aafSPeter Avalos 10519f304aafSPeter Avalos if (timercmp(&bw->bwstart, &bw->bwend, >)) { 10529f304aafSPeter Avalos timersub(&bw->bwstart, &bw->bwend, &bw->bwend); 10539f304aafSPeter Avalos 10549f304aafSPeter Avalos /* Adjust the wait time */ 10559f304aafSPeter Avalos if (bw->bwend.tv_sec) { 10569f304aafSPeter Avalos bw->thresh /= 2; 10579f304aafSPeter Avalos if (bw->thresh < bw->buflen / 4) 10589f304aafSPeter Avalos bw->thresh = bw->buflen / 4; 10599f304aafSPeter Avalos } else if (bw->bwend.tv_usec < 10000) { 10609f304aafSPeter Avalos bw->thresh *= 2; 10619f304aafSPeter Avalos if (bw->thresh > bw->buflen * 8) 10629f304aafSPeter Avalos bw->thresh = bw->buflen * 8; 10639f304aafSPeter Avalos } 10649f304aafSPeter Avalos 10659f304aafSPeter Avalos TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts); 10669f304aafSPeter Avalos while (nanosleep(&ts, &rm) == -1) { 10679f304aafSPeter Avalos if (errno != EINTR) 10689f304aafSPeter Avalos break; 10699f304aafSPeter Avalos ts = rm; 10709f304aafSPeter Avalos } 10719f304aafSPeter Avalos } 10729f304aafSPeter Avalos 10739f304aafSPeter Avalos bw->lamt = 0; 10749f304aafSPeter Avalos gettimeofday(&bw->bwstart, NULL); 10759f304aafSPeter Avalos } 10769f304aafSPeter Avalos 10779f304aafSPeter Avalos /* Make a template filename for mk[sd]temp() */ 10789f304aafSPeter Avalos void 10799f304aafSPeter Avalos mktemp_proto(char *s, size_t len) 10809f304aafSPeter Avalos { 10819f304aafSPeter Avalos const char *tmpdir; 10829f304aafSPeter Avalos int r; 10839f304aafSPeter Avalos 10849f304aafSPeter Avalos if ((tmpdir = getenv("TMPDIR")) != NULL) { 10859f304aafSPeter Avalos r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir); 10869f304aafSPeter Avalos if (r > 0 && (size_t)r < len) 10879f304aafSPeter Avalos return; 10889f304aafSPeter Avalos } 10899f304aafSPeter Avalos r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX"); 10909f304aafSPeter Avalos if (r < 0 || (size_t)r >= len) 10919f304aafSPeter Avalos fatal("%s: template string too short", __func__); 10929f304aafSPeter Avalos } 10939f304aafSPeter Avalos 10949f304aafSPeter Avalos static const struct { 10959f304aafSPeter Avalos const char *name; 10969f304aafSPeter Avalos int value; 10979f304aafSPeter Avalos } ipqos[] = { 1098*ce74bacaSMatthew Dillon { "none", INT_MAX }, /* can't use 0 here; that's CS0 */ 10999f304aafSPeter Avalos { "af11", IPTOS_DSCP_AF11 }, 11009f304aafSPeter Avalos { "af12", IPTOS_DSCP_AF12 }, 11019f304aafSPeter Avalos { "af13", IPTOS_DSCP_AF13 }, 110299e85e0dSPeter Avalos { "af21", IPTOS_DSCP_AF21 }, 11039f304aafSPeter Avalos { "af22", IPTOS_DSCP_AF22 }, 11049f304aafSPeter Avalos { "af23", IPTOS_DSCP_AF23 }, 11059f304aafSPeter Avalos { "af31", IPTOS_DSCP_AF31 }, 11069f304aafSPeter Avalos { "af32", IPTOS_DSCP_AF32 }, 11079f304aafSPeter Avalos { "af33", IPTOS_DSCP_AF33 }, 11089f304aafSPeter Avalos { "af41", IPTOS_DSCP_AF41 }, 11099f304aafSPeter Avalos { "af42", IPTOS_DSCP_AF42 }, 11109f304aafSPeter Avalos { "af43", IPTOS_DSCP_AF43 }, 11119f304aafSPeter Avalos { "cs0", IPTOS_DSCP_CS0 }, 11129f304aafSPeter Avalos { "cs1", IPTOS_DSCP_CS1 }, 11139f304aafSPeter Avalos { "cs2", IPTOS_DSCP_CS2 }, 11149f304aafSPeter Avalos { "cs3", IPTOS_DSCP_CS3 }, 11159f304aafSPeter Avalos { "cs4", IPTOS_DSCP_CS4 }, 11169f304aafSPeter Avalos { "cs5", IPTOS_DSCP_CS5 }, 11179f304aafSPeter Avalos { "cs6", IPTOS_DSCP_CS6 }, 11189f304aafSPeter Avalos { "cs7", IPTOS_DSCP_CS7 }, 11199f304aafSPeter Avalos { "ef", IPTOS_DSCP_EF }, 11209f304aafSPeter Avalos { "lowdelay", IPTOS_LOWDELAY }, 11219f304aafSPeter Avalos { "throughput", IPTOS_THROUGHPUT }, 11229f304aafSPeter Avalos { "reliability", IPTOS_RELIABILITY }, 11239f304aafSPeter Avalos { NULL, -1 } 11249f304aafSPeter Avalos }; 11259f304aafSPeter Avalos 11269f304aafSPeter Avalos int 11279f304aafSPeter Avalos parse_ipqos(const char *cp) 11289f304aafSPeter Avalos { 11299f304aafSPeter Avalos u_int i; 11309f304aafSPeter Avalos char *ep; 11319f304aafSPeter Avalos long val; 11329f304aafSPeter Avalos 11339f304aafSPeter Avalos if (cp == NULL) 11349f304aafSPeter Avalos return -1; 11359f304aafSPeter Avalos for (i = 0; ipqos[i].name != NULL; i++) { 11369f304aafSPeter Avalos if (strcasecmp(cp, ipqos[i].name) == 0) 11379f304aafSPeter Avalos return ipqos[i].value; 11389f304aafSPeter Avalos } 11399f304aafSPeter Avalos /* Try parsing as an integer */ 11409f304aafSPeter Avalos val = strtol(cp, &ep, 0); 11419f304aafSPeter Avalos if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255) 11429f304aafSPeter Avalos return -1; 11439f304aafSPeter Avalos return val; 11449f304aafSPeter Avalos } 11459f304aafSPeter Avalos 11461c188a7fSPeter Avalos const char * 11471c188a7fSPeter Avalos iptos2str(int iptos) 11481c188a7fSPeter Avalos { 11491c188a7fSPeter Avalos int i; 11501c188a7fSPeter Avalos static char iptos_str[sizeof "0xff"]; 11511c188a7fSPeter Avalos 11521c188a7fSPeter Avalos for (i = 0; ipqos[i].name != NULL; i++) { 11531c188a7fSPeter Avalos if (ipqos[i].value == iptos) 11541c188a7fSPeter Avalos return ipqos[i].name; 11551c188a7fSPeter Avalos } 11561c188a7fSPeter Avalos snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos); 11571c188a7fSPeter Avalos return iptos_str; 11581c188a7fSPeter Avalos } 115936e94dc5SPeter Avalos 116036e94dc5SPeter Avalos void 116136e94dc5SPeter Avalos lowercase(char *s) 116236e94dc5SPeter Avalos { 116336e94dc5SPeter Avalos for (; *s; s++) 116436e94dc5SPeter Avalos *s = tolower((u_char)*s); 116536e94dc5SPeter Avalos } 116636e94dc5SPeter Avalos 116736e94dc5SPeter Avalos int 116836e94dc5SPeter Avalos unix_listener(const char *path, int backlog, int unlink_first) 116936e94dc5SPeter Avalos { 117036e94dc5SPeter Avalos struct sockaddr_un sunaddr; 117136e94dc5SPeter Avalos int saved_errno, sock; 117236e94dc5SPeter Avalos 117336e94dc5SPeter Avalos memset(&sunaddr, 0, sizeof(sunaddr)); 117436e94dc5SPeter Avalos sunaddr.sun_family = AF_UNIX; 117536e94dc5SPeter Avalos if (strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) { 117636e94dc5SPeter Avalos error("%s: \"%s\" too long for Unix domain socket", __func__, 117736e94dc5SPeter Avalos path); 117836e94dc5SPeter Avalos errno = ENAMETOOLONG; 117936e94dc5SPeter Avalos return -1; 118036e94dc5SPeter Avalos } 118136e94dc5SPeter Avalos 118236e94dc5SPeter Avalos sock = socket(PF_UNIX, SOCK_STREAM, 0); 118336e94dc5SPeter Avalos if (sock < 0) { 118436e94dc5SPeter Avalos saved_errno = errno; 118536e94dc5SPeter Avalos error("socket: %.100s", strerror(errno)); 118636e94dc5SPeter Avalos errno = saved_errno; 118736e94dc5SPeter Avalos return -1; 118836e94dc5SPeter Avalos } 118936e94dc5SPeter Avalos if (unlink_first == 1) { 119036e94dc5SPeter Avalos if (unlink(path) != 0 && errno != ENOENT) 119136e94dc5SPeter Avalos error("unlink(%s): %.100s", path, strerror(errno)); 119236e94dc5SPeter Avalos } 119336e94dc5SPeter Avalos if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { 119436e94dc5SPeter Avalos saved_errno = errno; 119536e94dc5SPeter Avalos error("bind: %.100s", strerror(errno)); 119636e94dc5SPeter Avalos close(sock); 119736e94dc5SPeter Avalos error("%s: cannot bind to path: %s", __func__, path); 119836e94dc5SPeter Avalos errno = saved_errno; 119936e94dc5SPeter Avalos return -1; 120036e94dc5SPeter Avalos } 120136e94dc5SPeter Avalos if (listen(sock, backlog) < 0) { 120236e94dc5SPeter Avalos saved_errno = errno; 120336e94dc5SPeter Avalos error("listen: %.100s", strerror(errno)); 120436e94dc5SPeter Avalos close(sock); 120536e94dc5SPeter Avalos unlink(path); 120636e94dc5SPeter Avalos error("%s: cannot listen on path: %s", __func__, path); 120736e94dc5SPeter Avalos errno = saved_errno; 120836e94dc5SPeter Avalos return -1; 120936e94dc5SPeter Avalos } 121036e94dc5SPeter Avalos return sock; 121136e94dc5SPeter Avalos } 121236e94dc5SPeter Avalos 1213856ea928SPeter Avalos void 1214856ea928SPeter Avalos sock_set_v6only(int s) 1215856ea928SPeter Avalos { 1216e9778795SPeter Avalos #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) 1217856ea928SPeter Avalos int on = 1; 1218856ea928SPeter Avalos 1219856ea928SPeter Avalos debug3("%s: set socket %d IPV6_V6ONLY", __func__, s); 1220856ea928SPeter Avalos if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) 1221856ea928SPeter Avalos error("setsockopt IPV6_V6ONLY: %s", strerror(errno)); 1222856ea928SPeter Avalos #endif 1223856ea928SPeter Avalos } 1224e9778795SPeter Avalos 1225e9778795SPeter Avalos /* 1226e9778795SPeter Avalos * Compares two strings that maybe be NULL. Returns non-zero if strings 1227e9778795SPeter Avalos * are both NULL or are identical, returns zero otherwise. 1228e9778795SPeter Avalos */ 1229e9778795SPeter Avalos static int 1230e9778795SPeter Avalos strcmp_maybe_null(const char *a, const char *b) 1231e9778795SPeter Avalos { 1232e9778795SPeter Avalos if ((a == NULL && b != NULL) || (a != NULL && b == NULL)) 1233e9778795SPeter Avalos return 0; 1234e9778795SPeter Avalos if (a != NULL && strcmp(a, b) != 0) 1235e9778795SPeter Avalos return 0; 1236e9778795SPeter Avalos return 1; 1237e9778795SPeter Avalos } 1238e9778795SPeter Avalos 1239e9778795SPeter Avalos /* 1240e9778795SPeter Avalos * Compare two forwards, returning non-zero if they are identical or 1241e9778795SPeter Avalos * zero otherwise. 1242e9778795SPeter Avalos */ 1243e9778795SPeter Avalos int 1244e9778795SPeter Avalos forward_equals(const struct Forward *a, const struct Forward *b) 1245e9778795SPeter Avalos { 1246e9778795SPeter Avalos if (strcmp_maybe_null(a->listen_host, b->listen_host) == 0) 1247e9778795SPeter Avalos return 0; 1248e9778795SPeter Avalos if (a->listen_port != b->listen_port) 1249e9778795SPeter Avalos return 0; 1250e9778795SPeter Avalos if (strcmp_maybe_null(a->listen_path, b->listen_path) == 0) 1251e9778795SPeter Avalos return 0; 1252e9778795SPeter Avalos if (strcmp_maybe_null(a->connect_host, b->connect_host) == 0) 1253e9778795SPeter Avalos return 0; 1254e9778795SPeter Avalos if (a->connect_port != b->connect_port) 1255e9778795SPeter Avalos return 0; 1256e9778795SPeter Avalos if (strcmp_maybe_null(a->connect_path, b->connect_path) == 0) 1257e9778795SPeter Avalos return 0; 1258e9778795SPeter Avalos /* allocated_port and handle are not checked */ 1259e9778795SPeter Avalos return 1; 1260e9778795SPeter Avalos } 1261e9778795SPeter Avalos 1262*ce74bacaSMatthew Dillon /* returns 1 if bind to specified port by specified user is permitted */ 1263*ce74bacaSMatthew Dillon int 1264*ce74bacaSMatthew Dillon bind_permitted(int port, uid_t uid) 1265*ce74bacaSMatthew Dillon { 1266*ce74bacaSMatthew Dillon if (port < IPPORT_RESERVED && uid != 0) 1267*ce74bacaSMatthew Dillon return 0; 1268*ce74bacaSMatthew Dillon return 1; 1269*ce74bacaSMatthew Dillon } 1270*ce74bacaSMatthew Dillon 1271*ce74bacaSMatthew Dillon /* returns 1 if process is already daemonized, 0 otherwise */ 1272*ce74bacaSMatthew Dillon int 1273*ce74bacaSMatthew Dillon daemonized(void) 1274*ce74bacaSMatthew Dillon { 1275*ce74bacaSMatthew Dillon int fd; 1276*ce74bacaSMatthew Dillon 1277*ce74bacaSMatthew Dillon if ((fd = open(_PATH_TTY, O_RDONLY | O_NOCTTY)) >= 0) { 1278*ce74bacaSMatthew Dillon close(fd); 1279*ce74bacaSMatthew Dillon return 0; /* have controlling terminal */ 1280*ce74bacaSMatthew Dillon } 1281*ce74bacaSMatthew Dillon if (getppid() != 1) 1282*ce74bacaSMatthew Dillon return 0; /* parent is not init */ 1283*ce74bacaSMatthew Dillon if (getsid(0) != getpid()) 1284*ce74bacaSMatthew Dillon return 0; /* not session leader */ 1285*ce74bacaSMatthew Dillon debug3("already daemonized"); 1286*ce74bacaSMatthew Dillon return 1; 1287*ce74bacaSMatthew Dillon } 1288*ce74bacaSMatthew Dillon 1289*ce74bacaSMatthew Dillon 1290*ce74bacaSMatthew Dillon /* 1291*ce74bacaSMatthew Dillon * Splits 's' into an argument vector. Handles quoted string and basic 1292*ce74bacaSMatthew Dillon * escape characters (\\, \", \'). Caller must free the argument vector 1293*ce74bacaSMatthew Dillon * and its members. 1294*ce74bacaSMatthew Dillon */ 1295*ce74bacaSMatthew Dillon int 1296*ce74bacaSMatthew Dillon argv_split(const char *s, int *argcp, char ***argvp) 1297*ce74bacaSMatthew Dillon { 1298*ce74bacaSMatthew Dillon int r = SSH_ERR_INTERNAL_ERROR; 1299*ce74bacaSMatthew Dillon int argc = 0, quote, i, j; 1300*ce74bacaSMatthew Dillon char *arg, **argv = xcalloc(1, sizeof(*argv)); 1301*ce74bacaSMatthew Dillon 1302*ce74bacaSMatthew Dillon *argvp = NULL; 1303*ce74bacaSMatthew Dillon *argcp = 0; 1304*ce74bacaSMatthew Dillon 1305*ce74bacaSMatthew Dillon for (i = 0; s[i] != '\0'; i++) { 1306*ce74bacaSMatthew Dillon /* Skip leading whitespace */ 1307*ce74bacaSMatthew Dillon if (s[i] == ' ' || s[i] == '\t') 1308*ce74bacaSMatthew Dillon continue; 1309*ce74bacaSMatthew Dillon 1310*ce74bacaSMatthew Dillon /* Start of a token */ 1311*ce74bacaSMatthew Dillon quote = 0; 1312*ce74bacaSMatthew Dillon if (s[i] == '\\' && 1313*ce74bacaSMatthew Dillon (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\')) 1314*ce74bacaSMatthew Dillon i++; 1315*ce74bacaSMatthew Dillon else if (s[i] == '\'' || s[i] == '"') 1316*ce74bacaSMatthew Dillon quote = s[i++]; 1317*ce74bacaSMatthew Dillon 1318*ce74bacaSMatthew Dillon argv = xreallocarray(argv, (argc + 2), sizeof(*argv)); 1319*ce74bacaSMatthew Dillon arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1); 1320*ce74bacaSMatthew Dillon argv[argc] = NULL; 1321*ce74bacaSMatthew Dillon 1322*ce74bacaSMatthew Dillon /* Copy the token in, removing escapes */ 1323*ce74bacaSMatthew Dillon for (j = 0; s[i] != '\0'; i++) { 1324*ce74bacaSMatthew Dillon if (s[i] == '\\') { 1325*ce74bacaSMatthew Dillon if (s[i + 1] == '\'' || 1326*ce74bacaSMatthew Dillon s[i + 1] == '\"' || 1327*ce74bacaSMatthew Dillon s[i + 1] == '\\') { 1328*ce74bacaSMatthew Dillon i++; /* Skip '\' */ 1329*ce74bacaSMatthew Dillon arg[j++] = s[i]; 1330*ce74bacaSMatthew Dillon } else { 1331*ce74bacaSMatthew Dillon /* Unrecognised escape */ 1332*ce74bacaSMatthew Dillon arg[j++] = s[i]; 1333*ce74bacaSMatthew Dillon } 1334*ce74bacaSMatthew Dillon } else if (quote == 0 && (s[i] == ' ' || s[i] == '\t')) 1335*ce74bacaSMatthew Dillon break; /* done */ 1336*ce74bacaSMatthew Dillon else if (quote != 0 && s[i] == quote) 1337*ce74bacaSMatthew Dillon break; /* done */ 1338*ce74bacaSMatthew Dillon else 1339*ce74bacaSMatthew Dillon arg[j++] = s[i]; 1340*ce74bacaSMatthew Dillon } 1341*ce74bacaSMatthew Dillon if (s[i] == '\0') { 1342*ce74bacaSMatthew Dillon if (quote != 0) { 1343*ce74bacaSMatthew Dillon /* Ran out of string looking for close quote */ 1344*ce74bacaSMatthew Dillon r = SSH_ERR_INVALID_FORMAT; 1345*ce74bacaSMatthew Dillon goto out; 1346*ce74bacaSMatthew Dillon } 1347*ce74bacaSMatthew Dillon break; 1348*ce74bacaSMatthew Dillon } 1349*ce74bacaSMatthew Dillon } 1350*ce74bacaSMatthew Dillon /* Success */ 1351*ce74bacaSMatthew Dillon *argcp = argc; 1352*ce74bacaSMatthew Dillon *argvp = argv; 1353*ce74bacaSMatthew Dillon argc = 0; 1354*ce74bacaSMatthew Dillon argv = NULL; 1355*ce74bacaSMatthew Dillon r = 0; 1356*ce74bacaSMatthew Dillon out: 1357*ce74bacaSMatthew Dillon if (argc != 0 && argv != NULL) { 1358*ce74bacaSMatthew Dillon for (i = 0; i < argc; i++) 1359*ce74bacaSMatthew Dillon free(argv[i]); 1360*ce74bacaSMatthew Dillon free(argv); 1361*ce74bacaSMatthew Dillon } 1362*ce74bacaSMatthew Dillon return r; 1363*ce74bacaSMatthew Dillon } 1364*ce74bacaSMatthew Dillon 1365*ce74bacaSMatthew Dillon /* 1366*ce74bacaSMatthew Dillon * Reassemble an argument vector into a string, quoting and escaping as 1367*ce74bacaSMatthew Dillon * necessary. Caller must free returned string. 1368*ce74bacaSMatthew Dillon */ 1369*ce74bacaSMatthew Dillon char * 1370*ce74bacaSMatthew Dillon argv_assemble(int argc, char **argv) 1371*ce74bacaSMatthew Dillon { 1372*ce74bacaSMatthew Dillon int i, j, ws, r; 1373*ce74bacaSMatthew Dillon char c, *ret; 1374*ce74bacaSMatthew Dillon struct sshbuf *buf, *arg; 1375*ce74bacaSMatthew Dillon 1376*ce74bacaSMatthew Dillon if ((buf = sshbuf_new()) == NULL || (arg = sshbuf_new()) == NULL) 1377*ce74bacaSMatthew Dillon fatal("%s: sshbuf_new failed", __func__); 1378*ce74bacaSMatthew Dillon 1379*ce74bacaSMatthew Dillon for (i = 0; i < argc; i++) { 1380*ce74bacaSMatthew Dillon ws = 0; 1381*ce74bacaSMatthew Dillon sshbuf_reset(arg); 1382*ce74bacaSMatthew Dillon for (j = 0; argv[i][j] != '\0'; j++) { 1383*ce74bacaSMatthew Dillon r = 0; 1384*ce74bacaSMatthew Dillon c = argv[i][j]; 1385*ce74bacaSMatthew Dillon switch (c) { 1386*ce74bacaSMatthew Dillon case ' ': 1387*ce74bacaSMatthew Dillon case '\t': 1388*ce74bacaSMatthew Dillon ws = 1; 1389*ce74bacaSMatthew Dillon r = sshbuf_put_u8(arg, c); 1390*ce74bacaSMatthew Dillon break; 1391*ce74bacaSMatthew Dillon case '\\': 1392*ce74bacaSMatthew Dillon case '\'': 1393*ce74bacaSMatthew Dillon case '"': 1394*ce74bacaSMatthew Dillon if ((r = sshbuf_put_u8(arg, '\\')) != 0) 1395*ce74bacaSMatthew Dillon break; 1396*ce74bacaSMatthew Dillon /* FALLTHROUGH */ 1397*ce74bacaSMatthew Dillon default: 1398*ce74bacaSMatthew Dillon r = sshbuf_put_u8(arg, c); 1399*ce74bacaSMatthew Dillon break; 1400*ce74bacaSMatthew Dillon } 1401*ce74bacaSMatthew Dillon if (r != 0) 1402*ce74bacaSMatthew Dillon fatal("%s: sshbuf_put_u8: %s", 1403*ce74bacaSMatthew Dillon __func__, ssh_err(r)); 1404*ce74bacaSMatthew Dillon } 1405*ce74bacaSMatthew Dillon if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) || 1406*ce74bacaSMatthew Dillon (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) || 1407*ce74bacaSMatthew Dillon (r = sshbuf_putb(buf, arg)) != 0 || 1408*ce74bacaSMatthew Dillon (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0)) 1409*ce74bacaSMatthew Dillon fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1410*ce74bacaSMatthew Dillon } 1411*ce74bacaSMatthew Dillon if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL) 1412*ce74bacaSMatthew Dillon fatal("%s: malloc failed", __func__); 1413*ce74bacaSMatthew Dillon memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf)); 1414*ce74bacaSMatthew Dillon ret[sshbuf_len(buf)] = '\0'; 1415*ce74bacaSMatthew Dillon sshbuf_free(buf); 1416*ce74bacaSMatthew Dillon sshbuf_free(arg); 1417*ce74bacaSMatthew Dillon return ret; 1418*ce74bacaSMatthew Dillon } 1419*ce74bacaSMatthew Dillon 1420*ce74bacaSMatthew Dillon /* 1421*ce74bacaSMatthew Dillon * Runs command in a subprocess wuth a minimal environment. 1422*ce74bacaSMatthew Dillon * Returns pid on success, 0 on failure. 1423*ce74bacaSMatthew Dillon * The child stdout and stderr maybe captured, left attached or sent to 1424*ce74bacaSMatthew Dillon * /dev/null depending on the contents of flags. 1425*ce74bacaSMatthew Dillon * "tag" is prepended to log messages. 1426*ce74bacaSMatthew Dillon * NB. "command" is only used for logging; the actual command executed is 1427*ce74bacaSMatthew Dillon * av[0]. 1428*ce74bacaSMatthew Dillon */ 1429*ce74bacaSMatthew Dillon pid_t 1430*ce74bacaSMatthew Dillon subprocess(const char *tag, struct passwd *pw, const char *command, 1431*ce74bacaSMatthew Dillon int ac, char **av, FILE **child, u_int flags) 1432*ce74bacaSMatthew Dillon { 1433*ce74bacaSMatthew Dillon FILE *f = NULL; 1434*ce74bacaSMatthew Dillon struct stat st; 1435*ce74bacaSMatthew Dillon int fd, devnull, p[2], i; 1436*ce74bacaSMatthew Dillon pid_t pid; 1437*ce74bacaSMatthew Dillon char *cp, errmsg[512]; 1438*ce74bacaSMatthew Dillon u_int envsize; 1439*ce74bacaSMatthew Dillon char **child_env; 1440*ce74bacaSMatthew Dillon 1441*ce74bacaSMatthew Dillon if (child != NULL) 1442*ce74bacaSMatthew Dillon *child = NULL; 1443*ce74bacaSMatthew Dillon 1444*ce74bacaSMatthew Dillon debug3("%s: %s command \"%s\" running as %s (flags 0x%x)", __func__, 1445*ce74bacaSMatthew Dillon tag, command, pw->pw_name, flags); 1446*ce74bacaSMatthew Dillon 1447*ce74bacaSMatthew Dillon /* Check consistency */ 1448*ce74bacaSMatthew Dillon if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && 1449*ce74bacaSMatthew Dillon (flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) { 1450*ce74bacaSMatthew Dillon error("%s: inconsistent flags", __func__); 1451*ce74bacaSMatthew Dillon return 0; 1452*ce74bacaSMatthew Dillon } 1453*ce74bacaSMatthew Dillon if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) != (child == NULL)) { 1454*ce74bacaSMatthew Dillon error("%s: inconsistent flags/output", __func__); 1455*ce74bacaSMatthew Dillon return 0; 1456*ce74bacaSMatthew Dillon } 1457*ce74bacaSMatthew Dillon 1458*ce74bacaSMatthew Dillon /* 1459*ce74bacaSMatthew Dillon * If executing an explicit binary, then verify the it exists 1460*ce74bacaSMatthew Dillon * and appears safe-ish to execute 1461*ce74bacaSMatthew Dillon */ 1462*ce74bacaSMatthew Dillon if (*av[0] != '/') { 1463*ce74bacaSMatthew Dillon error("%s path is not absolute", tag); 1464*ce74bacaSMatthew Dillon return 0; 1465*ce74bacaSMatthew Dillon } 1466*ce74bacaSMatthew Dillon temporarily_use_uid(pw); 1467*ce74bacaSMatthew Dillon if (stat(av[0], &st) < 0) { 1468*ce74bacaSMatthew Dillon error("Could not stat %s \"%s\": %s", tag, 1469*ce74bacaSMatthew Dillon av[0], strerror(errno)); 1470*ce74bacaSMatthew Dillon restore_uid(); 1471*ce74bacaSMatthew Dillon return 0; 1472*ce74bacaSMatthew Dillon } 1473*ce74bacaSMatthew Dillon if (safe_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) { 1474*ce74bacaSMatthew Dillon error("Unsafe %s \"%s\": %s", tag, av[0], errmsg); 1475*ce74bacaSMatthew Dillon restore_uid(); 1476*ce74bacaSMatthew Dillon return 0; 1477*ce74bacaSMatthew Dillon } 1478*ce74bacaSMatthew Dillon /* Prepare to keep the child's stdout if requested */ 1479*ce74bacaSMatthew Dillon if (pipe(p) != 0) { 1480*ce74bacaSMatthew Dillon error("%s: pipe: %s", tag, strerror(errno)); 1481*ce74bacaSMatthew Dillon restore_uid(); 1482*ce74bacaSMatthew Dillon return 0; 1483*ce74bacaSMatthew Dillon } 1484*ce74bacaSMatthew Dillon restore_uid(); 1485*ce74bacaSMatthew Dillon 1486*ce74bacaSMatthew Dillon switch ((pid = fork())) { 1487*ce74bacaSMatthew Dillon case -1: /* error */ 1488*ce74bacaSMatthew Dillon error("%s: fork: %s", tag, strerror(errno)); 1489*ce74bacaSMatthew Dillon close(p[0]); 1490*ce74bacaSMatthew Dillon close(p[1]); 1491*ce74bacaSMatthew Dillon return 0; 1492*ce74bacaSMatthew Dillon case 0: /* child */ 1493*ce74bacaSMatthew Dillon /* Prepare a minimal environment for the child. */ 1494*ce74bacaSMatthew Dillon envsize = 5; 1495*ce74bacaSMatthew Dillon child_env = xcalloc(sizeof(*child_env), envsize); 1496*ce74bacaSMatthew Dillon child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH); 1497*ce74bacaSMatthew Dillon child_set_env(&child_env, &envsize, "USER", pw->pw_name); 1498*ce74bacaSMatthew Dillon child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name); 1499*ce74bacaSMatthew Dillon child_set_env(&child_env, &envsize, "HOME", pw->pw_dir); 1500*ce74bacaSMatthew Dillon if ((cp = getenv("LANG")) != NULL) 1501*ce74bacaSMatthew Dillon child_set_env(&child_env, &envsize, "LANG", cp); 1502*ce74bacaSMatthew Dillon 1503*ce74bacaSMatthew Dillon for (i = 0; i < NSIG; i++) 1504*ce74bacaSMatthew Dillon signal(i, SIG_DFL); 1505*ce74bacaSMatthew Dillon 1506*ce74bacaSMatthew Dillon if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { 1507*ce74bacaSMatthew Dillon error("%s: open %s: %s", tag, _PATH_DEVNULL, 1508*ce74bacaSMatthew Dillon strerror(errno)); 1509*ce74bacaSMatthew Dillon _exit(1); 1510*ce74bacaSMatthew Dillon } 1511*ce74bacaSMatthew Dillon if (dup2(devnull, STDIN_FILENO) == -1) { 1512*ce74bacaSMatthew Dillon error("%s: dup2: %s", tag, strerror(errno)); 1513*ce74bacaSMatthew Dillon _exit(1); 1514*ce74bacaSMatthew Dillon } 1515*ce74bacaSMatthew Dillon 1516*ce74bacaSMatthew Dillon /* Set up stdout as requested; leave stderr in place for now. */ 1517*ce74bacaSMatthew Dillon fd = -1; 1518*ce74bacaSMatthew Dillon if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) 1519*ce74bacaSMatthew Dillon fd = p[1]; 1520*ce74bacaSMatthew Dillon else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0) 1521*ce74bacaSMatthew Dillon fd = devnull; 1522*ce74bacaSMatthew Dillon if (fd != -1 && dup2(fd, STDOUT_FILENO) == -1) { 1523*ce74bacaSMatthew Dillon error("%s: dup2: %s", tag, strerror(errno)); 1524*ce74bacaSMatthew Dillon _exit(1); 1525*ce74bacaSMatthew Dillon } 1526*ce74bacaSMatthew Dillon closefrom(STDERR_FILENO + 1); 1527*ce74bacaSMatthew Dillon 1528*ce74bacaSMatthew Dillon /* Don't use permanently_set_uid() here to avoid fatal() */ 1529*ce74bacaSMatthew Dillon if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { 1530*ce74bacaSMatthew Dillon error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, 1531*ce74bacaSMatthew Dillon strerror(errno)); 1532*ce74bacaSMatthew Dillon _exit(1); 1533*ce74bacaSMatthew Dillon } 1534*ce74bacaSMatthew Dillon if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { 1535*ce74bacaSMatthew Dillon error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid, 1536*ce74bacaSMatthew Dillon strerror(errno)); 1537*ce74bacaSMatthew Dillon _exit(1); 1538*ce74bacaSMatthew Dillon } 1539*ce74bacaSMatthew Dillon /* stdin is pointed to /dev/null at this point */ 1540*ce74bacaSMatthew Dillon if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && 1541*ce74bacaSMatthew Dillon dup2(STDIN_FILENO, STDERR_FILENO) == -1) { 1542*ce74bacaSMatthew Dillon error("%s: dup2: %s", tag, strerror(errno)); 1543*ce74bacaSMatthew Dillon _exit(1); 1544*ce74bacaSMatthew Dillon } 1545*ce74bacaSMatthew Dillon 1546*ce74bacaSMatthew Dillon execve(av[0], av, child_env); 1547*ce74bacaSMatthew Dillon error("%s exec \"%s\": %s", tag, command, strerror(errno)); 1548*ce74bacaSMatthew Dillon _exit(127); 1549*ce74bacaSMatthew Dillon default: /* parent */ 1550*ce74bacaSMatthew Dillon break; 1551*ce74bacaSMatthew Dillon } 1552*ce74bacaSMatthew Dillon 1553*ce74bacaSMatthew Dillon close(p[1]); 1554*ce74bacaSMatthew Dillon if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) 1555*ce74bacaSMatthew Dillon close(p[0]); 1556*ce74bacaSMatthew Dillon else if ((f = fdopen(p[0], "r")) == NULL) { 1557*ce74bacaSMatthew Dillon error("%s: fdopen: %s", tag, strerror(errno)); 1558*ce74bacaSMatthew Dillon close(p[0]); 1559*ce74bacaSMatthew Dillon /* Don't leave zombie child */ 1560*ce74bacaSMatthew Dillon kill(pid, SIGTERM); 1561*ce74bacaSMatthew Dillon while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) 1562*ce74bacaSMatthew Dillon ; 1563*ce74bacaSMatthew Dillon return 0; 1564*ce74bacaSMatthew Dillon } 1565*ce74bacaSMatthew Dillon /* Success */ 1566*ce74bacaSMatthew Dillon debug3("%s: %s pid %ld", __func__, tag, (long)pid); 1567*ce74bacaSMatthew Dillon if (child != NULL) 1568*ce74bacaSMatthew Dillon *child = f; 1569*ce74bacaSMatthew Dillon return pid; 1570*ce74bacaSMatthew Dillon } 1571*ce74bacaSMatthew Dillon 1572*ce74bacaSMatthew Dillon /* Returns 0 if pid exited cleanly, non-zero otherwise */ 1573*ce74bacaSMatthew Dillon int 1574*ce74bacaSMatthew Dillon exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet) 1575*ce74bacaSMatthew Dillon { 1576*ce74bacaSMatthew Dillon int status; 1577*ce74bacaSMatthew Dillon 1578*ce74bacaSMatthew Dillon while (waitpid(pid, &status, 0) == -1) { 1579*ce74bacaSMatthew Dillon if (errno != EINTR) { 1580*ce74bacaSMatthew Dillon error("%s: waitpid: %s", tag, strerror(errno)); 1581*ce74bacaSMatthew Dillon return -1; 1582*ce74bacaSMatthew Dillon } 1583*ce74bacaSMatthew Dillon } 1584*ce74bacaSMatthew Dillon if (WIFSIGNALED(status)) { 1585*ce74bacaSMatthew Dillon error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status)); 1586*ce74bacaSMatthew Dillon return -1; 1587*ce74bacaSMatthew Dillon } else if (WEXITSTATUS(status) != 0) { 1588*ce74bacaSMatthew Dillon do_log2(quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO, 1589*ce74bacaSMatthew Dillon "%s %s failed, status %d", tag, cmd, WEXITSTATUS(status)); 1590*ce74bacaSMatthew Dillon return -1; 1591*ce74bacaSMatthew Dillon } 1592*ce74bacaSMatthew Dillon return 0; 1593*ce74bacaSMatthew Dillon } 1594*ce74bacaSMatthew Dillon 1595*ce74bacaSMatthew Dillon /* 1596*ce74bacaSMatthew Dillon * Check a given path for security. This is defined as all components 1597*ce74bacaSMatthew Dillon * of the path to the file must be owned by either the owner of 1598*ce74bacaSMatthew Dillon * of the file or root and no directories must be group or world writable. 1599*ce74bacaSMatthew Dillon * 1600*ce74bacaSMatthew Dillon * XXX Should any specific check be done for sym links ? 1601*ce74bacaSMatthew Dillon * 1602*ce74bacaSMatthew Dillon * Takes a file name, its stat information (preferably from fstat() to 1603*ce74bacaSMatthew Dillon * avoid races), the uid of the expected owner, their home directory and an 1604*ce74bacaSMatthew Dillon * error buffer plus max size as arguments. 1605*ce74bacaSMatthew Dillon * 1606*ce74bacaSMatthew Dillon * Returns 0 on success and -1 on failure 1607*ce74bacaSMatthew Dillon */ 1608*ce74bacaSMatthew Dillon int 1609*ce74bacaSMatthew Dillon safe_path(const char *name, struct stat *stp, const char *pw_dir, 1610*ce74bacaSMatthew Dillon uid_t uid, char *err, size_t errlen) 1611*ce74bacaSMatthew Dillon { 1612*ce74bacaSMatthew Dillon char buf[PATH_MAX], homedir[PATH_MAX]; 1613*ce74bacaSMatthew Dillon char *cp; 1614*ce74bacaSMatthew Dillon int comparehome = 0; 1615*ce74bacaSMatthew Dillon struct stat st; 1616*ce74bacaSMatthew Dillon 1617*ce74bacaSMatthew Dillon if (realpath(name, buf) == NULL) { 1618*ce74bacaSMatthew Dillon snprintf(err, errlen, "realpath %s failed: %s", name, 1619*ce74bacaSMatthew Dillon strerror(errno)); 1620*ce74bacaSMatthew Dillon return -1; 1621*ce74bacaSMatthew Dillon } 1622*ce74bacaSMatthew Dillon if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) 1623*ce74bacaSMatthew Dillon comparehome = 1; 1624*ce74bacaSMatthew Dillon 1625*ce74bacaSMatthew Dillon if (!S_ISREG(stp->st_mode)) { 1626*ce74bacaSMatthew Dillon snprintf(err, errlen, "%s is not a regular file", buf); 1627*ce74bacaSMatthew Dillon return -1; 1628*ce74bacaSMatthew Dillon } 1629*ce74bacaSMatthew Dillon if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 1630*ce74bacaSMatthew Dillon (stp->st_mode & 022) != 0) { 1631*ce74bacaSMatthew Dillon snprintf(err, errlen, "bad ownership or modes for file %s", 1632*ce74bacaSMatthew Dillon buf); 1633*ce74bacaSMatthew Dillon return -1; 1634*ce74bacaSMatthew Dillon } 1635*ce74bacaSMatthew Dillon 1636*ce74bacaSMatthew Dillon /* for each component of the canonical path, walking upwards */ 1637*ce74bacaSMatthew Dillon for (;;) { 1638*ce74bacaSMatthew Dillon if ((cp = dirname(buf)) == NULL) { 1639*ce74bacaSMatthew Dillon snprintf(err, errlen, "dirname() failed"); 1640*ce74bacaSMatthew Dillon return -1; 1641*ce74bacaSMatthew Dillon } 1642*ce74bacaSMatthew Dillon strlcpy(buf, cp, sizeof(buf)); 1643*ce74bacaSMatthew Dillon 1644*ce74bacaSMatthew Dillon if (stat(buf, &st) < 0 || 1645*ce74bacaSMatthew Dillon (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 1646*ce74bacaSMatthew Dillon (st.st_mode & 022) != 0) { 1647*ce74bacaSMatthew Dillon snprintf(err, errlen, 1648*ce74bacaSMatthew Dillon "bad ownership or modes for directory %s", buf); 1649*ce74bacaSMatthew Dillon return -1; 1650*ce74bacaSMatthew Dillon } 1651*ce74bacaSMatthew Dillon 1652*ce74bacaSMatthew Dillon /* If are past the homedir then we can stop */ 1653*ce74bacaSMatthew Dillon if (comparehome && strcmp(homedir, buf) == 0) 1654*ce74bacaSMatthew Dillon break; 1655*ce74bacaSMatthew Dillon 1656*ce74bacaSMatthew Dillon /* 1657*ce74bacaSMatthew Dillon * dirname should always complete with a "/" path, 1658*ce74bacaSMatthew Dillon * but we can be paranoid and check for "." too 1659*ce74bacaSMatthew Dillon */ 1660*ce74bacaSMatthew Dillon if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) 1661*ce74bacaSMatthew Dillon break; 1662*ce74bacaSMatthew Dillon } 1663*ce74bacaSMatthew Dillon return 0; 1664*ce74bacaSMatthew Dillon } 1665*ce74bacaSMatthew Dillon 1666*ce74bacaSMatthew Dillon /* 1667*ce74bacaSMatthew Dillon * Version of safe_path() that accepts an open file descriptor to 1668*ce74bacaSMatthew Dillon * avoid races. 1669*ce74bacaSMatthew Dillon * 1670*ce74bacaSMatthew Dillon * Returns 0 on success and -1 on failure 1671*ce74bacaSMatthew Dillon */ 1672*ce74bacaSMatthew Dillon int 1673*ce74bacaSMatthew Dillon safe_path_fd(int fd, const char *file, struct passwd *pw, 1674*ce74bacaSMatthew Dillon char *err, size_t errlen) 1675*ce74bacaSMatthew Dillon { 1676*ce74bacaSMatthew Dillon struct stat st; 1677*ce74bacaSMatthew Dillon 1678*ce74bacaSMatthew Dillon /* check the open file to avoid races */ 1679*ce74bacaSMatthew Dillon if (fstat(fd, &st) < 0) { 1680*ce74bacaSMatthew Dillon snprintf(err, errlen, "cannot stat file %s: %s", 1681*ce74bacaSMatthew Dillon file, strerror(errno)); 1682*ce74bacaSMatthew Dillon return -1; 1683*ce74bacaSMatthew Dillon } 1684*ce74bacaSMatthew Dillon return safe_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen); 1685*ce74bacaSMatthew Dillon } 1686*ce74bacaSMatthew Dillon 1687*ce74bacaSMatthew Dillon /* 1688*ce74bacaSMatthew Dillon * Sets the value of the given variable in the environment. If the variable 1689*ce74bacaSMatthew Dillon * already exists, its value is overridden. 1690*ce74bacaSMatthew Dillon */ 1691*ce74bacaSMatthew Dillon void 1692*ce74bacaSMatthew Dillon child_set_env(char ***envp, u_int *envsizep, const char *name, 1693*ce74bacaSMatthew Dillon const char *value) 1694*ce74bacaSMatthew Dillon { 1695*ce74bacaSMatthew Dillon char **env; 1696*ce74bacaSMatthew Dillon u_int envsize; 1697*ce74bacaSMatthew Dillon u_int i, namelen; 1698*ce74bacaSMatthew Dillon 1699*ce74bacaSMatthew Dillon if (strchr(name, '=') != NULL) { 1700*ce74bacaSMatthew Dillon error("Invalid environment variable \"%.100s\"", name); 1701*ce74bacaSMatthew Dillon return; 1702*ce74bacaSMatthew Dillon } 1703*ce74bacaSMatthew Dillon 1704*ce74bacaSMatthew Dillon /* 1705*ce74bacaSMatthew Dillon * If we're passed an uninitialized list, allocate a single null 1706*ce74bacaSMatthew Dillon * entry before continuing. 1707*ce74bacaSMatthew Dillon */ 1708*ce74bacaSMatthew Dillon if (*envp == NULL && *envsizep == 0) { 1709*ce74bacaSMatthew Dillon *envp = xmalloc(sizeof(char *)); 1710*ce74bacaSMatthew Dillon *envp[0] = NULL; 1711*ce74bacaSMatthew Dillon *envsizep = 1; 1712*ce74bacaSMatthew Dillon } 1713*ce74bacaSMatthew Dillon 1714*ce74bacaSMatthew Dillon /* 1715*ce74bacaSMatthew Dillon * Find the slot where the value should be stored. If the variable 1716*ce74bacaSMatthew Dillon * already exists, we reuse the slot; otherwise we append a new slot 1717*ce74bacaSMatthew Dillon * at the end of the array, expanding if necessary. 1718*ce74bacaSMatthew Dillon */ 1719*ce74bacaSMatthew Dillon env = *envp; 1720*ce74bacaSMatthew Dillon namelen = strlen(name); 1721*ce74bacaSMatthew Dillon for (i = 0; env[i]; i++) 1722*ce74bacaSMatthew Dillon if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') 1723*ce74bacaSMatthew Dillon break; 1724*ce74bacaSMatthew Dillon if (env[i]) { 1725*ce74bacaSMatthew Dillon /* Reuse the slot. */ 1726*ce74bacaSMatthew Dillon free(env[i]); 1727*ce74bacaSMatthew Dillon } else { 1728*ce74bacaSMatthew Dillon /* New variable. Expand if necessary. */ 1729*ce74bacaSMatthew Dillon envsize = *envsizep; 1730*ce74bacaSMatthew Dillon if (i >= envsize - 1) { 1731*ce74bacaSMatthew Dillon if (envsize >= 1000) 1732*ce74bacaSMatthew Dillon fatal("child_set_env: too many env vars"); 1733*ce74bacaSMatthew Dillon envsize += 50; 1734*ce74bacaSMatthew Dillon env = (*envp) = xreallocarray(env, envsize, sizeof(char *)); 1735*ce74bacaSMatthew Dillon *envsizep = envsize; 1736*ce74bacaSMatthew Dillon } 1737*ce74bacaSMatthew Dillon /* Need to set the NULL pointer at end of array beyond the new slot. */ 1738*ce74bacaSMatthew Dillon env[i + 1] = NULL; 1739*ce74bacaSMatthew Dillon } 1740*ce74bacaSMatthew Dillon 1741*ce74bacaSMatthew Dillon /* Allocate space and format the variable in the appropriate slot. */ 1742*ce74bacaSMatthew Dillon env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); 1743*ce74bacaSMatthew Dillon snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); 1744*ce74bacaSMatthew Dillon } 1745*ce74bacaSMatthew Dillon 1746