mirror of
git://anongit.mindrot.org/openssh.git
synced 2024-11-30 18:03:32 +08:00
d783435315
[OVERVIEW atomicio.c atomicio.h auth-bsdauth.c auth-chall.c auth-krb5.c] [auth-options.c auth-options.h auth-passwd.c auth-rh-rsa.c auth-rhosts.c] [auth-rsa.c auth-skey.c auth.c auth.h auth1.c auth2-chall.c auth2-gss.c] [auth2-hostbased.c auth2-kbdint.c auth2-none.c auth2-passwd.c ] [auth2-pubkey.c auth2.c authfd.c authfd.h authfile.c bufaux.c bufbn.c] [buffer.c buffer.h canohost.c channels.c channels.h cipher-3des1.c] [cipher-bf1.c cipher-ctr.c cipher.c cleanup.c clientloop.c compat.c] [compress.c deattack.c dh.c dispatch.c dns.c dns.h fatal.c groupaccess.c] [groupaccess.h gss-genr.c gss-serv-krb5.c gss-serv.c hostfile.c kex.c] [kex.h kexdh.c kexdhc.c kexdhs.c kexgex.c kexgexc.c kexgexs.c key.c] [key.h log.c log.h mac.c match.c md-sha256.c misc.c misc.h moduli.c] [monitor.c monitor_fdpass.c monitor_mm.c monitor_mm.h monitor_wrap.c] [monitor_wrap.h msg.c nchan.c packet.c progressmeter.c readconf.c] [readconf.h readpass.c rsa.c scard.c scard.h scp.c servconf.c servconf.h] [serverloop.c session.c session.h sftp-client.c sftp-common.c] [sftp-common.h sftp-glob.c sftp-server.c sftp.c ssh-add.c ssh-agent.c] [ssh-dss.c ssh-gss.h ssh-keygen.c ssh-keyscan.c ssh-keysign.c ssh-rsa.c] [ssh.c ssh.h sshconnect.c sshconnect.h sshconnect1.c sshconnect2.c] [sshd.c sshlogin.c sshlogin.h sshpty.c sshpty.h sshtty.c ttymodes.c] [uidswap.c uidswap.h uuencode.c uuencode.h xmalloc.c xmalloc.h] [loginrec.c loginrec.h openbsd-compat/port-aix.c openbsd-compat/port-tun.h] almost entirely get rid of the culture of ".h files that include .h files" ok djm, sort of ok stevesk makes the pain stop in one easy step NB. portable commit contains everything *except* removing includes.h, as that will take a fair bit more work as we move headers that are required for portability workarounds to defines.h. (also, this step wasn't "easy")
824 lines
17 KiB
C
824 lines
17 KiB
C
/* $OpenBSD: misc.c,v 1.64 2006/08/03 03:34:42 deraadt Exp $ */
|
|
/*
|
|
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
|
* Copyright (c) 2005,2006 Damien Miller. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/param.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#ifdef HAVE_PATHS_H
|
|
# include <paths.h>
|
|
#include <pwd.h>
|
|
#endif
|
|
#ifdef SSH_TUN_OPENBSD
|
|
#include <net/if.h>
|
|
#endif
|
|
|
|
#include "xmalloc.h"
|
|
#include "misc.h"
|
|
#include "log.h"
|
|
#include "ssh.h"
|
|
|
|
/* remove newline at end of string */
|
|
char *
|
|
chop(char *s)
|
|
{
|
|
char *t = s;
|
|
while (*t) {
|
|
if (*t == '\n' || *t == '\r') {
|
|
*t = '\0';
|
|
return s;
|
|
}
|
|
t++;
|
|
}
|
|
return s;
|
|
|
|
}
|
|
|
|
/* set/unset filedescriptor to non-blocking */
|
|
int
|
|
set_nonblock(int fd)
|
|
{
|
|
int val;
|
|
|
|
val = fcntl(fd, F_GETFL, 0);
|
|
if (val < 0) {
|
|
error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
|
|
return (-1);
|
|
}
|
|
if (val & O_NONBLOCK) {
|
|
debug3("fd %d is O_NONBLOCK", fd);
|
|
return (0);
|
|
}
|
|
debug2("fd %d setting O_NONBLOCK", fd);
|
|
val |= O_NONBLOCK;
|
|
if (fcntl(fd, F_SETFL, val) == -1) {
|
|
debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd,
|
|
strerror(errno));
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
unset_nonblock(int fd)
|
|
{
|
|
int val;
|
|
|
|
val = fcntl(fd, F_GETFL, 0);
|
|
if (val < 0) {
|
|
error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
|
|
return (-1);
|
|
}
|
|
if (!(val & O_NONBLOCK)) {
|
|
debug3("fd %d is not O_NONBLOCK", fd);
|
|
return (0);
|
|
}
|
|
debug("fd %d clearing O_NONBLOCK", fd);
|
|
val &= ~O_NONBLOCK;
|
|
if (fcntl(fd, F_SETFL, val) == -1) {
|
|
debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s",
|
|
fd, strerror(errno));
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* disable nagle on socket */
|
|
void
|
|
set_nodelay(int fd)
|
|
{
|
|
int opt;
|
|
socklen_t optlen;
|
|
|
|
optlen = sizeof opt;
|
|
if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) {
|
|
debug("getsockopt TCP_NODELAY: %.100s", strerror(errno));
|
|
return;
|
|
}
|
|
if (opt == 1) {
|
|
debug2("fd %d is TCP_NODELAY", fd);
|
|
return;
|
|
}
|
|
opt = 1;
|
|
debug2("fd %d setting TCP_NODELAY", fd);
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1)
|
|
error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
|
|
}
|
|
|
|
/* Characters considered whitespace in strsep calls. */
|
|
#define WHITESPACE " \t\r\n"
|
|
#define QUOTE "\""
|
|
|
|
/* return next token in configuration line */
|
|
char *
|
|
strdelim(char **s)
|
|
{
|
|
char *old;
|
|
int wspace = 0;
|
|
|
|
if (*s == NULL)
|
|
return NULL;
|
|
|
|
old = *s;
|
|
|
|
*s = strpbrk(*s, WHITESPACE QUOTE "=");
|
|
if (*s == NULL)
|
|
return (old);
|
|
|
|
if (*s[0] == '\"') {
|
|
memmove(*s, *s + 1, strlen(*s)); /* move nul too */
|
|
/* Find matching quote */
|
|
if ((*s = strpbrk(*s, QUOTE)) == NULL) {
|
|
return (NULL); /* no matching quote */
|
|
} else {
|
|
*s[0] = '\0';
|
|
return (old);
|
|
}
|
|
}
|
|
|
|
/* Allow only one '=' to be skipped */
|
|
if (*s[0] == '=')
|
|
wspace = 1;
|
|
*s[0] = '\0';
|
|
|
|
/* Skip any extra whitespace after first token */
|
|
*s += strspn(*s + 1, WHITESPACE) + 1;
|
|
if (*s[0] == '=' && !wspace)
|
|
*s += strspn(*s + 1, WHITESPACE) + 1;
|
|
|
|
return (old);
|
|
}
|
|
|
|
struct passwd *
|
|
pwcopy(struct passwd *pw)
|
|
{
|
|
struct passwd *copy = xcalloc(1, sizeof(*copy));
|
|
|
|
copy->pw_name = xstrdup(pw->pw_name);
|
|
copy->pw_passwd = xstrdup(pw->pw_passwd);
|
|
copy->pw_gecos = xstrdup(pw->pw_gecos);
|
|
copy->pw_uid = pw->pw_uid;
|
|
copy->pw_gid = pw->pw_gid;
|
|
#ifdef HAVE_PW_EXPIRE_IN_PASSWD
|
|
copy->pw_expire = pw->pw_expire;
|
|
#endif
|
|
#ifdef HAVE_PW_CHANGE_IN_PASSWD
|
|
copy->pw_change = pw->pw_change;
|
|
#endif
|
|
#ifdef HAVE_PW_CLASS_IN_PASSWD
|
|
copy->pw_class = xstrdup(pw->pw_class);
|
|
#endif
|
|
copy->pw_dir = xstrdup(pw->pw_dir);
|
|
copy->pw_shell = xstrdup(pw->pw_shell);
|
|
return copy;
|
|
}
|
|
|
|
/*
|
|
* Convert ASCII string to TCP/IP port number.
|
|
* Port must be >0 and <=65535.
|
|
* Return 0 if invalid.
|
|
*/
|
|
int
|
|
a2port(const char *s)
|
|
{
|
|
long port;
|
|
char *endp;
|
|
|
|
errno = 0;
|
|
port = strtol(s, &endp, 0);
|
|
if (s == endp || *endp != '\0' ||
|
|
(errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) ||
|
|
port <= 0 || port > 65535)
|
|
return 0;
|
|
|
|
return port;
|
|
}
|
|
|
|
int
|
|
a2tun(const char *s, int *remote)
|
|
{
|
|
const char *errstr = NULL;
|
|
char *sp, *ep;
|
|
int tun;
|
|
|
|
if (remote != NULL) {
|
|
*remote = SSH_TUNID_ANY;
|
|
sp = xstrdup(s);
|
|
if ((ep = strchr(sp, ':')) == NULL) {
|
|
xfree(sp);
|
|
return (a2tun(s, NULL));
|
|
}
|
|
ep[0] = '\0'; ep++;
|
|
*remote = a2tun(ep, NULL);
|
|
tun = a2tun(sp, NULL);
|
|
xfree(sp);
|
|
return (*remote == SSH_TUNID_ERR ? *remote : tun);
|
|
}
|
|
|
|
if (strcasecmp(s, "any") == 0)
|
|
return (SSH_TUNID_ANY);
|
|
|
|
tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (SSH_TUNID_ERR);
|
|
|
|
return (tun);
|
|
}
|
|
|
|
#define SECONDS 1
|
|
#define MINUTES (SECONDS * 60)
|
|
#define HOURS (MINUTES * 60)
|
|
#define DAYS (HOURS * 24)
|
|
#define WEEKS (DAYS * 7)
|
|
|
|
/*
|
|
* Convert a time string into seconds; format is
|
|
* a sequence of:
|
|
* time[qualifier]
|
|
*
|
|
* Valid time qualifiers are:
|
|
* <none> seconds
|
|
* s|S seconds
|
|
* m|M minutes
|
|
* h|H hours
|
|
* d|D days
|
|
* w|W weeks
|
|
*
|
|
* Examples:
|
|
* 90m 90 minutes
|
|
* 1h30m 90 minutes
|
|
* 2d 2 days
|
|
* 1w 1 week
|
|
*
|
|
* Return -1 if time string is invalid.
|
|
*/
|
|
long
|
|
convtime(const char *s)
|
|
{
|
|
long total, secs;
|
|
const char *p;
|
|
char *endp;
|
|
|
|
errno = 0;
|
|
total = 0;
|
|
p = s;
|
|
|
|
if (p == NULL || *p == '\0')
|
|
return -1;
|
|
|
|
while (*p) {
|
|
secs = strtol(p, &endp, 10);
|
|
if (p == endp ||
|
|
(errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) ||
|
|
secs < 0)
|
|
return -1;
|
|
|
|
switch (*endp++) {
|
|
case '\0':
|
|
endp--;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
break;
|
|
case 'm':
|
|
case 'M':
|
|
secs *= MINUTES;
|
|
break;
|
|
case 'h':
|
|
case 'H':
|
|
secs *= HOURS;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
secs *= DAYS;
|
|
break;
|
|
case 'w':
|
|
case 'W':
|
|
secs *= WEEKS;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
total += secs;
|
|
if (total < 0)
|
|
return -1;
|
|
p = endp;
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
/*
|
|
* Returns a standardized host+port identifier string.
|
|
* Caller must free returned string.
|
|
*/
|
|
char *
|
|
put_host_port(const char *host, u_short port)
|
|
{
|
|
char *hoststr;
|
|
|
|
if (port == 0 || port == SSH_DEFAULT_PORT)
|
|
return(xstrdup(host));
|
|
if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0)
|
|
fatal("put_host_port: asprintf: %s", strerror(errno));
|
|
debug3("put_host_port: %s", hoststr);
|
|
return hoststr;
|
|
}
|
|
|
|
/*
|
|
* Search for next delimiter between hostnames/addresses and ports.
|
|
* Argument may be modified (for termination).
|
|
* Returns *cp if parsing succeeds.
|
|
* *cp is set to the start of the next delimiter, if one was found.
|
|
* If this is the last field, *cp is set to NULL.
|
|
*/
|
|
char *
|
|
hpdelim(char **cp)
|
|
{
|
|
char *s, *old;
|
|
|
|
if (cp == NULL || *cp == NULL)
|
|
return NULL;
|
|
|
|
old = s = *cp;
|
|
if (*s == '[') {
|
|
if ((s = strchr(s, ']')) == NULL)
|
|
return NULL;
|
|
else
|
|
s++;
|
|
} else if ((s = strpbrk(s, ":/")) == NULL)
|
|
s = *cp + strlen(*cp); /* skip to end (see first case below) */
|
|
|
|
switch (*s) {
|
|
case '\0':
|
|
*cp = NULL; /* no more fields*/
|
|
break;
|
|
|
|
case ':':
|
|
case '/':
|
|
*s = '\0'; /* terminate */
|
|
*cp = s + 1;
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
return old;
|
|
}
|
|
|
|
char *
|
|
cleanhostname(char *host)
|
|
{
|
|
if (*host == '[' && host[strlen(host) - 1] == ']') {
|
|
host[strlen(host) - 1] = '\0';
|
|
return (host + 1);
|
|
} else
|
|
return host;
|
|
}
|
|
|
|
char *
|
|
colon(char *cp)
|
|
{
|
|
int flag = 0;
|
|
|
|
if (*cp == ':') /* Leading colon is part of file name. */
|
|
return (0);
|
|
if (*cp == '[')
|
|
flag = 1;
|
|
|
|
for (; *cp; ++cp) {
|
|
if (*cp == '@' && *(cp+1) == '[')
|
|
flag = 1;
|
|
if (*cp == ']' && *(cp+1) == ':' && flag)
|
|
return (cp+1);
|
|
if (*cp == ':' && !flag)
|
|
return (cp);
|
|
if (*cp == '/')
|
|
return (0);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/* function to assist building execv() arguments */
|
|
void
|
|
addargs(arglist *args, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *cp;
|
|
u_int nalloc;
|
|
int r;
|
|
|
|
va_start(ap, fmt);
|
|
r = vasprintf(&cp, fmt, ap);
|
|
va_end(ap);
|
|
if (r == -1)
|
|
fatal("addargs: argument too long");
|
|
|
|
nalloc = args->nalloc;
|
|
if (args->list == NULL) {
|
|
nalloc = 32;
|
|
args->num = 0;
|
|
} else if (args->num+2 >= nalloc)
|
|
nalloc *= 2;
|
|
|
|
args->list = xrealloc(args->list, nalloc, sizeof(char *));
|
|
args->nalloc = nalloc;
|
|
args->list[args->num++] = cp;
|
|
args->list[args->num] = NULL;
|
|
}
|
|
|
|
void
|
|
replacearg(arglist *args, u_int which, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *cp;
|
|
int r;
|
|
|
|
va_start(ap, fmt);
|
|
r = vasprintf(&cp, fmt, ap);
|
|
va_end(ap);
|
|
if (r == -1)
|
|
fatal("replacearg: argument too long");
|
|
|
|
if (which >= args->num)
|
|
fatal("replacearg: tried to replace invalid arg %d >= %d",
|
|
which, args->num);
|
|
xfree(args->list[which]);
|
|
args->list[which] = cp;
|
|
}
|
|
|
|
void
|
|
freeargs(arglist *args)
|
|
{
|
|
u_int i;
|
|
|
|
if (args->list != NULL) {
|
|
for (i = 0; i < args->num; i++)
|
|
xfree(args->list[i]);
|
|
xfree(args->list);
|
|
args->nalloc = args->num = 0;
|
|
args->list = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Expands tildes in the file name. Returns data allocated by xmalloc.
|
|
* Warning: this calls getpw*.
|
|
*/
|
|
char *
|
|
tilde_expand_filename(const char *filename, uid_t uid)
|
|
{
|
|
const char *path;
|
|
char user[128], ret[MAXPATHLEN];
|
|
struct passwd *pw;
|
|
u_int len, slash;
|
|
|
|
if (*filename != '~')
|
|
return (xstrdup(filename));
|
|
filename++;
|
|
|
|
path = strchr(filename, '/');
|
|
if (path != NULL && path > filename) { /* ~user/path */
|
|
slash = path - filename;
|
|
if (slash > sizeof(user) - 1)
|
|
fatal("tilde_expand_filename: ~username too long");
|
|
memcpy(user, filename, slash);
|
|
user[slash] = '\0';
|
|
if ((pw = getpwnam(user)) == NULL)
|
|
fatal("tilde_expand_filename: No such user %s", user);
|
|
} else if ((pw = getpwuid(uid)) == NULL) /* ~/path */
|
|
fatal("tilde_expand_filename: No such uid %d", uid);
|
|
|
|
if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret))
|
|
fatal("tilde_expand_filename: Path too long");
|
|
|
|
/* Make sure directory has a trailing '/' */
|
|
len = strlen(pw->pw_dir);
|
|
if ((len == 0 || pw->pw_dir[len - 1] != '/') &&
|
|
strlcat(ret, "/", sizeof(ret)) >= sizeof(ret))
|
|
fatal("tilde_expand_filename: Path too long");
|
|
|
|
/* Skip leading '/' from specified path */
|
|
if (path != NULL)
|
|
filename = path + 1;
|
|
if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret))
|
|
fatal("tilde_expand_filename: Path too long");
|
|
|
|
return (xstrdup(ret));
|
|
}
|
|
|
|
/*
|
|
* Expand a string with a set of %[char] escapes. A number of escapes may be
|
|
* specified as (char *escape_chars, char *replacement) pairs. The list must
|
|
* be terminated by a NULL escape_char. Returns replaced string in memory
|
|
* allocated by xmalloc.
|
|
*/
|
|
char *
|
|
percent_expand(const char *string, ...)
|
|
{
|
|
#define EXPAND_MAX_KEYS 16
|
|
struct {
|
|
const char *key;
|
|
const char *repl;
|
|
} keys[EXPAND_MAX_KEYS];
|
|
u_int num_keys, i, j;
|
|
char buf[4096];
|
|
va_list ap;
|
|
|
|
/* Gather keys */
|
|
va_start(ap, string);
|
|
for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) {
|
|
keys[num_keys].key = va_arg(ap, char *);
|
|
if (keys[num_keys].key == NULL)
|
|
break;
|
|
keys[num_keys].repl = va_arg(ap, char *);
|
|
if (keys[num_keys].repl == NULL)
|
|
fatal("percent_expand: NULL replacement");
|
|
}
|
|
va_end(ap);
|
|
|
|
if (num_keys >= EXPAND_MAX_KEYS)
|
|
fatal("percent_expand: too many keys");
|
|
|
|
/* Expand string */
|
|
*buf = '\0';
|
|
for (i = 0; *string != '\0'; string++) {
|
|
if (*string != '%') {
|
|
append:
|
|
buf[i++] = *string;
|
|
if (i >= sizeof(buf))
|
|
fatal("percent_expand: string too long");
|
|
buf[i] = '\0';
|
|
continue;
|
|
}
|
|
string++;
|
|
if (*string == '%')
|
|
goto append;
|
|
for (j = 0; j < num_keys; j++) {
|
|
if (strchr(keys[j].key, *string) != NULL) {
|
|
i = strlcat(buf, keys[j].repl, sizeof(buf));
|
|
if (i >= sizeof(buf))
|
|
fatal("percent_expand: string too long");
|
|
break;
|
|
}
|
|
}
|
|
if (j >= num_keys)
|
|
fatal("percent_expand: unknown key %%%c", *string);
|
|
}
|
|
return (xstrdup(buf));
|
|
#undef EXPAND_MAX_KEYS
|
|
}
|
|
|
|
/*
|
|
* Read an entire line from a public key file into a static buffer, discarding
|
|
* lines that exceed the buffer size. Returns 0 on success, -1 on failure.
|
|
*/
|
|
int
|
|
read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
|
|
u_long *lineno)
|
|
{
|
|
while (fgets(buf, bufsz, f) != NULL) {
|
|
(*lineno)++;
|
|
if (buf[strlen(buf) - 1] == '\n' || feof(f)) {
|
|
return 0;
|
|
} else {
|
|
debug("%s: %s line %lu exceeds size limit", __func__,
|
|
filename, *lineno);
|
|
/* discard remainder of line */
|
|
while (fgetc(f) != '\n' && !feof(f))
|
|
; /* nothing */
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
tun_open(int tun, int mode)
|
|
{
|
|
#if defined(CUSTOM_SYS_TUN_OPEN)
|
|
return (sys_tun_open(tun, mode));
|
|
#elif defined(SSH_TUN_OPENBSD)
|
|
struct ifreq ifr;
|
|
char name[100];
|
|
int fd = -1, sock;
|
|
|
|
/* Open the tunnel device */
|
|
if (tun <= SSH_TUNID_MAX) {
|
|
snprintf(name, sizeof(name), "/dev/tun%d", tun);
|
|
fd = open(name, O_RDWR);
|
|
} else if (tun == SSH_TUNID_ANY) {
|
|
for (tun = 100; tun >= 0; tun--) {
|
|
snprintf(name, sizeof(name), "/dev/tun%d", tun);
|
|
if ((fd = open(name, O_RDWR)) >= 0)
|
|
break;
|
|
}
|
|
} else {
|
|
debug("%s: invalid tunnel %u", __func__, tun);
|
|
return (-1);
|
|
}
|
|
|
|
if (fd < 0) {
|
|
debug("%s: %s open failed: %s", __func__, name, strerror(errno));
|
|
return (-1);
|
|
}
|
|
|
|
debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
|
|
|
|
/* Set the tunnel device operation mode */
|
|
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun);
|
|
if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
|
|
goto failed;
|
|
|
|
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
|
|
goto failed;
|
|
|
|
/* Set interface mode */
|
|
ifr.ifr_flags &= ~IFF_UP;
|
|
if (mode == SSH_TUNMODE_ETHERNET)
|
|
ifr.ifr_flags |= IFF_LINK0;
|
|
else
|
|
ifr.ifr_flags &= ~IFF_LINK0;
|
|
if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
|
|
goto failed;
|
|
|
|
/* Bring interface up */
|
|
ifr.ifr_flags |= IFF_UP;
|
|
if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
|
|
goto failed;
|
|
|
|
close(sock);
|
|
return (fd);
|
|
|
|
failed:
|
|
if (fd >= 0)
|
|
close(fd);
|
|
if (sock >= 0)
|
|
close(sock);
|
|
debug("%s: failed to set %s mode %d: %s", __func__, name,
|
|
mode, strerror(errno));
|
|
return (-1);
|
|
#else
|
|
error("Tunnel interfaces are not supported on this platform");
|
|
return (-1);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
sanitise_stdfd(void)
|
|
{
|
|
int nullfd, dupfd;
|
|
|
|
if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
|
|
fprintf(stderr, "Couldn't open /dev/null: %s", strerror(errno));
|
|
exit(1);
|
|
}
|
|
while (++dupfd <= 2) {
|
|
/* Only clobber closed fds */
|
|
if (fcntl(dupfd, F_GETFL, 0) >= 0)
|
|
continue;
|
|
if (dup2(nullfd, dupfd) == -1) {
|
|
fprintf(stderr, "dup2: %s", strerror(errno));
|
|
exit(1);
|
|
}
|
|
}
|
|
if (nullfd > 2)
|
|
close(nullfd);
|
|
}
|
|
|
|
char *
|
|
tohex(const void *vp, size_t l)
|
|
{
|
|
const u_char *p = (const u_char *)vp;
|
|
char b[3], *r;
|
|
size_t i, hl;
|
|
|
|
if (l > 65536)
|
|
return xstrdup("tohex: length > 65536");
|
|
|
|
hl = l * 2 + 1;
|
|
r = xcalloc(1, hl);
|
|
for (i = 0; i < l; i++) {
|
|
snprintf(b, sizeof(b), "%02x", p[i]);
|
|
strlcat(r, b, hl);
|
|
}
|
|
return (r);
|
|
}
|
|
|
|
u_int64_t
|
|
get_u64(const void *vp)
|
|
{
|
|
const u_char *p = (const u_char *)vp;
|
|
u_int64_t v;
|
|
|
|
v = (u_int64_t)p[0] << 56;
|
|
v |= (u_int64_t)p[1] << 48;
|
|
v |= (u_int64_t)p[2] << 40;
|
|
v |= (u_int64_t)p[3] << 32;
|
|
v |= (u_int64_t)p[4] << 24;
|
|
v |= (u_int64_t)p[5] << 16;
|
|
v |= (u_int64_t)p[6] << 8;
|
|
v |= (u_int64_t)p[7];
|
|
|
|
return (v);
|
|
}
|
|
|
|
u_int32_t
|
|
get_u32(const void *vp)
|
|
{
|
|
const u_char *p = (const u_char *)vp;
|
|
u_int32_t v;
|
|
|
|
v = (u_int32_t)p[0] << 24;
|
|
v |= (u_int32_t)p[1] << 16;
|
|
v |= (u_int32_t)p[2] << 8;
|
|
v |= (u_int32_t)p[3];
|
|
|
|
return (v);
|
|
}
|
|
|
|
u_int16_t
|
|
get_u16(const void *vp)
|
|
{
|
|
const u_char *p = (const u_char *)vp;
|
|
u_int16_t v;
|
|
|
|
v = (u_int16_t)p[0] << 8;
|
|
v |= (u_int16_t)p[1];
|
|
|
|
return (v);
|
|
}
|
|
|
|
void
|
|
put_u64(void *vp, u_int64_t v)
|
|
{
|
|
u_char *p = (u_char *)vp;
|
|
|
|
p[0] = (u_char)(v >> 56) & 0xff;
|
|
p[1] = (u_char)(v >> 48) & 0xff;
|
|
p[2] = (u_char)(v >> 40) & 0xff;
|
|
p[3] = (u_char)(v >> 32) & 0xff;
|
|
p[4] = (u_char)(v >> 24) & 0xff;
|
|
p[5] = (u_char)(v >> 16) & 0xff;
|
|
p[6] = (u_char)(v >> 8) & 0xff;
|
|
p[7] = (u_char)v & 0xff;
|
|
}
|
|
|
|
void
|
|
put_u32(void *vp, u_int32_t v)
|
|
{
|
|
u_char *p = (u_char *)vp;
|
|
|
|
p[0] = (u_char)(v >> 24) & 0xff;
|
|
p[1] = (u_char)(v >> 16) & 0xff;
|
|
p[2] = (u_char)(v >> 8) & 0xff;
|
|
p[3] = (u_char)v & 0xff;
|
|
}
|
|
|
|
|
|
void
|
|
put_u16(void *vp, u_int16_t v)
|
|
{
|
|
u_char *p = (u_char *)vp;
|
|
|
|
p[0] = (u_char)(v >> 8) & 0xff;
|
|
p[1] = (u_char)v & 0xff;
|
|
}
|