1255767Sdes/* $OpenBSD: misc.c,v 1.91 2013/07/12 00:43:50 djm Exp $ */ 2224638Sbrooks/* $FreeBSD$ */ 376259Sgreen/* 476259Sgreen * Copyright (c) 2000 Markus Friedl. All rights reserved. 5162852Sdes * Copyright (c) 2005,2006 Damien Miller. All rights reserved. 676259Sgreen * 776259Sgreen * Redistribution and use in source and binary forms, with or without 876259Sgreen * modification, are permitted provided that the following conditions 976259Sgreen * are met: 1076259Sgreen * 1. Redistributions of source code must retain the above copyright 1176259Sgreen * notice, this list of conditions and the following disclaimer. 1276259Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1376259Sgreen * notice, this list of conditions and the following disclaimer in the 1476259Sgreen * documentation and/or other materials provided with the distribution. 1576259Sgreen * 1676259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1776259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1876259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1976259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2076259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2176259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2276259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2376259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2476259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2576259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2676259Sgreen */ 2776259Sgreen 2876259Sgreen#include "includes.h" 2976259Sgreen 30162852Sdes#include <sys/types.h> 31162852Sdes#include <sys/ioctl.h> 32162852Sdes#include <sys/socket.h> 33162852Sdes#include <sys/param.h> 34162852Sdes 35162852Sdes#include <stdarg.h> 36162852Sdes#include <stdio.h> 37162852Sdes#include <stdlib.h> 38162852Sdes#include <string.h> 39221420Sdes#include <time.h> 40162852Sdes#include <unistd.h> 41162852Sdes 42162852Sdes#include <netinet/in.h> 43221420Sdes#include <netinet/in_systm.h> 44221420Sdes#include <netinet/ip.h> 45162852Sdes#include <netinet/tcp.h> 46162852Sdes 47162852Sdes#include <errno.h> 48162852Sdes#include <fcntl.h> 49181111Sdes#include <netdb.h> 50162852Sdes#ifdef HAVE_PATHS_H 51162852Sdes# include <paths.h> 52162852Sdes#include <pwd.h> 53162852Sdes#endif 54157016Sdes#ifdef SSH_TUN_OPENBSD 55157016Sdes#include <net/if.h> 56157016Sdes#endif 57157016Sdes 58162852Sdes#include "xmalloc.h" 5976259Sgreen#include "misc.h" 6076259Sgreen#include "log.h" 61162852Sdes#include "ssh.h" 6276259Sgreen 6392555Sdes/* remove newline at end of string */ 6476259Sgreenchar * 6576259Sgreenchop(char *s) 6676259Sgreen{ 6776259Sgreen char *t = s; 6876259Sgreen while (*t) { 6992555Sdes if (*t == '\n' || *t == '\r') { 7076259Sgreen *t = '\0'; 7176259Sgreen return s; 7276259Sgreen } 7376259Sgreen t++; 7476259Sgreen } 7576259Sgreen return s; 7676259Sgreen 7776259Sgreen} 7876259Sgreen 7992555Sdes/* set/unset filedescriptor to non-blocking */ 80137015Sdesint 8176259Sgreenset_nonblock(int fd) 8276259Sgreen{ 8376259Sgreen int val; 8492555Sdes 8576259Sgreen val = fcntl(fd, F_GETFL, 0); 8676259Sgreen if (val < 0) { 8776259Sgreen error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 88137015Sdes return (-1); 8976259Sgreen } 9076259Sgreen if (val & O_NONBLOCK) { 91137015Sdes debug3("fd %d is O_NONBLOCK", fd); 92137015Sdes return (0); 9376259Sgreen } 94124208Sdes debug2("fd %d setting O_NONBLOCK", fd); 9576259Sgreen val |= O_NONBLOCK; 96137015Sdes if (fcntl(fd, F_SETFL, val) == -1) { 97137015Sdes debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, 98137015Sdes strerror(errno)); 99137015Sdes return (-1); 100137015Sdes } 101137015Sdes return (0); 10276259Sgreen} 10376259Sgreen 104137015Sdesint 10592555Sdesunset_nonblock(int fd) 10692555Sdes{ 10792555Sdes int val; 10892555Sdes 10992555Sdes val = fcntl(fd, F_GETFL, 0); 11092555Sdes if (val < 0) { 11192555Sdes error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 112137015Sdes return (-1); 11392555Sdes } 11492555Sdes if (!(val & O_NONBLOCK)) { 115137015Sdes debug3("fd %d is not O_NONBLOCK", fd); 116137015Sdes return (0); 11792555Sdes } 11892555Sdes debug("fd %d clearing O_NONBLOCK", fd); 11992555Sdes val &= ~O_NONBLOCK; 120137015Sdes if (fcntl(fd, F_SETFL, val) == -1) { 121137015Sdes debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", 12292555Sdes fd, strerror(errno)); 123137015Sdes return (-1); 124137015Sdes } 125137015Sdes return (0); 12692555Sdes} 12792555Sdes 128181111Sdesconst char * 129181111Sdesssh_gai_strerror(int gaierr) 130181111Sdes{ 131255767Sdes if (gaierr == EAI_SYSTEM && errno != 0) 132181111Sdes return strerror(errno); 133181111Sdes return gai_strerror(gaierr); 134181111Sdes} 135181111Sdes 13692555Sdes/* disable nagle on socket */ 13792555Sdesvoid 13892555Sdesset_nodelay(int fd) 13992555Sdes{ 14092555Sdes int opt; 14192555Sdes socklen_t optlen; 14292555Sdes 14392555Sdes optlen = sizeof opt; 14492555Sdes if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { 145126274Sdes debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); 14692555Sdes return; 14792555Sdes } 14892555Sdes if (opt == 1) { 14992555Sdes debug2("fd %d is TCP_NODELAY", fd); 15092555Sdes return; 15192555Sdes } 15292555Sdes opt = 1; 153113908Sdes debug2("fd %d setting TCP_NODELAY", fd); 15492555Sdes if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) 15592555Sdes error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); 15692555Sdes} 15792555Sdes 15876259Sgreen/* Characters considered whitespace in strsep calls. */ 15976259Sgreen#define WHITESPACE " \t\r\n" 160162852Sdes#define QUOTE "\"" 16176259Sgreen 16292555Sdes/* return next token in configuration line */ 16376259Sgreenchar * 16476259Sgreenstrdelim(char **s) 16576259Sgreen{ 16676259Sgreen char *old; 16776259Sgreen int wspace = 0; 16876259Sgreen 16976259Sgreen if (*s == NULL) 17076259Sgreen return NULL; 17176259Sgreen 17276259Sgreen old = *s; 17376259Sgreen 174162852Sdes *s = strpbrk(*s, WHITESPACE QUOTE "="); 17576259Sgreen if (*s == NULL) 17676259Sgreen return (old); 17776259Sgreen 178162852Sdes if (*s[0] == '\"') { 179162852Sdes memmove(*s, *s + 1, strlen(*s)); /* move nul too */ 180162852Sdes /* Find matching quote */ 181162852Sdes if ((*s = strpbrk(*s, QUOTE)) == NULL) { 182162852Sdes return (NULL); /* no matching quote */ 183162852Sdes } else { 184162852Sdes *s[0] = '\0'; 185215116Sdes *s += strspn(*s + 1, WHITESPACE) + 1; 186162852Sdes return (old); 187162852Sdes } 188162852Sdes } 189162852Sdes 19076259Sgreen /* Allow only one '=' to be skipped */ 19176259Sgreen if (*s[0] == '=') 19276259Sgreen wspace = 1; 19376259Sgreen *s[0] = '\0'; 19476259Sgreen 195162852Sdes /* Skip any extra whitespace after first token */ 19676259Sgreen *s += strspn(*s + 1, WHITESPACE) + 1; 19776259Sgreen if (*s[0] == '=' && !wspace) 19876259Sgreen *s += strspn(*s + 1, WHITESPACE) + 1; 19976259Sgreen 20076259Sgreen return (old); 20176259Sgreen} 20276259Sgreen 20376259Sgreenstruct passwd * 20476259Sgreenpwcopy(struct passwd *pw) 20576259Sgreen{ 206162852Sdes struct passwd *copy = xcalloc(1, sizeof(*copy)); 20776259Sgreen 20876259Sgreen copy->pw_name = xstrdup(pw->pw_name); 20976259Sgreen copy->pw_passwd = xstrdup(pw->pw_passwd); 210255767Sdes#ifdef HAVE_STRUCT_PASSWD_PW_GECOS 21176259Sgreen copy->pw_gecos = xstrdup(pw->pw_gecos); 212255767Sdes#endif 21376259Sgreen copy->pw_uid = pw->pw_uid; 21476259Sgreen copy->pw_gid = pw->pw_gid; 215255767Sdes#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE 21692555Sdes copy->pw_expire = pw->pw_expire; 21798937Sdes#endif 218255767Sdes#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE 21992555Sdes copy->pw_change = pw->pw_change; 22098937Sdes#endif 221255767Sdes#ifdef HAVE_STRUCT_PASSWD_PW_CLASS 22276259Sgreen copy->pw_class = xstrdup(pw->pw_class); 22398937Sdes#endif 22476259Sgreen copy->pw_dir = xstrdup(pw->pw_dir); 22576259Sgreen copy->pw_shell = xstrdup(pw->pw_shell); 22676259Sgreen return copy; 22776259Sgreen} 22876259Sgreen 22992555Sdes/* 23092555Sdes * Convert ASCII string to TCP/IP port number. 231192595Sdes * Port must be >=0 and <=65535. 232192595Sdes * Return -1 if invalid. 23392555Sdes */ 23492555Sdesint 23592555Sdesa2port(const char *s) 23676259Sgreen{ 237192595Sdes long long port; 238192595Sdes const char *errstr; 23976259Sgreen 240192595Sdes port = strtonum(s, 0, 65535, &errstr); 241192595Sdes if (errstr != NULL) 242192595Sdes return -1; 243192595Sdes return (int)port; 24476259Sgreen} 24592555Sdes 246157016Sdesint 247157016Sdesa2tun(const char *s, int *remote) 248157016Sdes{ 249157016Sdes const char *errstr = NULL; 250157016Sdes char *sp, *ep; 251157016Sdes int tun; 252157016Sdes 253157016Sdes if (remote != NULL) { 254157016Sdes *remote = SSH_TUNID_ANY; 255157016Sdes sp = xstrdup(s); 256157016Sdes if ((ep = strchr(sp, ':')) == NULL) { 257255767Sdes free(sp); 258157016Sdes return (a2tun(s, NULL)); 259157016Sdes } 260157016Sdes ep[0] = '\0'; ep++; 261157016Sdes *remote = a2tun(ep, NULL); 262157016Sdes tun = a2tun(sp, NULL); 263255767Sdes free(sp); 264157016Sdes return (*remote == SSH_TUNID_ERR ? *remote : tun); 265157016Sdes } 266157016Sdes 267157016Sdes if (strcasecmp(s, "any") == 0) 268157016Sdes return (SSH_TUNID_ANY); 269157016Sdes 270157016Sdes tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); 271157016Sdes if (errstr != NULL) 272157016Sdes return (SSH_TUNID_ERR); 273157016Sdes 274157016Sdes return (tun); 275157016Sdes} 276157016Sdes 27792555Sdes#define SECONDS 1 27892555Sdes#define MINUTES (SECONDS * 60) 27992555Sdes#define HOURS (MINUTES * 60) 28092555Sdes#define DAYS (HOURS * 24) 28192555Sdes#define WEEKS (DAYS * 7) 28292555Sdes 28392555Sdes/* 28492555Sdes * Convert a time string into seconds; format is 28592555Sdes * a sequence of: 28692555Sdes * time[qualifier] 28792555Sdes * 28892555Sdes * Valid time qualifiers are: 28992555Sdes * <none> seconds 29092555Sdes * s|S seconds 29192555Sdes * m|M minutes 29292555Sdes * h|H hours 29392555Sdes * d|D days 29492555Sdes * w|W weeks 29592555Sdes * 29692555Sdes * Examples: 29792555Sdes * 90m 90 minutes 29892555Sdes * 1h30m 90 minutes 29992555Sdes * 2d 2 days 30092555Sdes * 1w 1 week 30192555Sdes * 30292555Sdes * Return -1 if time string is invalid. 30392555Sdes */ 30492555Sdeslong 30592555Sdesconvtime(const char *s) 30692555Sdes{ 30792555Sdes long total, secs; 30892555Sdes const char *p; 30992555Sdes char *endp; 31092555Sdes 31192555Sdes errno = 0; 31292555Sdes total = 0; 31392555Sdes p = s; 31492555Sdes 31592555Sdes if (p == NULL || *p == '\0') 31692555Sdes return -1; 31792555Sdes 31892555Sdes while (*p) { 31992555Sdes secs = strtol(p, &endp, 10); 32092555Sdes if (p == endp || 32192555Sdes (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 32292555Sdes secs < 0) 32392555Sdes return -1; 32492555Sdes 32592555Sdes switch (*endp++) { 32692555Sdes case '\0': 32792555Sdes endp--; 328162852Sdes break; 32992555Sdes case 's': 33092555Sdes case 'S': 33192555Sdes break; 33292555Sdes case 'm': 33392555Sdes case 'M': 33492555Sdes secs *= MINUTES; 33592555Sdes break; 33692555Sdes case 'h': 33792555Sdes case 'H': 33892555Sdes secs *= HOURS; 33992555Sdes break; 34092555Sdes case 'd': 34192555Sdes case 'D': 34292555Sdes secs *= DAYS; 34392555Sdes break; 34492555Sdes case 'w': 34592555Sdes case 'W': 34692555Sdes secs *= WEEKS; 34792555Sdes break; 34892555Sdes default: 34992555Sdes return -1; 35092555Sdes } 35192555Sdes total += secs; 35292555Sdes if (total < 0) 35392555Sdes return -1; 35492555Sdes p = endp; 35592555Sdes } 35692555Sdes 35792555Sdes return total; 35892555Sdes} 35992555Sdes 360146998Sdes/* 361162852Sdes * Returns a standardized host+port identifier string. 362162852Sdes * Caller must free returned string. 363162852Sdes */ 364162852Sdeschar * 365162852Sdesput_host_port(const char *host, u_short port) 366162852Sdes{ 367162852Sdes char *hoststr; 368162852Sdes 369162852Sdes if (port == 0 || port == SSH_DEFAULT_PORT) 370162852Sdes return(xstrdup(host)); 371162852Sdes if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0) 372162852Sdes fatal("put_host_port: asprintf: %s", strerror(errno)); 373162852Sdes debug3("put_host_port: %s", hoststr); 374162852Sdes return hoststr; 375162852Sdes} 376162852Sdes 377162852Sdes/* 378146998Sdes * Search for next delimiter between hostnames/addresses and ports. 379146998Sdes * Argument may be modified (for termination). 380146998Sdes * Returns *cp if parsing succeeds. 381146998Sdes * *cp is set to the start of the next delimiter, if one was found. 382146998Sdes * If this is the last field, *cp is set to NULL. 383146998Sdes */ 38492555Sdeschar * 385146998Sdeshpdelim(char **cp) 386146998Sdes{ 387146998Sdes char *s, *old; 388146998Sdes 389146998Sdes if (cp == NULL || *cp == NULL) 390146998Sdes return NULL; 391146998Sdes 392146998Sdes old = s = *cp; 393146998Sdes if (*s == '[') { 394146998Sdes if ((s = strchr(s, ']')) == NULL) 395146998Sdes return NULL; 396146998Sdes else 397146998Sdes s++; 398146998Sdes } else if ((s = strpbrk(s, ":/")) == NULL) 399146998Sdes s = *cp + strlen(*cp); /* skip to end (see first case below) */ 400146998Sdes 401146998Sdes switch (*s) { 402146998Sdes case '\0': 403146998Sdes *cp = NULL; /* no more fields*/ 404146998Sdes break; 405147001Sdes 406146998Sdes case ':': 407146998Sdes case '/': 408146998Sdes *s = '\0'; /* terminate */ 409146998Sdes *cp = s + 1; 410146998Sdes break; 411147001Sdes 412146998Sdes default: 413146998Sdes return NULL; 414146998Sdes } 415146998Sdes 416146998Sdes return old; 417146998Sdes} 418146998Sdes 419146998Sdeschar * 42092555Sdescleanhostname(char *host) 42192555Sdes{ 42292555Sdes if (*host == '[' && host[strlen(host) - 1] == ']') { 42392555Sdes host[strlen(host) - 1] = '\0'; 42492555Sdes return (host + 1); 42592555Sdes } else 42692555Sdes return host; 42792555Sdes} 42892555Sdes 42992555Sdeschar * 43092555Sdescolon(char *cp) 43192555Sdes{ 43292555Sdes int flag = 0; 43392555Sdes 43492555Sdes if (*cp == ':') /* Leading colon is part of file name. */ 435215116Sdes return NULL; 43692555Sdes if (*cp == '[') 43792555Sdes flag = 1; 43892555Sdes 43992555Sdes for (; *cp; ++cp) { 44092555Sdes if (*cp == '@' && *(cp+1) == '[') 44192555Sdes flag = 1; 44292555Sdes if (*cp == ']' && *(cp+1) == ':' && flag) 44392555Sdes return (cp+1); 44492555Sdes if (*cp == ':' && !flag) 44592555Sdes return (cp); 44692555Sdes if (*cp == '/') 447215116Sdes return NULL; 44892555Sdes } 449215116Sdes return NULL; 45092555Sdes} 45192555Sdes 45292555Sdes/* function to assist building execv() arguments */ 45392555Sdesvoid 45492555Sdesaddargs(arglist *args, char *fmt, ...) 45592555Sdes{ 45692555Sdes va_list ap; 457157016Sdes char *cp; 458137015Sdes u_int nalloc; 459157016Sdes int r; 46092555Sdes 46192555Sdes va_start(ap, fmt); 462157016Sdes r = vasprintf(&cp, fmt, ap); 46392555Sdes va_end(ap); 464157016Sdes if (r == -1) 465157016Sdes fatal("addargs: argument too long"); 46692555Sdes 467120161Snectar nalloc = args->nalloc; 46892555Sdes if (args->list == NULL) { 469120161Snectar nalloc = 32; 47092555Sdes args->num = 0; 471120161Snectar } else if (args->num+2 >= nalloc) 472120161Snectar nalloc *= 2; 47392555Sdes 474162852Sdes args->list = xrealloc(args->list, nalloc, sizeof(char *)); 475120161Snectar args->nalloc = nalloc; 476157016Sdes args->list[args->num++] = cp; 47792555Sdes args->list[args->num] = NULL; 47892555Sdes} 479146998Sdes 480157016Sdesvoid 481157016Sdesreplacearg(arglist *args, u_int which, char *fmt, ...) 482157016Sdes{ 483157016Sdes va_list ap; 484157016Sdes char *cp; 485157016Sdes int r; 486157016Sdes 487157016Sdes va_start(ap, fmt); 488157016Sdes r = vasprintf(&cp, fmt, ap); 489157016Sdes va_end(ap); 490157016Sdes if (r == -1) 491157016Sdes fatal("replacearg: argument too long"); 492157016Sdes 493157016Sdes if (which >= args->num) 494157016Sdes fatal("replacearg: tried to replace invalid arg %d >= %d", 495157016Sdes which, args->num); 496255767Sdes free(args->list[which]); 497157016Sdes args->list[which] = cp; 498157016Sdes} 499157016Sdes 500157016Sdesvoid 501157016Sdesfreeargs(arglist *args) 502157016Sdes{ 503157016Sdes u_int i; 504157016Sdes 505157016Sdes if (args->list != NULL) { 506157016Sdes for (i = 0; i < args->num; i++) 507255767Sdes free(args->list[i]); 508255767Sdes free(args->list); 509157016Sdes args->nalloc = args->num = 0; 510157016Sdes args->list = NULL; 511157016Sdes } 512157016Sdes} 513157016Sdes 514146998Sdes/* 515149749Sdes * Expands tildes in the file name. Returns data allocated by xmalloc. 516149749Sdes * Warning: this calls getpw*. 517149749Sdes */ 518149749Sdeschar * 519149749Sdestilde_expand_filename(const char *filename, uid_t uid) 520149749Sdes{ 521255767Sdes const char *path, *sep; 522255767Sdes char user[128], *ret; 523149749Sdes struct passwd *pw; 524149749Sdes u_int len, slash; 525149749Sdes 526149749Sdes if (*filename != '~') 527149749Sdes return (xstrdup(filename)); 528149749Sdes filename++; 529149749Sdes 530149749Sdes path = strchr(filename, '/'); 531149749Sdes if (path != NULL && path > filename) { /* ~user/path */ 532149749Sdes slash = path - filename; 533149749Sdes if (slash > sizeof(user) - 1) 534149749Sdes fatal("tilde_expand_filename: ~username too long"); 535149749Sdes memcpy(user, filename, slash); 536149749Sdes user[slash] = '\0'; 537149749Sdes if ((pw = getpwnam(user)) == NULL) 538149749Sdes fatal("tilde_expand_filename: No such user %s", user); 539149749Sdes } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ 540181111Sdes fatal("tilde_expand_filename: No such uid %ld", (long)uid); 541149749Sdes 542149749Sdes /* Make sure directory has a trailing '/' */ 543149749Sdes len = strlen(pw->pw_dir); 544255767Sdes if (len == 0 || pw->pw_dir[len - 1] != '/') 545255767Sdes sep = "/"; 546255767Sdes else 547255767Sdes sep = ""; 548149749Sdes 549149749Sdes /* Skip leading '/' from specified path */ 550149749Sdes if (path != NULL) 551149749Sdes filename = path + 1; 552255767Sdes 553255767Sdes if (xasprintf(&ret, "%s%s%s", pw->pw_dir, sep, filename) >= MAXPATHLEN) 554149749Sdes fatal("tilde_expand_filename: Path too long"); 555149749Sdes 556255767Sdes return (ret); 557149749Sdes} 558149749Sdes 559149749Sdes/* 560149749Sdes * Expand a string with a set of %[char] escapes. A number of escapes may be 561149749Sdes * specified as (char *escape_chars, char *replacement) pairs. The list must 562149749Sdes * be terminated by a NULL escape_char. Returns replaced string in memory 563149749Sdes * allocated by xmalloc. 564149749Sdes */ 565149749Sdeschar * 566149749Sdespercent_expand(const char *string, ...) 567149749Sdes{ 568149749Sdes#define EXPAND_MAX_KEYS 16 569204917Sdes u_int num_keys, i, j; 570149749Sdes struct { 571149749Sdes const char *key; 572149749Sdes const char *repl; 573149749Sdes } keys[EXPAND_MAX_KEYS]; 574149749Sdes char buf[4096]; 575149749Sdes va_list ap; 576149749Sdes 577149749Sdes /* Gather keys */ 578149749Sdes va_start(ap, string); 579149749Sdes for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { 580149749Sdes keys[num_keys].key = va_arg(ap, char *); 581149749Sdes if (keys[num_keys].key == NULL) 582149749Sdes break; 583149749Sdes keys[num_keys].repl = va_arg(ap, char *); 584149749Sdes if (keys[num_keys].repl == NULL) 585204917Sdes fatal("%s: NULL replacement", __func__); 586149749Sdes } 587204917Sdes if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) 588204917Sdes fatal("%s: too many keys", __func__); 589149749Sdes va_end(ap); 590149749Sdes 591149749Sdes /* Expand string */ 592149749Sdes *buf = '\0'; 593149749Sdes for (i = 0; *string != '\0'; string++) { 594149749Sdes if (*string != '%') { 595149749Sdes append: 596149749Sdes buf[i++] = *string; 597149749Sdes if (i >= sizeof(buf)) 598204917Sdes fatal("%s: string too long", __func__); 599149749Sdes buf[i] = '\0'; 600149749Sdes continue; 601149749Sdes } 602149749Sdes string++; 603204917Sdes /* %% case */ 604149749Sdes if (*string == '%') 605149749Sdes goto append; 606149749Sdes for (j = 0; j < num_keys; j++) { 607149749Sdes if (strchr(keys[j].key, *string) != NULL) { 608149749Sdes i = strlcat(buf, keys[j].repl, sizeof(buf)); 609149749Sdes if (i >= sizeof(buf)) 610204917Sdes fatal("%s: string too long", __func__); 611149749Sdes break; 612149749Sdes } 613149749Sdes } 614149749Sdes if (j >= num_keys) 615204917Sdes fatal("%s: unknown key %%%c", __func__, *string); 616149749Sdes } 617149749Sdes return (xstrdup(buf)); 618149749Sdes#undef EXPAND_MAX_KEYS 619149749Sdes} 620149749Sdes 621149749Sdes/* 622146998Sdes * Read an entire line from a public key file into a static buffer, discarding 623146998Sdes * lines that exceed the buffer size. Returns 0 on success, -1 on failure. 624146998Sdes */ 625146998Sdesint 626146998Sdesread_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, 627146998Sdes u_long *lineno) 628146998Sdes{ 629146998Sdes while (fgets(buf, bufsz, f) != NULL) { 630181111Sdes if (buf[0] == '\0') 631181111Sdes continue; 632146998Sdes (*lineno)++; 633146998Sdes if (buf[strlen(buf) - 1] == '\n' || feof(f)) { 634146998Sdes return 0; 635146998Sdes } else { 636146998Sdes debug("%s: %s line %lu exceeds size limit", __func__, 637146998Sdes filename, *lineno); 638146998Sdes /* discard remainder of line */ 639147001Sdes while (fgetc(f) != '\n' && !feof(f)) 640146998Sdes ; /* nothing */ 641146998Sdes } 642146998Sdes } 643146998Sdes return -1; 644146998Sdes} 645149749Sdes 646157016Sdesint 647157016Sdestun_open(int tun, int mode) 648157016Sdes{ 649157016Sdes#if defined(CUSTOM_SYS_TUN_OPEN) 650157016Sdes return (sys_tun_open(tun, mode)); 651157016Sdes#elif defined(SSH_TUN_OPENBSD) 652157016Sdes struct ifreq ifr; 653157016Sdes char name[100]; 654157016Sdes int fd = -1, sock; 655157016Sdes 656157016Sdes /* Open the tunnel device */ 657157016Sdes if (tun <= SSH_TUNID_MAX) { 658157016Sdes snprintf(name, sizeof(name), "/dev/tun%d", tun); 659157016Sdes fd = open(name, O_RDWR); 660157016Sdes } else if (tun == SSH_TUNID_ANY) { 661157016Sdes for (tun = 100; tun >= 0; tun--) { 662157016Sdes snprintf(name, sizeof(name), "/dev/tun%d", tun); 663157016Sdes if ((fd = open(name, O_RDWR)) >= 0) 664157016Sdes break; 665157016Sdes } 666157016Sdes } else { 667157016Sdes debug("%s: invalid tunnel %u", __func__, tun); 668157016Sdes return (-1); 669157016Sdes } 670157016Sdes 671157016Sdes if (fd < 0) { 672157016Sdes debug("%s: %s open failed: %s", __func__, name, strerror(errno)); 673157016Sdes return (-1); 674157016Sdes } 675157016Sdes 676157016Sdes debug("%s: %s mode %d fd %d", __func__, name, mode, fd); 677157016Sdes 678157016Sdes /* Set the tunnel device operation mode */ 679157016Sdes snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun); 680157016Sdes if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 681157016Sdes goto failed; 682157016Sdes 683157016Sdes if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) 684157016Sdes goto failed; 685157016Sdes 686157016Sdes /* Set interface mode */ 687157016Sdes ifr.ifr_flags &= ~IFF_UP; 688157016Sdes if (mode == SSH_TUNMODE_ETHERNET) 689157016Sdes ifr.ifr_flags |= IFF_LINK0; 690157016Sdes else 691157016Sdes ifr.ifr_flags &= ~IFF_LINK0; 692157016Sdes if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) 693157016Sdes goto failed; 694157016Sdes 695157016Sdes /* Bring interface up */ 696157016Sdes ifr.ifr_flags |= IFF_UP; 697157016Sdes if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) 698157016Sdes goto failed; 699157016Sdes 700157016Sdes close(sock); 701157016Sdes return (fd); 702157016Sdes 703157016Sdes failed: 704157016Sdes if (fd >= 0) 705157016Sdes close(fd); 706157016Sdes if (sock >= 0) 707157016Sdes close(sock); 708157016Sdes debug("%s: failed to set %s mode %d: %s", __func__, name, 709157016Sdes mode, strerror(errno)); 710157016Sdes return (-1); 711157016Sdes#else 712157016Sdes error("Tunnel interfaces are not supported on this platform"); 713157016Sdes return (-1); 714157016Sdes#endif 715157016Sdes} 716157016Sdes 717157016Sdesvoid 718157016Sdessanitise_stdfd(void) 719157016Sdes{ 720157016Sdes int nullfd, dupfd; 721157016Sdes 722157016Sdes if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 723192595Sdes fprintf(stderr, "Couldn't open /dev/null: %s\n", 724192595Sdes strerror(errno)); 725157016Sdes exit(1); 726157016Sdes } 727157016Sdes while (++dupfd <= 2) { 728157016Sdes /* Only clobber closed fds */ 729157016Sdes if (fcntl(dupfd, F_GETFL, 0) >= 0) 730157016Sdes continue; 731157016Sdes if (dup2(nullfd, dupfd) == -1) { 732192595Sdes fprintf(stderr, "dup2: %s\n", strerror(errno)); 733157016Sdes exit(1); 734157016Sdes } 735157016Sdes } 736157016Sdes if (nullfd > 2) 737157016Sdes close(nullfd); 738157016Sdes} 739157016Sdes 740149749Sdeschar * 741162852Sdestohex(const void *vp, size_t l) 742149749Sdes{ 743162852Sdes const u_char *p = (const u_char *)vp; 744149749Sdes char b[3], *r; 745162852Sdes size_t i, hl; 746149749Sdes 747162852Sdes if (l > 65536) 748162852Sdes return xstrdup("tohex: length > 65536"); 749162852Sdes 750149749Sdes hl = l * 2 + 1; 751162852Sdes r = xcalloc(1, hl); 752149749Sdes for (i = 0; i < l; i++) { 753162852Sdes snprintf(b, sizeof(b), "%02x", p[i]); 754149749Sdes strlcat(r, b, hl); 755149749Sdes } 756149749Sdes return (r); 757149749Sdes} 758149749Sdes 759162852Sdesu_int64_t 760162852Sdesget_u64(const void *vp) 761162852Sdes{ 762162852Sdes const u_char *p = (const u_char *)vp; 763162852Sdes u_int64_t v; 764162852Sdes 765162852Sdes v = (u_int64_t)p[0] << 56; 766162852Sdes v |= (u_int64_t)p[1] << 48; 767162852Sdes v |= (u_int64_t)p[2] << 40; 768162852Sdes v |= (u_int64_t)p[3] << 32; 769162852Sdes v |= (u_int64_t)p[4] << 24; 770162852Sdes v |= (u_int64_t)p[5] << 16; 771162852Sdes v |= (u_int64_t)p[6] << 8; 772162852Sdes v |= (u_int64_t)p[7]; 773162852Sdes 774162852Sdes return (v); 775162852Sdes} 776162852Sdes 777162852Sdesu_int32_t 778162852Sdesget_u32(const void *vp) 779162852Sdes{ 780162852Sdes const u_char *p = (const u_char *)vp; 781162852Sdes u_int32_t v; 782162852Sdes 783162852Sdes v = (u_int32_t)p[0] << 24; 784162852Sdes v |= (u_int32_t)p[1] << 16; 785162852Sdes v |= (u_int32_t)p[2] << 8; 786162852Sdes v |= (u_int32_t)p[3]; 787162852Sdes 788162852Sdes return (v); 789162852Sdes} 790162852Sdes 791162852Sdesu_int16_t 792162852Sdesget_u16(const void *vp) 793162852Sdes{ 794162852Sdes const u_char *p = (const u_char *)vp; 795162852Sdes u_int16_t v; 796162852Sdes 797162852Sdes v = (u_int16_t)p[0] << 8; 798162852Sdes v |= (u_int16_t)p[1]; 799162852Sdes 800162852Sdes return (v); 801162852Sdes} 802162852Sdes 803162852Sdesvoid 804162852Sdesput_u64(void *vp, u_int64_t v) 805162852Sdes{ 806162852Sdes u_char *p = (u_char *)vp; 807162852Sdes 808162852Sdes p[0] = (u_char)(v >> 56) & 0xff; 809162852Sdes p[1] = (u_char)(v >> 48) & 0xff; 810162852Sdes p[2] = (u_char)(v >> 40) & 0xff; 811162852Sdes p[3] = (u_char)(v >> 32) & 0xff; 812162852Sdes p[4] = (u_char)(v >> 24) & 0xff; 813162852Sdes p[5] = (u_char)(v >> 16) & 0xff; 814162852Sdes p[6] = (u_char)(v >> 8) & 0xff; 815162852Sdes p[7] = (u_char)v & 0xff; 816162852Sdes} 817162852Sdes 818162852Sdesvoid 819162852Sdesput_u32(void *vp, u_int32_t v) 820162852Sdes{ 821162852Sdes u_char *p = (u_char *)vp; 822162852Sdes 823162852Sdes p[0] = (u_char)(v >> 24) & 0xff; 824162852Sdes p[1] = (u_char)(v >> 16) & 0xff; 825162852Sdes p[2] = (u_char)(v >> 8) & 0xff; 826162852Sdes p[3] = (u_char)v & 0xff; 827162852Sdes} 828162852Sdes 829162852Sdes 830162852Sdesvoid 831162852Sdesput_u16(void *vp, u_int16_t v) 832162852Sdes{ 833162852Sdes u_char *p = (u_char *)vp; 834162852Sdes 835162852Sdes p[0] = (u_char)(v >> 8) & 0xff; 836162852Sdes p[1] = (u_char)v & 0xff; 837162852Sdes} 838181111Sdes 839181111Sdesvoid 840181111Sdesms_subtract_diff(struct timeval *start, int *ms) 841181111Sdes{ 842181111Sdes struct timeval diff, finish; 843181111Sdes 844181111Sdes gettimeofday(&finish, NULL); 845181111Sdes timersub(&finish, start, &diff); 846181111Sdes *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); 847181111Sdes} 848181111Sdes 849181111Sdesvoid 850181111Sdesms_to_timeval(struct timeval *tv, int ms) 851181111Sdes{ 852181111Sdes if (ms < 0) 853181111Sdes ms = 0; 854181111Sdes tv->tv_sec = ms / 1000; 855181111Sdes tv->tv_usec = (ms % 1000) * 1000; 856181111Sdes} 857181111Sdes 858255767Sdestime_t 859255767Sdesmonotime(void) 860255767Sdes{ 861255767Sdes#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) 862255767Sdes struct timespec ts; 863255767Sdes static int gettime_failed = 0; 864255767Sdes 865255767Sdes if (!gettime_failed) { 866255767Sdes if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) 867255767Sdes return (ts.tv_sec); 868255767Sdes debug3("clock_gettime: %s", strerror(errno)); 869255767Sdes gettime_failed = 1; 870255767Sdes } 871255767Sdes#endif 872255767Sdes 873255767Sdes return time(NULL); 874255767Sdes} 875255767Sdes 876221420Sdesvoid 877221420Sdesbandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen) 878221420Sdes{ 879221420Sdes bw->buflen = buflen; 880221420Sdes bw->rate = kbps; 881221420Sdes bw->thresh = bw->rate; 882221420Sdes bw->lamt = 0; 883221420Sdes timerclear(&bw->bwstart); 884221420Sdes timerclear(&bw->bwend); 885221420Sdes} 886221420Sdes 887221420Sdes/* Callback from read/write loop to insert bandwidth-limiting delays */ 888221420Sdesvoid 889221420Sdesbandwidth_limit(struct bwlimit *bw, size_t read_len) 890221420Sdes{ 891221420Sdes u_int64_t waitlen; 892221420Sdes struct timespec ts, rm; 893221420Sdes 894221420Sdes if (!timerisset(&bw->bwstart)) { 895221420Sdes gettimeofday(&bw->bwstart, NULL); 896221420Sdes return; 897221420Sdes } 898221420Sdes 899221420Sdes bw->lamt += read_len; 900221420Sdes if (bw->lamt < bw->thresh) 901221420Sdes return; 902221420Sdes 903221420Sdes gettimeofday(&bw->bwend, NULL); 904221420Sdes timersub(&bw->bwend, &bw->bwstart, &bw->bwend); 905221420Sdes if (!timerisset(&bw->bwend)) 906221420Sdes return; 907221420Sdes 908221420Sdes bw->lamt *= 8; 909221420Sdes waitlen = (double)1000000L * bw->lamt / bw->rate; 910221420Sdes 911221420Sdes bw->bwstart.tv_sec = waitlen / 1000000L; 912221420Sdes bw->bwstart.tv_usec = waitlen % 1000000L; 913221420Sdes 914221420Sdes if (timercmp(&bw->bwstart, &bw->bwend, >)) { 915221420Sdes timersub(&bw->bwstart, &bw->bwend, &bw->bwend); 916221420Sdes 917221420Sdes /* Adjust the wait time */ 918221420Sdes if (bw->bwend.tv_sec) { 919221420Sdes bw->thresh /= 2; 920221420Sdes if (bw->thresh < bw->buflen / 4) 921221420Sdes bw->thresh = bw->buflen / 4; 922221420Sdes } else if (bw->bwend.tv_usec < 10000) { 923221420Sdes bw->thresh *= 2; 924221420Sdes if (bw->thresh > bw->buflen * 8) 925221420Sdes bw->thresh = bw->buflen * 8; 926221420Sdes } 927221420Sdes 928221420Sdes TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts); 929221420Sdes while (nanosleep(&ts, &rm) == -1) { 930221420Sdes if (errno != EINTR) 931221420Sdes break; 932221420Sdes ts = rm; 933221420Sdes } 934221420Sdes } 935221420Sdes 936221420Sdes bw->lamt = 0; 937221420Sdes gettimeofday(&bw->bwstart, NULL); 938221420Sdes} 939221420Sdes 940221420Sdes/* Make a template filename for mk[sd]temp() */ 941221420Sdesvoid 942221420Sdesmktemp_proto(char *s, size_t len) 943221420Sdes{ 944221420Sdes const char *tmpdir; 945221420Sdes int r; 946221420Sdes 947221420Sdes if ((tmpdir = getenv("TMPDIR")) != NULL) { 948221420Sdes r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir); 949221420Sdes if (r > 0 && (size_t)r < len) 950221420Sdes return; 951221420Sdes } 952221420Sdes r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX"); 953221420Sdes if (r < 0 || (size_t)r >= len) 954221420Sdes fatal("%s: template string too short", __func__); 955221420Sdes} 956221420Sdes 957221420Sdesstatic const struct { 958221420Sdes const char *name; 959221420Sdes int value; 960221420Sdes} ipqos[] = { 961221420Sdes { "af11", IPTOS_DSCP_AF11 }, 962221420Sdes { "af12", IPTOS_DSCP_AF12 }, 963221420Sdes { "af13", IPTOS_DSCP_AF13 }, 964240075Sdes { "af21", IPTOS_DSCP_AF21 }, 965221420Sdes { "af22", IPTOS_DSCP_AF22 }, 966221420Sdes { "af23", IPTOS_DSCP_AF23 }, 967221420Sdes { "af31", IPTOS_DSCP_AF31 }, 968221420Sdes { "af32", IPTOS_DSCP_AF32 }, 969221420Sdes { "af33", IPTOS_DSCP_AF33 }, 970221420Sdes { "af41", IPTOS_DSCP_AF41 }, 971221420Sdes { "af42", IPTOS_DSCP_AF42 }, 972221420Sdes { "af43", IPTOS_DSCP_AF43 }, 973221420Sdes { "cs0", IPTOS_DSCP_CS0 }, 974221420Sdes { "cs1", IPTOS_DSCP_CS1 }, 975221420Sdes { "cs2", IPTOS_DSCP_CS2 }, 976221420Sdes { "cs3", IPTOS_DSCP_CS3 }, 977221420Sdes { "cs4", IPTOS_DSCP_CS4 }, 978221420Sdes { "cs5", IPTOS_DSCP_CS5 }, 979221420Sdes { "cs6", IPTOS_DSCP_CS6 }, 980221420Sdes { "cs7", IPTOS_DSCP_CS7 }, 981221420Sdes { "ef", IPTOS_DSCP_EF }, 982221420Sdes { "lowdelay", IPTOS_LOWDELAY }, 983221420Sdes { "throughput", IPTOS_THROUGHPUT }, 984221420Sdes { "reliability", IPTOS_RELIABILITY }, 985221420Sdes { NULL, -1 } 986221420Sdes}; 987221420Sdes 988215116Sdesint 989221420Sdesparse_ipqos(const char *cp) 990215116Sdes{ 991221420Sdes u_int i; 992221420Sdes char *ep; 993221420Sdes long val; 994215116Sdes 995221420Sdes if (cp == NULL) 996221420Sdes return -1; 997221420Sdes for (i = 0; ipqos[i].name != NULL; i++) { 998221420Sdes if (strcasecmp(cp, ipqos[i].name) == 0) 999221420Sdes return ipqos[i].value; 1000221420Sdes } 1001221420Sdes /* Try parsing as an integer */ 1002221420Sdes val = strtol(cp, &ep, 0); 1003221420Sdes if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255) 1004221420Sdes return -1; 1005221420Sdes return val; 1006215116Sdes} 1007221420Sdes 1008226046Sdesconst char * 1009226046Sdesiptos2str(int iptos) 1010226046Sdes{ 1011226046Sdes int i; 1012226046Sdes static char iptos_str[sizeof "0xff"]; 1013226046Sdes 1014226046Sdes for (i = 0; ipqos[i].name != NULL; i++) { 1015226046Sdes if (ipqos[i].value == iptos) 1016226046Sdes return ipqos[i].name; 1017226046Sdes } 1018226046Sdes snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos); 1019226046Sdes return iptos_str; 1020226046Sdes} 1021204917Sdesvoid 1022204917Sdessock_set_v6only(int s) 1023204917Sdes{ 1024204917Sdes#ifdef IPV6_V6ONLY 1025204917Sdes int on = 1; 1026204917Sdes 1027204917Sdes debug3("%s: set socket %d IPV6_V6ONLY", __func__, s); 1028204917Sdes if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) 1029204917Sdes error("setsockopt IPV6_V6ONLY: %s", strerror(errno)); 1030204917Sdes#endif 1031204917Sdes} 1032224638Sbrooks 1033224638Sbrooksvoid 1034224638Sbrookssock_get_rcvbuf(int *size, int rcvbuf) 1035224638Sbrooks{ 1036224638Sbrooks int sock, socksize; 1037224638Sbrooks socklen_t socksizelen = sizeof(socksize); 1038224638Sbrooks 1039224638Sbrooks /* 1040224638Sbrooks * Create a socket but do not connect it. We use it 1041224638Sbrooks * only to get the rcv socket size. 1042224638Sbrooks */ 1043224638Sbrooks sock = socket(AF_INET6, SOCK_STREAM, 0); 1044224638Sbrooks if (sock < 0) 1045224638Sbrooks sock = socket(AF_INET, SOCK_STREAM, 0); 1046224638Sbrooks if (sock < 0) 1047224638Sbrooks return; 1048224638Sbrooks 1049224638Sbrooks /* 1050224638Sbrooks * If the tcp_rcv_buf option is set and passed in, attempt to set the 1051224638Sbrooks * buffer size to its value. 1052224638Sbrooks */ 1053224638Sbrooks if (rcvbuf) 1054224638Sbrooks setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf, 1055224638Sbrooks sizeof(rcvbuf)); 1056224638Sbrooks 1057224638Sbrooks if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, 1058224638Sbrooks &socksize, &socksizelen) == 0) 1059224638Sbrooks if (size != NULL) 1060224638Sbrooks *size = socksize; 1061224638Sbrooks close(sock); 1062224638Sbrooks} 1063