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