misc.c revision 149749
176259Sgreen/* 276259Sgreen * Copyright (c) 2000 Markus Friedl. All rights reserved. 3149749Sdes * Copyright (c) 2005 Damien Miller. All rights reserved. 476259Sgreen * 576259Sgreen * Redistribution and use in source and binary forms, with or without 676259Sgreen * modification, are permitted provided that the following conditions 776259Sgreen * are met: 876259Sgreen * 1. Redistributions of source code must retain the above copyright 976259Sgreen * notice, this list of conditions and the following disclaimer. 1076259Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1176259Sgreen * notice, this list of conditions and the following disclaimer in the 1276259Sgreen * documentation and/or other materials provided with the distribution. 1376259Sgreen * 1476259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1576259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1676259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1776259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1876259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1976259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2076259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2176259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2276259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2376259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2476259Sgreen */ 2576259Sgreen 2676259Sgreen#include "includes.h" 27149749SdesRCSID("$OpenBSD: misc.c,v 1.34 2005/07/08 09:26:18 dtucker Exp $"); 2876259Sgreen 2976259Sgreen#include "misc.h" 3076259Sgreen#include "log.h" 3176259Sgreen#include "xmalloc.h" 3276259Sgreen 3392555Sdes/* remove newline at end of string */ 3476259Sgreenchar * 3576259Sgreenchop(char *s) 3676259Sgreen{ 3776259Sgreen char *t = s; 3876259Sgreen while (*t) { 3992555Sdes if (*t == '\n' || *t == '\r') { 4076259Sgreen *t = '\0'; 4176259Sgreen return s; 4276259Sgreen } 4376259Sgreen t++; 4476259Sgreen } 4576259Sgreen return s; 4676259Sgreen 4776259Sgreen} 4876259Sgreen 4992555Sdes/* set/unset filedescriptor to non-blocking */ 50137015Sdesint 5176259Sgreenset_nonblock(int fd) 5276259Sgreen{ 5376259Sgreen int val; 5492555Sdes 5576259Sgreen val = fcntl(fd, F_GETFL, 0); 5676259Sgreen if (val < 0) { 5776259Sgreen error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 58137015Sdes return (-1); 5976259Sgreen } 6076259Sgreen if (val & O_NONBLOCK) { 61137015Sdes debug3("fd %d is O_NONBLOCK", fd); 62137015Sdes return (0); 6376259Sgreen } 64124208Sdes debug2("fd %d setting O_NONBLOCK", fd); 6576259Sgreen val |= O_NONBLOCK; 66137015Sdes if (fcntl(fd, F_SETFL, val) == -1) { 67137015Sdes debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, 68137015Sdes strerror(errno)); 69137015Sdes return (-1); 70137015Sdes } 71137015Sdes return (0); 7276259Sgreen} 7376259Sgreen 74137015Sdesint 7592555Sdesunset_nonblock(int fd) 7692555Sdes{ 7792555Sdes int val; 7892555Sdes 7992555Sdes val = fcntl(fd, F_GETFL, 0); 8092555Sdes if (val < 0) { 8192555Sdes error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 82137015Sdes return (-1); 8392555Sdes } 8492555Sdes if (!(val & O_NONBLOCK)) { 85137015Sdes debug3("fd %d is not O_NONBLOCK", fd); 86137015Sdes return (0); 8792555Sdes } 8892555Sdes debug("fd %d clearing O_NONBLOCK", fd); 8992555Sdes val &= ~O_NONBLOCK; 90137015Sdes if (fcntl(fd, F_SETFL, val) == -1) { 91137015Sdes debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", 9292555Sdes fd, strerror(errno)); 93137015Sdes return (-1); 94137015Sdes } 95137015Sdes return (0); 9692555Sdes} 9792555Sdes 9892555Sdes/* disable nagle on socket */ 9992555Sdesvoid 10092555Sdesset_nodelay(int fd) 10192555Sdes{ 10292555Sdes int opt; 10392555Sdes socklen_t optlen; 10492555Sdes 10592555Sdes optlen = sizeof opt; 10692555Sdes if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { 107126274Sdes debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); 10892555Sdes return; 10992555Sdes } 11092555Sdes if (opt == 1) { 11192555Sdes debug2("fd %d is TCP_NODELAY", fd); 11292555Sdes return; 11392555Sdes } 11492555Sdes opt = 1; 115113908Sdes debug2("fd %d setting TCP_NODELAY", fd); 11692555Sdes if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) 11792555Sdes error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); 11892555Sdes} 11992555Sdes 12076259Sgreen/* Characters considered whitespace in strsep calls. */ 12176259Sgreen#define WHITESPACE " \t\r\n" 12276259Sgreen 12392555Sdes/* return next token in configuration line */ 12476259Sgreenchar * 12576259Sgreenstrdelim(char **s) 12676259Sgreen{ 12776259Sgreen char *old; 12876259Sgreen int wspace = 0; 12976259Sgreen 13076259Sgreen if (*s == NULL) 13176259Sgreen return NULL; 13276259Sgreen 13376259Sgreen old = *s; 13476259Sgreen 13576259Sgreen *s = strpbrk(*s, WHITESPACE "="); 13676259Sgreen if (*s == NULL) 13776259Sgreen return (old); 13876259Sgreen 13976259Sgreen /* Allow only one '=' to be skipped */ 14076259Sgreen if (*s[0] == '=') 14176259Sgreen wspace = 1; 14276259Sgreen *s[0] = '\0'; 14376259Sgreen 14476259Sgreen *s += strspn(*s + 1, WHITESPACE) + 1; 14576259Sgreen if (*s[0] == '=' && !wspace) 14676259Sgreen *s += strspn(*s + 1, WHITESPACE) + 1; 14776259Sgreen 14876259Sgreen return (old); 14976259Sgreen} 15076259Sgreen 15176259Sgreenstruct passwd * 15276259Sgreenpwcopy(struct passwd *pw) 15376259Sgreen{ 15476259Sgreen struct passwd *copy = xmalloc(sizeof(*copy)); 15576259Sgreen 15676259Sgreen memset(copy, 0, sizeof(*copy)); 15776259Sgreen copy->pw_name = xstrdup(pw->pw_name); 15876259Sgreen copy->pw_passwd = xstrdup(pw->pw_passwd); 15976259Sgreen copy->pw_gecos = xstrdup(pw->pw_gecos); 16076259Sgreen copy->pw_uid = pw->pw_uid; 16176259Sgreen copy->pw_gid = pw->pw_gid; 16298937Sdes#ifdef HAVE_PW_EXPIRE_IN_PASSWD 16392555Sdes copy->pw_expire = pw->pw_expire; 16498937Sdes#endif 16598937Sdes#ifdef HAVE_PW_CHANGE_IN_PASSWD 16692555Sdes copy->pw_change = pw->pw_change; 16798937Sdes#endif 16898937Sdes#ifdef HAVE_PW_CLASS_IN_PASSWD 16976259Sgreen copy->pw_class = xstrdup(pw->pw_class); 17098937Sdes#endif 17176259Sgreen copy->pw_dir = xstrdup(pw->pw_dir); 17276259Sgreen copy->pw_shell = xstrdup(pw->pw_shell); 17376259Sgreen return copy; 17476259Sgreen} 17576259Sgreen 17692555Sdes/* 17792555Sdes * Convert ASCII string to TCP/IP port number. 17892555Sdes * Port must be >0 and <=65535. 17992555Sdes * Return 0 if invalid. 18092555Sdes */ 18192555Sdesint 18292555Sdesa2port(const char *s) 18376259Sgreen{ 18476259Sgreen long port; 18576259Sgreen char *endp; 18676259Sgreen 18776259Sgreen errno = 0; 18876259Sgreen port = strtol(s, &endp, 0); 18976259Sgreen if (s == endp || *endp != '\0' || 19076259Sgreen (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) || 19176259Sgreen port <= 0 || port > 65535) 19276259Sgreen return 0; 19376259Sgreen 19476259Sgreen return port; 19576259Sgreen} 19692555Sdes 19792555Sdes#define SECONDS 1 19892555Sdes#define MINUTES (SECONDS * 60) 19992555Sdes#define HOURS (MINUTES * 60) 20092555Sdes#define DAYS (HOURS * 24) 20192555Sdes#define WEEKS (DAYS * 7) 20292555Sdes 20392555Sdes/* 20492555Sdes * Convert a time string into seconds; format is 20592555Sdes * a sequence of: 20692555Sdes * time[qualifier] 20792555Sdes * 20892555Sdes * Valid time qualifiers are: 20992555Sdes * <none> seconds 21092555Sdes * s|S seconds 21192555Sdes * m|M minutes 21292555Sdes * h|H hours 21392555Sdes * d|D days 21492555Sdes * w|W weeks 21592555Sdes * 21692555Sdes * Examples: 21792555Sdes * 90m 90 minutes 21892555Sdes * 1h30m 90 minutes 21992555Sdes * 2d 2 days 22092555Sdes * 1w 1 week 22192555Sdes * 22292555Sdes * Return -1 if time string is invalid. 22392555Sdes */ 22492555Sdeslong 22592555Sdesconvtime(const char *s) 22692555Sdes{ 22792555Sdes long total, secs; 22892555Sdes const char *p; 22992555Sdes char *endp; 23092555Sdes 23192555Sdes errno = 0; 23292555Sdes total = 0; 23392555Sdes p = s; 23492555Sdes 23592555Sdes if (p == NULL || *p == '\0') 23692555Sdes return -1; 23792555Sdes 23892555Sdes while (*p) { 23992555Sdes secs = strtol(p, &endp, 10); 24092555Sdes if (p == endp || 24192555Sdes (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 24292555Sdes secs < 0) 24392555Sdes return -1; 24492555Sdes 24592555Sdes switch (*endp++) { 24692555Sdes case '\0': 24792555Sdes endp--; 24892555Sdes case 's': 24992555Sdes case 'S': 25092555Sdes break; 25192555Sdes case 'm': 25292555Sdes case 'M': 25392555Sdes secs *= MINUTES; 25492555Sdes break; 25592555Sdes case 'h': 25692555Sdes case 'H': 25792555Sdes secs *= HOURS; 25892555Sdes break; 25992555Sdes case 'd': 26092555Sdes case 'D': 26192555Sdes secs *= DAYS; 26292555Sdes break; 26392555Sdes case 'w': 26492555Sdes case 'W': 26592555Sdes secs *= WEEKS; 26692555Sdes break; 26792555Sdes default: 26892555Sdes return -1; 26992555Sdes } 27092555Sdes total += secs; 27192555Sdes if (total < 0) 27292555Sdes return -1; 27392555Sdes p = endp; 27492555Sdes } 27592555Sdes 27692555Sdes return total; 27792555Sdes} 27892555Sdes 279146998Sdes/* 280146998Sdes * Search for next delimiter between hostnames/addresses and ports. 281146998Sdes * Argument may be modified (for termination). 282146998Sdes * Returns *cp if parsing succeeds. 283146998Sdes * *cp is set to the start of the next delimiter, if one was found. 284146998Sdes * If this is the last field, *cp is set to NULL. 285146998Sdes */ 28692555Sdeschar * 287146998Sdeshpdelim(char **cp) 288146998Sdes{ 289146998Sdes char *s, *old; 290146998Sdes 291146998Sdes if (cp == NULL || *cp == NULL) 292146998Sdes return NULL; 293146998Sdes 294146998Sdes old = s = *cp; 295146998Sdes if (*s == '[') { 296146998Sdes if ((s = strchr(s, ']')) == NULL) 297146998Sdes return NULL; 298146998Sdes else 299146998Sdes s++; 300146998Sdes } else if ((s = strpbrk(s, ":/")) == NULL) 301146998Sdes s = *cp + strlen(*cp); /* skip to end (see first case below) */ 302146998Sdes 303146998Sdes switch (*s) { 304146998Sdes case '\0': 305146998Sdes *cp = NULL; /* no more fields*/ 306146998Sdes break; 307147001Sdes 308146998Sdes case ':': 309146998Sdes case '/': 310146998Sdes *s = '\0'; /* terminate */ 311146998Sdes *cp = s + 1; 312146998Sdes break; 313147001Sdes 314146998Sdes default: 315146998Sdes return NULL; 316146998Sdes } 317146998Sdes 318146998Sdes return old; 319146998Sdes} 320146998Sdes 321146998Sdeschar * 32292555Sdescleanhostname(char *host) 32392555Sdes{ 32492555Sdes if (*host == '[' && host[strlen(host) - 1] == ']') { 32592555Sdes host[strlen(host) - 1] = '\0'; 32692555Sdes return (host + 1); 32792555Sdes } else 32892555Sdes return host; 32992555Sdes} 33092555Sdes 33192555Sdeschar * 33292555Sdescolon(char *cp) 33392555Sdes{ 33492555Sdes int flag = 0; 33592555Sdes 33692555Sdes if (*cp == ':') /* Leading colon is part of file name. */ 33792555Sdes return (0); 33892555Sdes if (*cp == '[') 33992555Sdes flag = 1; 34092555Sdes 34192555Sdes for (; *cp; ++cp) { 34292555Sdes if (*cp == '@' && *(cp+1) == '[') 34392555Sdes flag = 1; 34492555Sdes if (*cp == ']' && *(cp+1) == ':' && flag) 34592555Sdes return (cp+1); 34692555Sdes if (*cp == ':' && !flag) 34792555Sdes return (cp); 34892555Sdes if (*cp == '/') 34992555Sdes return (0); 35092555Sdes } 35192555Sdes return (0); 35292555Sdes} 35392555Sdes 35492555Sdes/* function to assist building execv() arguments */ 35592555Sdesvoid 35692555Sdesaddargs(arglist *args, char *fmt, ...) 35792555Sdes{ 35892555Sdes va_list ap; 35992555Sdes char buf[1024]; 360137015Sdes u_int nalloc; 36192555Sdes 36292555Sdes va_start(ap, fmt); 36392555Sdes vsnprintf(buf, sizeof(buf), fmt, ap); 36492555Sdes va_end(ap); 36592555Sdes 366120161Snectar nalloc = args->nalloc; 36792555Sdes if (args->list == NULL) { 368120161Snectar nalloc = 32; 36992555Sdes args->num = 0; 370120161Snectar } else if (args->num+2 >= nalloc) 371120161Snectar nalloc *= 2; 37292555Sdes 373120161Snectar args->list = xrealloc(args->list, nalloc * sizeof(char *)); 374120161Snectar args->nalloc = nalloc; 37592555Sdes args->list[args->num++] = xstrdup(buf); 37692555Sdes args->list[args->num] = NULL; 37792555Sdes} 378146998Sdes 379146998Sdes/* 380149749Sdes * Expands tildes in the file name. Returns data allocated by xmalloc. 381149749Sdes * Warning: this calls getpw*. 382149749Sdes */ 383149749Sdeschar * 384149749Sdestilde_expand_filename(const char *filename, uid_t uid) 385149749Sdes{ 386149749Sdes const char *path; 387149749Sdes char user[128], ret[MAXPATHLEN]; 388149749Sdes struct passwd *pw; 389149749Sdes u_int len, slash; 390149749Sdes 391149749Sdes if (*filename != '~') 392149749Sdes return (xstrdup(filename)); 393149749Sdes filename++; 394149749Sdes 395149749Sdes path = strchr(filename, '/'); 396149749Sdes if (path != NULL && path > filename) { /* ~user/path */ 397149749Sdes slash = path - filename; 398149749Sdes if (slash > sizeof(user) - 1) 399149749Sdes fatal("tilde_expand_filename: ~username too long"); 400149749Sdes memcpy(user, filename, slash); 401149749Sdes user[slash] = '\0'; 402149749Sdes if ((pw = getpwnam(user)) == NULL) 403149749Sdes fatal("tilde_expand_filename: No such user %s", user); 404149749Sdes } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ 405149749Sdes fatal("tilde_expand_filename: No such uid %d", uid); 406149749Sdes 407149749Sdes if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret)) 408149749Sdes fatal("tilde_expand_filename: Path too long"); 409149749Sdes 410149749Sdes /* Make sure directory has a trailing '/' */ 411149749Sdes len = strlen(pw->pw_dir); 412149749Sdes if ((len == 0 || pw->pw_dir[len - 1] != '/') && 413149749Sdes strlcat(ret, "/", sizeof(ret)) >= sizeof(ret)) 414149749Sdes fatal("tilde_expand_filename: Path too long"); 415149749Sdes 416149749Sdes /* Skip leading '/' from specified path */ 417149749Sdes if (path != NULL) 418149749Sdes filename = path + 1; 419149749Sdes if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret)) 420149749Sdes fatal("tilde_expand_filename: Path too long"); 421149749Sdes 422149749Sdes return (xstrdup(ret)); 423149749Sdes} 424149749Sdes 425149749Sdes/* 426149749Sdes * Expand a string with a set of %[char] escapes. A number of escapes may be 427149749Sdes * specified as (char *escape_chars, char *replacement) pairs. The list must 428149749Sdes * be terminated by a NULL escape_char. Returns replaced string in memory 429149749Sdes * allocated by xmalloc. 430149749Sdes */ 431149749Sdeschar * 432149749Sdespercent_expand(const char *string, ...) 433149749Sdes{ 434149749Sdes#define EXPAND_MAX_KEYS 16 435149749Sdes struct { 436149749Sdes const char *key; 437149749Sdes const char *repl; 438149749Sdes } keys[EXPAND_MAX_KEYS]; 439149749Sdes u_int num_keys, i, j; 440149749Sdes char buf[4096]; 441149749Sdes va_list ap; 442149749Sdes 443149749Sdes /* Gather keys */ 444149749Sdes va_start(ap, string); 445149749Sdes for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { 446149749Sdes keys[num_keys].key = va_arg(ap, char *); 447149749Sdes if (keys[num_keys].key == NULL) 448149749Sdes break; 449149749Sdes keys[num_keys].repl = va_arg(ap, char *); 450149749Sdes if (keys[num_keys].repl == NULL) 451149749Sdes fatal("percent_expand: NULL replacement"); 452149749Sdes } 453149749Sdes va_end(ap); 454149749Sdes 455149749Sdes if (num_keys >= EXPAND_MAX_KEYS) 456149749Sdes fatal("percent_expand: too many keys"); 457149749Sdes 458149749Sdes /* Expand string */ 459149749Sdes *buf = '\0'; 460149749Sdes for (i = 0; *string != '\0'; string++) { 461149749Sdes if (*string != '%') { 462149749Sdes append: 463149749Sdes buf[i++] = *string; 464149749Sdes if (i >= sizeof(buf)) 465149749Sdes fatal("percent_expand: string too long"); 466149749Sdes buf[i] = '\0'; 467149749Sdes continue; 468149749Sdes } 469149749Sdes string++; 470149749Sdes if (*string == '%') 471149749Sdes goto append; 472149749Sdes for (j = 0; j < num_keys; j++) { 473149749Sdes if (strchr(keys[j].key, *string) != NULL) { 474149749Sdes i = strlcat(buf, keys[j].repl, sizeof(buf)); 475149749Sdes if (i >= sizeof(buf)) 476149749Sdes fatal("percent_expand: string too long"); 477149749Sdes break; 478149749Sdes } 479149749Sdes } 480149749Sdes if (j >= num_keys) 481149749Sdes fatal("percent_expand: unknown key %%%c", *string); 482149749Sdes } 483149749Sdes return (xstrdup(buf)); 484149749Sdes#undef EXPAND_MAX_KEYS 485149749Sdes} 486149749Sdes 487149749Sdes/* 488146998Sdes * Read an entire line from a public key file into a static buffer, discarding 489146998Sdes * lines that exceed the buffer size. Returns 0 on success, -1 on failure. 490146998Sdes */ 491146998Sdesint 492146998Sdesread_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, 493146998Sdes u_long *lineno) 494146998Sdes{ 495146998Sdes while (fgets(buf, bufsz, f) != NULL) { 496146998Sdes (*lineno)++; 497146998Sdes if (buf[strlen(buf) - 1] == '\n' || feof(f)) { 498146998Sdes return 0; 499146998Sdes } else { 500146998Sdes debug("%s: %s line %lu exceeds size limit", __func__, 501146998Sdes filename, *lineno); 502146998Sdes /* discard remainder of line */ 503147001Sdes while (fgetc(f) != '\n' && !feof(f)) 504146998Sdes ; /* nothing */ 505146998Sdes } 506146998Sdes } 507146998Sdes return -1; 508146998Sdes} 509149749Sdes 510149749Sdeschar * 511149749Sdestohex(const u_char *d, u_int l) 512149749Sdes{ 513149749Sdes char b[3], *r; 514149749Sdes u_int i, hl; 515149749Sdes 516149749Sdes hl = l * 2 + 1; 517149749Sdes r = xmalloc(hl); 518149749Sdes *r = '\0'; 519149749Sdes for (i = 0; i < l; i++) { 520149749Sdes snprintf(b, sizeof(b), "%02x", d[i]); 521149749Sdes strlcat(r, b, hl); 522149749Sdes } 523149749Sdes return (r); 524149749Sdes} 525149749Sdes 526