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