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