1*664f4763Szrj /* $OpenBSD: misc.c,v 1.137 2019/01/23 21:50:56 dtucker 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> 32ce74bacaSMatthew Dillon #include <sys/stat.h> 33e9778795SPeter Avalos #include <sys/time.h> 34ce74bacaSMatthew Dillon #include <sys/wait.h> 3536e94dc5SPeter Avalos #include <sys/un.h> 3618de8d7fSPeter Avalos 37e9778795SPeter Avalos #include <limits.h> 38ce74bacaSMatthew Dillon #ifdef HAVE_LIBGEN_H 39ce74bacaSMatthew Dillon # include <libgen.h> 40ce74bacaSMatthew Dillon #endif 41*664f4763Szrj #include <poll.h> 42ce74bacaSMatthew Dillon #include <signal.h> 4318de8d7fSPeter Avalos #include <stdarg.h> 4418de8d7fSPeter Avalos #include <stdio.h> 4518de8d7fSPeter Avalos #include <stdlib.h> 4618de8d7fSPeter Avalos #include <string.h> 479f304aafSPeter Avalos #include <time.h> 4818de8d7fSPeter Avalos #include <unistd.h> 4918de8d7fSPeter Avalos 5018de8d7fSPeter Avalos #include <netinet/in.h> 519f304aafSPeter Avalos #include <netinet/in_systm.h> 529f304aafSPeter Avalos #include <netinet/ip.h> 5318de8d7fSPeter Avalos #include <netinet/tcp.h> 54*664f4763Szrj #include <arpa/inet.h> 5518de8d7fSPeter Avalos 5636e94dc5SPeter Avalos #include <ctype.h> 5718de8d7fSPeter Avalos #include <errno.h> 5818de8d7fSPeter Avalos #include <fcntl.h> 5918de8d7fSPeter Avalos #include <netdb.h> 6018de8d7fSPeter Avalos #ifdef HAVE_PATHS_H 6118de8d7fSPeter Avalos # include <paths.h> 6218de8d7fSPeter Avalos #include <pwd.h> 6318de8d7fSPeter Avalos #endif 6418de8d7fSPeter Avalos #ifdef SSH_TUN_OPENBSD 6518de8d7fSPeter Avalos #include <net/if.h> 6618de8d7fSPeter Avalos #endif 6718de8d7fSPeter Avalos 6818de8d7fSPeter Avalos #include "xmalloc.h" 6918de8d7fSPeter Avalos #include "misc.h" 7018de8d7fSPeter Avalos #include "log.h" 7118de8d7fSPeter Avalos #include "ssh.h" 72ce74bacaSMatthew Dillon #include "sshbuf.h" 73ce74bacaSMatthew Dillon #include "ssherr.h" 74ce74bacaSMatthew Dillon #include "platform.h" 7518de8d7fSPeter Avalos 7618de8d7fSPeter Avalos /* remove newline at end of string */ 7718de8d7fSPeter Avalos char * 7818de8d7fSPeter Avalos chop(char *s) 7918de8d7fSPeter Avalos { 8018de8d7fSPeter Avalos char *t = s; 8118de8d7fSPeter Avalos while (*t) { 8218de8d7fSPeter Avalos if (*t == '\n' || *t == '\r') { 8318de8d7fSPeter Avalos *t = '\0'; 8418de8d7fSPeter Avalos return s; 8518de8d7fSPeter Avalos } 8618de8d7fSPeter Avalos t++; 8718de8d7fSPeter Avalos } 8818de8d7fSPeter Avalos return s; 8918de8d7fSPeter Avalos 9018de8d7fSPeter Avalos } 9118de8d7fSPeter Avalos 9218de8d7fSPeter Avalos /* set/unset filedescriptor to non-blocking */ 9318de8d7fSPeter Avalos int 9418de8d7fSPeter Avalos set_nonblock(int fd) 9518de8d7fSPeter Avalos { 9618de8d7fSPeter Avalos int val; 9718de8d7fSPeter Avalos 98e9778795SPeter Avalos val = fcntl(fd, F_GETFL); 9918de8d7fSPeter Avalos if (val < 0) { 100e9778795SPeter Avalos error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); 10118de8d7fSPeter Avalos return (-1); 10218de8d7fSPeter Avalos } 10318de8d7fSPeter Avalos if (val & O_NONBLOCK) { 10418de8d7fSPeter Avalos debug3("fd %d is O_NONBLOCK", fd); 10518de8d7fSPeter Avalos return (0); 10618de8d7fSPeter Avalos } 10718de8d7fSPeter Avalos debug2("fd %d setting O_NONBLOCK", fd); 10818de8d7fSPeter Avalos val |= O_NONBLOCK; 10918de8d7fSPeter Avalos if (fcntl(fd, F_SETFL, val) == -1) { 11018de8d7fSPeter Avalos debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, 11118de8d7fSPeter Avalos strerror(errno)); 11218de8d7fSPeter Avalos return (-1); 11318de8d7fSPeter Avalos } 11418de8d7fSPeter Avalos return (0); 11518de8d7fSPeter Avalos } 11618de8d7fSPeter Avalos 11718de8d7fSPeter Avalos int 11818de8d7fSPeter Avalos unset_nonblock(int fd) 11918de8d7fSPeter Avalos { 12018de8d7fSPeter Avalos int val; 12118de8d7fSPeter Avalos 122e9778795SPeter Avalos val = fcntl(fd, F_GETFL); 12318de8d7fSPeter Avalos if (val < 0) { 124e9778795SPeter Avalos error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); 12518de8d7fSPeter Avalos return (-1); 12618de8d7fSPeter Avalos } 12718de8d7fSPeter Avalos if (!(val & O_NONBLOCK)) { 12818de8d7fSPeter Avalos debug3("fd %d is not O_NONBLOCK", fd); 12918de8d7fSPeter Avalos return (0); 13018de8d7fSPeter Avalos } 13118de8d7fSPeter Avalos debug("fd %d clearing O_NONBLOCK", fd); 13218de8d7fSPeter Avalos val &= ~O_NONBLOCK; 13318de8d7fSPeter Avalos if (fcntl(fd, F_SETFL, val) == -1) { 13418de8d7fSPeter Avalos debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", 13518de8d7fSPeter Avalos fd, strerror(errno)); 13618de8d7fSPeter Avalos return (-1); 13718de8d7fSPeter Avalos } 13818de8d7fSPeter Avalos return (0); 13918de8d7fSPeter Avalos } 14018de8d7fSPeter Avalos 14118de8d7fSPeter Avalos const char * 14218de8d7fSPeter Avalos ssh_gai_strerror(int gaierr) 14318de8d7fSPeter Avalos { 14436e94dc5SPeter Avalos if (gaierr == EAI_SYSTEM && errno != 0) 14518de8d7fSPeter Avalos return strerror(errno); 14618de8d7fSPeter Avalos return gai_strerror(gaierr); 14718de8d7fSPeter Avalos } 14818de8d7fSPeter Avalos 14918de8d7fSPeter Avalos /* disable nagle on socket */ 15018de8d7fSPeter Avalos void 15118de8d7fSPeter Avalos set_nodelay(int fd) 15218de8d7fSPeter Avalos { 15318de8d7fSPeter Avalos int opt; 15418de8d7fSPeter Avalos socklen_t optlen; 15518de8d7fSPeter Avalos 15618de8d7fSPeter Avalos optlen = sizeof opt; 15718de8d7fSPeter Avalos if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { 15818de8d7fSPeter Avalos debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); 15918de8d7fSPeter Avalos return; 16018de8d7fSPeter Avalos } 16118de8d7fSPeter Avalos if (opt == 1) { 16218de8d7fSPeter Avalos debug2("fd %d is TCP_NODELAY", fd); 16318de8d7fSPeter Avalos return; 16418de8d7fSPeter Avalos } 16518de8d7fSPeter Avalos opt = 1; 16618de8d7fSPeter Avalos debug2("fd %d setting TCP_NODELAY", fd); 16718de8d7fSPeter Avalos if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) 16818de8d7fSPeter Avalos error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); 16918de8d7fSPeter Avalos } 17018de8d7fSPeter Avalos 171*664f4763Szrj /* Allow local port reuse in TIME_WAIT */ 172*664f4763Szrj int 173*664f4763Szrj set_reuseaddr(int fd) 174*664f4763Szrj { 175*664f4763Szrj int on = 1; 176*664f4763Szrj 177*664f4763Szrj if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { 178*664f4763Szrj error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); 179*664f4763Szrj return -1; 180*664f4763Szrj } 181*664f4763Szrj return 0; 182*664f4763Szrj } 183*664f4763Szrj 184*664f4763Szrj /* Get/set routing domain */ 185*664f4763Szrj char * 186*664f4763Szrj get_rdomain(int fd) 187*664f4763Szrj { 188*664f4763Szrj #if defined(HAVE_SYS_GET_RDOMAIN) 189*664f4763Szrj return sys_get_rdomain(fd); 190*664f4763Szrj #elif defined(__OpenBSD__) 191*664f4763Szrj int rtable; 192*664f4763Szrj char *ret; 193*664f4763Szrj socklen_t len = sizeof(rtable); 194*664f4763Szrj 195*664f4763Szrj if (getsockopt(fd, SOL_SOCKET, SO_RTABLE, &rtable, &len) == -1) { 196*664f4763Szrj error("Failed to get routing domain for fd %d: %s", 197*664f4763Szrj fd, strerror(errno)); 198*664f4763Szrj return NULL; 199*664f4763Szrj } 200*664f4763Szrj xasprintf(&ret, "%d", rtable); 201*664f4763Szrj return ret; 202*664f4763Szrj #else /* defined(__OpenBSD__) */ 203*664f4763Szrj return NULL; 204*664f4763Szrj #endif 205*664f4763Szrj } 206*664f4763Szrj 207*664f4763Szrj int 208*664f4763Szrj set_rdomain(int fd, const char *name) 209*664f4763Szrj { 210*664f4763Szrj #if defined(HAVE_SYS_SET_RDOMAIN) 211*664f4763Szrj return sys_set_rdomain(fd, name); 212*664f4763Szrj #elif defined(__OpenBSD__) 213*664f4763Szrj int rtable; 214*664f4763Szrj const char *errstr; 215*664f4763Szrj 216*664f4763Szrj if (name == NULL) 217*664f4763Szrj return 0; /* default table */ 218*664f4763Szrj 219*664f4763Szrj rtable = (int)strtonum(name, 0, 255, &errstr); 220*664f4763Szrj if (errstr != NULL) { 221*664f4763Szrj /* Shouldn't happen */ 222*664f4763Szrj error("Invalid routing domain \"%s\": %s", name, errstr); 223*664f4763Szrj return -1; 224*664f4763Szrj } 225*664f4763Szrj if (setsockopt(fd, SOL_SOCKET, SO_RTABLE, 226*664f4763Szrj &rtable, sizeof(rtable)) == -1) { 227*664f4763Szrj error("Failed to set routing domain %d on fd %d: %s", 228*664f4763Szrj rtable, fd, strerror(errno)); 229*664f4763Szrj return -1; 230*664f4763Szrj } 231*664f4763Szrj return 0; 232*664f4763Szrj #else /* defined(__OpenBSD__) */ 233*664f4763Szrj error("Setting routing domain is not supported on this platform"); 234*664f4763Szrj return -1; 235*664f4763Szrj #endif 236*664f4763Szrj } 237*664f4763Szrj 238*664f4763Szrj /* 239*664f4763Szrj * Wait up to *timeoutp milliseconds for fd to be readable. Updates 240*664f4763Szrj * *timeoutp with time remaining. 241*664f4763Szrj * Returns 0 if fd ready or -1 on timeout or error (see errno). 242*664f4763Szrj */ 243*664f4763Szrj int 244*664f4763Szrj waitrfd(int fd, int *timeoutp) 245*664f4763Szrj { 246*664f4763Szrj struct pollfd pfd; 247*664f4763Szrj struct timeval t_start; 248*664f4763Szrj int oerrno, r; 249*664f4763Szrj 250*664f4763Szrj monotime_tv(&t_start); 251*664f4763Szrj pfd.fd = fd; 252*664f4763Szrj pfd.events = POLLIN; 253*664f4763Szrj for (; *timeoutp >= 0;) { 254*664f4763Szrj r = poll(&pfd, 1, *timeoutp); 255*664f4763Szrj oerrno = errno; 256*664f4763Szrj ms_subtract_diff(&t_start, timeoutp); 257*664f4763Szrj errno = oerrno; 258*664f4763Szrj if (r > 0) 259*664f4763Szrj return 0; 260*664f4763Szrj else if (r == -1 && errno != EAGAIN) 261*664f4763Szrj return -1; 262*664f4763Szrj else if (r == 0) 263*664f4763Szrj break; 264*664f4763Szrj } 265*664f4763Szrj /* timeout */ 266*664f4763Szrj errno = ETIMEDOUT; 267*664f4763Szrj return -1; 268*664f4763Szrj } 269*664f4763Szrj 270*664f4763Szrj /* 271*664f4763Szrj * Attempt a non-blocking connect(2) to the specified address, waiting up to 272*664f4763Szrj * *timeoutp milliseconds for the connection to complete. If the timeout is 273*664f4763Szrj * <=0, then wait indefinitely. 274*664f4763Szrj * 275*664f4763Szrj * Returns 0 on success or -1 on failure. 276*664f4763Szrj */ 277*664f4763Szrj int 278*664f4763Szrj timeout_connect(int sockfd, const struct sockaddr *serv_addr, 279*664f4763Szrj socklen_t addrlen, int *timeoutp) 280*664f4763Szrj { 281*664f4763Szrj int optval = 0; 282*664f4763Szrj socklen_t optlen = sizeof(optval); 283*664f4763Szrj 284*664f4763Szrj /* No timeout: just do a blocking connect() */ 285*664f4763Szrj if (timeoutp == NULL || *timeoutp <= 0) 286*664f4763Szrj return connect(sockfd, serv_addr, addrlen); 287*664f4763Szrj 288*664f4763Szrj set_nonblock(sockfd); 289*664f4763Szrj if (connect(sockfd, serv_addr, addrlen) == 0) { 290*664f4763Szrj /* Succeeded already? */ 291*664f4763Szrj unset_nonblock(sockfd); 292*664f4763Szrj return 0; 293*664f4763Szrj } else if (errno != EINPROGRESS) 294*664f4763Szrj return -1; 295*664f4763Szrj 296*664f4763Szrj if (waitrfd(sockfd, timeoutp) == -1) 297*664f4763Szrj return -1; 298*664f4763Szrj 299*664f4763Szrj /* Completed or failed */ 300*664f4763Szrj if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { 301*664f4763Szrj debug("getsockopt: %s", strerror(errno)); 302*664f4763Szrj return -1; 303*664f4763Szrj } 304*664f4763Szrj if (optval != 0) { 305*664f4763Szrj errno = optval; 306*664f4763Szrj return -1; 307*664f4763Szrj } 308*664f4763Szrj unset_nonblock(sockfd); 309*664f4763Szrj return 0; 310*664f4763Szrj } 311*664f4763Szrj 31218de8d7fSPeter Avalos /* Characters considered whitespace in strsep calls. */ 31318de8d7fSPeter Avalos #define WHITESPACE " \t\r\n" 31418de8d7fSPeter Avalos #define QUOTE "\"" 31518de8d7fSPeter Avalos 31618de8d7fSPeter Avalos /* return next token in configuration line */ 317*664f4763Szrj static char * 318*664f4763Szrj strdelim_internal(char **s, int split_equals) 31918de8d7fSPeter Avalos { 32018de8d7fSPeter Avalos char *old; 32118de8d7fSPeter Avalos int wspace = 0; 32218de8d7fSPeter Avalos 32318de8d7fSPeter Avalos if (*s == NULL) 32418de8d7fSPeter Avalos return NULL; 32518de8d7fSPeter Avalos 32618de8d7fSPeter Avalos old = *s; 32718de8d7fSPeter Avalos 328*664f4763Szrj *s = strpbrk(*s, 329*664f4763Szrj split_equals ? WHITESPACE QUOTE "=" : WHITESPACE QUOTE); 33018de8d7fSPeter Avalos if (*s == NULL) 33118de8d7fSPeter Avalos return (old); 33218de8d7fSPeter Avalos 33318de8d7fSPeter Avalos if (*s[0] == '\"') { 33418de8d7fSPeter Avalos memmove(*s, *s + 1, strlen(*s)); /* move nul too */ 33518de8d7fSPeter Avalos /* Find matching quote */ 33618de8d7fSPeter Avalos if ((*s = strpbrk(*s, QUOTE)) == NULL) { 33718de8d7fSPeter Avalos return (NULL); /* no matching quote */ 33818de8d7fSPeter Avalos } else { 33918de8d7fSPeter Avalos *s[0] = '\0'; 340856ea928SPeter Avalos *s += strspn(*s + 1, WHITESPACE) + 1; 34118de8d7fSPeter Avalos return (old); 34218de8d7fSPeter Avalos } 34318de8d7fSPeter Avalos } 34418de8d7fSPeter Avalos 34518de8d7fSPeter Avalos /* Allow only one '=' to be skipped */ 346*664f4763Szrj if (split_equals && *s[0] == '=') 34718de8d7fSPeter Avalos wspace = 1; 34818de8d7fSPeter Avalos *s[0] = '\0'; 34918de8d7fSPeter Avalos 35018de8d7fSPeter Avalos /* Skip any extra whitespace after first token */ 35118de8d7fSPeter Avalos *s += strspn(*s + 1, WHITESPACE) + 1; 352*664f4763Szrj if (split_equals && *s[0] == '=' && !wspace) 35318de8d7fSPeter Avalos *s += strspn(*s + 1, WHITESPACE) + 1; 35418de8d7fSPeter Avalos 35518de8d7fSPeter Avalos return (old); 35618de8d7fSPeter Avalos } 35718de8d7fSPeter Avalos 358*664f4763Szrj /* 359*664f4763Szrj * Return next token in configuration line; splts on whitespace or a 360*664f4763Szrj * single '=' character. 361*664f4763Szrj */ 362*664f4763Szrj char * 363*664f4763Szrj strdelim(char **s) 364*664f4763Szrj { 365*664f4763Szrj return strdelim_internal(s, 1); 366*664f4763Szrj } 367*664f4763Szrj 368*664f4763Szrj /* 369*664f4763Szrj * Return next token in configuration line; splts on whitespace only. 370*664f4763Szrj */ 371*664f4763Szrj char * 372*664f4763Szrj strdelimw(char **s) 373*664f4763Szrj { 374*664f4763Szrj return strdelim_internal(s, 0); 375*664f4763Szrj } 376*664f4763Szrj 37718de8d7fSPeter Avalos struct passwd * 37818de8d7fSPeter Avalos pwcopy(struct passwd *pw) 37918de8d7fSPeter Avalos { 38018de8d7fSPeter Avalos struct passwd *copy = xcalloc(1, sizeof(*copy)); 38118de8d7fSPeter Avalos 38218de8d7fSPeter Avalos copy->pw_name = xstrdup(pw->pw_name); 38318de8d7fSPeter Avalos copy->pw_passwd = xstrdup(pw->pw_passwd); 38436e94dc5SPeter Avalos #ifdef HAVE_STRUCT_PASSWD_PW_GECOS 38518de8d7fSPeter Avalos copy->pw_gecos = xstrdup(pw->pw_gecos); 38636e94dc5SPeter Avalos #endif 38718de8d7fSPeter Avalos copy->pw_uid = pw->pw_uid; 38818de8d7fSPeter Avalos copy->pw_gid = pw->pw_gid; 38936e94dc5SPeter Avalos #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE 39018de8d7fSPeter Avalos copy->pw_expire = pw->pw_expire; 39118de8d7fSPeter Avalos #endif 39236e94dc5SPeter Avalos #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE 39318de8d7fSPeter Avalos copy->pw_change = pw->pw_change; 39418de8d7fSPeter Avalos #endif 39536e94dc5SPeter Avalos #ifdef HAVE_STRUCT_PASSWD_PW_CLASS 39618de8d7fSPeter Avalos copy->pw_class = xstrdup(pw->pw_class); 39718de8d7fSPeter Avalos #endif 39818de8d7fSPeter Avalos copy->pw_dir = xstrdup(pw->pw_dir); 39918de8d7fSPeter Avalos copy->pw_shell = xstrdup(pw->pw_shell); 40018de8d7fSPeter Avalos return copy; 40118de8d7fSPeter Avalos } 40218de8d7fSPeter Avalos 40318de8d7fSPeter Avalos /* 40418de8d7fSPeter Avalos * Convert ASCII string to TCP/IP port number. 405cb5eb4f1SPeter Avalos * Port must be >=0 and <=65535. 406cb5eb4f1SPeter Avalos * Return -1 if invalid. 40718de8d7fSPeter Avalos */ 40818de8d7fSPeter Avalos int 40918de8d7fSPeter Avalos a2port(const char *s) 41018de8d7fSPeter Avalos { 411*664f4763Szrj struct servent *se; 412cb5eb4f1SPeter Avalos long long port; 413cb5eb4f1SPeter Avalos const char *errstr; 41418de8d7fSPeter Avalos 415cb5eb4f1SPeter Avalos port = strtonum(s, 0, 65535, &errstr); 416*664f4763Szrj if (errstr == NULL) 417cb5eb4f1SPeter Avalos return (int)port; 418*664f4763Szrj if ((se = getservbyname(s, "tcp")) != NULL) 419*664f4763Szrj return ntohs(se->s_port); 420*664f4763Szrj return -1; 42118de8d7fSPeter Avalos } 42218de8d7fSPeter Avalos 42318de8d7fSPeter Avalos int 42418de8d7fSPeter Avalos a2tun(const char *s, int *remote) 42518de8d7fSPeter Avalos { 42618de8d7fSPeter Avalos const char *errstr = NULL; 42718de8d7fSPeter Avalos char *sp, *ep; 42818de8d7fSPeter Avalos int tun; 42918de8d7fSPeter Avalos 43018de8d7fSPeter Avalos if (remote != NULL) { 43118de8d7fSPeter Avalos *remote = SSH_TUNID_ANY; 43218de8d7fSPeter Avalos sp = xstrdup(s); 43318de8d7fSPeter Avalos if ((ep = strchr(sp, ':')) == NULL) { 43436e94dc5SPeter Avalos free(sp); 43518de8d7fSPeter Avalos return (a2tun(s, NULL)); 43618de8d7fSPeter Avalos } 43718de8d7fSPeter Avalos ep[0] = '\0'; ep++; 43818de8d7fSPeter Avalos *remote = a2tun(ep, NULL); 43918de8d7fSPeter Avalos tun = a2tun(sp, NULL); 44036e94dc5SPeter Avalos free(sp); 44118de8d7fSPeter Avalos return (*remote == SSH_TUNID_ERR ? *remote : tun); 44218de8d7fSPeter Avalos } 44318de8d7fSPeter Avalos 44418de8d7fSPeter Avalos if (strcasecmp(s, "any") == 0) 44518de8d7fSPeter Avalos return (SSH_TUNID_ANY); 44618de8d7fSPeter Avalos 44718de8d7fSPeter Avalos tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); 44818de8d7fSPeter Avalos if (errstr != NULL) 44918de8d7fSPeter Avalos return (SSH_TUNID_ERR); 45018de8d7fSPeter Avalos 45118de8d7fSPeter Avalos return (tun); 45218de8d7fSPeter Avalos } 45318de8d7fSPeter Avalos 45418de8d7fSPeter Avalos #define SECONDS 1 45518de8d7fSPeter Avalos #define MINUTES (SECONDS * 60) 45618de8d7fSPeter Avalos #define HOURS (MINUTES * 60) 45718de8d7fSPeter Avalos #define DAYS (HOURS * 24) 45818de8d7fSPeter Avalos #define WEEKS (DAYS * 7) 45918de8d7fSPeter Avalos 46018de8d7fSPeter Avalos /* 46118de8d7fSPeter Avalos * Convert a time string into seconds; format is 46218de8d7fSPeter Avalos * a sequence of: 46318de8d7fSPeter Avalos * time[qualifier] 46418de8d7fSPeter Avalos * 46518de8d7fSPeter Avalos * Valid time qualifiers are: 46618de8d7fSPeter Avalos * <none> seconds 46718de8d7fSPeter Avalos * s|S seconds 46818de8d7fSPeter Avalos * m|M minutes 46918de8d7fSPeter Avalos * h|H hours 47018de8d7fSPeter Avalos * d|D days 47118de8d7fSPeter Avalos * w|W weeks 47218de8d7fSPeter Avalos * 47318de8d7fSPeter Avalos * Examples: 47418de8d7fSPeter Avalos * 90m 90 minutes 47518de8d7fSPeter Avalos * 1h30m 90 minutes 47618de8d7fSPeter Avalos * 2d 2 days 47718de8d7fSPeter Avalos * 1w 1 week 47818de8d7fSPeter Avalos * 47918de8d7fSPeter Avalos * Return -1 if time string is invalid. 48018de8d7fSPeter Avalos */ 48118de8d7fSPeter Avalos long 48218de8d7fSPeter Avalos convtime(const char *s) 48318de8d7fSPeter Avalos { 484ce74bacaSMatthew Dillon long total, secs, multiplier = 1; 48518de8d7fSPeter Avalos const char *p; 48618de8d7fSPeter Avalos char *endp; 48718de8d7fSPeter Avalos 48818de8d7fSPeter Avalos errno = 0; 48918de8d7fSPeter Avalos total = 0; 49018de8d7fSPeter Avalos p = s; 49118de8d7fSPeter Avalos 49218de8d7fSPeter Avalos if (p == NULL || *p == '\0') 49318de8d7fSPeter Avalos return -1; 49418de8d7fSPeter Avalos 49518de8d7fSPeter Avalos while (*p) { 49618de8d7fSPeter Avalos secs = strtol(p, &endp, 10); 49718de8d7fSPeter Avalos if (p == endp || 49818de8d7fSPeter Avalos (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 49918de8d7fSPeter Avalos secs < 0) 50018de8d7fSPeter Avalos return -1; 50118de8d7fSPeter Avalos 50218de8d7fSPeter Avalos switch (*endp++) { 50318de8d7fSPeter Avalos case '\0': 50418de8d7fSPeter Avalos endp--; 50518de8d7fSPeter Avalos break; 50618de8d7fSPeter Avalos case 's': 50718de8d7fSPeter Avalos case 'S': 50818de8d7fSPeter Avalos break; 50918de8d7fSPeter Avalos case 'm': 51018de8d7fSPeter Avalos case 'M': 511ce74bacaSMatthew Dillon multiplier = MINUTES; 51218de8d7fSPeter Avalos break; 51318de8d7fSPeter Avalos case 'h': 51418de8d7fSPeter Avalos case 'H': 515ce74bacaSMatthew Dillon multiplier = HOURS; 51618de8d7fSPeter Avalos break; 51718de8d7fSPeter Avalos case 'd': 51818de8d7fSPeter Avalos case 'D': 519ce74bacaSMatthew Dillon multiplier = DAYS; 52018de8d7fSPeter Avalos break; 52118de8d7fSPeter Avalos case 'w': 52218de8d7fSPeter Avalos case 'W': 523ce74bacaSMatthew Dillon multiplier = WEEKS; 52418de8d7fSPeter Avalos break; 52518de8d7fSPeter Avalos default: 52618de8d7fSPeter Avalos return -1; 52718de8d7fSPeter Avalos } 528ce74bacaSMatthew Dillon if (secs >= LONG_MAX / multiplier) 529ce74bacaSMatthew Dillon return -1; 530ce74bacaSMatthew Dillon secs *= multiplier; 531ce74bacaSMatthew Dillon if (total >= LONG_MAX - secs) 532ce74bacaSMatthew Dillon return -1; 53318de8d7fSPeter Avalos total += secs; 53418de8d7fSPeter Avalos if (total < 0) 53518de8d7fSPeter Avalos return -1; 53618de8d7fSPeter Avalos p = endp; 53718de8d7fSPeter Avalos } 53818de8d7fSPeter Avalos 53918de8d7fSPeter Avalos return total; 54018de8d7fSPeter Avalos } 54118de8d7fSPeter Avalos 54218de8d7fSPeter Avalos /* 54318de8d7fSPeter Avalos * Returns a standardized host+port identifier string. 54418de8d7fSPeter Avalos * Caller must free returned string. 54518de8d7fSPeter Avalos */ 54618de8d7fSPeter Avalos char * 54718de8d7fSPeter Avalos put_host_port(const char *host, u_short port) 54818de8d7fSPeter Avalos { 54918de8d7fSPeter Avalos char *hoststr; 55018de8d7fSPeter Avalos 55118de8d7fSPeter Avalos if (port == 0 || port == SSH_DEFAULT_PORT) 55218de8d7fSPeter Avalos return(xstrdup(host)); 55318de8d7fSPeter Avalos if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0) 55418de8d7fSPeter Avalos fatal("put_host_port: asprintf: %s", strerror(errno)); 55518de8d7fSPeter Avalos debug3("put_host_port: %s", hoststr); 55618de8d7fSPeter Avalos return hoststr; 55718de8d7fSPeter Avalos } 55818de8d7fSPeter Avalos 55918de8d7fSPeter Avalos /* 56018de8d7fSPeter Avalos * Search for next delimiter between hostnames/addresses and ports. 56118de8d7fSPeter Avalos * Argument may be modified (for termination). 56218de8d7fSPeter Avalos * Returns *cp if parsing succeeds. 563*664f4763Szrj * *cp is set to the start of the next field, if one was found. 564*664f4763Szrj * The delimiter char, if present, is stored in delim. 56518de8d7fSPeter Avalos * If this is the last field, *cp is set to NULL. 56618de8d7fSPeter Avalos */ 56718de8d7fSPeter Avalos char * 568*664f4763Szrj hpdelim2(char **cp, char *delim) 56918de8d7fSPeter Avalos { 57018de8d7fSPeter Avalos char *s, *old; 57118de8d7fSPeter Avalos 57218de8d7fSPeter Avalos if (cp == NULL || *cp == NULL) 57318de8d7fSPeter Avalos return NULL; 57418de8d7fSPeter Avalos 57518de8d7fSPeter Avalos old = s = *cp; 57618de8d7fSPeter Avalos if (*s == '[') { 57718de8d7fSPeter Avalos if ((s = strchr(s, ']')) == NULL) 57818de8d7fSPeter Avalos return NULL; 57918de8d7fSPeter Avalos else 58018de8d7fSPeter Avalos s++; 58118de8d7fSPeter Avalos } else if ((s = strpbrk(s, ":/")) == NULL) 58218de8d7fSPeter Avalos s = *cp + strlen(*cp); /* skip to end (see first case below) */ 58318de8d7fSPeter Avalos 58418de8d7fSPeter Avalos switch (*s) { 58518de8d7fSPeter Avalos case '\0': 58618de8d7fSPeter Avalos *cp = NULL; /* no more fields*/ 58718de8d7fSPeter Avalos break; 58818de8d7fSPeter Avalos 58918de8d7fSPeter Avalos case ':': 59018de8d7fSPeter Avalos case '/': 591*664f4763Szrj if (delim != NULL) 592*664f4763Szrj *delim = *s; 59318de8d7fSPeter Avalos *s = '\0'; /* terminate */ 59418de8d7fSPeter Avalos *cp = s + 1; 59518de8d7fSPeter Avalos break; 59618de8d7fSPeter Avalos 59718de8d7fSPeter Avalos default: 59818de8d7fSPeter Avalos return NULL; 59918de8d7fSPeter Avalos } 60018de8d7fSPeter Avalos 60118de8d7fSPeter Avalos return old; 60218de8d7fSPeter Avalos } 60318de8d7fSPeter Avalos 60418de8d7fSPeter Avalos char * 605*664f4763Szrj hpdelim(char **cp) 606*664f4763Szrj { 607*664f4763Szrj return hpdelim2(cp, NULL); 608*664f4763Szrj } 609*664f4763Szrj 610*664f4763Szrj char * 61118de8d7fSPeter Avalos cleanhostname(char *host) 61218de8d7fSPeter Avalos { 61318de8d7fSPeter Avalos if (*host == '[' && host[strlen(host) - 1] == ']') { 61418de8d7fSPeter Avalos host[strlen(host) - 1] = '\0'; 61518de8d7fSPeter Avalos return (host + 1); 61618de8d7fSPeter Avalos } else 61718de8d7fSPeter Avalos return host; 61818de8d7fSPeter Avalos } 61918de8d7fSPeter Avalos 62018de8d7fSPeter Avalos char * 62118de8d7fSPeter Avalos colon(char *cp) 62218de8d7fSPeter Avalos { 62318de8d7fSPeter Avalos int flag = 0; 62418de8d7fSPeter Avalos 62518de8d7fSPeter Avalos if (*cp == ':') /* Leading colon is part of file name. */ 626856ea928SPeter Avalos return NULL; 62718de8d7fSPeter Avalos if (*cp == '[') 62818de8d7fSPeter Avalos flag = 1; 62918de8d7fSPeter Avalos 63018de8d7fSPeter Avalos for (; *cp; ++cp) { 63118de8d7fSPeter Avalos if (*cp == '@' && *(cp+1) == '[') 63218de8d7fSPeter Avalos flag = 1; 63318de8d7fSPeter Avalos if (*cp == ']' && *(cp+1) == ':' && flag) 63418de8d7fSPeter Avalos return (cp+1); 63518de8d7fSPeter Avalos if (*cp == ':' && !flag) 63618de8d7fSPeter Avalos return (cp); 63718de8d7fSPeter Avalos if (*cp == '/') 638856ea928SPeter Avalos return NULL; 63918de8d7fSPeter Avalos } 640856ea928SPeter Avalos return NULL; 64118de8d7fSPeter Avalos } 64218de8d7fSPeter Avalos 643e9778795SPeter Avalos /* 644*664f4763Szrj * Parse a [user@]host:[path] string. 645*664f4763Szrj * Caller must free returned user, host and path. 646*664f4763Szrj * Any of the pointer return arguments may be NULL (useful for syntax checking). 647*664f4763Szrj * If user was not specified then *userp will be set to NULL. 648*664f4763Szrj * If host was not specified then *hostp will be set to NULL. 649*664f4763Szrj * If path was not specified then *pathp will be set to ".". 650*664f4763Szrj * Returns 0 on success, -1 on failure. 651*664f4763Szrj */ 652*664f4763Szrj int 653*664f4763Szrj parse_user_host_path(const char *s, char **userp, char **hostp, char **pathp) 654*664f4763Szrj { 655*664f4763Szrj char *user = NULL, *host = NULL, *path = NULL; 656*664f4763Szrj char *sdup, *tmp; 657*664f4763Szrj int ret = -1; 658*664f4763Szrj 659*664f4763Szrj if (userp != NULL) 660*664f4763Szrj *userp = NULL; 661*664f4763Szrj if (hostp != NULL) 662*664f4763Szrj *hostp = NULL; 663*664f4763Szrj if (pathp != NULL) 664*664f4763Szrj *pathp = NULL; 665*664f4763Szrj 666*664f4763Szrj sdup = xstrdup(s); 667*664f4763Szrj 668*664f4763Szrj /* Check for remote syntax: [user@]host:[path] */ 669*664f4763Szrj if ((tmp = colon(sdup)) == NULL) 670*664f4763Szrj goto out; 671*664f4763Szrj 672*664f4763Szrj /* Extract optional path */ 673*664f4763Szrj *tmp++ = '\0'; 674*664f4763Szrj if (*tmp == '\0') 675*664f4763Szrj tmp = "."; 676*664f4763Szrj path = xstrdup(tmp); 677*664f4763Szrj 678*664f4763Szrj /* Extract optional user and mandatory host */ 679*664f4763Szrj tmp = strrchr(sdup, '@'); 680*664f4763Szrj if (tmp != NULL) { 681*664f4763Szrj *tmp++ = '\0'; 682*664f4763Szrj host = xstrdup(cleanhostname(tmp)); 683*664f4763Szrj if (*sdup != '\0') 684*664f4763Szrj user = xstrdup(sdup); 685*664f4763Szrj } else { 686*664f4763Szrj host = xstrdup(cleanhostname(sdup)); 687*664f4763Szrj user = NULL; 688*664f4763Szrj } 689*664f4763Szrj 690*664f4763Szrj /* Success */ 691*664f4763Szrj if (userp != NULL) { 692*664f4763Szrj *userp = user; 693*664f4763Szrj user = NULL; 694*664f4763Szrj } 695*664f4763Szrj if (hostp != NULL) { 696*664f4763Szrj *hostp = host; 697*664f4763Szrj host = NULL; 698*664f4763Szrj } 699*664f4763Szrj if (pathp != NULL) { 700*664f4763Szrj *pathp = path; 701*664f4763Szrj path = NULL; 702*664f4763Szrj } 703*664f4763Szrj ret = 0; 704*664f4763Szrj out: 705*664f4763Szrj free(sdup); 706*664f4763Szrj free(user); 707*664f4763Szrj free(host); 708*664f4763Szrj free(path); 709*664f4763Szrj return ret; 710*664f4763Szrj } 711*664f4763Szrj 712*664f4763Szrj /* 713e9778795SPeter Avalos * Parse a [user@]host[:port] string. 714e9778795SPeter Avalos * Caller must free returned user and host. 715e9778795SPeter Avalos * Any of the pointer return arguments may be NULL (useful for syntax checking). 716e9778795SPeter Avalos * If user was not specified then *userp will be set to NULL. 717e9778795SPeter Avalos * If port was not specified then *portp will be -1. 718e9778795SPeter Avalos * Returns 0 on success, -1 on failure. 719e9778795SPeter Avalos */ 720e9778795SPeter Avalos int 721e9778795SPeter Avalos parse_user_host_port(const char *s, char **userp, char **hostp, int *portp) 722e9778795SPeter Avalos { 723e9778795SPeter Avalos char *sdup, *cp, *tmp; 724e9778795SPeter Avalos char *user = NULL, *host = NULL; 725e9778795SPeter Avalos int port = -1, ret = -1; 726e9778795SPeter Avalos 727e9778795SPeter Avalos if (userp != NULL) 728e9778795SPeter Avalos *userp = NULL; 729e9778795SPeter Avalos if (hostp != NULL) 730e9778795SPeter Avalos *hostp = NULL; 731e9778795SPeter Avalos if (portp != NULL) 732e9778795SPeter Avalos *portp = -1; 733e9778795SPeter Avalos 734e9778795SPeter Avalos if ((sdup = tmp = strdup(s)) == NULL) 735e9778795SPeter Avalos return -1; 736e9778795SPeter Avalos /* Extract optional username */ 737*664f4763Szrj if ((cp = strrchr(tmp, '@')) != NULL) { 738e9778795SPeter Avalos *cp = '\0'; 739e9778795SPeter Avalos if (*tmp == '\0') 740e9778795SPeter Avalos goto out; 741e9778795SPeter Avalos if ((user = strdup(tmp)) == NULL) 742e9778795SPeter Avalos goto out; 743e9778795SPeter Avalos tmp = cp + 1; 744e9778795SPeter Avalos } 745e9778795SPeter Avalos /* Extract mandatory hostname */ 746e9778795SPeter Avalos if ((cp = hpdelim(&tmp)) == NULL || *cp == '\0') 747e9778795SPeter Avalos goto out; 748e9778795SPeter Avalos host = xstrdup(cleanhostname(cp)); 749e9778795SPeter Avalos /* Convert and verify optional port */ 750e9778795SPeter Avalos if (tmp != NULL && *tmp != '\0') { 751e9778795SPeter Avalos if ((port = a2port(tmp)) <= 0) 752e9778795SPeter Avalos goto out; 753e9778795SPeter Avalos } 754e9778795SPeter Avalos /* Success */ 755e9778795SPeter Avalos if (userp != NULL) { 756e9778795SPeter Avalos *userp = user; 757e9778795SPeter Avalos user = NULL; 758e9778795SPeter Avalos } 759e9778795SPeter Avalos if (hostp != NULL) { 760e9778795SPeter Avalos *hostp = host; 761e9778795SPeter Avalos host = NULL; 762e9778795SPeter Avalos } 763e9778795SPeter Avalos if (portp != NULL) 764e9778795SPeter Avalos *portp = port; 765e9778795SPeter Avalos ret = 0; 766e9778795SPeter Avalos out: 767e9778795SPeter Avalos free(sdup); 768e9778795SPeter Avalos free(user); 769e9778795SPeter Avalos free(host); 770e9778795SPeter Avalos return ret; 771e9778795SPeter Avalos } 772e9778795SPeter Avalos 773*664f4763Szrj /* 774*664f4763Szrj * Converts a two-byte hex string to decimal. 775*664f4763Szrj * Returns the decimal value or -1 for invalid input. 776*664f4763Szrj */ 777*664f4763Szrj static int 778*664f4763Szrj hexchar(const char *s) 779*664f4763Szrj { 780*664f4763Szrj unsigned char result[2]; 781*664f4763Szrj int i; 782*664f4763Szrj 783*664f4763Szrj for (i = 0; i < 2; i++) { 784*664f4763Szrj if (s[i] >= '0' && s[i] <= '9') 785*664f4763Szrj result[i] = (unsigned char)(s[i] - '0'); 786*664f4763Szrj else if (s[i] >= 'a' && s[i] <= 'f') 787*664f4763Szrj result[i] = (unsigned char)(s[i] - 'a') + 10; 788*664f4763Szrj else if (s[i] >= 'A' && s[i] <= 'F') 789*664f4763Szrj result[i] = (unsigned char)(s[i] - 'A') + 10; 790*664f4763Szrj else 791*664f4763Szrj return -1; 792*664f4763Szrj } 793*664f4763Szrj return (result[0] << 4) | result[1]; 794*664f4763Szrj } 795*664f4763Szrj 796*664f4763Szrj /* 797*664f4763Szrj * Decode an url-encoded string. 798*664f4763Szrj * Returns a newly allocated string on success or NULL on failure. 799*664f4763Szrj */ 800*664f4763Szrj static char * 801*664f4763Szrj urldecode(const char *src) 802*664f4763Szrj { 803*664f4763Szrj char *ret, *dst; 804*664f4763Szrj int ch; 805*664f4763Szrj 806*664f4763Szrj ret = xmalloc(strlen(src) + 1); 807*664f4763Szrj for (dst = ret; *src != '\0'; src++) { 808*664f4763Szrj switch (*src) { 809*664f4763Szrj case '+': 810*664f4763Szrj *dst++ = ' '; 811*664f4763Szrj break; 812*664f4763Szrj case '%': 813*664f4763Szrj if (!isxdigit((unsigned char)src[1]) || 814*664f4763Szrj !isxdigit((unsigned char)src[2]) || 815*664f4763Szrj (ch = hexchar(src + 1)) == -1) { 816*664f4763Szrj free(ret); 817*664f4763Szrj return NULL; 818*664f4763Szrj } 819*664f4763Szrj *dst++ = ch; 820*664f4763Szrj src += 2; 821*664f4763Szrj break; 822*664f4763Szrj default: 823*664f4763Szrj *dst++ = *src; 824*664f4763Szrj break; 825*664f4763Szrj } 826*664f4763Szrj } 827*664f4763Szrj *dst = '\0'; 828*664f4763Szrj 829*664f4763Szrj return ret; 830*664f4763Szrj } 831*664f4763Szrj 832*664f4763Szrj /* 833*664f4763Szrj * Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI. 834*664f4763Szrj * See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04 835*664f4763Szrj * Either user or path may be url-encoded (but not host or port). 836*664f4763Szrj * Caller must free returned user, host and path. 837*664f4763Szrj * Any of the pointer return arguments may be NULL (useful for syntax checking) 838*664f4763Szrj * but the scheme must always be specified. 839*664f4763Szrj * If user was not specified then *userp will be set to NULL. 840*664f4763Szrj * If port was not specified then *portp will be -1. 841*664f4763Szrj * If path was not specified then *pathp will be set to NULL. 842*664f4763Szrj * Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri. 843*664f4763Szrj */ 844*664f4763Szrj int 845*664f4763Szrj parse_uri(const char *scheme, const char *uri, char **userp, char **hostp, 846*664f4763Szrj int *portp, char **pathp) 847*664f4763Szrj { 848*664f4763Szrj char *uridup, *cp, *tmp, ch; 849*664f4763Szrj char *user = NULL, *host = NULL, *path = NULL; 850*664f4763Szrj int port = -1, ret = -1; 851*664f4763Szrj size_t len; 852*664f4763Szrj 853*664f4763Szrj len = strlen(scheme); 854*664f4763Szrj if (strncmp(uri, scheme, len) != 0 || strncmp(uri + len, "://", 3) != 0) 855*664f4763Szrj return 1; 856*664f4763Szrj uri += len + 3; 857*664f4763Szrj 858*664f4763Szrj if (userp != NULL) 859*664f4763Szrj *userp = NULL; 860*664f4763Szrj if (hostp != NULL) 861*664f4763Szrj *hostp = NULL; 862*664f4763Szrj if (portp != NULL) 863*664f4763Szrj *portp = -1; 864*664f4763Szrj if (pathp != NULL) 865*664f4763Szrj *pathp = NULL; 866*664f4763Szrj 867*664f4763Szrj uridup = tmp = xstrdup(uri); 868*664f4763Szrj 869*664f4763Szrj /* Extract optional ssh-info (username + connection params) */ 870*664f4763Szrj if ((cp = strchr(tmp, '@')) != NULL) { 871*664f4763Szrj char *delim; 872*664f4763Szrj 873*664f4763Szrj *cp = '\0'; 874*664f4763Szrj /* Extract username and connection params */ 875*664f4763Szrj if ((delim = strchr(tmp, ';')) != NULL) { 876*664f4763Szrj /* Just ignore connection params for now */ 877*664f4763Szrj *delim = '\0'; 878*664f4763Szrj } 879*664f4763Szrj if (*tmp == '\0') { 880*664f4763Szrj /* Empty username */ 881*664f4763Szrj goto out; 882*664f4763Szrj } 883*664f4763Szrj if ((user = urldecode(tmp)) == NULL) 884*664f4763Szrj goto out; 885*664f4763Szrj tmp = cp + 1; 886*664f4763Szrj } 887*664f4763Szrj 888*664f4763Szrj /* Extract mandatory hostname */ 889*664f4763Szrj if ((cp = hpdelim2(&tmp, &ch)) == NULL || *cp == '\0') 890*664f4763Szrj goto out; 891*664f4763Szrj host = xstrdup(cleanhostname(cp)); 892*664f4763Szrj if (!valid_domain(host, 0, NULL)) 893*664f4763Szrj goto out; 894*664f4763Szrj 895*664f4763Szrj if (tmp != NULL && *tmp != '\0') { 896*664f4763Szrj if (ch == ':') { 897*664f4763Szrj /* Convert and verify port. */ 898*664f4763Szrj if ((cp = strchr(tmp, '/')) != NULL) 899*664f4763Szrj *cp = '\0'; 900*664f4763Szrj if ((port = a2port(tmp)) <= 0) 901*664f4763Szrj goto out; 902*664f4763Szrj tmp = cp ? cp + 1 : NULL; 903*664f4763Szrj } 904*664f4763Szrj if (tmp != NULL && *tmp != '\0') { 905*664f4763Szrj /* Extract optional path */ 906*664f4763Szrj if ((path = urldecode(tmp)) == NULL) 907*664f4763Szrj goto out; 908*664f4763Szrj } 909*664f4763Szrj } 910*664f4763Szrj 911*664f4763Szrj /* Success */ 912*664f4763Szrj if (userp != NULL) { 913*664f4763Szrj *userp = user; 914*664f4763Szrj user = NULL; 915*664f4763Szrj } 916*664f4763Szrj if (hostp != NULL) { 917*664f4763Szrj *hostp = host; 918*664f4763Szrj host = NULL; 919*664f4763Szrj } 920*664f4763Szrj if (portp != NULL) 921*664f4763Szrj *portp = port; 922*664f4763Szrj if (pathp != NULL) { 923*664f4763Szrj *pathp = path; 924*664f4763Szrj path = NULL; 925*664f4763Szrj } 926*664f4763Szrj ret = 0; 927*664f4763Szrj out: 928*664f4763Szrj free(uridup); 929*664f4763Szrj free(user); 930*664f4763Szrj free(host); 931*664f4763Szrj free(path); 932*664f4763Szrj return ret; 933*664f4763Szrj } 934*664f4763Szrj 93518de8d7fSPeter Avalos /* function to assist building execv() arguments */ 93618de8d7fSPeter Avalos void 93718de8d7fSPeter Avalos addargs(arglist *args, char *fmt, ...) 93818de8d7fSPeter Avalos { 93918de8d7fSPeter Avalos va_list ap; 94018de8d7fSPeter Avalos char *cp; 94118de8d7fSPeter Avalos u_int nalloc; 94218de8d7fSPeter Avalos int r; 94318de8d7fSPeter Avalos 94418de8d7fSPeter Avalos va_start(ap, fmt); 94518de8d7fSPeter Avalos r = vasprintf(&cp, fmt, ap); 94618de8d7fSPeter Avalos va_end(ap); 94718de8d7fSPeter Avalos if (r == -1) 94818de8d7fSPeter Avalos fatal("addargs: argument too long"); 94918de8d7fSPeter Avalos 95018de8d7fSPeter Avalos nalloc = args->nalloc; 95118de8d7fSPeter Avalos if (args->list == NULL) { 95218de8d7fSPeter Avalos nalloc = 32; 95318de8d7fSPeter Avalos args->num = 0; 95418de8d7fSPeter Avalos } else if (args->num+2 >= nalloc) 95518de8d7fSPeter Avalos nalloc *= 2; 95618de8d7fSPeter Avalos 957ce74bacaSMatthew Dillon args->list = xrecallocarray(args->list, args->nalloc, nalloc, sizeof(char *)); 95818de8d7fSPeter Avalos args->nalloc = nalloc; 95918de8d7fSPeter Avalos args->list[args->num++] = cp; 96018de8d7fSPeter Avalos args->list[args->num] = NULL; 96118de8d7fSPeter Avalos } 96218de8d7fSPeter Avalos 96318de8d7fSPeter Avalos void 96418de8d7fSPeter Avalos replacearg(arglist *args, u_int which, char *fmt, ...) 96518de8d7fSPeter Avalos { 96618de8d7fSPeter Avalos va_list ap; 96718de8d7fSPeter Avalos char *cp; 96818de8d7fSPeter Avalos int r; 96918de8d7fSPeter Avalos 97018de8d7fSPeter Avalos va_start(ap, fmt); 97118de8d7fSPeter Avalos r = vasprintf(&cp, fmt, ap); 97218de8d7fSPeter Avalos va_end(ap); 97318de8d7fSPeter Avalos if (r == -1) 97418de8d7fSPeter Avalos fatal("replacearg: argument too long"); 97518de8d7fSPeter Avalos 97618de8d7fSPeter Avalos if (which >= args->num) 97718de8d7fSPeter Avalos fatal("replacearg: tried to replace invalid arg %d >= %d", 97818de8d7fSPeter Avalos which, args->num); 97936e94dc5SPeter Avalos free(args->list[which]); 98018de8d7fSPeter Avalos args->list[which] = cp; 98118de8d7fSPeter Avalos } 98218de8d7fSPeter Avalos 98318de8d7fSPeter Avalos void 98418de8d7fSPeter Avalos freeargs(arglist *args) 98518de8d7fSPeter Avalos { 98618de8d7fSPeter Avalos u_int i; 98718de8d7fSPeter Avalos 98818de8d7fSPeter Avalos if (args->list != NULL) { 98918de8d7fSPeter Avalos for (i = 0; i < args->num; i++) 99036e94dc5SPeter Avalos free(args->list[i]); 99136e94dc5SPeter Avalos free(args->list); 99218de8d7fSPeter Avalos args->nalloc = args->num = 0; 99318de8d7fSPeter Avalos args->list = NULL; 99418de8d7fSPeter Avalos } 99518de8d7fSPeter Avalos } 99618de8d7fSPeter Avalos 99718de8d7fSPeter Avalos /* 99818de8d7fSPeter Avalos * Expands tildes in the file name. Returns data allocated by xmalloc. 99918de8d7fSPeter Avalos * Warning: this calls getpw*. 100018de8d7fSPeter Avalos */ 100118de8d7fSPeter Avalos char * 100218de8d7fSPeter Avalos tilde_expand_filename(const char *filename, uid_t uid) 100318de8d7fSPeter Avalos { 100436e94dc5SPeter Avalos const char *path, *sep; 100536e94dc5SPeter Avalos char user[128], *ret; 100618de8d7fSPeter Avalos struct passwd *pw; 100718de8d7fSPeter Avalos u_int len, slash; 100818de8d7fSPeter Avalos 100918de8d7fSPeter Avalos if (*filename != '~') 101018de8d7fSPeter Avalos return (xstrdup(filename)); 101118de8d7fSPeter Avalos filename++; 101218de8d7fSPeter Avalos 101318de8d7fSPeter Avalos path = strchr(filename, '/'); 101418de8d7fSPeter Avalos if (path != NULL && path > filename) { /* ~user/path */ 101518de8d7fSPeter Avalos slash = path - filename; 101618de8d7fSPeter Avalos if (slash > sizeof(user) - 1) 101718de8d7fSPeter Avalos fatal("tilde_expand_filename: ~username too long"); 101818de8d7fSPeter Avalos memcpy(user, filename, slash); 101918de8d7fSPeter Avalos user[slash] = '\0'; 102018de8d7fSPeter Avalos if ((pw = getpwnam(user)) == NULL) 102118de8d7fSPeter Avalos fatal("tilde_expand_filename: No such user %s", user); 102218de8d7fSPeter Avalos } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ 102318de8d7fSPeter Avalos fatal("tilde_expand_filename: No such uid %ld", (long)uid); 102418de8d7fSPeter Avalos 102518de8d7fSPeter Avalos /* Make sure directory has a trailing '/' */ 102618de8d7fSPeter Avalos len = strlen(pw->pw_dir); 102736e94dc5SPeter Avalos if (len == 0 || pw->pw_dir[len - 1] != '/') 102836e94dc5SPeter Avalos sep = "/"; 102936e94dc5SPeter Avalos else 103036e94dc5SPeter Avalos sep = ""; 103118de8d7fSPeter Avalos 103218de8d7fSPeter Avalos /* Skip leading '/' from specified path */ 103318de8d7fSPeter Avalos if (path != NULL) 103418de8d7fSPeter Avalos filename = path + 1; 103536e94dc5SPeter Avalos 1036e9778795SPeter Avalos if (xasprintf(&ret, "%s%s%s", pw->pw_dir, sep, filename) >= PATH_MAX) 103718de8d7fSPeter Avalos fatal("tilde_expand_filename: Path too long"); 103818de8d7fSPeter Avalos 103936e94dc5SPeter Avalos return (ret); 104018de8d7fSPeter Avalos } 104118de8d7fSPeter Avalos 104218de8d7fSPeter Avalos /* 104318de8d7fSPeter Avalos * Expand a string with a set of %[char] escapes. A number of escapes may be 104418de8d7fSPeter Avalos * specified as (char *escape_chars, char *replacement) pairs. The list must 104518de8d7fSPeter Avalos * be terminated by a NULL escape_char. Returns replaced string in memory 104618de8d7fSPeter Avalos * allocated by xmalloc. 104718de8d7fSPeter Avalos */ 104818de8d7fSPeter Avalos char * 104918de8d7fSPeter Avalos percent_expand(const char *string, ...) 105018de8d7fSPeter Avalos { 105118de8d7fSPeter Avalos #define EXPAND_MAX_KEYS 16 1052856ea928SPeter Avalos u_int num_keys, i, j; 105318de8d7fSPeter Avalos struct { 105418de8d7fSPeter Avalos const char *key; 105518de8d7fSPeter Avalos const char *repl; 105618de8d7fSPeter Avalos } keys[EXPAND_MAX_KEYS]; 105718de8d7fSPeter Avalos char buf[4096]; 105818de8d7fSPeter Avalos va_list ap; 105918de8d7fSPeter Avalos 106018de8d7fSPeter Avalos /* Gather keys */ 106118de8d7fSPeter Avalos va_start(ap, string); 106218de8d7fSPeter Avalos for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { 106318de8d7fSPeter Avalos keys[num_keys].key = va_arg(ap, char *); 106418de8d7fSPeter Avalos if (keys[num_keys].key == NULL) 106518de8d7fSPeter Avalos break; 106618de8d7fSPeter Avalos keys[num_keys].repl = va_arg(ap, char *); 106718de8d7fSPeter Avalos if (keys[num_keys].repl == NULL) 1068856ea928SPeter Avalos fatal("%s: NULL replacement", __func__); 106918de8d7fSPeter Avalos } 1070856ea928SPeter Avalos if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) 1071856ea928SPeter Avalos fatal("%s: too many keys", __func__); 107218de8d7fSPeter Avalos va_end(ap); 107318de8d7fSPeter Avalos 107418de8d7fSPeter Avalos /* Expand string */ 107518de8d7fSPeter Avalos *buf = '\0'; 107618de8d7fSPeter Avalos for (i = 0; *string != '\0'; string++) { 107718de8d7fSPeter Avalos if (*string != '%') { 107818de8d7fSPeter Avalos append: 107918de8d7fSPeter Avalos buf[i++] = *string; 108018de8d7fSPeter Avalos if (i >= sizeof(buf)) 1081856ea928SPeter Avalos fatal("%s: string too long", __func__); 108218de8d7fSPeter Avalos buf[i] = '\0'; 108318de8d7fSPeter Avalos continue; 108418de8d7fSPeter Avalos } 108518de8d7fSPeter Avalos string++; 1086856ea928SPeter Avalos /* %% case */ 108718de8d7fSPeter Avalos if (*string == '%') 108818de8d7fSPeter Avalos goto append; 1089e9778795SPeter Avalos if (*string == '\0') 1090e9778795SPeter Avalos fatal("%s: invalid format", __func__); 109118de8d7fSPeter Avalos for (j = 0; j < num_keys; j++) { 109218de8d7fSPeter Avalos if (strchr(keys[j].key, *string) != NULL) { 109318de8d7fSPeter Avalos i = strlcat(buf, keys[j].repl, sizeof(buf)); 109418de8d7fSPeter Avalos if (i >= sizeof(buf)) 1095856ea928SPeter Avalos fatal("%s: string too long", __func__); 109618de8d7fSPeter Avalos break; 109718de8d7fSPeter Avalos } 109818de8d7fSPeter Avalos } 109918de8d7fSPeter Avalos if (j >= num_keys) 1100856ea928SPeter Avalos fatal("%s: unknown key %%%c", __func__, *string); 110118de8d7fSPeter Avalos } 110218de8d7fSPeter Avalos return (xstrdup(buf)); 110318de8d7fSPeter Avalos #undef EXPAND_MAX_KEYS 110418de8d7fSPeter Avalos } 110518de8d7fSPeter Avalos 110618de8d7fSPeter Avalos int 1107*664f4763Szrj tun_open(int tun, int mode, char **ifname) 110818de8d7fSPeter Avalos { 110918de8d7fSPeter Avalos #if defined(CUSTOM_SYS_TUN_OPEN) 1110*664f4763Szrj return (sys_tun_open(tun, mode, ifname)); 111118de8d7fSPeter Avalos #elif defined(SSH_TUN_OPENBSD) 111218de8d7fSPeter Avalos struct ifreq ifr; 111318de8d7fSPeter Avalos char name[100]; 111418de8d7fSPeter Avalos int fd = -1, sock; 1115e9778795SPeter Avalos const char *tunbase = "tun"; 1116e9778795SPeter Avalos 1117*664f4763Szrj if (ifname != NULL) 1118*664f4763Szrj *ifname = NULL; 1119*664f4763Szrj 1120e9778795SPeter Avalos if (mode == SSH_TUNMODE_ETHERNET) 1121e9778795SPeter Avalos tunbase = "tap"; 112218de8d7fSPeter Avalos 112318de8d7fSPeter Avalos /* Open the tunnel device */ 112418de8d7fSPeter Avalos if (tun <= SSH_TUNID_MAX) { 1125e9778795SPeter Avalos snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); 112618de8d7fSPeter Avalos fd = open(name, O_RDWR); 112718de8d7fSPeter Avalos } else if (tun == SSH_TUNID_ANY) { 112818de8d7fSPeter Avalos for (tun = 100; tun >= 0; tun--) { 1129e9778795SPeter Avalos snprintf(name, sizeof(name), "/dev/%s%d", 1130e9778795SPeter Avalos tunbase, tun); 113118de8d7fSPeter Avalos if ((fd = open(name, O_RDWR)) >= 0) 113218de8d7fSPeter Avalos break; 113318de8d7fSPeter Avalos } 113418de8d7fSPeter Avalos } else { 113518de8d7fSPeter Avalos debug("%s: invalid tunnel %u", __func__, tun); 1136e9778795SPeter Avalos return -1; 113718de8d7fSPeter Avalos } 113818de8d7fSPeter Avalos 113918de8d7fSPeter Avalos if (fd < 0) { 1140e9778795SPeter Avalos debug("%s: %s open: %s", __func__, name, strerror(errno)); 1141e9778795SPeter Avalos return -1; 114218de8d7fSPeter Avalos } 114318de8d7fSPeter Avalos 114418de8d7fSPeter Avalos debug("%s: %s mode %d fd %d", __func__, name, mode, fd); 114518de8d7fSPeter Avalos 1146e9778795SPeter Avalos /* Bring interface up if it is not already */ 1147e9778795SPeter Avalos snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun); 114818de8d7fSPeter Avalos if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 114918de8d7fSPeter Avalos goto failed; 115018de8d7fSPeter Avalos 1151e9778795SPeter Avalos if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) { 1152e9778795SPeter Avalos debug("%s: get interface %s flags: %s", __func__, 1153e9778795SPeter Avalos ifr.ifr_name, strerror(errno)); 115418de8d7fSPeter Avalos goto failed; 1155e9778795SPeter Avalos } 115618de8d7fSPeter Avalos 1157e9778795SPeter Avalos if (!(ifr.ifr_flags & IFF_UP)) { 115818de8d7fSPeter Avalos ifr.ifr_flags |= IFF_UP; 1159e9778795SPeter Avalos if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) { 1160e9778795SPeter Avalos debug("%s: activate interface %s: %s", __func__, 1161e9778795SPeter Avalos ifr.ifr_name, strerror(errno)); 116218de8d7fSPeter Avalos goto failed; 1163e9778795SPeter Avalos } 1164e9778795SPeter Avalos } 116518de8d7fSPeter Avalos 1166*664f4763Szrj if (ifname != NULL) 1167*664f4763Szrj *ifname = xstrdup(ifr.ifr_name); 1168*664f4763Szrj 116918de8d7fSPeter Avalos close(sock); 1170e9778795SPeter Avalos return fd; 117118de8d7fSPeter Avalos 117218de8d7fSPeter Avalos failed: 117318de8d7fSPeter Avalos if (fd >= 0) 117418de8d7fSPeter Avalos close(fd); 117518de8d7fSPeter Avalos if (sock >= 0) 117618de8d7fSPeter Avalos close(sock); 1177e9778795SPeter Avalos return -1; 117818de8d7fSPeter Avalos #else 117918de8d7fSPeter Avalos error("Tunnel interfaces are not supported on this platform"); 118018de8d7fSPeter Avalos return (-1); 118118de8d7fSPeter Avalos #endif 118218de8d7fSPeter Avalos } 118318de8d7fSPeter Avalos 118418de8d7fSPeter Avalos void 118518de8d7fSPeter Avalos sanitise_stdfd(void) 118618de8d7fSPeter Avalos { 118718de8d7fSPeter Avalos int nullfd, dupfd; 118818de8d7fSPeter Avalos 118918de8d7fSPeter Avalos if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 1190cb5eb4f1SPeter Avalos fprintf(stderr, "Couldn't open /dev/null: %s\n", 1191cb5eb4f1SPeter Avalos strerror(errno)); 119218de8d7fSPeter Avalos exit(1); 119318de8d7fSPeter Avalos } 1194e9778795SPeter Avalos while (++dupfd <= STDERR_FILENO) { 1195e9778795SPeter Avalos /* Only populate closed fds. */ 1196e9778795SPeter Avalos if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) { 119718de8d7fSPeter Avalos if (dup2(nullfd, dupfd) == -1) { 1198cb5eb4f1SPeter Avalos fprintf(stderr, "dup2: %s\n", strerror(errno)); 119918de8d7fSPeter Avalos exit(1); 120018de8d7fSPeter Avalos } 120118de8d7fSPeter Avalos } 1202e9778795SPeter Avalos } 1203e9778795SPeter Avalos if (nullfd > STDERR_FILENO) 120418de8d7fSPeter Avalos close(nullfd); 120518de8d7fSPeter Avalos } 120618de8d7fSPeter Avalos 120718de8d7fSPeter Avalos char * 120818de8d7fSPeter Avalos tohex(const void *vp, size_t l) 120918de8d7fSPeter Avalos { 121018de8d7fSPeter Avalos const u_char *p = (const u_char *)vp; 121118de8d7fSPeter Avalos char b[3], *r; 121218de8d7fSPeter Avalos size_t i, hl; 121318de8d7fSPeter Avalos 121418de8d7fSPeter Avalos if (l > 65536) 121518de8d7fSPeter Avalos return xstrdup("tohex: length > 65536"); 121618de8d7fSPeter Avalos 121718de8d7fSPeter Avalos hl = l * 2 + 1; 121818de8d7fSPeter Avalos r = xcalloc(1, hl); 121918de8d7fSPeter Avalos for (i = 0; i < l; i++) { 122018de8d7fSPeter Avalos snprintf(b, sizeof(b), "%02x", p[i]); 122118de8d7fSPeter Avalos strlcat(r, b, hl); 122218de8d7fSPeter Avalos } 122318de8d7fSPeter Avalos return (r); 122418de8d7fSPeter Avalos } 122518de8d7fSPeter Avalos 122618de8d7fSPeter Avalos u_int64_t 122718de8d7fSPeter Avalos get_u64(const void *vp) 122818de8d7fSPeter Avalos { 122918de8d7fSPeter Avalos const u_char *p = (const u_char *)vp; 123018de8d7fSPeter Avalos u_int64_t v; 123118de8d7fSPeter Avalos 123218de8d7fSPeter Avalos v = (u_int64_t)p[0] << 56; 123318de8d7fSPeter Avalos v |= (u_int64_t)p[1] << 48; 123418de8d7fSPeter Avalos v |= (u_int64_t)p[2] << 40; 123518de8d7fSPeter Avalos v |= (u_int64_t)p[3] << 32; 123618de8d7fSPeter Avalos v |= (u_int64_t)p[4] << 24; 123718de8d7fSPeter Avalos v |= (u_int64_t)p[5] << 16; 123818de8d7fSPeter Avalos v |= (u_int64_t)p[6] << 8; 123918de8d7fSPeter Avalos v |= (u_int64_t)p[7]; 124018de8d7fSPeter Avalos 124118de8d7fSPeter Avalos return (v); 124218de8d7fSPeter Avalos } 124318de8d7fSPeter Avalos 124418de8d7fSPeter Avalos u_int32_t 124518de8d7fSPeter Avalos get_u32(const void *vp) 124618de8d7fSPeter Avalos { 124718de8d7fSPeter Avalos const u_char *p = (const u_char *)vp; 124818de8d7fSPeter Avalos u_int32_t v; 124918de8d7fSPeter Avalos 125018de8d7fSPeter Avalos v = (u_int32_t)p[0] << 24; 125118de8d7fSPeter Avalos v |= (u_int32_t)p[1] << 16; 125218de8d7fSPeter Avalos v |= (u_int32_t)p[2] << 8; 125318de8d7fSPeter Avalos v |= (u_int32_t)p[3]; 125418de8d7fSPeter Avalos 125518de8d7fSPeter Avalos return (v); 125618de8d7fSPeter Avalos } 125718de8d7fSPeter Avalos 125836e94dc5SPeter Avalos u_int32_t 125936e94dc5SPeter Avalos get_u32_le(const void *vp) 126036e94dc5SPeter Avalos { 126136e94dc5SPeter Avalos const u_char *p = (const u_char *)vp; 126236e94dc5SPeter Avalos u_int32_t v; 126336e94dc5SPeter Avalos 126436e94dc5SPeter Avalos v = (u_int32_t)p[0]; 126536e94dc5SPeter Avalos v |= (u_int32_t)p[1] << 8; 126636e94dc5SPeter Avalos v |= (u_int32_t)p[2] << 16; 126736e94dc5SPeter Avalos v |= (u_int32_t)p[3] << 24; 126836e94dc5SPeter Avalos 126936e94dc5SPeter Avalos return (v); 127036e94dc5SPeter Avalos } 127136e94dc5SPeter Avalos 127218de8d7fSPeter Avalos u_int16_t 127318de8d7fSPeter Avalos get_u16(const void *vp) 127418de8d7fSPeter Avalos { 127518de8d7fSPeter Avalos const u_char *p = (const u_char *)vp; 127618de8d7fSPeter Avalos u_int16_t v; 127718de8d7fSPeter Avalos 127818de8d7fSPeter Avalos v = (u_int16_t)p[0] << 8; 127918de8d7fSPeter Avalos v |= (u_int16_t)p[1]; 128018de8d7fSPeter Avalos 128118de8d7fSPeter Avalos return (v); 128218de8d7fSPeter Avalos } 128318de8d7fSPeter Avalos 128418de8d7fSPeter Avalos void 128518de8d7fSPeter Avalos put_u64(void *vp, u_int64_t v) 128618de8d7fSPeter Avalos { 128718de8d7fSPeter Avalos u_char *p = (u_char *)vp; 128818de8d7fSPeter Avalos 128918de8d7fSPeter Avalos p[0] = (u_char)(v >> 56) & 0xff; 129018de8d7fSPeter Avalos p[1] = (u_char)(v >> 48) & 0xff; 129118de8d7fSPeter Avalos p[2] = (u_char)(v >> 40) & 0xff; 129218de8d7fSPeter Avalos p[3] = (u_char)(v >> 32) & 0xff; 129318de8d7fSPeter Avalos p[4] = (u_char)(v >> 24) & 0xff; 129418de8d7fSPeter Avalos p[5] = (u_char)(v >> 16) & 0xff; 129518de8d7fSPeter Avalos p[6] = (u_char)(v >> 8) & 0xff; 129618de8d7fSPeter Avalos p[7] = (u_char)v & 0xff; 129718de8d7fSPeter Avalos } 129818de8d7fSPeter Avalos 129918de8d7fSPeter Avalos void 130018de8d7fSPeter Avalos put_u32(void *vp, u_int32_t v) 130118de8d7fSPeter Avalos { 130218de8d7fSPeter Avalos u_char *p = (u_char *)vp; 130318de8d7fSPeter Avalos 130418de8d7fSPeter Avalos p[0] = (u_char)(v >> 24) & 0xff; 130518de8d7fSPeter Avalos p[1] = (u_char)(v >> 16) & 0xff; 130618de8d7fSPeter Avalos p[2] = (u_char)(v >> 8) & 0xff; 130718de8d7fSPeter Avalos p[3] = (u_char)v & 0xff; 130818de8d7fSPeter Avalos } 130918de8d7fSPeter Avalos 131036e94dc5SPeter Avalos void 131136e94dc5SPeter Avalos put_u32_le(void *vp, u_int32_t v) 131236e94dc5SPeter Avalos { 131336e94dc5SPeter Avalos u_char *p = (u_char *)vp; 131436e94dc5SPeter Avalos 131536e94dc5SPeter Avalos p[0] = (u_char)v & 0xff; 131636e94dc5SPeter Avalos p[1] = (u_char)(v >> 8) & 0xff; 131736e94dc5SPeter Avalos p[2] = (u_char)(v >> 16) & 0xff; 131836e94dc5SPeter Avalos p[3] = (u_char)(v >> 24) & 0xff; 131936e94dc5SPeter Avalos } 132018de8d7fSPeter Avalos 132118de8d7fSPeter Avalos void 132218de8d7fSPeter Avalos put_u16(void *vp, u_int16_t v) 132318de8d7fSPeter Avalos { 132418de8d7fSPeter Avalos u_char *p = (u_char *)vp; 132518de8d7fSPeter Avalos 132618de8d7fSPeter Avalos p[0] = (u_char)(v >> 8) & 0xff; 132718de8d7fSPeter Avalos p[1] = (u_char)v & 0xff; 132818de8d7fSPeter Avalos } 132918de8d7fSPeter Avalos 133018de8d7fSPeter Avalos void 133118de8d7fSPeter Avalos ms_subtract_diff(struct timeval *start, int *ms) 133218de8d7fSPeter Avalos { 133318de8d7fSPeter Avalos struct timeval diff, finish; 133418de8d7fSPeter Avalos 1335*664f4763Szrj monotime_tv(&finish); 133618de8d7fSPeter Avalos timersub(&finish, start, &diff); 133718de8d7fSPeter Avalos *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); 133818de8d7fSPeter Avalos } 133918de8d7fSPeter Avalos 134018de8d7fSPeter Avalos void 134118de8d7fSPeter Avalos ms_to_timeval(struct timeval *tv, int ms) 134218de8d7fSPeter Avalos { 134318de8d7fSPeter Avalos if (ms < 0) 134418de8d7fSPeter Avalos ms = 0; 134518de8d7fSPeter Avalos tv->tv_sec = ms / 1000; 134618de8d7fSPeter Avalos tv->tv_usec = (ms % 1000) * 1000; 134718de8d7fSPeter Avalos } 134818de8d7fSPeter Avalos 1349*664f4763Szrj void 1350*664f4763Szrj monotime_ts(struct timespec *ts) 135136e94dc5SPeter Avalos { 1352*664f4763Szrj struct timeval tv; 1353*664f4763Szrj #if defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_BOOTTIME) || \ 1354*664f4763Szrj defined(CLOCK_MONOTONIC) || defined(CLOCK_REALTIME)) 135536e94dc5SPeter Avalos static int gettime_failed = 0; 135636e94dc5SPeter Avalos 135736e94dc5SPeter Avalos if (!gettime_failed) { 1358*664f4763Szrj # ifdef CLOCK_BOOTTIME 1359*664f4763Szrj if (clock_gettime(CLOCK_BOOTTIME, ts) == 0) 1360*664f4763Szrj return; 1361*664f4763Szrj # endif /* CLOCK_BOOTTIME */ 1362*664f4763Szrj # ifdef CLOCK_MONOTONIC 1363*664f4763Szrj if (clock_gettime(CLOCK_MONOTONIC, ts) == 0) 1364*664f4763Szrj return; 1365*664f4763Szrj # endif /* CLOCK_MONOTONIC */ 1366*664f4763Szrj # ifdef CLOCK_REALTIME 1367*664f4763Szrj /* Not monotonic, but we're almost out of options here. */ 1368*664f4763Szrj if (clock_gettime(CLOCK_REALTIME, ts) == 0) 1369*664f4763Szrj return; 1370*664f4763Szrj # endif /* CLOCK_REALTIME */ 137136e94dc5SPeter Avalos debug3("clock_gettime: %s", strerror(errno)); 137236e94dc5SPeter Avalos gettime_failed = 1; 137336e94dc5SPeter Avalos } 1374*664f4763Szrj #endif /* HAVE_CLOCK_GETTIME && (BOOTTIME || MONOTONIC || REALTIME) */ 1375*664f4763Szrj gettimeofday(&tv, NULL); 1376*664f4763Szrj ts->tv_sec = tv.tv_sec; 1377*664f4763Szrj ts->tv_nsec = (long)tv.tv_usec * 1000; 1378*664f4763Szrj } 137936e94dc5SPeter Avalos 1380*664f4763Szrj void 1381*664f4763Szrj monotime_tv(struct timeval *tv) 1382*664f4763Szrj { 1383*664f4763Szrj struct timespec ts; 1384*664f4763Szrj 1385*664f4763Szrj monotime_ts(&ts); 1386*664f4763Szrj tv->tv_sec = ts.tv_sec; 1387*664f4763Szrj tv->tv_usec = ts.tv_nsec / 1000; 1388*664f4763Szrj } 1389*664f4763Szrj 1390*664f4763Szrj time_t 1391*664f4763Szrj monotime(void) 1392*664f4763Szrj { 1393*664f4763Szrj struct timespec ts; 1394*664f4763Szrj 1395*664f4763Szrj monotime_ts(&ts); 1396*664f4763Szrj return ts.tv_sec; 139736e94dc5SPeter Avalos } 139836e94dc5SPeter Avalos 1399e9778795SPeter Avalos double 1400e9778795SPeter Avalos monotime_double(void) 1401e9778795SPeter Avalos { 1402e9778795SPeter Avalos struct timespec ts; 1403e9778795SPeter Avalos 1404*664f4763Szrj monotime_ts(&ts); 1405*664f4763Szrj return ts.tv_sec + ((double)ts.tv_nsec / 1000000000); 1406e9778795SPeter Avalos } 1407e9778795SPeter Avalos 14089f304aafSPeter Avalos void 14099f304aafSPeter Avalos bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen) 1410856ea928SPeter Avalos { 14119f304aafSPeter Avalos bw->buflen = buflen; 14129f304aafSPeter Avalos bw->rate = kbps; 1413*664f4763Szrj bw->thresh = buflen; 14149f304aafSPeter Avalos bw->lamt = 0; 14159f304aafSPeter Avalos timerclear(&bw->bwstart); 14169f304aafSPeter Avalos timerclear(&bw->bwend); 1417856ea928SPeter Avalos } 14189f304aafSPeter Avalos 14199f304aafSPeter Avalos /* Callback from read/write loop to insert bandwidth-limiting delays */ 14209f304aafSPeter Avalos void 14219f304aafSPeter Avalos bandwidth_limit(struct bwlimit *bw, size_t read_len) 14229f304aafSPeter Avalos { 14239f304aafSPeter Avalos u_int64_t waitlen; 14249f304aafSPeter Avalos struct timespec ts, rm; 14259f304aafSPeter Avalos 1426*664f4763Szrj bw->lamt += read_len; 14279f304aafSPeter Avalos if (!timerisset(&bw->bwstart)) { 1428*664f4763Szrj monotime_tv(&bw->bwstart); 14299f304aafSPeter Avalos return; 14309f304aafSPeter Avalos } 14319f304aafSPeter Avalos if (bw->lamt < bw->thresh) 14329f304aafSPeter Avalos return; 14339f304aafSPeter Avalos 1434*664f4763Szrj monotime_tv(&bw->bwend); 14359f304aafSPeter Avalos timersub(&bw->bwend, &bw->bwstart, &bw->bwend); 14369f304aafSPeter Avalos if (!timerisset(&bw->bwend)) 14379f304aafSPeter Avalos return; 14389f304aafSPeter Avalos 14399f304aafSPeter Avalos bw->lamt *= 8; 14409f304aafSPeter Avalos waitlen = (double)1000000L * bw->lamt / bw->rate; 14419f304aafSPeter Avalos 14429f304aafSPeter Avalos bw->bwstart.tv_sec = waitlen / 1000000L; 14439f304aafSPeter Avalos bw->bwstart.tv_usec = waitlen % 1000000L; 14449f304aafSPeter Avalos 14459f304aafSPeter Avalos if (timercmp(&bw->bwstart, &bw->bwend, >)) { 14469f304aafSPeter Avalos timersub(&bw->bwstart, &bw->bwend, &bw->bwend); 14479f304aafSPeter Avalos 14489f304aafSPeter Avalos /* Adjust the wait time */ 14499f304aafSPeter Avalos if (bw->bwend.tv_sec) { 14509f304aafSPeter Avalos bw->thresh /= 2; 14519f304aafSPeter Avalos if (bw->thresh < bw->buflen / 4) 14529f304aafSPeter Avalos bw->thresh = bw->buflen / 4; 14539f304aafSPeter Avalos } else if (bw->bwend.tv_usec < 10000) { 14549f304aafSPeter Avalos bw->thresh *= 2; 14559f304aafSPeter Avalos if (bw->thresh > bw->buflen * 8) 14569f304aafSPeter Avalos bw->thresh = bw->buflen * 8; 14579f304aafSPeter Avalos } 14589f304aafSPeter Avalos 14599f304aafSPeter Avalos TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts); 14609f304aafSPeter Avalos while (nanosleep(&ts, &rm) == -1) { 14619f304aafSPeter Avalos if (errno != EINTR) 14629f304aafSPeter Avalos break; 14639f304aafSPeter Avalos ts = rm; 14649f304aafSPeter Avalos } 14659f304aafSPeter Avalos } 14669f304aafSPeter Avalos 14679f304aafSPeter Avalos bw->lamt = 0; 1468*664f4763Szrj monotime_tv(&bw->bwstart); 14699f304aafSPeter Avalos } 14709f304aafSPeter Avalos 14719f304aafSPeter Avalos /* Make a template filename for mk[sd]temp() */ 14729f304aafSPeter Avalos void 14739f304aafSPeter Avalos mktemp_proto(char *s, size_t len) 14749f304aafSPeter Avalos { 14759f304aafSPeter Avalos const char *tmpdir; 14769f304aafSPeter Avalos int r; 14779f304aafSPeter Avalos 14789f304aafSPeter Avalos if ((tmpdir = getenv("TMPDIR")) != NULL) { 14799f304aafSPeter Avalos r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir); 14809f304aafSPeter Avalos if (r > 0 && (size_t)r < len) 14819f304aafSPeter Avalos return; 14829f304aafSPeter Avalos } 14839f304aafSPeter Avalos r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX"); 14849f304aafSPeter Avalos if (r < 0 || (size_t)r >= len) 14859f304aafSPeter Avalos fatal("%s: template string too short", __func__); 14869f304aafSPeter Avalos } 14879f304aafSPeter Avalos 14889f304aafSPeter Avalos static const struct { 14899f304aafSPeter Avalos const char *name; 14909f304aafSPeter Avalos int value; 14919f304aafSPeter Avalos } ipqos[] = { 1492ce74bacaSMatthew Dillon { "none", INT_MAX }, /* can't use 0 here; that's CS0 */ 14939f304aafSPeter Avalos { "af11", IPTOS_DSCP_AF11 }, 14949f304aafSPeter Avalos { "af12", IPTOS_DSCP_AF12 }, 14959f304aafSPeter Avalos { "af13", IPTOS_DSCP_AF13 }, 149699e85e0dSPeter Avalos { "af21", IPTOS_DSCP_AF21 }, 14979f304aafSPeter Avalos { "af22", IPTOS_DSCP_AF22 }, 14989f304aafSPeter Avalos { "af23", IPTOS_DSCP_AF23 }, 14999f304aafSPeter Avalos { "af31", IPTOS_DSCP_AF31 }, 15009f304aafSPeter Avalos { "af32", IPTOS_DSCP_AF32 }, 15019f304aafSPeter Avalos { "af33", IPTOS_DSCP_AF33 }, 15029f304aafSPeter Avalos { "af41", IPTOS_DSCP_AF41 }, 15039f304aafSPeter Avalos { "af42", IPTOS_DSCP_AF42 }, 15049f304aafSPeter Avalos { "af43", IPTOS_DSCP_AF43 }, 15059f304aafSPeter Avalos { "cs0", IPTOS_DSCP_CS0 }, 15069f304aafSPeter Avalos { "cs1", IPTOS_DSCP_CS1 }, 15079f304aafSPeter Avalos { "cs2", IPTOS_DSCP_CS2 }, 15089f304aafSPeter Avalos { "cs3", IPTOS_DSCP_CS3 }, 15099f304aafSPeter Avalos { "cs4", IPTOS_DSCP_CS4 }, 15109f304aafSPeter Avalos { "cs5", IPTOS_DSCP_CS5 }, 15119f304aafSPeter Avalos { "cs6", IPTOS_DSCP_CS6 }, 15129f304aafSPeter Avalos { "cs7", IPTOS_DSCP_CS7 }, 15139f304aafSPeter Avalos { "ef", IPTOS_DSCP_EF }, 15149f304aafSPeter Avalos { "lowdelay", IPTOS_LOWDELAY }, 15159f304aafSPeter Avalos { "throughput", IPTOS_THROUGHPUT }, 15169f304aafSPeter Avalos { "reliability", IPTOS_RELIABILITY }, 15179f304aafSPeter Avalos { NULL, -1 } 15189f304aafSPeter Avalos }; 15199f304aafSPeter Avalos 15209f304aafSPeter Avalos int 15219f304aafSPeter Avalos parse_ipqos(const char *cp) 15229f304aafSPeter Avalos { 15239f304aafSPeter Avalos u_int i; 15249f304aafSPeter Avalos char *ep; 15259f304aafSPeter Avalos long val; 15269f304aafSPeter Avalos 15279f304aafSPeter Avalos if (cp == NULL) 15289f304aafSPeter Avalos return -1; 15299f304aafSPeter Avalos for (i = 0; ipqos[i].name != NULL; i++) { 15309f304aafSPeter Avalos if (strcasecmp(cp, ipqos[i].name) == 0) 15319f304aafSPeter Avalos return ipqos[i].value; 15329f304aafSPeter Avalos } 15339f304aafSPeter Avalos /* Try parsing as an integer */ 15349f304aafSPeter Avalos val = strtol(cp, &ep, 0); 15359f304aafSPeter Avalos if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255) 15369f304aafSPeter Avalos return -1; 15379f304aafSPeter Avalos return val; 15389f304aafSPeter Avalos } 15399f304aafSPeter Avalos 15401c188a7fSPeter Avalos const char * 15411c188a7fSPeter Avalos iptos2str(int iptos) 15421c188a7fSPeter Avalos { 15431c188a7fSPeter Avalos int i; 15441c188a7fSPeter Avalos static char iptos_str[sizeof "0xff"]; 15451c188a7fSPeter Avalos 15461c188a7fSPeter Avalos for (i = 0; ipqos[i].name != NULL; i++) { 15471c188a7fSPeter Avalos if (ipqos[i].value == iptos) 15481c188a7fSPeter Avalos return ipqos[i].name; 15491c188a7fSPeter Avalos } 15501c188a7fSPeter Avalos snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos); 15511c188a7fSPeter Avalos return iptos_str; 15521c188a7fSPeter Avalos } 155336e94dc5SPeter Avalos 155436e94dc5SPeter Avalos void 155536e94dc5SPeter Avalos lowercase(char *s) 155636e94dc5SPeter Avalos { 155736e94dc5SPeter Avalos for (; *s; s++) 155836e94dc5SPeter Avalos *s = tolower((u_char)*s); 155936e94dc5SPeter Avalos } 156036e94dc5SPeter Avalos 156136e94dc5SPeter Avalos int 156236e94dc5SPeter Avalos unix_listener(const char *path, int backlog, int unlink_first) 156336e94dc5SPeter Avalos { 156436e94dc5SPeter Avalos struct sockaddr_un sunaddr; 156536e94dc5SPeter Avalos int saved_errno, sock; 156636e94dc5SPeter Avalos 156736e94dc5SPeter Avalos memset(&sunaddr, 0, sizeof(sunaddr)); 156836e94dc5SPeter Avalos sunaddr.sun_family = AF_UNIX; 1569*664f4763Szrj if (strlcpy(sunaddr.sun_path, path, 1570*664f4763Szrj sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) { 1571*664f4763Szrj error("%s: path \"%s\" too long for Unix domain socket", 1572*664f4763Szrj __func__, path); 157336e94dc5SPeter Avalos errno = ENAMETOOLONG; 157436e94dc5SPeter Avalos return -1; 157536e94dc5SPeter Avalos } 157636e94dc5SPeter Avalos 157736e94dc5SPeter Avalos sock = socket(PF_UNIX, SOCK_STREAM, 0); 157836e94dc5SPeter Avalos if (sock < 0) { 157936e94dc5SPeter Avalos saved_errno = errno; 1580*664f4763Szrj error("%s: socket: %.100s", __func__, strerror(errno)); 158136e94dc5SPeter Avalos errno = saved_errno; 158236e94dc5SPeter Avalos return -1; 158336e94dc5SPeter Avalos } 158436e94dc5SPeter Avalos if (unlink_first == 1) { 158536e94dc5SPeter Avalos if (unlink(path) != 0 && errno != ENOENT) 158636e94dc5SPeter Avalos error("unlink(%s): %.100s", path, strerror(errno)); 158736e94dc5SPeter Avalos } 158836e94dc5SPeter Avalos if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { 158936e94dc5SPeter Avalos saved_errno = errno; 1590*664f4763Szrj error("%s: cannot bind to path %s: %s", 1591*664f4763Szrj __func__, path, strerror(errno)); 159236e94dc5SPeter Avalos close(sock); 159336e94dc5SPeter Avalos errno = saved_errno; 159436e94dc5SPeter Avalos return -1; 159536e94dc5SPeter Avalos } 159636e94dc5SPeter Avalos if (listen(sock, backlog) < 0) { 159736e94dc5SPeter Avalos saved_errno = errno; 1598*664f4763Szrj error("%s: cannot listen on path %s: %s", 1599*664f4763Szrj __func__, path, strerror(errno)); 160036e94dc5SPeter Avalos close(sock); 160136e94dc5SPeter Avalos unlink(path); 160236e94dc5SPeter Avalos errno = saved_errno; 160336e94dc5SPeter Avalos return -1; 160436e94dc5SPeter Avalos } 160536e94dc5SPeter Avalos return sock; 160636e94dc5SPeter Avalos } 160736e94dc5SPeter Avalos 1608856ea928SPeter Avalos void 1609856ea928SPeter Avalos sock_set_v6only(int s) 1610856ea928SPeter Avalos { 1611e9778795SPeter Avalos #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) 1612856ea928SPeter Avalos int on = 1; 1613856ea928SPeter Avalos 1614856ea928SPeter Avalos debug3("%s: set socket %d IPV6_V6ONLY", __func__, s); 1615856ea928SPeter Avalos if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) 1616856ea928SPeter Avalos error("setsockopt IPV6_V6ONLY: %s", strerror(errno)); 1617856ea928SPeter Avalos #endif 1618856ea928SPeter Avalos } 1619e9778795SPeter Avalos 1620e9778795SPeter Avalos /* 1621e9778795SPeter Avalos * Compares two strings that maybe be NULL. Returns non-zero if strings 1622e9778795SPeter Avalos * are both NULL or are identical, returns zero otherwise. 1623e9778795SPeter Avalos */ 1624e9778795SPeter Avalos static int 1625e9778795SPeter Avalos strcmp_maybe_null(const char *a, const char *b) 1626e9778795SPeter Avalos { 1627e9778795SPeter Avalos if ((a == NULL && b != NULL) || (a != NULL && b == NULL)) 1628e9778795SPeter Avalos return 0; 1629e9778795SPeter Avalos if (a != NULL && strcmp(a, b) != 0) 1630e9778795SPeter Avalos return 0; 1631e9778795SPeter Avalos return 1; 1632e9778795SPeter Avalos } 1633e9778795SPeter Avalos 1634e9778795SPeter Avalos /* 1635e9778795SPeter Avalos * Compare two forwards, returning non-zero if they are identical or 1636e9778795SPeter Avalos * zero otherwise. 1637e9778795SPeter Avalos */ 1638e9778795SPeter Avalos int 1639e9778795SPeter Avalos forward_equals(const struct Forward *a, const struct Forward *b) 1640e9778795SPeter Avalos { 1641e9778795SPeter Avalos if (strcmp_maybe_null(a->listen_host, b->listen_host) == 0) 1642e9778795SPeter Avalos return 0; 1643e9778795SPeter Avalos if (a->listen_port != b->listen_port) 1644e9778795SPeter Avalos return 0; 1645e9778795SPeter Avalos if (strcmp_maybe_null(a->listen_path, b->listen_path) == 0) 1646e9778795SPeter Avalos return 0; 1647e9778795SPeter Avalos if (strcmp_maybe_null(a->connect_host, b->connect_host) == 0) 1648e9778795SPeter Avalos return 0; 1649e9778795SPeter Avalos if (a->connect_port != b->connect_port) 1650e9778795SPeter Avalos return 0; 1651e9778795SPeter Avalos if (strcmp_maybe_null(a->connect_path, b->connect_path) == 0) 1652e9778795SPeter Avalos return 0; 1653e9778795SPeter Avalos /* allocated_port and handle are not checked */ 1654e9778795SPeter Avalos return 1; 1655e9778795SPeter Avalos } 1656e9778795SPeter Avalos 1657ce74bacaSMatthew Dillon /* returns 1 if process is already daemonized, 0 otherwise */ 1658ce74bacaSMatthew Dillon int 1659ce74bacaSMatthew Dillon daemonized(void) 1660ce74bacaSMatthew Dillon { 1661ce74bacaSMatthew Dillon int fd; 1662ce74bacaSMatthew Dillon 1663ce74bacaSMatthew Dillon if ((fd = open(_PATH_TTY, O_RDONLY | O_NOCTTY)) >= 0) { 1664ce74bacaSMatthew Dillon close(fd); 1665ce74bacaSMatthew Dillon return 0; /* have controlling terminal */ 1666ce74bacaSMatthew Dillon } 1667ce74bacaSMatthew Dillon if (getppid() != 1) 1668ce74bacaSMatthew Dillon return 0; /* parent is not init */ 1669ce74bacaSMatthew Dillon if (getsid(0) != getpid()) 1670ce74bacaSMatthew Dillon return 0; /* not session leader */ 1671ce74bacaSMatthew Dillon debug3("already daemonized"); 1672ce74bacaSMatthew Dillon return 1; 1673ce74bacaSMatthew Dillon } 1674ce74bacaSMatthew Dillon 1675ce74bacaSMatthew Dillon 1676ce74bacaSMatthew Dillon /* 1677ce74bacaSMatthew Dillon * Splits 's' into an argument vector. Handles quoted string and basic 1678ce74bacaSMatthew Dillon * escape characters (\\, \", \'). Caller must free the argument vector 1679ce74bacaSMatthew Dillon * and its members. 1680ce74bacaSMatthew Dillon */ 1681ce74bacaSMatthew Dillon int 1682ce74bacaSMatthew Dillon argv_split(const char *s, int *argcp, char ***argvp) 1683ce74bacaSMatthew Dillon { 1684ce74bacaSMatthew Dillon int r = SSH_ERR_INTERNAL_ERROR; 1685ce74bacaSMatthew Dillon int argc = 0, quote, i, j; 1686ce74bacaSMatthew Dillon char *arg, **argv = xcalloc(1, sizeof(*argv)); 1687ce74bacaSMatthew Dillon 1688ce74bacaSMatthew Dillon *argvp = NULL; 1689ce74bacaSMatthew Dillon *argcp = 0; 1690ce74bacaSMatthew Dillon 1691ce74bacaSMatthew Dillon for (i = 0; s[i] != '\0'; i++) { 1692ce74bacaSMatthew Dillon /* Skip leading whitespace */ 1693ce74bacaSMatthew Dillon if (s[i] == ' ' || s[i] == '\t') 1694ce74bacaSMatthew Dillon continue; 1695ce74bacaSMatthew Dillon 1696ce74bacaSMatthew Dillon /* Start of a token */ 1697ce74bacaSMatthew Dillon quote = 0; 1698ce74bacaSMatthew Dillon if (s[i] == '\\' && 1699ce74bacaSMatthew Dillon (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\')) 1700ce74bacaSMatthew Dillon i++; 1701ce74bacaSMatthew Dillon else if (s[i] == '\'' || s[i] == '"') 1702ce74bacaSMatthew Dillon quote = s[i++]; 1703ce74bacaSMatthew Dillon 1704ce74bacaSMatthew Dillon argv = xreallocarray(argv, (argc + 2), sizeof(*argv)); 1705ce74bacaSMatthew Dillon arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1); 1706ce74bacaSMatthew Dillon argv[argc] = NULL; 1707ce74bacaSMatthew Dillon 1708ce74bacaSMatthew Dillon /* Copy the token in, removing escapes */ 1709ce74bacaSMatthew Dillon for (j = 0; s[i] != '\0'; i++) { 1710ce74bacaSMatthew Dillon if (s[i] == '\\') { 1711ce74bacaSMatthew Dillon if (s[i + 1] == '\'' || 1712ce74bacaSMatthew Dillon s[i + 1] == '\"' || 1713ce74bacaSMatthew Dillon s[i + 1] == '\\') { 1714ce74bacaSMatthew Dillon i++; /* Skip '\' */ 1715ce74bacaSMatthew Dillon arg[j++] = s[i]; 1716ce74bacaSMatthew Dillon } else { 1717ce74bacaSMatthew Dillon /* Unrecognised escape */ 1718ce74bacaSMatthew Dillon arg[j++] = s[i]; 1719ce74bacaSMatthew Dillon } 1720ce74bacaSMatthew Dillon } else if (quote == 0 && (s[i] == ' ' || s[i] == '\t')) 1721ce74bacaSMatthew Dillon break; /* done */ 1722ce74bacaSMatthew Dillon else if (quote != 0 && s[i] == quote) 1723ce74bacaSMatthew Dillon break; /* done */ 1724ce74bacaSMatthew Dillon else 1725ce74bacaSMatthew Dillon arg[j++] = s[i]; 1726ce74bacaSMatthew Dillon } 1727ce74bacaSMatthew Dillon if (s[i] == '\0') { 1728ce74bacaSMatthew Dillon if (quote != 0) { 1729ce74bacaSMatthew Dillon /* Ran out of string looking for close quote */ 1730ce74bacaSMatthew Dillon r = SSH_ERR_INVALID_FORMAT; 1731ce74bacaSMatthew Dillon goto out; 1732ce74bacaSMatthew Dillon } 1733ce74bacaSMatthew Dillon break; 1734ce74bacaSMatthew Dillon } 1735ce74bacaSMatthew Dillon } 1736ce74bacaSMatthew Dillon /* Success */ 1737ce74bacaSMatthew Dillon *argcp = argc; 1738ce74bacaSMatthew Dillon *argvp = argv; 1739ce74bacaSMatthew Dillon argc = 0; 1740ce74bacaSMatthew Dillon argv = NULL; 1741ce74bacaSMatthew Dillon r = 0; 1742ce74bacaSMatthew Dillon out: 1743ce74bacaSMatthew Dillon if (argc != 0 && argv != NULL) { 1744ce74bacaSMatthew Dillon for (i = 0; i < argc; i++) 1745ce74bacaSMatthew Dillon free(argv[i]); 1746ce74bacaSMatthew Dillon free(argv); 1747ce74bacaSMatthew Dillon } 1748ce74bacaSMatthew Dillon return r; 1749ce74bacaSMatthew Dillon } 1750ce74bacaSMatthew Dillon 1751ce74bacaSMatthew Dillon /* 1752ce74bacaSMatthew Dillon * Reassemble an argument vector into a string, quoting and escaping as 1753ce74bacaSMatthew Dillon * necessary. Caller must free returned string. 1754ce74bacaSMatthew Dillon */ 1755ce74bacaSMatthew Dillon char * 1756ce74bacaSMatthew Dillon argv_assemble(int argc, char **argv) 1757ce74bacaSMatthew Dillon { 1758ce74bacaSMatthew Dillon int i, j, ws, r; 1759ce74bacaSMatthew Dillon char c, *ret; 1760ce74bacaSMatthew Dillon struct sshbuf *buf, *arg; 1761ce74bacaSMatthew Dillon 1762ce74bacaSMatthew Dillon if ((buf = sshbuf_new()) == NULL || (arg = sshbuf_new()) == NULL) 1763ce74bacaSMatthew Dillon fatal("%s: sshbuf_new failed", __func__); 1764ce74bacaSMatthew Dillon 1765ce74bacaSMatthew Dillon for (i = 0; i < argc; i++) { 1766ce74bacaSMatthew Dillon ws = 0; 1767ce74bacaSMatthew Dillon sshbuf_reset(arg); 1768ce74bacaSMatthew Dillon for (j = 0; argv[i][j] != '\0'; j++) { 1769ce74bacaSMatthew Dillon r = 0; 1770ce74bacaSMatthew Dillon c = argv[i][j]; 1771ce74bacaSMatthew Dillon switch (c) { 1772ce74bacaSMatthew Dillon case ' ': 1773ce74bacaSMatthew Dillon case '\t': 1774ce74bacaSMatthew Dillon ws = 1; 1775ce74bacaSMatthew Dillon r = sshbuf_put_u8(arg, c); 1776ce74bacaSMatthew Dillon break; 1777ce74bacaSMatthew Dillon case '\\': 1778ce74bacaSMatthew Dillon case '\'': 1779ce74bacaSMatthew Dillon case '"': 1780ce74bacaSMatthew Dillon if ((r = sshbuf_put_u8(arg, '\\')) != 0) 1781ce74bacaSMatthew Dillon break; 1782ce74bacaSMatthew Dillon /* FALLTHROUGH */ 1783ce74bacaSMatthew Dillon default: 1784ce74bacaSMatthew Dillon r = sshbuf_put_u8(arg, c); 1785ce74bacaSMatthew Dillon break; 1786ce74bacaSMatthew Dillon } 1787ce74bacaSMatthew Dillon if (r != 0) 1788ce74bacaSMatthew Dillon fatal("%s: sshbuf_put_u8: %s", 1789ce74bacaSMatthew Dillon __func__, ssh_err(r)); 1790ce74bacaSMatthew Dillon } 1791ce74bacaSMatthew Dillon if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) || 1792ce74bacaSMatthew Dillon (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) || 1793ce74bacaSMatthew Dillon (r = sshbuf_putb(buf, arg)) != 0 || 1794ce74bacaSMatthew Dillon (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0)) 1795ce74bacaSMatthew Dillon fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1796ce74bacaSMatthew Dillon } 1797ce74bacaSMatthew Dillon if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL) 1798ce74bacaSMatthew Dillon fatal("%s: malloc failed", __func__); 1799ce74bacaSMatthew Dillon memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf)); 1800ce74bacaSMatthew Dillon ret[sshbuf_len(buf)] = '\0'; 1801ce74bacaSMatthew Dillon sshbuf_free(buf); 1802ce74bacaSMatthew Dillon sshbuf_free(arg); 1803ce74bacaSMatthew Dillon return ret; 1804ce74bacaSMatthew Dillon } 1805ce74bacaSMatthew Dillon 1806ce74bacaSMatthew Dillon /* Returns 0 if pid exited cleanly, non-zero otherwise */ 1807ce74bacaSMatthew Dillon int 1808ce74bacaSMatthew Dillon exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet) 1809ce74bacaSMatthew Dillon { 1810ce74bacaSMatthew Dillon int status; 1811ce74bacaSMatthew Dillon 1812ce74bacaSMatthew Dillon while (waitpid(pid, &status, 0) == -1) { 1813ce74bacaSMatthew Dillon if (errno != EINTR) { 1814ce74bacaSMatthew Dillon error("%s: waitpid: %s", tag, strerror(errno)); 1815ce74bacaSMatthew Dillon return -1; 1816ce74bacaSMatthew Dillon } 1817ce74bacaSMatthew Dillon } 1818ce74bacaSMatthew Dillon if (WIFSIGNALED(status)) { 1819ce74bacaSMatthew Dillon error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status)); 1820ce74bacaSMatthew Dillon return -1; 1821ce74bacaSMatthew Dillon } else if (WEXITSTATUS(status) != 0) { 1822ce74bacaSMatthew Dillon do_log2(quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO, 1823ce74bacaSMatthew Dillon "%s %s failed, status %d", tag, cmd, WEXITSTATUS(status)); 1824ce74bacaSMatthew Dillon return -1; 1825ce74bacaSMatthew Dillon } 1826ce74bacaSMatthew Dillon return 0; 1827ce74bacaSMatthew Dillon } 1828ce74bacaSMatthew Dillon 1829ce74bacaSMatthew Dillon /* 1830ce74bacaSMatthew Dillon * Check a given path for security. This is defined as all components 1831ce74bacaSMatthew Dillon * of the path to the file must be owned by either the owner of 1832ce74bacaSMatthew Dillon * of the file or root and no directories must be group or world writable. 1833ce74bacaSMatthew Dillon * 1834ce74bacaSMatthew Dillon * XXX Should any specific check be done for sym links ? 1835ce74bacaSMatthew Dillon * 1836ce74bacaSMatthew Dillon * Takes a file name, its stat information (preferably from fstat() to 1837ce74bacaSMatthew Dillon * avoid races), the uid of the expected owner, their home directory and an 1838ce74bacaSMatthew Dillon * error buffer plus max size as arguments. 1839ce74bacaSMatthew Dillon * 1840ce74bacaSMatthew Dillon * Returns 0 on success and -1 on failure 1841ce74bacaSMatthew Dillon */ 1842ce74bacaSMatthew Dillon int 1843ce74bacaSMatthew Dillon safe_path(const char *name, struct stat *stp, const char *pw_dir, 1844ce74bacaSMatthew Dillon uid_t uid, char *err, size_t errlen) 1845ce74bacaSMatthew Dillon { 1846ce74bacaSMatthew Dillon char buf[PATH_MAX], homedir[PATH_MAX]; 1847ce74bacaSMatthew Dillon char *cp; 1848ce74bacaSMatthew Dillon int comparehome = 0; 1849ce74bacaSMatthew Dillon struct stat st; 1850ce74bacaSMatthew Dillon 1851ce74bacaSMatthew Dillon if (realpath(name, buf) == NULL) { 1852ce74bacaSMatthew Dillon snprintf(err, errlen, "realpath %s failed: %s", name, 1853ce74bacaSMatthew Dillon strerror(errno)); 1854ce74bacaSMatthew Dillon return -1; 1855ce74bacaSMatthew Dillon } 1856ce74bacaSMatthew Dillon if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) 1857ce74bacaSMatthew Dillon comparehome = 1; 1858ce74bacaSMatthew Dillon 1859ce74bacaSMatthew Dillon if (!S_ISREG(stp->st_mode)) { 1860ce74bacaSMatthew Dillon snprintf(err, errlen, "%s is not a regular file", buf); 1861ce74bacaSMatthew Dillon return -1; 1862ce74bacaSMatthew Dillon } 1863ce74bacaSMatthew Dillon if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 1864ce74bacaSMatthew Dillon (stp->st_mode & 022) != 0) { 1865ce74bacaSMatthew Dillon snprintf(err, errlen, "bad ownership or modes for file %s", 1866ce74bacaSMatthew Dillon buf); 1867ce74bacaSMatthew Dillon return -1; 1868ce74bacaSMatthew Dillon } 1869ce74bacaSMatthew Dillon 1870ce74bacaSMatthew Dillon /* for each component of the canonical path, walking upwards */ 1871ce74bacaSMatthew Dillon for (;;) { 1872ce74bacaSMatthew Dillon if ((cp = dirname(buf)) == NULL) { 1873ce74bacaSMatthew Dillon snprintf(err, errlen, "dirname() failed"); 1874ce74bacaSMatthew Dillon return -1; 1875ce74bacaSMatthew Dillon } 1876ce74bacaSMatthew Dillon strlcpy(buf, cp, sizeof(buf)); 1877ce74bacaSMatthew Dillon 1878ce74bacaSMatthew Dillon if (stat(buf, &st) < 0 || 1879ce74bacaSMatthew Dillon (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 1880ce74bacaSMatthew Dillon (st.st_mode & 022) != 0) { 1881ce74bacaSMatthew Dillon snprintf(err, errlen, 1882ce74bacaSMatthew Dillon "bad ownership or modes for directory %s", buf); 1883ce74bacaSMatthew Dillon return -1; 1884ce74bacaSMatthew Dillon } 1885ce74bacaSMatthew Dillon 1886ce74bacaSMatthew Dillon /* If are past the homedir then we can stop */ 1887ce74bacaSMatthew Dillon if (comparehome && strcmp(homedir, buf) == 0) 1888ce74bacaSMatthew Dillon break; 1889ce74bacaSMatthew Dillon 1890ce74bacaSMatthew Dillon /* 1891ce74bacaSMatthew Dillon * dirname should always complete with a "/" path, 1892ce74bacaSMatthew Dillon * but we can be paranoid and check for "." too 1893ce74bacaSMatthew Dillon */ 1894ce74bacaSMatthew Dillon if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) 1895ce74bacaSMatthew Dillon break; 1896ce74bacaSMatthew Dillon } 1897ce74bacaSMatthew Dillon return 0; 1898ce74bacaSMatthew Dillon } 1899ce74bacaSMatthew Dillon 1900ce74bacaSMatthew Dillon /* 1901ce74bacaSMatthew Dillon * Version of safe_path() that accepts an open file descriptor to 1902ce74bacaSMatthew Dillon * avoid races. 1903ce74bacaSMatthew Dillon * 1904ce74bacaSMatthew Dillon * Returns 0 on success and -1 on failure 1905ce74bacaSMatthew Dillon */ 1906ce74bacaSMatthew Dillon int 1907ce74bacaSMatthew Dillon safe_path_fd(int fd, const char *file, struct passwd *pw, 1908ce74bacaSMatthew Dillon char *err, size_t errlen) 1909ce74bacaSMatthew Dillon { 1910ce74bacaSMatthew Dillon struct stat st; 1911ce74bacaSMatthew Dillon 1912ce74bacaSMatthew Dillon /* check the open file to avoid races */ 1913ce74bacaSMatthew Dillon if (fstat(fd, &st) < 0) { 1914ce74bacaSMatthew Dillon snprintf(err, errlen, "cannot stat file %s: %s", 1915ce74bacaSMatthew Dillon file, strerror(errno)); 1916ce74bacaSMatthew Dillon return -1; 1917ce74bacaSMatthew Dillon } 1918ce74bacaSMatthew Dillon return safe_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen); 1919ce74bacaSMatthew Dillon } 1920ce74bacaSMatthew Dillon 1921ce74bacaSMatthew Dillon /* 1922ce74bacaSMatthew Dillon * Sets the value of the given variable in the environment. If the variable 1923ce74bacaSMatthew Dillon * already exists, its value is overridden. 1924ce74bacaSMatthew Dillon */ 1925ce74bacaSMatthew Dillon void 1926ce74bacaSMatthew Dillon child_set_env(char ***envp, u_int *envsizep, const char *name, 1927ce74bacaSMatthew Dillon const char *value) 1928ce74bacaSMatthew Dillon { 1929ce74bacaSMatthew Dillon char **env; 1930ce74bacaSMatthew Dillon u_int envsize; 1931ce74bacaSMatthew Dillon u_int i, namelen; 1932ce74bacaSMatthew Dillon 1933ce74bacaSMatthew Dillon if (strchr(name, '=') != NULL) { 1934ce74bacaSMatthew Dillon error("Invalid environment variable \"%.100s\"", name); 1935ce74bacaSMatthew Dillon return; 1936ce74bacaSMatthew Dillon } 1937ce74bacaSMatthew Dillon 1938ce74bacaSMatthew Dillon /* 1939ce74bacaSMatthew Dillon * If we're passed an uninitialized list, allocate a single null 1940ce74bacaSMatthew Dillon * entry before continuing. 1941ce74bacaSMatthew Dillon */ 1942ce74bacaSMatthew Dillon if (*envp == NULL && *envsizep == 0) { 1943ce74bacaSMatthew Dillon *envp = xmalloc(sizeof(char *)); 1944ce74bacaSMatthew Dillon *envp[0] = NULL; 1945ce74bacaSMatthew Dillon *envsizep = 1; 1946ce74bacaSMatthew Dillon } 1947ce74bacaSMatthew Dillon 1948ce74bacaSMatthew Dillon /* 1949ce74bacaSMatthew Dillon * Find the slot where the value should be stored. If the variable 1950ce74bacaSMatthew Dillon * already exists, we reuse the slot; otherwise we append a new slot 1951ce74bacaSMatthew Dillon * at the end of the array, expanding if necessary. 1952ce74bacaSMatthew Dillon */ 1953ce74bacaSMatthew Dillon env = *envp; 1954ce74bacaSMatthew Dillon namelen = strlen(name); 1955ce74bacaSMatthew Dillon for (i = 0; env[i]; i++) 1956ce74bacaSMatthew Dillon if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') 1957ce74bacaSMatthew Dillon break; 1958ce74bacaSMatthew Dillon if (env[i]) { 1959ce74bacaSMatthew Dillon /* Reuse the slot. */ 1960ce74bacaSMatthew Dillon free(env[i]); 1961ce74bacaSMatthew Dillon } else { 1962ce74bacaSMatthew Dillon /* New variable. Expand if necessary. */ 1963ce74bacaSMatthew Dillon envsize = *envsizep; 1964ce74bacaSMatthew Dillon if (i >= envsize - 1) { 1965ce74bacaSMatthew Dillon if (envsize >= 1000) 1966ce74bacaSMatthew Dillon fatal("child_set_env: too many env vars"); 1967ce74bacaSMatthew Dillon envsize += 50; 1968ce74bacaSMatthew Dillon env = (*envp) = xreallocarray(env, envsize, sizeof(char *)); 1969ce74bacaSMatthew Dillon *envsizep = envsize; 1970ce74bacaSMatthew Dillon } 1971ce74bacaSMatthew Dillon /* Need to set the NULL pointer at end of array beyond the new slot. */ 1972ce74bacaSMatthew Dillon env[i + 1] = NULL; 1973ce74bacaSMatthew Dillon } 1974ce74bacaSMatthew Dillon 1975ce74bacaSMatthew Dillon /* Allocate space and format the variable in the appropriate slot. */ 1976*664f4763Szrj /* XXX xasprintf */ 1977ce74bacaSMatthew Dillon env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); 1978ce74bacaSMatthew Dillon snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); 1979ce74bacaSMatthew Dillon } 1980ce74bacaSMatthew Dillon 1981*664f4763Szrj /* 1982*664f4763Szrj * Check and optionally lowercase a domain name, also removes trailing '.' 1983*664f4763Szrj * Returns 1 on success and 0 on failure, storing an error message in errstr. 1984*664f4763Szrj */ 1985*664f4763Szrj int 1986*664f4763Szrj valid_domain(char *name, int makelower, const char **errstr) 1987*664f4763Szrj { 1988*664f4763Szrj size_t i, l = strlen(name); 1989*664f4763Szrj u_char c, last = '\0'; 1990*664f4763Szrj static char errbuf[256]; 1991*664f4763Szrj 1992*664f4763Szrj if (l == 0) { 1993*664f4763Szrj strlcpy(errbuf, "empty domain name", sizeof(errbuf)); 1994*664f4763Szrj goto bad; 1995*664f4763Szrj } 1996*664f4763Szrj if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) { 1997*664f4763Szrj snprintf(errbuf, sizeof(errbuf), "domain name \"%.100s\" " 1998*664f4763Szrj "starts with invalid character", name); 1999*664f4763Szrj goto bad; 2000*664f4763Szrj } 2001*664f4763Szrj for (i = 0; i < l; i++) { 2002*664f4763Szrj c = tolower((u_char)name[i]); 2003*664f4763Szrj if (makelower) 2004*664f4763Szrj name[i] = (char)c; 2005*664f4763Szrj if (last == '.' && c == '.') { 2006*664f4763Szrj snprintf(errbuf, sizeof(errbuf), "domain name " 2007*664f4763Szrj "\"%.100s\" contains consecutive separators", name); 2008*664f4763Szrj goto bad; 2009*664f4763Szrj } 2010*664f4763Szrj if (c != '.' && c != '-' && !isalnum(c) && 2011*664f4763Szrj c != '_') /* technically invalid, but common */ { 2012*664f4763Szrj snprintf(errbuf, sizeof(errbuf), "domain name " 2013*664f4763Szrj "\"%.100s\" contains invalid characters", name); 2014*664f4763Szrj goto bad; 2015*664f4763Szrj } 2016*664f4763Szrj last = c; 2017*664f4763Szrj } 2018*664f4763Szrj if (name[l - 1] == '.') 2019*664f4763Szrj name[l - 1] = '\0'; 2020*664f4763Szrj if (errstr != NULL) 2021*664f4763Szrj *errstr = NULL; 2022*664f4763Szrj return 1; 2023*664f4763Szrj bad: 2024*664f4763Szrj if (errstr != NULL) 2025*664f4763Szrj *errstr = errbuf; 2026*664f4763Szrj return 0; 2027*664f4763Szrj } 2028*664f4763Szrj 2029*664f4763Szrj /* 2030*664f4763Szrj * Verify that a environment variable name (not including initial '$') is 2031*664f4763Szrj * valid; consisting of one or more alphanumeric or underscore characters only. 2032*664f4763Szrj * Returns 1 on valid, 0 otherwise. 2033*664f4763Szrj */ 2034*664f4763Szrj int 2035*664f4763Szrj valid_env_name(const char *name) 2036*664f4763Szrj { 2037*664f4763Szrj const char *cp; 2038*664f4763Szrj 2039*664f4763Szrj if (name[0] == '\0') 2040*664f4763Szrj return 0; 2041*664f4763Szrj for (cp = name; *cp != '\0'; cp++) { 2042*664f4763Szrj if (!isalnum((u_char)*cp) && *cp != '_') 2043*664f4763Szrj return 0; 2044*664f4763Szrj } 2045*664f4763Szrj return 1; 2046*664f4763Szrj } 2047*664f4763Szrj 2048*664f4763Szrj const char * 2049*664f4763Szrj atoi_err(const char *nptr, int *val) 2050*664f4763Szrj { 2051*664f4763Szrj const char *errstr = NULL; 2052*664f4763Szrj long long num; 2053*664f4763Szrj 2054*664f4763Szrj if (nptr == NULL || *nptr == '\0') 2055*664f4763Szrj return "missing"; 2056*664f4763Szrj num = strtonum(nptr, 0, INT_MAX, &errstr); 2057*664f4763Szrj if (errstr == NULL) 2058*664f4763Szrj *val = (int)num; 2059*664f4763Szrj return errstr; 2060*664f4763Szrj } 2061*664f4763Szrj 2062*664f4763Szrj int 2063*664f4763Szrj parse_absolute_time(const char *s, uint64_t *tp) 2064*664f4763Szrj { 2065*664f4763Szrj struct tm tm; 2066*664f4763Szrj time_t tt; 2067*664f4763Szrj char buf[32], *fmt; 2068*664f4763Szrj 2069*664f4763Szrj *tp = 0; 2070*664f4763Szrj 2071*664f4763Szrj /* 2072*664f4763Szrj * POSIX strptime says "The application shall ensure that there 2073*664f4763Szrj * is white-space or other non-alphanumeric characters between 2074*664f4763Szrj * any two conversion specifications" so arrange things this way. 2075*664f4763Szrj */ 2076*664f4763Szrj switch (strlen(s)) { 2077*664f4763Szrj case 8: /* YYYYMMDD */ 2078*664f4763Szrj fmt = "%Y-%m-%d"; 2079*664f4763Szrj snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6); 2080*664f4763Szrj break; 2081*664f4763Szrj case 12: /* YYYYMMDDHHMM */ 2082*664f4763Szrj fmt = "%Y-%m-%dT%H:%M"; 2083*664f4763Szrj snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s", 2084*664f4763Szrj s, s + 4, s + 6, s + 8, s + 10); 2085*664f4763Szrj break; 2086*664f4763Szrj case 14: /* YYYYMMDDHHMMSS */ 2087*664f4763Szrj fmt = "%Y-%m-%dT%H:%M:%S"; 2088*664f4763Szrj snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s", 2089*664f4763Szrj s, s + 4, s + 6, s + 8, s + 10, s + 12); 2090*664f4763Szrj break; 2091*664f4763Szrj default: 2092*664f4763Szrj return SSH_ERR_INVALID_FORMAT; 2093*664f4763Szrj } 2094*664f4763Szrj 2095*664f4763Szrj memset(&tm, 0, sizeof(tm)); 2096*664f4763Szrj if (strptime(buf, fmt, &tm) == NULL) 2097*664f4763Szrj return SSH_ERR_INVALID_FORMAT; 2098*664f4763Szrj if ((tt = mktime(&tm)) < 0) 2099*664f4763Szrj return SSH_ERR_INVALID_FORMAT; 2100*664f4763Szrj /* success */ 2101*664f4763Szrj *tp = (uint64_t)tt; 2102*664f4763Szrj return 0; 2103*664f4763Szrj } 2104*664f4763Szrj 2105*664f4763Szrj void 2106*664f4763Szrj format_absolute_time(uint64_t t, char *buf, size_t len) 2107*664f4763Szrj { 2108*664f4763Szrj time_t tt = t > INT_MAX ? INT_MAX : t; /* XXX revisit in 2038 :P */ 2109*664f4763Szrj struct tm tm; 2110*664f4763Szrj 2111*664f4763Szrj localtime_r(&tt, &tm); 2112*664f4763Szrj strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm); 2113*664f4763Szrj } 2114*664f4763Szrj 2115*664f4763Szrj /* check if path is absolute */ 2116*664f4763Szrj int 2117*664f4763Szrj path_absolute(const char *path) 2118*664f4763Szrj { 2119*664f4763Szrj return (*path == '/') ? 1 : 0; 2120*664f4763Szrj } 2121