misc.c revision 157016
176259Sgreen/*
276259Sgreen * Copyright (c) 2000 Markus Friedl.  All rights reserved.
3149749Sdes * Copyright (c) 2005 Damien Miller.  All rights reserved.
476259Sgreen *
576259Sgreen * Redistribution and use in source and binary forms, with or without
676259Sgreen * modification, are permitted provided that the following conditions
776259Sgreen * are met:
876259Sgreen * 1. Redistributions of source code must retain the above copyright
976259Sgreen *    notice, this list of conditions and the following disclaimer.
1076259Sgreen * 2. Redistributions in binary form must reproduce the above copyright
1176259Sgreen *    notice, this list of conditions and the following disclaimer in the
1276259Sgreen *    documentation and/or other materials provided with the distribution.
1376259Sgreen *
1476259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1576259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1676259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1776259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1876259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1976259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2076259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2176259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2276259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2376259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2476259Sgreen */
2576259Sgreen
2676259Sgreen#include "includes.h"
27157016SdesRCSID("$OpenBSD: misc.c,v 1.42 2006/01/31 10:19:02 djm Exp $");
2876259Sgreen
29157016Sdes#ifdef SSH_TUN_OPENBSD
30157016Sdes#include <net/if.h>
31157016Sdes#endif
32157016Sdes
3376259Sgreen#include "misc.h"
3476259Sgreen#include "log.h"
3576259Sgreen#include "xmalloc.h"
3676259Sgreen
3792555Sdes/* remove newline at end of string */
3876259Sgreenchar *
3976259Sgreenchop(char *s)
4076259Sgreen{
4176259Sgreen	char *t = s;
4276259Sgreen	while (*t) {
4392555Sdes		if (*t == '\n' || *t == '\r') {
4476259Sgreen			*t = '\0';
4576259Sgreen			return s;
4676259Sgreen		}
4776259Sgreen		t++;
4876259Sgreen	}
4976259Sgreen	return s;
5076259Sgreen
5176259Sgreen}
5276259Sgreen
5392555Sdes/* set/unset filedescriptor to non-blocking */
54137015Sdesint
5576259Sgreenset_nonblock(int fd)
5676259Sgreen{
5776259Sgreen	int val;
5892555Sdes
5976259Sgreen	val = fcntl(fd, F_GETFL, 0);
6076259Sgreen	if (val < 0) {
6176259Sgreen		error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
62137015Sdes		return (-1);
6376259Sgreen	}
6476259Sgreen	if (val & O_NONBLOCK) {
65137015Sdes		debug3("fd %d is O_NONBLOCK", fd);
66137015Sdes		return (0);
6776259Sgreen	}
68124208Sdes	debug2("fd %d setting O_NONBLOCK", fd);
6976259Sgreen	val |= O_NONBLOCK;
70137015Sdes	if (fcntl(fd, F_SETFL, val) == -1) {
71137015Sdes		debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd,
72137015Sdes		    strerror(errno));
73137015Sdes		return (-1);
74137015Sdes	}
75137015Sdes	return (0);
7676259Sgreen}
7776259Sgreen
78137015Sdesint
7992555Sdesunset_nonblock(int fd)
8092555Sdes{
8192555Sdes	int val;
8292555Sdes
8392555Sdes	val = fcntl(fd, F_GETFL, 0);
8492555Sdes	if (val < 0) {
8592555Sdes		error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
86137015Sdes		return (-1);
8792555Sdes	}
8892555Sdes	if (!(val & O_NONBLOCK)) {
89137015Sdes		debug3("fd %d is not O_NONBLOCK", fd);
90137015Sdes		return (0);
9192555Sdes	}
9292555Sdes	debug("fd %d clearing O_NONBLOCK", fd);
9392555Sdes	val &= ~O_NONBLOCK;
94137015Sdes	if (fcntl(fd, F_SETFL, val) == -1) {
95137015Sdes		debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s",
9692555Sdes		    fd, strerror(errno));
97137015Sdes		return (-1);
98137015Sdes	}
99137015Sdes	return (0);
10092555Sdes}
10192555Sdes
10292555Sdes/* disable nagle on socket */
10392555Sdesvoid
10492555Sdesset_nodelay(int fd)
10592555Sdes{
10692555Sdes	int opt;
10792555Sdes	socklen_t optlen;
10892555Sdes
10992555Sdes	optlen = sizeof opt;
11092555Sdes	if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) {
111126274Sdes		debug("getsockopt TCP_NODELAY: %.100s", strerror(errno));
11292555Sdes		return;
11392555Sdes	}
11492555Sdes	if (opt == 1) {
11592555Sdes		debug2("fd %d is TCP_NODELAY", fd);
11692555Sdes		return;
11792555Sdes	}
11892555Sdes	opt = 1;
119113908Sdes	debug2("fd %d setting TCP_NODELAY", fd);
12092555Sdes	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1)
12192555Sdes		error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
12292555Sdes}
12392555Sdes
12476259Sgreen/* Characters considered whitespace in strsep calls. */
12576259Sgreen#define WHITESPACE " \t\r\n"
12676259Sgreen
12792555Sdes/* return next token in configuration line */
12876259Sgreenchar *
12976259Sgreenstrdelim(char **s)
13076259Sgreen{
13176259Sgreen	char *old;
13276259Sgreen	int wspace = 0;
13376259Sgreen
13476259Sgreen	if (*s == NULL)
13576259Sgreen		return NULL;
13676259Sgreen
13776259Sgreen	old = *s;
13876259Sgreen
13976259Sgreen	*s = strpbrk(*s, WHITESPACE "=");
14076259Sgreen	if (*s == NULL)
14176259Sgreen		return (old);
14276259Sgreen
14376259Sgreen	/* Allow only one '=' to be skipped */
14476259Sgreen	if (*s[0] == '=')
14576259Sgreen		wspace = 1;
14676259Sgreen	*s[0] = '\0';
14776259Sgreen
14876259Sgreen	*s += strspn(*s + 1, WHITESPACE) + 1;
14976259Sgreen	if (*s[0] == '=' && !wspace)
15076259Sgreen		*s += strspn(*s + 1, WHITESPACE) + 1;
15176259Sgreen
15276259Sgreen	return (old);
15376259Sgreen}
15476259Sgreen
15576259Sgreenstruct passwd *
15676259Sgreenpwcopy(struct passwd *pw)
15776259Sgreen{
15876259Sgreen	struct passwd *copy = xmalloc(sizeof(*copy));
15976259Sgreen
16076259Sgreen	memset(copy, 0, sizeof(*copy));
16176259Sgreen	copy->pw_name = xstrdup(pw->pw_name);
16276259Sgreen	copy->pw_passwd = xstrdup(pw->pw_passwd);
16376259Sgreen	copy->pw_gecos = xstrdup(pw->pw_gecos);
16476259Sgreen	copy->pw_uid = pw->pw_uid;
16576259Sgreen	copy->pw_gid = pw->pw_gid;
16698937Sdes#ifdef HAVE_PW_EXPIRE_IN_PASSWD
16792555Sdes	copy->pw_expire = pw->pw_expire;
16898937Sdes#endif
16998937Sdes#ifdef HAVE_PW_CHANGE_IN_PASSWD
17092555Sdes	copy->pw_change = pw->pw_change;
17198937Sdes#endif
17298937Sdes#ifdef HAVE_PW_CLASS_IN_PASSWD
17376259Sgreen	copy->pw_class = xstrdup(pw->pw_class);
17498937Sdes#endif
17576259Sgreen	copy->pw_dir = xstrdup(pw->pw_dir);
17676259Sgreen	copy->pw_shell = xstrdup(pw->pw_shell);
17776259Sgreen	return copy;
17876259Sgreen}
17976259Sgreen
18092555Sdes/*
18192555Sdes * Convert ASCII string to TCP/IP port number.
18292555Sdes * Port must be >0 and <=65535.
18392555Sdes * Return 0 if invalid.
18492555Sdes */
18592555Sdesint
18692555Sdesa2port(const char *s)
18776259Sgreen{
18876259Sgreen	long port;
18976259Sgreen	char *endp;
19076259Sgreen
19176259Sgreen	errno = 0;
19276259Sgreen	port = strtol(s, &endp, 0);
19376259Sgreen	if (s == endp || *endp != '\0' ||
19476259Sgreen	    (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) ||
19576259Sgreen	    port <= 0 || port > 65535)
19676259Sgreen		return 0;
19776259Sgreen
19876259Sgreen	return port;
19976259Sgreen}
20092555Sdes
201157016Sdesint
202157016Sdesa2tun(const char *s, int *remote)
203157016Sdes{
204157016Sdes	const char *errstr = NULL;
205157016Sdes	char *sp, *ep;
206157016Sdes	int tun;
207157016Sdes
208157016Sdes	if (remote != NULL) {
209157016Sdes		*remote = SSH_TUNID_ANY;
210157016Sdes		sp = xstrdup(s);
211157016Sdes		if ((ep = strchr(sp, ':')) == NULL) {
212157016Sdes			xfree(sp);
213157016Sdes			return (a2tun(s, NULL));
214157016Sdes		}
215157016Sdes		ep[0] = '\0'; ep++;
216157016Sdes		*remote = a2tun(ep, NULL);
217157016Sdes		tun = a2tun(sp, NULL);
218157016Sdes		xfree(sp);
219157016Sdes		return (*remote == SSH_TUNID_ERR ? *remote : tun);
220157016Sdes	}
221157016Sdes
222157016Sdes	if (strcasecmp(s, "any") == 0)
223157016Sdes		return (SSH_TUNID_ANY);
224157016Sdes
225157016Sdes	tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr);
226157016Sdes	if (errstr != NULL)
227157016Sdes		return (SSH_TUNID_ERR);
228157016Sdes
229157016Sdes	return (tun);
230157016Sdes}
231157016Sdes
23292555Sdes#define SECONDS		1
23392555Sdes#define MINUTES		(SECONDS * 60)
23492555Sdes#define HOURS		(MINUTES * 60)
23592555Sdes#define DAYS		(HOURS * 24)
23692555Sdes#define WEEKS		(DAYS * 7)
23792555Sdes
23892555Sdes/*
23992555Sdes * Convert a time string into seconds; format is
24092555Sdes * a sequence of:
24192555Sdes *      time[qualifier]
24292555Sdes *
24392555Sdes * Valid time qualifiers are:
24492555Sdes *      <none>  seconds
24592555Sdes *      s|S     seconds
24692555Sdes *      m|M     minutes
24792555Sdes *      h|H     hours
24892555Sdes *      d|D     days
24992555Sdes *      w|W     weeks
25092555Sdes *
25192555Sdes * Examples:
25292555Sdes *      90m     90 minutes
25392555Sdes *      1h30m   90 minutes
25492555Sdes *      2d      2 days
25592555Sdes *      1w      1 week
25692555Sdes *
25792555Sdes * Return -1 if time string is invalid.
25892555Sdes */
25992555Sdeslong
26092555Sdesconvtime(const char *s)
26192555Sdes{
26292555Sdes	long total, secs;
26392555Sdes	const char *p;
26492555Sdes	char *endp;
26592555Sdes
26692555Sdes	errno = 0;
26792555Sdes	total = 0;
26892555Sdes	p = s;
26992555Sdes
27092555Sdes	if (p == NULL || *p == '\0')
27192555Sdes		return -1;
27292555Sdes
27392555Sdes	while (*p) {
27492555Sdes		secs = strtol(p, &endp, 10);
27592555Sdes		if (p == endp ||
27692555Sdes		    (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) ||
27792555Sdes		    secs < 0)
27892555Sdes			return -1;
27992555Sdes
28092555Sdes		switch (*endp++) {
28192555Sdes		case '\0':
28292555Sdes			endp--;
28392555Sdes		case 's':
28492555Sdes		case 'S':
28592555Sdes			break;
28692555Sdes		case 'm':
28792555Sdes		case 'M':
28892555Sdes			secs *= MINUTES;
28992555Sdes			break;
29092555Sdes		case 'h':
29192555Sdes		case 'H':
29292555Sdes			secs *= HOURS;
29392555Sdes			break;
29492555Sdes		case 'd':
29592555Sdes		case 'D':
29692555Sdes			secs *= DAYS;
29792555Sdes			break;
29892555Sdes		case 'w':
29992555Sdes		case 'W':
30092555Sdes			secs *= WEEKS;
30192555Sdes			break;
30292555Sdes		default:
30392555Sdes			return -1;
30492555Sdes		}
30592555Sdes		total += secs;
30692555Sdes		if (total < 0)
30792555Sdes			return -1;
30892555Sdes		p = endp;
30992555Sdes	}
31092555Sdes
31192555Sdes	return total;
31292555Sdes}
31392555Sdes
314146998Sdes/*
315146998Sdes * Search for next delimiter between hostnames/addresses and ports.
316146998Sdes * Argument may be modified (for termination).
317146998Sdes * Returns *cp if parsing succeeds.
318146998Sdes * *cp is set to the start of the next delimiter, if one was found.
319146998Sdes * If this is the last field, *cp is set to NULL.
320146998Sdes */
32192555Sdeschar *
322146998Sdeshpdelim(char **cp)
323146998Sdes{
324146998Sdes	char *s, *old;
325146998Sdes
326146998Sdes	if (cp == NULL || *cp == NULL)
327146998Sdes		return NULL;
328146998Sdes
329146998Sdes	old = s = *cp;
330146998Sdes	if (*s == '[') {
331146998Sdes		if ((s = strchr(s, ']')) == NULL)
332146998Sdes			return NULL;
333146998Sdes		else
334146998Sdes			s++;
335146998Sdes	} else if ((s = strpbrk(s, ":/")) == NULL)
336146998Sdes		s = *cp + strlen(*cp); /* skip to end (see first case below) */
337146998Sdes
338146998Sdes	switch (*s) {
339146998Sdes	case '\0':
340146998Sdes		*cp = NULL;	/* no more fields*/
341146998Sdes		break;
342147001Sdes
343146998Sdes	case ':':
344146998Sdes	case '/':
345146998Sdes		*s = '\0';	/* terminate */
346146998Sdes		*cp = s + 1;
347146998Sdes		break;
348147001Sdes
349146998Sdes	default:
350146998Sdes		return NULL;
351146998Sdes	}
352146998Sdes
353146998Sdes	return old;
354146998Sdes}
355146998Sdes
356146998Sdeschar *
35792555Sdescleanhostname(char *host)
35892555Sdes{
35992555Sdes	if (*host == '[' && host[strlen(host) - 1] == ']') {
36092555Sdes		host[strlen(host) - 1] = '\0';
36192555Sdes		return (host + 1);
36292555Sdes	} else
36392555Sdes		return host;
36492555Sdes}
36592555Sdes
36692555Sdeschar *
36792555Sdescolon(char *cp)
36892555Sdes{
36992555Sdes	int flag = 0;
37092555Sdes
37192555Sdes	if (*cp == ':')		/* Leading colon is part of file name. */
37292555Sdes		return (0);
37392555Sdes	if (*cp == '[')
37492555Sdes		flag = 1;
37592555Sdes
37692555Sdes	for (; *cp; ++cp) {
37792555Sdes		if (*cp == '@' && *(cp+1) == '[')
37892555Sdes			flag = 1;
37992555Sdes		if (*cp == ']' && *(cp+1) == ':' && flag)
38092555Sdes			return (cp+1);
38192555Sdes		if (*cp == ':' && !flag)
38292555Sdes			return (cp);
38392555Sdes		if (*cp == '/')
38492555Sdes			return (0);
38592555Sdes	}
38692555Sdes	return (0);
38792555Sdes}
38892555Sdes
38992555Sdes/* function to assist building execv() arguments */
39092555Sdesvoid
39192555Sdesaddargs(arglist *args, char *fmt, ...)
39292555Sdes{
39392555Sdes	va_list ap;
394157016Sdes	char *cp;
395137015Sdes	u_int nalloc;
396157016Sdes	int r;
39792555Sdes
39892555Sdes	va_start(ap, fmt);
399157016Sdes	r = vasprintf(&cp, fmt, ap);
40092555Sdes	va_end(ap);
401157016Sdes	if (r == -1)
402157016Sdes		fatal("addargs: argument too long");
40392555Sdes
404120161Snectar	nalloc = args->nalloc;
40592555Sdes	if (args->list == NULL) {
406120161Snectar		nalloc = 32;
40792555Sdes		args->num = 0;
408120161Snectar	} else if (args->num+2 >= nalloc)
409120161Snectar		nalloc *= 2;
41092555Sdes
411120161Snectar	args->list = xrealloc(args->list, nalloc * sizeof(char *));
412120161Snectar	args->nalloc = nalloc;
413157016Sdes	args->list[args->num++] = cp;
41492555Sdes	args->list[args->num] = NULL;
41592555Sdes}
416146998Sdes
417157016Sdesvoid
418157016Sdesreplacearg(arglist *args, u_int which, char *fmt, ...)
419157016Sdes{
420157016Sdes	va_list ap;
421157016Sdes	char *cp;
422157016Sdes	int r;
423157016Sdes
424157016Sdes	va_start(ap, fmt);
425157016Sdes	r = vasprintf(&cp, fmt, ap);
426157016Sdes	va_end(ap);
427157016Sdes	if (r == -1)
428157016Sdes		fatal("replacearg: argument too long");
429157016Sdes
430157016Sdes	if (which >= args->num)
431157016Sdes		fatal("replacearg: tried to replace invalid arg %d >= %d",
432157016Sdes		    which, args->num);
433157016Sdes	xfree(args->list[which]);
434157016Sdes	args->list[which] = cp;
435157016Sdes}
436157016Sdes
437157016Sdesvoid
438157016Sdesfreeargs(arglist *args)
439157016Sdes{
440157016Sdes	u_int i;
441157016Sdes
442157016Sdes	if (args->list != NULL) {
443157016Sdes		for (i = 0; i < args->num; i++)
444157016Sdes			xfree(args->list[i]);
445157016Sdes		xfree(args->list);
446157016Sdes		args->nalloc = args->num = 0;
447157016Sdes		args->list = NULL;
448157016Sdes	}
449157016Sdes}
450157016Sdes
451146998Sdes/*
452149749Sdes * Expands tildes in the file name.  Returns data allocated by xmalloc.
453149749Sdes * Warning: this calls getpw*.
454149749Sdes */
455149749Sdeschar *
456149749Sdestilde_expand_filename(const char *filename, uid_t uid)
457149749Sdes{
458149749Sdes	const char *path;
459149749Sdes	char user[128], ret[MAXPATHLEN];
460149749Sdes	struct passwd *pw;
461149749Sdes	u_int len, slash;
462149749Sdes
463149749Sdes	if (*filename != '~')
464149749Sdes		return (xstrdup(filename));
465149749Sdes	filename++;
466149749Sdes
467149749Sdes	path = strchr(filename, '/');
468149749Sdes	if (path != NULL && path > filename) {		/* ~user/path */
469149749Sdes		slash = path - filename;
470149749Sdes		if (slash > sizeof(user) - 1)
471149749Sdes			fatal("tilde_expand_filename: ~username too long");
472149749Sdes		memcpy(user, filename, slash);
473149749Sdes		user[slash] = '\0';
474149749Sdes		if ((pw = getpwnam(user)) == NULL)
475149749Sdes			fatal("tilde_expand_filename: No such user %s", user);
476149749Sdes	} else if ((pw = getpwuid(uid)) == NULL)	/* ~/path */
477149749Sdes		fatal("tilde_expand_filename: No such uid %d", uid);
478149749Sdes
479149749Sdes	if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret))
480149749Sdes		fatal("tilde_expand_filename: Path too long");
481149749Sdes
482149749Sdes	/* Make sure directory has a trailing '/' */
483149749Sdes	len = strlen(pw->pw_dir);
484149749Sdes	if ((len == 0 || pw->pw_dir[len - 1] != '/') &&
485149749Sdes	    strlcat(ret, "/", sizeof(ret)) >= sizeof(ret))
486149749Sdes		fatal("tilde_expand_filename: Path too long");
487149749Sdes
488149749Sdes	/* Skip leading '/' from specified path */
489149749Sdes	if (path != NULL)
490149749Sdes		filename = path + 1;
491149749Sdes	if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret))
492149749Sdes		fatal("tilde_expand_filename: Path too long");
493149749Sdes
494149749Sdes	return (xstrdup(ret));
495149749Sdes}
496149749Sdes
497149749Sdes/*
498149749Sdes * Expand a string with a set of %[char] escapes. A number of escapes may be
499149749Sdes * specified as (char *escape_chars, char *replacement) pairs. The list must
500149749Sdes * be terminated by a NULL escape_char. Returns replaced string in memory
501149749Sdes * allocated by xmalloc.
502149749Sdes */
503149749Sdeschar *
504149749Sdespercent_expand(const char *string, ...)
505149749Sdes{
506149749Sdes#define EXPAND_MAX_KEYS	16
507149749Sdes	struct {
508149749Sdes		const char *key;
509149749Sdes		const char *repl;
510149749Sdes	} keys[EXPAND_MAX_KEYS];
511149749Sdes	u_int num_keys, i, j;
512149749Sdes	char buf[4096];
513149749Sdes	va_list ap;
514149749Sdes
515149749Sdes	/* Gather keys */
516149749Sdes	va_start(ap, string);
517149749Sdes	for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) {
518149749Sdes		keys[num_keys].key = va_arg(ap, char *);
519149749Sdes		if (keys[num_keys].key == NULL)
520149749Sdes			break;
521149749Sdes		keys[num_keys].repl = va_arg(ap, char *);
522149749Sdes		if (keys[num_keys].repl == NULL)
523149749Sdes			fatal("percent_expand: NULL replacement");
524149749Sdes	}
525149749Sdes	va_end(ap);
526149749Sdes
527149749Sdes	if (num_keys >= EXPAND_MAX_KEYS)
528149749Sdes		fatal("percent_expand: too many keys");
529149749Sdes
530149749Sdes	/* Expand string */
531149749Sdes	*buf = '\0';
532149749Sdes	for (i = 0; *string != '\0'; string++) {
533149749Sdes		if (*string != '%') {
534149749Sdes append:
535149749Sdes			buf[i++] = *string;
536149749Sdes			if (i >= sizeof(buf))
537149749Sdes				fatal("percent_expand: string too long");
538149749Sdes			buf[i] = '\0';
539149749Sdes			continue;
540149749Sdes		}
541149749Sdes		string++;
542149749Sdes		if (*string == '%')
543149749Sdes			goto append;
544149749Sdes		for (j = 0; j < num_keys; j++) {
545149749Sdes			if (strchr(keys[j].key, *string) != NULL) {
546149749Sdes				i = strlcat(buf, keys[j].repl, sizeof(buf));
547149749Sdes				if (i >= sizeof(buf))
548149749Sdes					fatal("percent_expand: string too long");
549149749Sdes				break;
550149749Sdes			}
551149749Sdes		}
552149749Sdes		if (j >= num_keys)
553149749Sdes			fatal("percent_expand: unknown key %%%c", *string);
554149749Sdes	}
555149749Sdes	return (xstrdup(buf));
556149749Sdes#undef EXPAND_MAX_KEYS
557149749Sdes}
558149749Sdes
559149749Sdes/*
560146998Sdes * Read an entire line from a public key file into a static buffer, discarding
561146998Sdes * lines that exceed the buffer size.  Returns 0 on success, -1 on failure.
562146998Sdes */
563146998Sdesint
564146998Sdesread_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
565146998Sdes   u_long *lineno)
566146998Sdes{
567146998Sdes	while (fgets(buf, bufsz, f) != NULL) {
568146998Sdes		(*lineno)++;
569146998Sdes		if (buf[strlen(buf) - 1] == '\n' || feof(f)) {
570146998Sdes			return 0;
571146998Sdes		} else {
572146998Sdes			debug("%s: %s line %lu exceeds size limit", __func__,
573146998Sdes			    filename, *lineno);
574146998Sdes			/* discard remainder of line */
575147001Sdes			while (fgetc(f) != '\n' && !feof(f))
576146998Sdes				;	/* nothing */
577146998Sdes		}
578146998Sdes	}
579146998Sdes	return -1;
580146998Sdes}
581149749Sdes
582157016Sdesint
583157016Sdestun_open(int tun, int mode)
584157016Sdes{
585157016Sdes#if defined(CUSTOM_SYS_TUN_OPEN)
586157016Sdes	return (sys_tun_open(tun, mode));
587157016Sdes#elif defined(SSH_TUN_OPENBSD)
588157016Sdes	struct ifreq ifr;
589157016Sdes	char name[100];
590157016Sdes	int fd = -1, sock;
591157016Sdes
592157016Sdes	/* Open the tunnel device */
593157016Sdes	if (tun <= SSH_TUNID_MAX) {
594157016Sdes		snprintf(name, sizeof(name), "/dev/tun%d", tun);
595157016Sdes		fd = open(name, O_RDWR);
596157016Sdes	} else if (tun == SSH_TUNID_ANY) {
597157016Sdes		for (tun = 100; tun >= 0; tun--) {
598157016Sdes			snprintf(name, sizeof(name), "/dev/tun%d", tun);
599157016Sdes			if ((fd = open(name, O_RDWR)) >= 0)
600157016Sdes				break;
601157016Sdes		}
602157016Sdes	} else {
603157016Sdes		debug("%s: invalid tunnel %u", __func__, tun);
604157016Sdes		return (-1);
605157016Sdes	}
606157016Sdes
607157016Sdes	if (fd < 0) {
608157016Sdes		debug("%s: %s open failed: %s", __func__, name, strerror(errno));
609157016Sdes		return (-1);
610157016Sdes	}
611157016Sdes
612157016Sdes	debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
613157016Sdes
614157016Sdes	/* Set the tunnel device operation mode */
615157016Sdes	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun);
616157016Sdes	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
617157016Sdes		goto failed;
618157016Sdes
619157016Sdes	if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
620157016Sdes		goto failed;
621157016Sdes
622157016Sdes	/* Set interface mode */
623157016Sdes	ifr.ifr_flags &= ~IFF_UP;
624157016Sdes	if (mode == SSH_TUNMODE_ETHERNET)
625157016Sdes		ifr.ifr_flags |= IFF_LINK0;
626157016Sdes	else
627157016Sdes		ifr.ifr_flags &= ~IFF_LINK0;
628157016Sdes	if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
629157016Sdes		goto failed;
630157016Sdes
631157016Sdes	/* Bring interface up */
632157016Sdes	ifr.ifr_flags |= IFF_UP;
633157016Sdes	if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
634157016Sdes		goto failed;
635157016Sdes
636157016Sdes	close(sock);
637157016Sdes	return (fd);
638157016Sdes
639157016Sdes failed:
640157016Sdes	if (fd >= 0)
641157016Sdes		close(fd);
642157016Sdes	if (sock >= 0)
643157016Sdes		close(sock);
644157016Sdes	debug("%s: failed to set %s mode %d: %s", __func__, name,
645157016Sdes	    mode, strerror(errno));
646157016Sdes	return (-1);
647157016Sdes#else
648157016Sdes	error("Tunnel interfaces are not supported on this platform");
649157016Sdes	return (-1);
650157016Sdes#endif
651157016Sdes}
652157016Sdes
653157016Sdesvoid
654157016Sdessanitise_stdfd(void)
655157016Sdes{
656157016Sdes	int nullfd, dupfd;
657157016Sdes
658157016Sdes	if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
659157016Sdes		fprintf(stderr, "Couldn't open /dev/null: %s", strerror(errno));
660157016Sdes		exit(1);
661157016Sdes	}
662157016Sdes	while (++dupfd <= 2) {
663157016Sdes		/* Only clobber closed fds */
664157016Sdes		if (fcntl(dupfd, F_GETFL, 0) >= 0)
665157016Sdes			continue;
666157016Sdes		if (dup2(nullfd, dupfd) == -1) {
667157016Sdes			fprintf(stderr, "dup2: %s", strerror(errno));
668157016Sdes			exit(1);
669157016Sdes		}
670157016Sdes	}
671157016Sdes	if (nullfd > 2)
672157016Sdes		close(nullfd);
673157016Sdes}
674157016Sdes
675149749Sdeschar *
676149749Sdestohex(const u_char *d, u_int l)
677149749Sdes{
678149749Sdes	char b[3], *r;
679149749Sdes	u_int i, hl;
680149749Sdes
681149749Sdes	hl = l * 2 + 1;
682149749Sdes	r = xmalloc(hl);
683149749Sdes	*r = '\0';
684149749Sdes	for (i = 0; i < l; i++) {
685149749Sdes		snprintf(b, sizeof(b), "%02x", d[i]);
686149749Sdes		strlcat(r, b, hl);
687149749Sdes	}
688149749Sdes	return (r);
689149749Sdes}
690149749Sdes
691