1 /* 2 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2005 Damien Miller. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "includes.h" 27 RCSID("$OpenBSD: misc.c,v 1.42 2006/01/31 10:19:02 djm Exp $"); 28 29 #ifdef SSH_TUN_OPENBSD 30 #include <net/if.h> 31 #endif 32 33 #include "misc.h" 34 #include "log.h" 35 #include "xmalloc.h" 36 37 /* remove newline at end of string */ 38 char * 39 chop(char *s) 40 { 41 char *t = s; 42 while (*t) { 43 if (*t == '\n' || *t == '\r') { 44 *t = '\0'; 45 return s; 46 } 47 t++; 48 } 49 return s; 50 51 } 52 53 /* set/unset filedescriptor to non-blocking */ 54 int 55 set_nonblock(int fd) 56 { 57 int val; 58 59 val = fcntl(fd, F_GETFL, 0); 60 if (val < 0) { 61 error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 62 return (-1); 63 } 64 if (val & O_NONBLOCK) { 65 debug3("fd %d is O_NONBLOCK", fd); 66 return (0); 67 } 68 debug2("fd %d setting O_NONBLOCK", fd); 69 val |= O_NONBLOCK; 70 if (fcntl(fd, F_SETFL, val) == -1) { 71 debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, 72 strerror(errno)); 73 return (-1); 74 } 75 return (0); 76 } 77 78 int 79 unset_nonblock(int fd) 80 { 81 int val; 82 83 val = fcntl(fd, F_GETFL, 0); 84 if (val < 0) { 85 error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 86 return (-1); 87 } 88 if (!(val & O_NONBLOCK)) { 89 debug3("fd %d is not O_NONBLOCK", fd); 90 return (0); 91 } 92 debug("fd %d clearing O_NONBLOCK", fd); 93 val &= ~O_NONBLOCK; 94 if (fcntl(fd, F_SETFL, val) == -1) { 95 debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", 96 fd, strerror(errno)); 97 return (-1); 98 } 99 return (0); 100 } 101 102 /* disable nagle on socket */ 103 void 104 set_nodelay(int fd) 105 { 106 int opt; 107 socklen_t optlen; 108 109 optlen = sizeof opt; 110 if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { 111 debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); 112 return; 113 } 114 if (opt == 1) { 115 debug2("fd %d is TCP_NODELAY", fd); 116 return; 117 } 118 opt = 1; 119 debug2("fd %d setting TCP_NODELAY", fd); 120 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) 121 error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); 122 } 123 124 /* Characters considered whitespace in strsep calls. */ 125 #define WHITESPACE " \t\r\n" 126 127 /* return next token in configuration line */ 128 char * 129 strdelim(char **s) 130 { 131 char *old; 132 int wspace = 0; 133 134 if (*s == NULL) 135 return NULL; 136 137 old = *s; 138 139 *s = strpbrk(*s, WHITESPACE "="); 140 if (*s == NULL) 141 return (old); 142 143 /* Allow only one '=' to be skipped */ 144 if (*s[0] == '=') 145 wspace = 1; 146 *s[0] = '\0'; 147 148 *s += strspn(*s + 1, WHITESPACE) + 1; 149 if (*s[0] == '=' && !wspace) 150 *s += strspn(*s + 1, WHITESPACE) + 1; 151 152 return (old); 153 } 154 155 struct passwd * 156 pwcopy(struct passwd *pw) 157 { 158 struct passwd *copy = xmalloc(sizeof(*copy)); 159 160 memset(copy, 0, sizeof(*copy)); 161 copy->pw_name = xstrdup(pw->pw_name); 162 copy->pw_passwd = xstrdup(pw->pw_passwd); 163 copy->pw_gecos = xstrdup(pw->pw_gecos); 164 copy->pw_uid = pw->pw_uid; 165 copy->pw_gid = pw->pw_gid; 166 #ifdef HAVE_PW_EXPIRE_IN_PASSWD 167 copy->pw_expire = pw->pw_expire; 168 #endif 169 #ifdef HAVE_PW_CHANGE_IN_PASSWD 170 copy->pw_change = pw->pw_change; 171 #endif 172 #ifdef HAVE_PW_CLASS_IN_PASSWD 173 copy->pw_class = xstrdup(pw->pw_class); 174 #endif 175 copy->pw_dir = xstrdup(pw->pw_dir); 176 copy->pw_shell = xstrdup(pw->pw_shell); 177 return copy; 178 } 179 180 /* 181 * Convert ASCII string to TCP/IP port number. 182 * Port must be >0 and <=65535. 183 * Return 0 if invalid. 184 */ 185 int 186 a2port(const char *s) 187 { 188 long port; 189 char *endp; 190 191 errno = 0; 192 port = strtol(s, &endp, 0); 193 if (s == endp || *endp != '\0' || 194 (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) || 195 port <= 0 || port > 65535) 196 return 0; 197 198 return port; 199 } 200 201 int 202 a2tun(const char *s, int *remote) 203 { 204 const char *errstr = NULL; 205 char *sp, *ep; 206 int tun; 207 208 if (remote != NULL) { 209 *remote = SSH_TUNID_ANY; 210 sp = xstrdup(s); 211 if ((ep = strchr(sp, ':')) == NULL) { 212 xfree(sp); 213 return (a2tun(s, NULL)); 214 } 215 ep[0] = '\0'; ep++; 216 *remote = a2tun(ep, NULL); 217 tun = a2tun(sp, NULL); 218 xfree(sp); 219 return (*remote == SSH_TUNID_ERR ? *remote : tun); 220 } 221 222 if (strcasecmp(s, "any") == 0) 223 return (SSH_TUNID_ANY); 224 225 tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); 226 if (errstr != NULL) 227 return (SSH_TUNID_ERR); 228 229 return (tun); 230 } 231 232 #define SECONDS 1 233 #define MINUTES (SECONDS * 60) 234 #define HOURS (MINUTES * 60) 235 #define DAYS (HOURS * 24) 236 #define WEEKS (DAYS * 7) 237 238 /* 239 * Convert a time string into seconds; format is 240 * a sequence of: 241 * time[qualifier] 242 * 243 * Valid time qualifiers are: 244 * <none> seconds 245 * s|S seconds 246 * m|M minutes 247 * h|H hours 248 * d|D days 249 * w|W weeks 250 * 251 * Examples: 252 * 90m 90 minutes 253 * 1h30m 90 minutes 254 * 2d 2 days 255 * 1w 1 week 256 * 257 * Return -1 if time string is invalid. 258 */ 259 long 260 convtime(const char *s) 261 { 262 long total, secs; 263 const char *p; 264 char *endp; 265 266 errno = 0; 267 total = 0; 268 p = s; 269 270 if (p == NULL || *p == '\0') 271 return -1; 272 273 while (*p) { 274 secs = strtol(p, &endp, 10); 275 if (p == endp || 276 (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 277 secs < 0) 278 return -1; 279 280 switch (*endp++) { 281 case '\0': 282 endp--; 283 case 's': 284 case 'S': 285 break; 286 case 'm': 287 case 'M': 288 secs *= MINUTES; 289 break; 290 case 'h': 291 case 'H': 292 secs *= HOURS; 293 break; 294 case 'd': 295 case 'D': 296 secs *= DAYS; 297 break; 298 case 'w': 299 case 'W': 300 secs *= WEEKS; 301 break; 302 default: 303 return -1; 304 } 305 total += secs; 306 if (total < 0) 307 return -1; 308 p = endp; 309 } 310 311 return total; 312 } 313 314 /* 315 * Search for next delimiter between hostnames/addresses and ports. 316 * Argument may be modified (for termination). 317 * Returns *cp if parsing succeeds. 318 * *cp is set to the start of the next delimiter, if one was found. 319 * If this is the last field, *cp is set to NULL. 320 */ 321 char * 322 hpdelim(char **cp) 323 { 324 char *s, *old; 325 326 if (cp == NULL || *cp == NULL) 327 return NULL; 328 329 old = s = *cp; 330 if (*s == '[') { 331 if ((s = strchr(s, ']')) == NULL) 332 return NULL; 333 else 334 s++; 335 } else if ((s = strpbrk(s, ":/")) == NULL) 336 s = *cp + strlen(*cp); /* skip to end (see first case below) */ 337 338 switch (*s) { 339 case '\0': 340 *cp = NULL; /* no more fields*/ 341 break; 342 343 case ':': 344 case '/': 345 *s = '\0'; /* terminate */ 346 *cp = s + 1; 347 break; 348 349 default: 350 return NULL; 351 } 352 353 return old; 354 } 355 356 char * 357 cleanhostname(char *host) 358 { 359 if (*host == '[' && host[strlen(host) - 1] == ']') { 360 host[strlen(host) - 1] = '\0'; 361 return (host + 1); 362 } else 363 return host; 364 } 365 366 char * 367 colon(char *cp) 368 { 369 int flag = 0; 370 371 if (*cp == ':') /* Leading colon is part of file name. */ 372 return (0); 373 if (*cp == '[') 374 flag = 1; 375 376 for (; *cp; ++cp) { 377 if (*cp == '@' && *(cp+1) == '[') 378 flag = 1; 379 if (*cp == ']' && *(cp+1) == ':' && flag) 380 return (cp+1); 381 if (*cp == ':' && !flag) 382 return (cp); 383 if (*cp == '/') 384 return (0); 385 } 386 return (0); 387 } 388 389 /* function to assist building execv() arguments */ 390 void 391 addargs(arglist *args, char *fmt, ...) 392 { 393 va_list ap; 394 char *cp; 395 u_int nalloc; 396 int r; 397 398 va_start(ap, fmt); 399 r = vasprintf(&cp, fmt, ap); 400 va_end(ap); 401 if (r == -1) 402 fatal("addargs: argument too long"); 403 404 nalloc = args->nalloc; 405 if (args->list == NULL) { 406 nalloc = 32; 407 args->num = 0; 408 } else if (args->num+2 >= nalloc) 409 nalloc *= 2; 410 411 args->list = xrealloc(args->list, nalloc * sizeof(char *)); 412 args->nalloc = nalloc; 413 args->list[args->num++] = cp; 414 args->list[args->num] = NULL; 415 } 416 417 void 418 replacearg(arglist *args, u_int which, char *fmt, ...) 419 { 420 va_list ap; 421 char *cp; 422 int r; 423 424 va_start(ap, fmt); 425 r = vasprintf(&cp, fmt, ap); 426 va_end(ap); 427 if (r == -1) 428 fatal("replacearg: argument too long"); 429 430 if (which >= args->num) 431 fatal("replacearg: tried to replace invalid arg %d >= %d", 432 which, args->num); 433 xfree(args->list[which]); 434 args->list[which] = cp; 435 } 436 437 void 438 freeargs(arglist *args) 439 { 440 u_int i; 441 442 if (args->list != NULL) { 443 for (i = 0; i < args->num; i++) 444 xfree(args->list[i]); 445 xfree(args->list); 446 args->nalloc = args->num = 0; 447 args->list = NULL; 448 } 449 } 450 451 /* 452 * Expands tildes in the file name. Returns data allocated by xmalloc. 453 * Warning: this calls getpw*. 454 */ 455 char * 456 tilde_expand_filename(const char *filename, uid_t uid) 457 { 458 const char *path; 459 char user[128], ret[MAXPATHLEN]; 460 struct passwd *pw; 461 u_int len, slash; 462 463 if (*filename != '~') 464 return (xstrdup(filename)); 465 filename++; 466 467 path = strchr(filename, '/'); 468 if (path != NULL && path > filename) { /* ~user/path */ 469 slash = path - filename; 470 if (slash > sizeof(user) - 1) 471 fatal("tilde_expand_filename: ~username too long"); 472 memcpy(user, filename, slash); 473 user[slash] = '\0'; 474 if ((pw = getpwnam(user)) == NULL) 475 fatal("tilde_expand_filename: No such user %s", user); 476 } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ 477 fatal("tilde_expand_filename: No such uid %d", uid); 478 479 if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret)) 480 fatal("tilde_expand_filename: Path too long"); 481 482 /* Make sure directory has a trailing '/' */ 483 len = strlen(pw->pw_dir); 484 if ((len == 0 || pw->pw_dir[len - 1] != '/') && 485 strlcat(ret, "/", sizeof(ret)) >= sizeof(ret)) 486 fatal("tilde_expand_filename: Path too long"); 487 488 /* Skip leading '/' from specified path */ 489 if (path != NULL) 490 filename = path + 1; 491 if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret)) 492 fatal("tilde_expand_filename: Path too long"); 493 494 return (xstrdup(ret)); 495 } 496 497 /* 498 * Expand a string with a set of %[char] escapes. A number of escapes may be 499 * specified as (char *escape_chars, char *replacement) pairs. The list must 500 * be terminated by a NULL escape_char. Returns replaced string in memory 501 * allocated by xmalloc. 502 */ 503 char * 504 percent_expand(const char *string, ...) 505 { 506 #define EXPAND_MAX_KEYS 16 507 struct { 508 const char *key; 509 const char *repl; 510 } keys[EXPAND_MAX_KEYS]; 511 u_int num_keys, i, j; 512 char buf[4096]; 513 va_list ap; 514 515 /* Gather keys */ 516 va_start(ap, string); 517 for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { 518 keys[num_keys].key = va_arg(ap, char *); 519 if (keys[num_keys].key == NULL) 520 break; 521 keys[num_keys].repl = va_arg(ap, char *); 522 if (keys[num_keys].repl == NULL) 523 fatal("percent_expand: NULL replacement"); 524 } 525 va_end(ap); 526 527 if (num_keys >= EXPAND_MAX_KEYS) 528 fatal("percent_expand: too many keys"); 529 530 /* Expand string */ 531 *buf = '\0'; 532 for (i = 0; *string != '\0'; string++) { 533 if (*string != '%') { 534 append: 535 buf[i++] = *string; 536 if (i >= sizeof(buf)) 537 fatal("percent_expand: string too long"); 538 buf[i] = '\0'; 539 continue; 540 } 541 string++; 542 if (*string == '%') 543 goto append; 544 for (j = 0; j < num_keys; j++) { 545 if (strchr(keys[j].key, *string) != NULL) { 546 i = strlcat(buf, keys[j].repl, sizeof(buf)); 547 if (i >= sizeof(buf)) 548 fatal("percent_expand: string too long"); 549 break; 550 } 551 } 552 if (j >= num_keys) 553 fatal("percent_expand: unknown key %%%c", *string); 554 } 555 return (xstrdup(buf)); 556 #undef EXPAND_MAX_KEYS 557 } 558 559 /* 560 * Read an entire line from a public key file into a static buffer, discarding 561 * lines that exceed the buffer size. Returns 0 on success, -1 on failure. 562 */ 563 int 564 read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, 565 u_long *lineno) 566 { 567 while (fgets(buf, bufsz, f) != NULL) { 568 (*lineno)++; 569 if (buf[strlen(buf) - 1] == '\n' || feof(f)) { 570 return 0; 571 } else { 572 debug("%s: %s line %lu exceeds size limit", __func__, 573 filename, *lineno); 574 /* discard remainder of line */ 575 while (fgetc(f) != '\n' && !feof(f)) 576 ; /* nothing */ 577 } 578 } 579 return -1; 580 } 581 582 int 583 tun_open(int tun, int mode) 584 { 585 #if defined(CUSTOM_SYS_TUN_OPEN) 586 return (sys_tun_open(tun, mode)); 587 #elif defined(SSH_TUN_OPENBSD) 588 struct ifreq ifr; 589 char name[100]; 590 int fd = -1, sock; 591 592 /* Open the tunnel device */ 593 if (tun <= SSH_TUNID_MAX) { 594 snprintf(name, sizeof(name), "/dev/tun%d", tun); 595 fd = open(name, O_RDWR); 596 } else if (tun == SSH_TUNID_ANY) { 597 for (tun = 100; tun >= 0; tun--) { 598 snprintf(name, sizeof(name), "/dev/tun%d", tun); 599 if ((fd = open(name, O_RDWR)) >= 0) 600 break; 601 } 602 } else { 603 debug("%s: invalid tunnel %u", __func__, tun); 604 return (-1); 605 } 606 607 if (fd < 0) { 608 debug("%s: %s open failed: %s", __func__, name, strerror(errno)); 609 return (-1); 610 } 611 612 debug("%s: %s mode %d fd %d", __func__, name, mode, fd); 613 614 /* Set the tunnel device operation mode */ 615 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun); 616 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 617 goto failed; 618 619 if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) 620 goto failed; 621 622 /* Set interface mode */ 623 ifr.ifr_flags &= ~IFF_UP; 624 if (mode == SSH_TUNMODE_ETHERNET) 625 ifr.ifr_flags |= IFF_LINK0; 626 else 627 ifr.ifr_flags &= ~IFF_LINK0; 628 if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) 629 goto failed; 630 631 /* Bring interface up */ 632 ifr.ifr_flags |= IFF_UP; 633 if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) 634 goto failed; 635 636 close(sock); 637 return (fd); 638 639 failed: 640 if (fd >= 0) 641 close(fd); 642 if (sock >= 0) 643 close(sock); 644 debug("%s: failed to set %s mode %d: %s", __func__, name, 645 mode, strerror(errno)); 646 return (-1); 647 #else 648 error("Tunnel interfaces are not supported on this platform"); 649 return (-1); 650 #endif 651 } 652 653 void 654 sanitise_stdfd(void) 655 { 656 int nullfd, dupfd; 657 658 if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 659 fprintf(stderr, "Couldn't open /dev/null: %s", strerror(errno)); 660 exit(1); 661 } 662 while (++dupfd <= 2) { 663 /* Only clobber closed fds */ 664 if (fcntl(dupfd, F_GETFL, 0) >= 0) 665 continue; 666 if (dup2(nullfd, dupfd) == -1) { 667 fprintf(stderr, "dup2: %s", strerror(errno)); 668 exit(1); 669 } 670 } 671 if (nullfd > 2) 672 close(nullfd); 673 } 674 675 char * 676 tohex(const u_char *d, u_int l) 677 { 678 char b[3], *r; 679 u_int i, hl; 680 681 hl = l * 2 + 1; 682 r = xmalloc(hl); 683 *r = '\0'; 684 for (i = 0; i < l; i++) { 685 snprintf(b, sizeof(b), "%02x", d[i]); 686 strlcat(r, b, hl); 687 } 688 return (r); 689 } 690 691