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