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