misc.c revision 240075
1240075Sdes/* $OpenBSD: misc.c,v 1.86 2011/09/05 05:59:08 djm Exp $ */ 2224638Sbrooks/* $FreeBSD: head/crypto/openssh/misc.c 240075 2012-09-03 16:51:41Z des $ */ 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{ 131181111Sdes if (gaierr == EAI_SYSTEM) 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); 21076259Sgreen copy->pw_gecos = xstrdup(pw->pw_gecos); 21176259Sgreen copy->pw_uid = pw->pw_uid; 21276259Sgreen copy->pw_gid = pw->pw_gid; 21398937Sdes#ifdef HAVE_PW_EXPIRE_IN_PASSWD 21492555Sdes copy->pw_expire = pw->pw_expire; 21598937Sdes#endif 21698937Sdes#ifdef HAVE_PW_CHANGE_IN_PASSWD 21792555Sdes copy->pw_change = pw->pw_change; 21898937Sdes#endif 21998937Sdes#ifdef HAVE_PW_CLASS_IN_PASSWD 22076259Sgreen copy->pw_class = xstrdup(pw->pw_class); 22198937Sdes#endif 22276259Sgreen copy->pw_dir = xstrdup(pw->pw_dir); 22376259Sgreen copy->pw_shell = xstrdup(pw->pw_shell); 22476259Sgreen return copy; 22576259Sgreen} 22676259Sgreen 22792555Sdes/* 22892555Sdes * Convert ASCII string to TCP/IP port number. 229192595Sdes * Port must be >=0 and <=65535. 230192595Sdes * Return -1 if invalid. 23192555Sdes */ 23292555Sdesint 23392555Sdesa2port(const char *s) 23476259Sgreen{ 235192595Sdes long long port; 236192595Sdes const char *errstr; 23776259Sgreen 238192595Sdes port = strtonum(s, 0, 65535, &errstr); 239192595Sdes if (errstr != NULL) 240192595Sdes return -1; 241192595Sdes return (int)port; 24276259Sgreen} 24392555Sdes 244157016Sdesint 245157016Sdesa2tun(const char *s, int *remote) 246157016Sdes{ 247157016Sdes const char *errstr = NULL; 248157016Sdes char *sp, *ep; 249157016Sdes int tun; 250157016Sdes 251157016Sdes if (remote != NULL) { 252157016Sdes *remote = SSH_TUNID_ANY; 253157016Sdes sp = xstrdup(s); 254157016Sdes if ((ep = strchr(sp, ':')) == NULL) { 255157016Sdes xfree(sp); 256157016Sdes return (a2tun(s, NULL)); 257157016Sdes } 258157016Sdes ep[0] = '\0'; ep++; 259157016Sdes *remote = a2tun(ep, NULL); 260157016Sdes tun = a2tun(sp, NULL); 261157016Sdes xfree(sp); 262157016Sdes return (*remote == SSH_TUNID_ERR ? *remote : tun); 263157016Sdes } 264157016Sdes 265157016Sdes if (strcasecmp(s, "any") == 0) 266157016Sdes return (SSH_TUNID_ANY); 267157016Sdes 268157016Sdes tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); 269157016Sdes if (errstr != NULL) 270157016Sdes return (SSH_TUNID_ERR); 271157016Sdes 272157016Sdes return (tun); 273157016Sdes} 274157016Sdes 27592555Sdes#define SECONDS 1 27692555Sdes#define MINUTES (SECONDS * 60) 27792555Sdes#define HOURS (MINUTES * 60) 27892555Sdes#define DAYS (HOURS * 24) 27992555Sdes#define WEEKS (DAYS * 7) 28092555Sdes 28192555Sdes/* 28292555Sdes * Convert a time string into seconds; format is 28392555Sdes * a sequence of: 28492555Sdes * time[qualifier] 28592555Sdes * 28692555Sdes * Valid time qualifiers are: 28792555Sdes * <none> seconds 28892555Sdes * s|S seconds 28992555Sdes * m|M minutes 29092555Sdes * h|H hours 29192555Sdes * d|D days 29292555Sdes * w|W weeks 29392555Sdes * 29492555Sdes * Examples: 29592555Sdes * 90m 90 minutes 29692555Sdes * 1h30m 90 minutes 29792555Sdes * 2d 2 days 29892555Sdes * 1w 1 week 29992555Sdes * 30092555Sdes * Return -1 if time string is invalid. 30192555Sdes */ 30292555Sdeslong 30392555Sdesconvtime(const char *s) 30492555Sdes{ 30592555Sdes long total, secs; 30692555Sdes const char *p; 30792555Sdes char *endp; 30892555Sdes 30992555Sdes errno = 0; 31092555Sdes total = 0; 31192555Sdes p = s; 31292555Sdes 31392555Sdes if (p == NULL || *p == '\0') 31492555Sdes return -1; 31592555Sdes 31692555Sdes while (*p) { 31792555Sdes secs = strtol(p, &endp, 10); 31892555Sdes if (p == endp || 31992555Sdes (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 32092555Sdes secs < 0) 32192555Sdes return -1; 32292555Sdes 32392555Sdes switch (*endp++) { 32492555Sdes case '\0': 32592555Sdes endp--; 326162852Sdes break; 32792555Sdes case 's': 32892555Sdes case 'S': 32992555Sdes break; 33092555Sdes case 'm': 33192555Sdes case 'M': 33292555Sdes secs *= MINUTES; 33392555Sdes break; 33492555Sdes case 'h': 33592555Sdes case 'H': 33692555Sdes secs *= HOURS; 33792555Sdes break; 33892555Sdes case 'd': 33992555Sdes case 'D': 34092555Sdes secs *= DAYS; 34192555Sdes break; 34292555Sdes case 'w': 34392555Sdes case 'W': 34492555Sdes secs *= WEEKS; 34592555Sdes break; 34692555Sdes default: 34792555Sdes return -1; 34892555Sdes } 34992555Sdes total += secs; 35092555Sdes if (total < 0) 35192555Sdes return -1; 35292555Sdes p = endp; 35392555Sdes } 35492555Sdes 35592555Sdes return total; 35692555Sdes} 35792555Sdes 358146998Sdes/* 359162852Sdes * Returns a standardized host+port identifier string. 360162852Sdes * Caller must free returned string. 361162852Sdes */ 362162852Sdeschar * 363162852Sdesput_host_port(const char *host, u_short port) 364162852Sdes{ 365162852Sdes char *hoststr; 366162852Sdes 367162852Sdes if (port == 0 || port == SSH_DEFAULT_PORT) 368162852Sdes return(xstrdup(host)); 369162852Sdes if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0) 370162852Sdes fatal("put_host_port: asprintf: %s", strerror(errno)); 371162852Sdes debug3("put_host_port: %s", hoststr); 372162852Sdes return hoststr; 373162852Sdes} 374162852Sdes 375162852Sdes/* 376146998Sdes * Search for next delimiter between hostnames/addresses and ports. 377146998Sdes * Argument may be modified (for termination). 378146998Sdes * Returns *cp if parsing succeeds. 379146998Sdes * *cp is set to the start of the next delimiter, if one was found. 380146998Sdes * If this is the last field, *cp is set to NULL. 381146998Sdes */ 38292555Sdeschar * 383146998Sdeshpdelim(char **cp) 384146998Sdes{ 385146998Sdes char *s, *old; 386146998Sdes 387146998Sdes if (cp == NULL || *cp == NULL) 388146998Sdes return NULL; 389146998Sdes 390146998Sdes old = s = *cp; 391146998Sdes if (*s == '[') { 392146998Sdes if ((s = strchr(s, ']')) == NULL) 393146998Sdes return NULL; 394146998Sdes else 395146998Sdes s++; 396146998Sdes } else if ((s = strpbrk(s, ":/")) == NULL) 397146998Sdes s = *cp + strlen(*cp); /* skip to end (see first case below) */ 398146998Sdes 399146998Sdes switch (*s) { 400146998Sdes case '\0': 401146998Sdes *cp = NULL; /* no more fields*/ 402146998Sdes break; 403147001Sdes 404146998Sdes case ':': 405146998Sdes case '/': 406146998Sdes *s = '\0'; /* terminate */ 407146998Sdes *cp = s + 1; 408146998Sdes break; 409147001Sdes 410146998Sdes default: 411146998Sdes return NULL; 412146998Sdes } 413146998Sdes 414146998Sdes return old; 415146998Sdes} 416146998Sdes 417146998Sdeschar * 41892555Sdescleanhostname(char *host) 41992555Sdes{ 42092555Sdes if (*host == '[' && host[strlen(host) - 1] == ']') { 42192555Sdes host[strlen(host) - 1] = '\0'; 42292555Sdes return (host + 1); 42392555Sdes } else 42492555Sdes return host; 42592555Sdes} 42692555Sdes 42792555Sdeschar * 42892555Sdescolon(char *cp) 42992555Sdes{ 43092555Sdes int flag = 0; 43192555Sdes 43292555Sdes if (*cp == ':') /* Leading colon is part of file name. */ 433215116Sdes return NULL; 43492555Sdes if (*cp == '[') 43592555Sdes flag = 1; 43692555Sdes 43792555Sdes for (; *cp; ++cp) { 43892555Sdes if (*cp == '@' && *(cp+1) == '[') 43992555Sdes flag = 1; 44092555Sdes if (*cp == ']' && *(cp+1) == ':' && flag) 44192555Sdes return (cp+1); 44292555Sdes if (*cp == ':' && !flag) 44392555Sdes return (cp); 44492555Sdes if (*cp == '/') 445215116Sdes return NULL; 44692555Sdes } 447215116Sdes return NULL; 44892555Sdes} 44992555Sdes 45092555Sdes/* function to assist building execv() arguments */ 45192555Sdesvoid 45292555Sdesaddargs(arglist *args, char *fmt, ...) 45392555Sdes{ 45492555Sdes va_list ap; 455157016Sdes char *cp; 456137015Sdes u_int nalloc; 457157016Sdes int r; 45892555Sdes 45992555Sdes va_start(ap, fmt); 460157016Sdes r = vasprintf(&cp, fmt, ap); 46192555Sdes va_end(ap); 462157016Sdes if (r == -1) 463157016Sdes fatal("addargs: argument too long"); 46492555Sdes 465120161Snectar nalloc = args->nalloc; 46692555Sdes if (args->list == NULL) { 467120161Snectar nalloc = 32; 46892555Sdes args->num = 0; 469120161Snectar } else if (args->num+2 >= nalloc) 470120161Snectar nalloc *= 2; 47192555Sdes 472162852Sdes args->list = xrealloc(args->list, nalloc, sizeof(char *)); 473120161Snectar args->nalloc = nalloc; 474157016Sdes args->list[args->num++] = cp; 47592555Sdes args->list[args->num] = NULL; 47692555Sdes} 477146998Sdes 478157016Sdesvoid 479157016Sdesreplacearg(arglist *args, u_int which, char *fmt, ...) 480157016Sdes{ 481157016Sdes va_list ap; 482157016Sdes char *cp; 483157016Sdes int r; 484157016Sdes 485157016Sdes va_start(ap, fmt); 486157016Sdes r = vasprintf(&cp, fmt, ap); 487157016Sdes va_end(ap); 488157016Sdes if (r == -1) 489157016Sdes fatal("replacearg: argument too long"); 490157016Sdes 491157016Sdes if (which >= args->num) 492157016Sdes fatal("replacearg: tried to replace invalid arg %d >= %d", 493157016Sdes which, args->num); 494157016Sdes xfree(args->list[which]); 495157016Sdes args->list[which] = cp; 496157016Sdes} 497157016Sdes 498157016Sdesvoid 499157016Sdesfreeargs(arglist *args) 500157016Sdes{ 501157016Sdes u_int i; 502157016Sdes 503157016Sdes if (args->list != NULL) { 504157016Sdes for (i = 0; i < args->num; i++) 505157016Sdes xfree(args->list[i]); 506157016Sdes xfree(args->list); 507157016Sdes args->nalloc = args->num = 0; 508157016Sdes args->list = NULL; 509157016Sdes } 510157016Sdes} 511157016Sdes 512146998Sdes/* 513149749Sdes * Expands tildes in the file name. Returns data allocated by xmalloc. 514149749Sdes * Warning: this calls getpw*. 515149749Sdes */ 516149749Sdeschar * 517149749Sdestilde_expand_filename(const char *filename, uid_t uid) 518149749Sdes{ 519149749Sdes const char *path; 520149749Sdes char user[128], ret[MAXPATHLEN]; 521149749Sdes struct passwd *pw; 522149749Sdes u_int len, slash; 523149749Sdes 524149749Sdes if (*filename != '~') 525149749Sdes return (xstrdup(filename)); 526149749Sdes filename++; 527149749Sdes 528149749Sdes path = strchr(filename, '/'); 529149749Sdes if (path != NULL && path > filename) { /* ~user/path */ 530149749Sdes slash = path - filename; 531149749Sdes if (slash > sizeof(user) - 1) 532149749Sdes fatal("tilde_expand_filename: ~username too long"); 533149749Sdes memcpy(user, filename, slash); 534149749Sdes user[slash] = '\0'; 535149749Sdes if ((pw = getpwnam(user)) == NULL) 536149749Sdes fatal("tilde_expand_filename: No such user %s", user); 537149749Sdes } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ 538181111Sdes fatal("tilde_expand_filename: No such uid %ld", (long)uid); 539149749Sdes 540149749Sdes if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret)) 541149749Sdes fatal("tilde_expand_filename: Path too long"); 542149749Sdes 543149749Sdes /* Make sure directory has a trailing '/' */ 544149749Sdes len = strlen(pw->pw_dir); 545149749Sdes if ((len == 0 || pw->pw_dir[len - 1] != '/') && 546149749Sdes strlcat(ret, "/", sizeof(ret)) >= sizeof(ret)) 547149749Sdes fatal("tilde_expand_filename: Path too long"); 548149749Sdes 549149749Sdes /* Skip leading '/' from specified path */ 550149749Sdes if (path != NULL) 551149749Sdes filename = path + 1; 552149749Sdes if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret)) 553149749Sdes fatal("tilde_expand_filename: Path too long"); 554149749Sdes 555149749Sdes return (xstrdup(ret)); 556149749Sdes} 557149749Sdes 558149749Sdes/* 559149749Sdes * Expand a string with a set of %[char] escapes. A number of escapes may be 560149749Sdes * specified as (char *escape_chars, char *replacement) pairs. The list must 561149749Sdes * be terminated by a NULL escape_char. Returns replaced string in memory 562149749Sdes * allocated by xmalloc. 563149749Sdes */ 564149749Sdeschar * 565149749Sdespercent_expand(const char *string, ...) 566149749Sdes{ 567149749Sdes#define EXPAND_MAX_KEYS 16 568204917Sdes u_int num_keys, i, j; 569149749Sdes struct { 570149749Sdes const char *key; 571149749Sdes const char *repl; 572149749Sdes } keys[EXPAND_MAX_KEYS]; 573149749Sdes char buf[4096]; 574149749Sdes va_list ap; 575149749Sdes 576149749Sdes /* Gather keys */ 577149749Sdes va_start(ap, string); 578149749Sdes for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { 579149749Sdes keys[num_keys].key = va_arg(ap, char *); 580149749Sdes if (keys[num_keys].key == NULL) 581149749Sdes break; 582149749Sdes keys[num_keys].repl = va_arg(ap, char *); 583149749Sdes if (keys[num_keys].repl == NULL) 584204917Sdes fatal("%s: NULL replacement", __func__); 585149749Sdes } 586204917Sdes if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) 587204917Sdes fatal("%s: too many keys", __func__); 588149749Sdes va_end(ap); 589149749Sdes 590149749Sdes /* Expand string */ 591149749Sdes *buf = '\0'; 592149749Sdes for (i = 0; *string != '\0'; string++) { 593149749Sdes if (*string != '%') { 594149749Sdes append: 595149749Sdes buf[i++] = *string; 596149749Sdes if (i >= sizeof(buf)) 597204917Sdes fatal("%s: string too long", __func__); 598149749Sdes buf[i] = '\0'; 599149749Sdes continue; 600149749Sdes } 601149749Sdes string++; 602204917Sdes /* %% case */ 603149749Sdes if (*string == '%') 604149749Sdes goto append; 605149749Sdes for (j = 0; j < num_keys; j++) { 606149749Sdes if (strchr(keys[j].key, *string) != NULL) { 607149749Sdes i = strlcat(buf, keys[j].repl, sizeof(buf)); 608149749Sdes if (i >= sizeof(buf)) 609204917Sdes fatal("%s: string too long", __func__); 610149749Sdes break; 611149749Sdes } 612149749Sdes } 613149749Sdes if (j >= num_keys) 614204917Sdes fatal("%s: unknown key %%%c", __func__, *string); 615149749Sdes } 616149749Sdes return (xstrdup(buf)); 617149749Sdes#undef EXPAND_MAX_KEYS 618149749Sdes} 619149749Sdes 620149749Sdes/* 621146998Sdes * Read an entire line from a public key file into a static buffer, discarding 622146998Sdes * lines that exceed the buffer size. Returns 0 on success, -1 on failure. 623146998Sdes */ 624146998Sdesint 625146998Sdesread_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, 626146998Sdes u_long *lineno) 627146998Sdes{ 628146998Sdes while (fgets(buf, bufsz, f) != NULL) { 629181111Sdes if (buf[0] == '\0') 630181111Sdes continue; 631146998Sdes (*lineno)++; 632146998Sdes if (buf[strlen(buf) - 1] == '\n' || feof(f)) { 633146998Sdes return 0; 634146998Sdes } else { 635146998Sdes debug("%s: %s line %lu exceeds size limit", __func__, 636146998Sdes filename, *lineno); 637146998Sdes /* discard remainder of line */ 638147001Sdes while (fgetc(f) != '\n' && !feof(f)) 639146998Sdes ; /* nothing */ 640146998Sdes } 641146998Sdes } 642146998Sdes return -1; 643146998Sdes} 644149749Sdes 645157016Sdesint 646157016Sdestun_open(int tun, int mode) 647157016Sdes{ 648157016Sdes#if defined(CUSTOM_SYS_TUN_OPEN) 649157016Sdes return (sys_tun_open(tun, mode)); 650157016Sdes#elif defined(SSH_TUN_OPENBSD) 651157016Sdes struct ifreq ifr; 652157016Sdes char name[100]; 653157016Sdes int fd = -1, sock; 654157016Sdes 655157016Sdes /* Open the tunnel device */ 656157016Sdes if (tun <= SSH_TUNID_MAX) { 657157016Sdes snprintf(name, sizeof(name), "/dev/tun%d", tun); 658157016Sdes fd = open(name, O_RDWR); 659157016Sdes } else if (tun == SSH_TUNID_ANY) { 660157016Sdes for (tun = 100; tun >= 0; tun--) { 661157016Sdes snprintf(name, sizeof(name), "/dev/tun%d", tun); 662157016Sdes if ((fd = open(name, O_RDWR)) >= 0) 663157016Sdes break; 664157016Sdes } 665157016Sdes } else { 666157016Sdes debug("%s: invalid tunnel %u", __func__, tun); 667157016Sdes return (-1); 668157016Sdes } 669157016Sdes 670157016Sdes if (fd < 0) { 671157016Sdes debug("%s: %s open failed: %s", __func__, name, strerror(errno)); 672157016Sdes return (-1); 673157016Sdes } 674157016Sdes 675157016Sdes debug("%s: %s mode %d fd %d", __func__, name, mode, fd); 676157016Sdes 677157016Sdes /* Set the tunnel device operation mode */ 678157016Sdes snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun); 679157016Sdes if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 680157016Sdes goto failed; 681157016Sdes 682157016Sdes if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) 683157016Sdes goto failed; 684157016Sdes 685157016Sdes /* Set interface mode */ 686157016Sdes ifr.ifr_flags &= ~IFF_UP; 687157016Sdes if (mode == SSH_TUNMODE_ETHERNET) 688157016Sdes ifr.ifr_flags |= IFF_LINK0; 689157016Sdes else 690157016Sdes ifr.ifr_flags &= ~IFF_LINK0; 691157016Sdes if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) 692157016Sdes goto failed; 693157016Sdes 694157016Sdes /* Bring interface up */ 695157016Sdes ifr.ifr_flags |= IFF_UP; 696157016Sdes if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) 697157016Sdes goto failed; 698157016Sdes 699157016Sdes close(sock); 700157016Sdes return (fd); 701157016Sdes 702157016Sdes failed: 703157016Sdes if (fd >= 0) 704157016Sdes close(fd); 705157016Sdes if (sock >= 0) 706157016Sdes close(sock); 707157016Sdes debug("%s: failed to set %s mode %d: %s", __func__, name, 708157016Sdes mode, strerror(errno)); 709157016Sdes return (-1); 710157016Sdes#else 711157016Sdes error("Tunnel interfaces are not supported on this platform"); 712157016Sdes return (-1); 713157016Sdes#endif 714157016Sdes} 715157016Sdes 716157016Sdesvoid 717157016Sdessanitise_stdfd(void) 718157016Sdes{ 719157016Sdes int nullfd, dupfd; 720157016Sdes 721157016Sdes if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 722192595Sdes fprintf(stderr, "Couldn't open /dev/null: %s\n", 723192595Sdes strerror(errno)); 724157016Sdes exit(1); 725157016Sdes } 726157016Sdes while (++dupfd <= 2) { 727157016Sdes /* Only clobber closed fds */ 728157016Sdes if (fcntl(dupfd, F_GETFL, 0) >= 0) 729157016Sdes continue; 730157016Sdes if (dup2(nullfd, dupfd) == -1) { 731192595Sdes fprintf(stderr, "dup2: %s\n", strerror(errno)); 732157016Sdes exit(1); 733157016Sdes } 734157016Sdes } 735157016Sdes if (nullfd > 2) 736157016Sdes close(nullfd); 737157016Sdes} 738157016Sdes 739149749Sdeschar * 740162852Sdestohex(const void *vp, size_t l) 741149749Sdes{ 742162852Sdes const u_char *p = (const u_char *)vp; 743149749Sdes char b[3], *r; 744162852Sdes size_t i, hl; 745149749Sdes 746162852Sdes if (l > 65536) 747162852Sdes return xstrdup("tohex: length > 65536"); 748162852Sdes 749149749Sdes hl = l * 2 + 1; 750162852Sdes r = xcalloc(1, hl); 751149749Sdes for (i = 0; i < l; i++) { 752162852Sdes snprintf(b, sizeof(b), "%02x", p[i]); 753149749Sdes strlcat(r, b, hl); 754149749Sdes } 755149749Sdes return (r); 756149749Sdes} 757149749Sdes 758162852Sdesu_int64_t 759162852Sdesget_u64(const void *vp) 760162852Sdes{ 761162852Sdes const u_char *p = (const u_char *)vp; 762162852Sdes u_int64_t v; 763162852Sdes 764162852Sdes v = (u_int64_t)p[0] << 56; 765162852Sdes v |= (u_int64_t)p[1] << 48; 766162852Sdes v |= (u_int64_t)p[2] << 40; 767162852Sdes v |= (u_int64_t)p[3] << 32; 768162852Sdes v |= (u_int64_t)p[4] << 24; 769162852Sdes v |= (u_int64_t)p[5] << 16; 770162852Sdes v |= (u_int64_t)p[6] << 8; 771162852Sdes v |= (u_int64_t)p[7]; 772162852Sdes 773162852Sdes return (v); 774162852Sdes} 775162852Sdes 776162852Sdesu_int32_t 777162852Sdesget_u32(const void *vp) 778162852Sdes{ 779162852Sdes const u_char *p = (const u_char *)vp; 780162852Sdes u_int32_t v; 781162852Sdes 782162852Sdes v = (u_int32_t)p[0] << 24; 783162852Sdes v |= (u_int32_t)p[1] << 16; 784162852Sdes v |= (u_int32_t)p[2] << 8; 785162852Sdes v |= (u_int32_t)p[3]; 786162852Sdes 787162852Sdes return (v); 788162852Sdes} 789162852Sdes 790162852Sdesu_int16_t 791162852Sdesget_u16(const void *vp) 792162852Sdes{ 793162852Sdes const u_char *p = (const u_char *)vp; 794162852Sdes u_int16_t v; 795162852Sdes 796162852Sdes v = (u_int16_t)p[0] << 8; 797162852Sdes v |= (u_int16_t)p[1]; 798162852Sdes 799162852Sdes return (v); 800162852Sdes} 801162852Sdes 802162852Sdesvoid 803162852Sdesput_u64(void *vp, u_int64_t v) 804162852Sdes{ 805162852Sdes u_char *p = (u_char *)vp; 806162852Sdes 807162852Sdes p[0] = (u_char)(v >> 56) & 0xff; 808162852Sdes p[1] = (u_char)(v >> 48) & 0xff; 809162852Sdes p[2] = (u_char)(v >> 40) & 0xff; 810162852Sdes p[3] = (u_char)(v >> 32) & 0xff; 811162852Sdes p[4] = (u_char)(v >> 24) & 0xff; 812162852Sdes p[5] = (u_char)(v >> 16) & 0xff; 813162852Sdes p[6] = (u_char)(v >> 8) & 0xff; 814162852Sdes p[7] = (u_char)v & 0xff; 815162852Sdes} 816162852Sdes 817162852Sdesvoid 818162852Sdesput_u32(void *vp, u_int32_t v) 819162852Sdes{ 820162852Sdes u_char *p = (u_char *)vp; 821162852Sdes 822162852Sdes p[0] = (u_char)(v >> 24) & 0xff; 823162852Sdes p[1] = (u_char)(v >> 16) & 0xff; 824162852Sdes p[2] = (u_char)(v >> 8) & 0xff; 825162852Sdes p[3] = (u_char)v & 0xff; 826162852Sdes} 827162852Sdes 828162852Sdes 829162852Sdesvoid 830162852Sdesput_u16(void *vp, u_int16_t v) 831162852Sdes{ 832162852Sdes u_char *p = (u_char *)vp; 833162852Sdes 834162852Sdes p[0] = (u_char)(v >> 8) & 0xff; 835162852Sdes p[1] = (u_char)v & 0xff; 836162852Sdes} 837181111Sdes 838181111Sdesvoid 839181111Sdesms_subtract_diff(struct timeval *start, int *ms) 840181111Sdes{ 841181111Sdes struct timeval diff, finish; 842181111Sdes 843181111Sdes gettimeofday(&finish, NULL); 844181111Sdes timersub(&finish, start, &diff); 845181111Sdes *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); 846181111Sdes} 847181111Sdes 848181111Sdesvoid 849181111Sdesms_to_timeval(struct timeval *tv, int ms) 850181111Sdes{ 851181111Sdes if (ms < 0) 852181111Sdes ms = 0; 853181111Sdes tv->tv_sec = ms / 1000; 854181111Sdes tv->tv_usec = (ms % 1000) * 1000; 855181111Sdes} 856181111Sdes 857221420Sdesvoid 858221420Sdesbandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen) 859221420Sdes{ 860221420Sdes bw->buflen = buflen; 861221420Sdes bw->rate = kbps; 862221420Sdes bw->thresh = bw->rate; 863221420Sdes bw->lamt = 0; 864221420Sdes timerclear(&bw->bwstart); 865221420Sdes timerclear(&bw->bwend); 866221420Sdes} 867221420Sdes 868221420Sdes/* Callback from read/write loop to insert bandwidth-limiting delays */ 869221420Sdesvoid 870221420Sdesbandwidth_limit(struct bwlimit *bw, size_t read_len) 871221420Sdes{ 872221420Sdes u_int64_t waitlen; 873221420Sdes struct timespec ts, rm; 874221420Sdes 875221420Sdes if (!timerisset(&bw->bwstart)) { 876221420Sdes gettimeofday(&bw->bwstart, NULL); 877221420Sdes return; 878221420Sdes } 879221420Sdes 880221420Sdes bw->lamt += read_len; 881221420Sdes if (bw->lamt < bw->thresh) 882221420Sdes return; 883221420Sdes 884221420Sdes gettimeofday(&bw->bwend, NULL); 885221420Sdes timersub(&bw->bwend, &bw->bwstart, &bw->bwend); 886221420Sdes if (!timerisset(&bw->bwend)) 887221420Sdes return; 888221420Sdes 889221420Sdes bw->lamt *= 8; 890221420Sdes waitlen = (double)1000000L * bw->lamt / bw->rate; 891221420Sdes 892221420Sdes bw->bwstart.tv_sec = waitlen / 1000000L; 893221420Sdes bw->bwstart.tv_usec = waitlen % 1000000L; 894221420Sdes 895221420Sdes if (timercmp(&bw->bwstart, &bw->bwend, >)) { 896221420Sdes timersub(&bw->bwstart, &bw->bwend, &bw->bwend); 897221420Sdes 898221420Sdes /* Adjust the wait time */ 899221420Sdes if (bw->bwend.tv_sec) { 900221420Sdes bw->thresh /= 2; 901221420Sdes if (bw->thresh < bw->buflen / 4) 902221420Sdes bw->thresh = bw->buflen / 4; 903221420Sdes } else if (bw->bwend.tv_usec < 10000) { 904221420Sdes bw->thresh *= 2; 905221420Sdes if (bw->thresh > bw->buflen * 8) 906221420Sdes bw->thresh = bw->buflen * 8; 907221420Sdes } 908221420Sdes 909221420Sdes TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts); 910221420Sdes while (nanosleep(&ts, &rm) == -1) { 911221420Sdes if (errno != EINTR) 912221420Sdes break; 913221420Sdes ts = rm; 914221420Sdes } 915221420Sdes } 916221420Sdes 917221420Sdes bw->lamt = 0; 918221420Sdes gettimeofday(&bw->bwstart, NULL); 919221420Sdes} 920221420Sdes 921221420Sdes/* Make a template filename for mk[sd]temp() */ 922221420Sdesvoid 923221420Sdesmktemp_proto(char *s, size_t len) 924221420Sdes{ 925221420Sdes const char *tmpdir; 926221420Sdes int r; 927221420Sdes 928221420Sdes if ((tmpdir = getenv("TMPDIR")) != NULL) { 929221420Sdes r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir); 930221420Sdes if (r > 0 && (size_t)r < len) 931221420Sdes return; 932221420Sdes } 933221420Sdes r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX"); 934221420Sdes if (r < 0 || (size_t)r >= len) 935221420Sdes fatal("%s: template string too short", __func__); 936221420Sdes} 937221420Sdes 938221420Sdesstatic const struct { 939221420Sdes const char *name; 940221420Sdes int value; 941221420Sdes} ipqos[] = { 942221420Sdes { "af11", IPTOS_DSCP_AF11 }, 943221420Sdes { "af12", IPTOS_DSCP_AF12 }, 944221420Sdes { "af13", IPTOS_DSCP_AF13 }, 945240075Sdes { "af21", IPTOS_DSCP_AF21 }, 946221420Sdes { "af22", IPTOS_DSCP_AF22 }, 947221420Sdes { "af23", IPTOS_DSCP_AF23 }, 948221420Sdes { "af31", IPTOS_DSCP_AF31 }, 949221420Sdes { "af32", IPTOS_DSCP_AF32 }, 950221420Sdes { "af33", IPTOS_DSCP_AF33 }, 951221420Sdes { "af41", IPTOS_DSCP_AF41 }, 952221420Sdes { "af42", IPTOS_DSCP_AF42 }, 953221420Sdes { "af43", IPTOS_DSCP_AF43 }, 954221420Sdes { "cs0", IPTOS_DSCP_CS0 }, 955221420Sdes { "cs1", IPTOS_DSCP_CS1 }, 956221420Sdes { "cs2", IPTOS_DSCP_CS2 }, 957221420Sdes { "cs3", IPTOS_DSCP_CS3 }, 958221420Sdes { "cs4", IPTOS_DSCP_CS4 }, 959221420Sdes { "cs5", IPTOS_DSCP_CS5 }, 960221420Sdes { "cs6", IPTOS_DSCP_CS6 }, 961221420Sdes { "cs7", IPTOS_DSCP_CS7 }, 962221420Sdes { "ef", IPTOS_DSCP_EF }, 963221420Sdes { "lowdelay", IPTOS_LOWDELAY }, 964221420Sdes { "throughput", IPTOS_THROUGHPUT }, 965221420Sdes { "reliability", IPTOS_RELIABILITY }, 966221420Sdes { NULL, -1 } 967221420Sdes}; 968221420Sdes 969215116Sdesint 970221420Sdesparse_ipqos(const char *cp) 971215116Sdes{ 972221420Sdes u_int i; 973221420Sdes char *ep; 974221420Sdes long val; 975215116Sdes 976221420Sdes if (cp == NULL) 977221420Sdes return -1; 978221420Sdes for (i = 0; ipqos[i].name != NULL; i++) { 979221420Sdes if (strcasecmp(cp, ipqos[i].name) == 0) 980221420Sdes return ipqos[i].value; 981221420Sdes } 982221420Sdes /* Try parsing as an integer */ 983221420Sdes val = strtol(cp, &ep, 0); 984221420Sdes if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255) 985221420Sdes return -1; 986221420Sdes return val; 987215116Sdes} 988221420Sdes 989226046Sdesconst char * 990226046Sdesiptos2str(int iptos) 991226046Sdes{ 992226046Sdes int i; 993226046Sdes static char iptos_str[sizeof "0xff"]; 994226046Sdes 995226046Sdes for (i = 0; ipqos[i].name != NULL; i++) { 996226046Sdes if (ipqos[i].value == iptos) 997226046Sdes return ipqos[i].name; 998226046Sdes } 999226046Sdes snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos); 1000226046Sdes return iptos_str; 1001226046Sdes} 1002204917Sdesvoid 1003204917Sdessock_set_v6only(int s) 1004204917Sdes{ 1005204917Sdes#ifdef IPV6_V6ONLY 1006204917Sdes int on = 1; 1007204917Sdes 1008204917Sdes debug3("%s: set socket %d IPV6_V6ONLY", __func__, s); 1009204917Sdes if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) 1010204917Sdes error("setsockopt IPV6_V6ONLY: %s", strerror(errno)); 1011204917Sdes#endif 1012204917Sdes} 1013224638Sbrooks 1014224638Sbrooksvoid 1015224638Sbrookssock_get_rcvbuf(int *size, int rcvbuf) 1016224638Sbrooks{ 1017224638Sbrooks int sock, socksize; 1018224638Sbrooks socklen_t socksizelen = sizeof(socksize); 1019224638Sbrooks 1020224638Sbrooks /* 1021224638Sbrooks * Create a socket but do not connect it. We use it 1022224638Sbrooks * only to get the rcv socket size. 1023224638Sbrooks */ 1024224638Sbrooks sock = socket(AF_INET6, SOCK_STREAM, 0); 1025224638Sbrooks if (sock < 0) 1026224638Sbrooks sock = socket(AF_INET, SOCK_STREAM, 0); 1027224638Sbrooks if (sock < 0) 1028224638Sbrooks return; 1029224638Sbrooks 1030224638Sbrooks /* 1031224638Sbrooks * If the tcp_rcv_buf option is set and passed in, attempt to set the 1032224638Sbrooks * buffer size to its value. 1033224638Sbrooks */ 1034224638Sbrooks if (rcvbuf) 1035224638Sbrooks setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf, 1036224638Sbrooks sizeof(rcvbuf)); 1037224638Sbrooks 1038224638Sbrooks if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, 1039224638Sbrooks &socksize, &socksizelen) == 0) 1040224638Sbrooks if (size != NULL) 1041224638Sbrooks *size = socksize; 1042224638Sbrooks close(sock); 1043224638Sbrooks} 1044