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