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