upstream commit
Add URI support to ssh, sftp and scp. For example ssh://user@host or sftp://user@host/path. The connection parameters described in draft-ietf-secsh-scp-sftp-ssh-uri-04 are not implemented since the ssh fingerprint format in the draft uses md5 with no way to specify the hash function type. OK djm@ Upstream-ID: 4ba3768b662d6722de59e6ecb00abf2d4bf9cacc
This commit is contained in:
parent
d27bff293c
commit
887669ef03
297
misc.c
297
misc.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: misc.c,v 1.113 2017/08/18 05:48:04 djm Exp $ */
|
||||
/* $OpenBSD: misc.c,v 1.114 2017/10/21 23:06:24 millert Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2005,2006 Damien Miller. All rights reserved.
|
||||
|
@ -395,11 +395,12 @@ put_host_port(const char *host, u_short port)
|
|||
* 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.
|
||||
* *cp is set to the start of the next field, if one was found.
|
||||
* The delimiter char, if present, is stored in delim.
|
||||
* If this is the last field, *cp is set to NULL.
|
||||
*/
|
||||
char *
|
||||
hpdelim(char **cp)
|
||||
static char *
|
||||
hpdelim2(char **cp, char *delim)
|
||||
{
|
||||
char *s, *old;
|
||||
|
||||
|
@ -422,6 +423,8 @@ hpdelim(char **cp)
|
|||
|
||||
case ':':
|
||||
case '/':
|
||||
if (delim != NULL)
|
||||
*delim = *s;
|
||||
*s = '\0'; /* terminate */
|
||||
*cp = s + 1;
|
||||
break;
|
||||
|
@ -433,6 +436,12 @@ hpdelim(char **cp)
|
|||
return old;
|
||||
}
|
||||
|
||||
char *
|
||||
hpdelim(char **cp)
|
||||
{
|
||||
return hpdelim2(cp, NULL);
|
||||
}
|
||||
|
||||
char *
|
||||
cleanhostname(char *host)
|
||||
{
|
||||
|
@ -466,6 +475,75 @@ colon(char *cp)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a [user@]host:[path] string.
|
||||
* Caller must free returned user, host and path.
|
||||
* Any of the pointer return arguments may be NULL (useful for syntax checking).
|
||||
* If user was not specified then *userp will be set to NULL.
|
||||
* If host was not specified then *hostp will be set to NULL.
|
||||
* If path was not specified then *pathp will be set to ".".
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
parse_user_host_path(const char *s, char **userp, char **hostp, char **pathp)
|
||||
{
|
||||
char *user = NULL, *host = NULL, *path = NULL;
|
||||
char *sdup, *tmp;
|
||||
int ret = -1;
|
||||
|
||||
if (userp != NULL)
|
||||
*userp = NULL;
|
||||
if (hostp != NULL)
|
||||
*hostp = NULL;
|
||||
if (pathp != NULL)
|
||||
*pathp = NULL;
|
||||
|
||||
sdup = tmp = xstrdup(s);
|
||||
|
||||
/* Check for remote syntax: [user@]host:[path] */
|
||||
if ((tmp = colon(sdup)) == NULL)
|
||||
goto out;
|
||||
|
||||
/* Extract optional path */
|
||||
*tmp++ = '\0';
|
||||
if (*tmp == '\0')
|
||||
tmp = ".";
|
||||
path = xstrdup(tmp);
|
||||
|
||||
/* Extract optional user and mandatory host */
|
||||
tmp = strrchr(sdup, '@');
|
||||
if (tmp != NULL) {
|
||||
*tmp++ = '\0';
|
||||
host = xstrdup(cleanhostname(tmp));
|
||||
if (*sdup != '\0')
|
||||
user = xstrdup(sdup);
|
||||
} else {
|
||||
host = xstrdup(cleanhostname(sdup));
|
||||
user = NULL;
|
||||
}
|
||||
|
||||
/* Success */
|
||||
if (userp != NULL) {
|
||||
*userp = user;
|
||||
user = NULL;
|
||||
}
|
||||
if (hostp != NULL) {
|
||||
*hostp = host;
|
||||
host = NULL;
|
||||
}
|
||||
if (pathp != NULL) {
|
||||
*pathp = path;
|
||||
path = NULL;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
free(sdup);
|
||||
free(user);
|
||||
free(host);
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a [user@]host[:port] string.
|
||||
* Caller must free returned user and host.
|
||||
|
@ -491,7 +569,7 @@ parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
|
|||
if ((sdup = tmp = strdup(s)) == NULL)
|
||||
return -1;
|
||||
/* Extract optional username */
|
||||
if ((cp = strchr(tmp, '@')) != NULL) {
|
||||
if ((cp = strrchr(tmp, '@')) != NULL) {
|
||||
*cp = '\0';
|
||||
if (*tmp == '\0')
|
||||
goto out;
|
||||
|
@ -527,6 +605,168 @@ parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a two-byte hex string to decimal.
|
||||
* Returns the decimal value or -1 for invalid input.
|
||||
*/
|
||||
static int
|
||||
hexchar(const char *s)
|
||||
{
|
||||
unsigned char result[2];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (s[i] >= '0' && s[i] <= '9')
|
||||
result[i] = (unsigned char)(s[i] - '0');
|
||||
else if (s[i] >= 'a' && s[i] <= 'f')
|
||||
result[i] = (unsigned char)(s[i] - 'a') + 10;
|
||||
else if (s[i] >= 'A' && s[i] <= 'F')
|
||||
result[i] = (unsigned char)(s[i] - 'A') + 10;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
return (result[0] << 4) | result[1];
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode an url-encoded string.
|
||||
* Returns a newly allocated string on success or NULL on failure.
|
||||
*/
|
||||
static char *
|
||||
urldecode(const char *src)
|
||||
{
|
||||
char *ret, *dst;
|
||||
int ch;
|
||||
|
||||
ret = xmalloc(strlen(src) + 1);
|
||||
for (dst = ret; *src != '\0'; src++) {
|
||||
switch (*src) {
|
||||
case '+':
|
||||
*dst++ = ' ';
|
||||
break;
|
||||
case '%':
|
||||
if (!isxdigit((unsigned char)src[1]) ||
|
||||
!isxdigit((unsigned char)src[2]) ||
|
||||
(ch = hexchar(src + 1)) == -1) {
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
*dst++ = ch;
|
||||
src += 2;
|
||||
break;
|
||||
default:
|
||||
*dst++ = *src;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*dst = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI.
|
||||
* See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04
|
||||
* Either user or path may be url-encoded (but not host or port).
|
||||
* Caller must free returned user, host and path.
|
||||
* Any of the pointer return arguments may be NULL (useful for syntax checking)
|
||||
* but the scheme must always be specified.
|
||||
* If user was not specified then *userp will be set to NULL.
|
||||
* If port was not specified then *portp will be -1.
|
||||
* If path was not specified then *pathp will be set to NULL.
|
||||
* Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri.
|
||||
*/
|
||||
int
|
||||
parse_uri(const char *scheme, const char *uri, char **userp, char **hostp,
|
||||
int *portp, char **pathp)
|
||||
{
|
||||
char *uridup, *cp, *tmp, ch;
|
||||
char *user = NULL, *host = NULL, *path = NULL;
|
||||
int port = -1, ret = -1;
|
||||
size_t len;
|
||||
|
||||
len = strlen(scheme);
|
||||
if (strncmp(uri, scheme, len) != 0 || strncmp(uri + len, "://", 3) != 0)
|
||||
return 1;
|
||||
uri += len + 3;
|
||||
|
||||
if (userp != NULL)
|
||||
*userp = NULL;
|
||||
if (hostp != NULL)
|
||||
*hostp = NULL;
|
||||
if (portp != NULL)
|
||||
*portp = -1;
|
||||
if (pathp != NULL)
|
||||
*pathp = NULL;
|
||||
|
||||
uridup = tmp = xstrdup(uri);
|
||||
|
||||
/* Extract optional ssh-info (username + connection params) */
|
||||
if ((cp = strchr(tmp, '@')) != NULL) {
|
||||
char *delim;
|
||||
|
||||
*cp = '\0';
|
||||
/* Extract username and connection params */
|
||||
if ((delim = strchr(tmp, ';')) != NULL) {
|
||||
/* Just ignore connection params for now */
|
||||
*delim = '\0';
|
||||
}
|
||||
if (*tmp == '\0') {
|
||||
/* Empty username */
|
||||
goto out;
|
||||
}
|
||||
if ((user = urldecode(tmp)) == NULL)
|
||||
goto out;
|
||||
tmp = cp + 1;
|
||||
}
|
||||
|
||||
/* Extract mandatory hostname */
|
||||
if ((cp = hpdelim2(&tmp, &ch)) == NULL || *cp == '\0')
|
||||
goto out;
|
||||
host = xstrdup(cleanhostname(cp));
|
||||
if (!valid_domain(host, 0, NULL))
|
||||
goto out;
|
||||
|
||||
if (tmp != NULL && *tmp != '\0') {
|
||||
if (ch == ':') {
|
||||
/* Convert and verify port. */
|
||||
if ((cp = strchr(tmp, '/')) != NULL)
|
||||
*cp = '\0';
|
||||
if ((port = a2port(tmp)) <= 0)
|
||||
goto out;
|
||||
tmp = cp ? cp + 1 : NULL;
|
||||
}
|
||||
if (tmp != NULL && *tmp != '\0') {
|
||||
/* Extract optional path */
|
||||
if ((path = urldecode(tmp)) == NULL)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Success */
|
||||
if (userp != NULL) {
|
||||
*userp = user;
|
||||
user = NULL;
|
||||
}
|
||||
if (hostp != NULL) {
|
||||
*hostp = host;
|
||||
host = NULL;
|
||||
}
|
||||
if (portp != NULL)
|
||||
*portp = port;
|
||||
if (pathp != NULL) {
|
||||
*pathp = path;
|
||||
path = NULL;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
free(uridup);
|
||||
free(user);
|
||||
free(host);
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* function to assist building execv() arguments */
|
||||
void
|
||||
addargs(arglist *args, char *fmt, ...)
|
||||
|
@ -1743,3 +1983,50 @@ child_set_env(char ***envp, u_int *envsizep, const char *name,
|
|||
snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check and optionally lowercase a domain name, also removes trailing '.'
|
||||
* Returns 1 on success and 0 on failure, storing an error message in errstr.
|
||||
*/
|
||||
int
|
||||
valid_domain(char *name, int makelower, const char **errstr)
|
||||
{
|
||||
size_t i, l = strlen(name);
|
||||
u_char c, last = '\0';
|
||||
static char errbuf[256];
|
||||
|
||||
if (l == 0) {
|
||||
strlcpy(errbuf, "empty domain name", sizeof(errbuf));
|
||||
goto bad;
|
||||
}
|
||||
if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) {
|
||||
snprintf(errbuf, sizeof(errbuf), "domain name \"%.100s\" "
|
||||
"starts with invalid character", name);
|
||||
goto bad;
|
||||
}
|
||||
for (i = 0; i < l; i++) {
|
||||
c = tolower((u_char)name[i]);
|
||||
if (makelower)
|
||||
name[i] = (char)c;
|
||||
if (last == '.' && c == '.') {
|
||||
snprintf(errbuf, sizeof(errbuf), "domain name "
|
||||
"\"%.100s\" contains consecutive separators", name);
|
||||
goto bad;
|
||||
}
|
||||
if (c != '.' && c != '-' && !isalnum(c) &&
|
||||
c != '_') /* technically invalid, but common */ {
|
||||
snprintf(errbuf, sizeof(errbuf), "domain name "
|
||||
"\"%.100s\" contains invalid characters", name);
|
||||
goto bad;
|
||||
}
|
||||
last = c;
|
||||
}
|
||||
if (name[l - 1] == '.')
|
||||
name[l - 1] = '\0';
|
||||
if (errstr != NULL)
|
||||
*errstr = NULL;
|
||||
return 1;
|
||||
bad:
|
||||
if (errstr != NULL)
|
||||
*errstr = errbuf;
|
||||
return 0;
|
||||
}
|
||||
|
|
5
misc.h
5
misc.h
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: misc.h,v 1.63 2017/08/18 05:48:04 djm Exp $ */
|
||||
/* $OpenBSD: misc.h,v 1.64 2017/10/21 23:06:24 millert Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
|
@ -54,7 +54,9 @@ char *put_host_port(const char *, u_short);
|
|||
char *hpdelim(char **);
|
||||
char *cleanhostname(char *);
|
||||
char *colon(char *);
|
||||
int parse_user_host_path(const char *, char **, char **, char **);
|
||||
int parse_user_host_port(const char *, char **, char **, int *);
|
||||
int parse_uri(const char *, const char *, char **, char **, int *, char **);
|
||||
long convtime(const char *);
|
||||
char *tilde_expand_filename(const char *, uid_t);
|
||||
char *percent_expand(const char *, ...) __attribute__((__sentinel__));
|
||||
|
@ -66,6 +68,7 @@ time_t monotime(void);
|
|||
double monotime_double(void);
|
||||
void lowercase(char *s);
|
||||
int unix_listener(const char *, int, int);
|
||||
int valid_domain(char *, int, const char **);
|
||||
|
||||
void sock_set_v6only(int);
|
||||
|
||||
|
|
54
readconf.c
54
readconf.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: readconf.c,v 1.279 2017/09/21 19:16:53 markus Exp $ */
|
||||
/* $OpenBSD: readconf.c,v 1.280 2017/10/21 23:06:24 millert Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -683,34 +683,6 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
|
|||
return result;
|
||||
}
|
||||
|
||||
/* Check and prepare a domain name: removes trailing '.' and lowercases */
|
||||
static void
|
||||
valid_domain(char *name, const char *filename, int linenum)
|
||||
{
|
||||
size_t i, l = strlen(name);
|
||||
u_char c, last = '\0';
|
||||
|
||||
if (l == 0)
|
||||
fatal("%s line %d: empty hostname suffix", filename, linenum);
|
||||
if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0]))
|
||||
fatal("%s line %d: hostname suffix \"%.100s\" "
|
||||
"starts with invalid character", filename, linenum, name);
|
||||
for (i = 0; i < l; i++) {
|
||||
c = tolower((u_char)name[i]);
|
||||
name[i] = (char)c;
|
||||
if (last == '.' && c == '.')
|
||||
fatal("%s line %d: hostname suffix \"%.100s\" contains "
|
||||
"consecutive separators", filename, linenum, name);
|
||||
if (c != '.' && c != '-' && !isalnum(c) &&
|
||||
c != '_') /* technically invalid, but common */
|
||||
fatal("%s line %d: hostname suffix \"%.100s\" contains "
|
||||
"invalid characters", filename, linenum, name);
|
||||
last = c;
|
||||
}
|
||||
if (name[l - 1] == '.')
|
||||
name[l - 1] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of the token pointed to by cp or oBadOption.
|
||||
*/
|
||||
|
@ -1562,7 +1534,11 @@ parse_keytypes:
|
|||
case oCanonicalDomains:
|
||||
value = options->num_canonical_domains != 0;
|
||||
while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
|
||||
valid_domain(arg, filename, linenum);
|
||||
const char *errstr;
|
||||
if (!valid_domain(arg, 1, &errstr)) {
|
||||
fatal("%s line %d: %s", filename, linenum,
|
||||
errstr);
|
||||
}
|
||||
if (!*activep || value)
|
||||
continue;
|
||||
if (options->num_canonical_domains >= MAX_CANON_DOMAINS)
|
||||
|
@ -2294,11 +2270,13 @@ parse_jump(const char *s, Options *o, int active)
|
|||
|
||||
if (first) {
|
||||
/* First argument and configuration is active */
|
||||
if (parse_user_host_port(cp, &user, &host, &port) != 0)
|
||||
if (parse_ssh_uri(cp, &user, &host, &port) == -1 ||
|
||||
parse_user_host_port(cp, &user, &host, &port) != 0)
|
||||
goto out;
|
||||
} else {
|
||||
/* Subsequent argument or inactive configuration */
|
||||
if (parse_user_host_port(cp, NULL, NULL, NULL) != 0)
|
||||
if (parse_ssh_uri(cp, NULL, NULL, NULL) == -1 ||
|
||||
parse_user_host_port(cp, NULL, NULL, NULL) != 0)
|
||||
goto out;
|
||||
}
|
||||
first = 0; /* only check syntax for subsequent hosts */
|
||||
|
@ -2323,6 +2301,18 @@ parse_jump(const char *s, Options *o, int active)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp)
|
||||
{
|
||||
char *path;
|
||||
int r;
|
||||
|
||||
r = parse_uri("ssh", uri, userp, hostp, portp, &path);
|
||||
if (r == 0 && path != NULL)
|
||||
r = -1; /* path not allowed */
|
||||
return r;
|
||||
}
|
||||
|
||||
/* XXX the following is a near-vebatim copy from servconf.c; refactor */
|
||||
static const char *
|
||||
fmt_multistate_int(int val, const struct multistate *m)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: readconf.h,v 1.123 2017/09/03 23:33:13 djm Exp $ */
|
||||
/* $OpenBSD: readconf.h,v 1.124 2017/10/21 23:06:24 millert Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
|
@ -204,6 +204,7 @@ int read_config_file(const char *, struct passwd *, const char *,
|
|||
const char *, Options *, int);
|
||||
int parse_forward(struct Forward *, const char *, int, int);
|
||||
int parse_jump(const char *, Options *, int);
|
||||
int parse_ssh_uri(const char *, char **, char **, int *);
|
||||
int default_ssh_port(void);
|
||||
int option_clear_or_none(const char *);
|
||||
void dump_client_config(Options *o, const char *host);
|
||||
|
|
41
scp.1
41
scp.1
|
@ -8,9 +8,9 @@
|
|||
.\"
|
||||
.\" Created: Sun May 7 00:14:37 1995 ylo
|
||||
.\"
|
||||
.\" $OpenBSD: scp.1,v 1.74 2017/05/03 21:49:18 naddy Exp $
|
||||
.\" $OpenBSD: scp.1,v 1.75 2017/10/21 23:06:24 millert Exp $
|
||||
.\"
|
||||
.Dd $Mdocdate: May 3 2017 $
|
||||
.Dd $Mdocdate: October 21 2017 $
|
||||
.Dt SCP 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -27,20 +27,8 @@
|
|||
.Op Fl o Ar ssh_option
|
||||
.Op Fl P Ar port
|
||||
.Op Fl S Ar program
|
||||
.Sm off
|
||||
.Oo
|
||||
.Op Ar user No @
|
||||
.Ar host1 :
|
||||
.Oc Ar file1
|
||||
.Sm on
|
||||
.Ar ...
|
||||
.Sm off
|
||||
.Oo
|
||||
.Op Ar user No @
|
||||
.Ar host2 :
|
||||
.Oc Ar file2
|
||||
.Sm on
|
||||
.Ek
|
||||
.Ar source ...
|
||||
.Ar target
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
copies files between hosts on a network.
|
||||
|
@ -53,15 +41,30 @@ same security as
|
|||
will ask for passwords or passphrases if they are needed for
|
||||
authentication.
|
||||
.Pp
|
||||
File names may contain a user and host specification to indicate
|
||||
that the file is to be copied to/from that host.
|
||||
The
|
||||
.Ar target
|
||||
and
|
||||
.Ar destination
|
||||
may be specified as a local pathname, a remote host with optional path
|
||||
in the form
|
||||
.Oo Ar user Ns @ Oc Ns Ar host Ns : Ns Oo Ar path Oc ,
|
||||
or an scp URI in the form
|
||||
.No scp:// Ns Oo Ar user Ns @ Oc Ns Ar host Ns
|
||||
.Oo : Ns Ar port Oc Ns Oo / Ns Ar path Oc .
|
||||
Local file names can be made explicit using absolute or relative pathnames
|
||||
to avoid
|
||||
.Nm
|
||||
treating file names containing
|
||||
.Sq :\&
|
||||
as host specifiers.
|
||||
Copies between two remote hosts are also permitted.
|
||||
.Pp
|
||||
When copying between two remote hosts, if the URI format is used, a
|
||||
.Ar port
|
||||
may only be specified on the
|
||||
.Ar target
|
||||
if the
|
||||
.Fl 3
|
||||
option is used.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
|
|
197
scp.c
197
scp.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: scp.c,v 1.192 2017/05/31 09:15:42 deraadt Exp $ */
|
||||
/* $OpenBSD: scp.c,v 1.193 2017/10/21 23:06:24 millert Exp $ */
|
||||
/*
|
||||
* scp - secure remote copy. This is basically patched BSD rcp which
|
||||
* uses ssh to do the data transfer (instead of using rcmd).
|
||||
|
@ -112,6 +112,7 @@
|
|||
#endif
|
||||
|
||||
#include "xmalloc.h"
|
||||
#include "ssh.h"
|
||||
#include "atomicio.h"
|
||||
#include "pathnames.h"
|
||||
#include "log.h"
|
||||
|
@ -123,8 +124,8 @@ extern char *__progname;
|
|||
|
||||
#define COPY_BUFLEN 16384
|
||||
|
||||
int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
|
||||
int do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout);
|
||||
int do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout);
|
||||
int do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout);
|
||||
|
||||
/* Struct for addargs */
|
||||
arglist args;
|
||||
|
@ -149,6 +150,9 @@ int showprogress = 1;
|
|||
*/
|
||||
int throughlocal = 0;
|
||||
|
||||
/* Non-standard port to use for the ssh connection or -1. */
|
||||
int sshport = -1;
|
||||
|
||||
/* This is the program to execute for the secured connection. ("ssh" or -S) */
|
||||
char *ssh_program = _PATH_SSH_PROGRAM;
|
||||
|
||||
|
@ -231,7 +235,7 @@ do_local_cmd(arglist *a)
|
|||
*/
|
||||
|
||||
int
|
||||
do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
|
||||
do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout)
|
||||
{
|
||||
int pin[2], pout[2], reserved[2];
|
||||
|
||||
|
@ -241,6 +245,9 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
|
|||
ssh_program, host,
|
||||
remuser ? remuser : "(unspecified)", cmd);
|
||||
|
||||
if (port == -1)
|
||||
port = sshport;
|
||||
|
||||
/*
|
||||
* Reserve two descriptors so that the real pipes won't get
|
||||
* descriptors 0 and 1 because that will screw up dup2 below.
|
||||
|
@ -274,6 +281,10 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
|
|||
close(pout[1]);
|
||||
|
||||
replacearg(&args, 0, "%s", ssh_program);
|
||||
if (port != -1) {
|
||||
addargs(&args, "-p");
|
||||
addargs(&args, "%d", port);
|
||||
}
|
||||
if (remuser != NULL) {
|
||||
addargs(&args, "-l");
|
||||
addargs(&args, "%s", remuser);
|
||||
|
@ -305,7 +316,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
|
|||
* This way the input and output of two commands can be connected.
|
||||
*/
|
||||
int
|
||||
do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout)
|
||||
do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
@ -316,6 +327,9 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout)
|
|||
ssh_program, host,
|
||||
remuser ? remuser : "(unspecified)", cmd);
|
||||
|
||||
if (port == -1)
|
||||
port = sshport;
|
||||
|
||||
/* Fork a child to execute the command on the remote host using ssh. */
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
|
@ -323,6 +337,10 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout)
|
|||
dup2(fdout, 1);
|
||||
|
||||
replacearg(&args, 0, "%s", ssh_program);
|
||||
if (port != -1) {
|
||||
addargs(&args, "-p");
|
||||
addargs(&args, "%d", port);
|
||||
}
|
||||
if (remuser != NULL) {
|
||||
addargs(&args, "-l");
|
||||
addargs(&args, "%s", remuser);
|
||||
|
@ -367,14 +385,14 @@ void rsource(char *, struct stat *);
|
|||
void sink(int, char *[]);
|
||||
void source(int, char *[]);
|
||||
void tolocal(int, char *[]);
|
||||
void toremote(char *, int, char *[]);
|
||||
void toremote(int, char *[]);
|
||||
void usage(void);
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int ch, fflag, tflag, status, n;
|
||||
char *targ, **newargv;
|
||||
char **newargv;
|
||||
const char *errstr;
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
|
@ -430,10 +448,9 @@ main(int argc, char **argv)
|
|||
addargs(&args, "%s", optarg);
|
||||
break;
|
||||
case 'P':
|
||||
addargs(&remote_remote_args, "-p");
|
||||
addargs(&remote_remote_args, "%s", optarg);
|
||||
addargs(&args, "-p");
|
||||
addargs(&args, "%s", optarg);
|
||||
sshport = a2port(optarg);
|
||||
if (sshport <= 0)
|
||||
fatal("bad port \"%s\"\n", optarg);
|
||||
break;
|
||||
case 'B':
|
||||
addargs(&remote_remote_args, "-oBatchmode=yes");
|
||||
|
@ -533,8 +550,8 @@ main(int argc, char **argv)
|
|||
|
||||
(void) signal(SIGPIPE, lostconn);
|
||||
|
||||
if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */
|
||||
toremote(targ, argc, argv);
|
||||
if (colon(argv[argc - 1])) /* Dest is remote host. */
|
||||
toremote(argc, argv);
|
||||
else {
|
||||
if (targetshouldbedirectory)
|
||||
verifydir(argv[argc - 1]);
|
||||
|
@ -590,71 +607,65 @@ do_times(int fd, int verb, const struct stat *sb)
|
|||
}
|
||||
|
||||
void
|
||||
toremote(char *targ, int argc, char **argv)
|
||||
toremote(int argc, char **argv)
|
||||
{
|
||||
char *bp, *host, *src, *suser, *thost, *tuser, *arg;
|
||||
char *suser = NULL, *host = NULL, *src = NULL;
|
||||
char *bp, *tuser, *thost, *targ;
|
||||
int sport = -1, tport = -1;
|
||||
arglist alist;
|
||||
int i;
|
||||
int i, r;
|
||||
u_int j;
|
||||
|
||||
memset(&alist, '\0', sizeof(alist));
|
||||
alist.list = NULL;
|
||||
|
||||
*targ++ = 0;
|
||||
if (*targ == 0)
|
||||
targ = ".";
|
||||
|
||||
arg = xstrdup(argv[argc - 1]);
|
||||
if ((thost = strrchr(arg, '@'))) {
|
||||
/* user@host */
|
||||
*thost++ = 0;
|
||||
tuser = arg;
|
||||
if (*tuser == '\0')
|
||||
tuser = NULL;
|
||||
} else {
|
||||
thost = arg;
|
||||
tuser = NULL;
|
||||
}
|
||||
|
||||
if (tuser != NULL && !okname(tuser)) {
|
||||
free(arg);
|
||||
return;
|
||||
/* Parse target */
|
||||
r = parse_uri("scp", argv[argc - 1], &tuser, &thost, &tport, &targ);
|
||||
if (r == -1)
|
||||
goto out; /* invalid URI */
|
||||
if (r != 0) {
|
||||
if (parse_user_host_path(argv[argc - 1], &tuser, &thost,
|
||||
&targ) == -1)
|
||||
goto out;
|
||||
}
|
||||
if (tuser != NULL && !okname(tuser))
|
||||
goto out;
|
||||
|
||||
/* Parse source files */
|
||||
for (i = 0; i < argc - 1; i++) {
|
||||
src = colon(argv[i]);
|
||||
if (src && throughlocal) { /* extended remote to remote */
|
||||
*src++ = 0;
|
||||
if (*src == 0)
|
||||
src = ".";
|
||||
host = strrchr(argv[i], '@');
|
||||
if (host) {
|
||||
*host++ = 0;
|
||||
host = cleanhostname(host);
|
||||
suser = argv[i];
|
||||
if (*suser == '\0')
|
||||
suser = pwd->pw_name;
|
||||
else if (!okname(suser))
|
||||
continue;
|
||||
} else {
|
||||
host = cleanhostname(argv[i]);
|
||||
suser = NULL;
|
||||
}
|
||||
free(suser);
|
||||
free(host);
|
||||
free(src);
|
||||
r = parse_uri("scp", argv[i], &suser, &host, &sport, &src);
|
||||
if (r == -1)
|
||||
continue; /* invalid URI */
|
||||
if (r != 0)
|
||||
parse_user_host_path(argv[i], &suser, &host, &src);
|
||||
if (suser != NULL && !okname(suser)) {
|
||||
++errs;
|
||||
continue;
|
||||
}
|
||||
if (host && throughlocal) { /* extended remote to remote */
|
||||
xasprintf(&bp, "%s -f %s%s", cmd,
|
||||
*src == '-' ? "-- " : "", src);
|
||||
if (do_cmd(host, suser, bp, &remin, &remout) < 0)
|
||||
if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0)
|
||||
exit(1);
|
||||
free(bp);
|
||||
host = cleanhostname(thost);
|
||||
xasprintf(&bp, "%s -t %s%s", cmd,
|
||||
*targ == '-' ? "-- " : "", targ);
|
||||
if (do_cmd2(host, tuser, bp, remin, remout) < 0)
|
||||
if (do_cmd2(thost, tuser, tport, bp, remin, remout) < 0)
|
||||
exit(1);
|
||||
free(bp);
|
||||
(void) close(remin);
|
||||
(void) close(remout);
|
||||
remin = remout = -1;
|
||||
} else if (src) { /* standard remote to remote */
|
||||
} else if (host) { /* standard remote to remote */
|
||||
if (tport != -1 && tport != SSH_DEFAULT_PORT) {
|
||||
/* This would require the remote support URIs */
|
||||
fatal("target port not supported with two "
|
||||
"remote hosts without the -3 option");
|
||||
}
|
||||
|
||||
freeargs(&alist);
|
||||
addargs(&alist, "%s", ssh_program);
|
||||
addargs(&alist, "-x");
|
||||
|
@ -664,23 +675,14 @@ toremote(char *targ, int argc, char **argv)
|
|||
addargs(&alist, "%s",
|
||||
remote_remote_args.list[j]);
|
||||
}
|
||||
*src++ = 0;
|
||||
if (*src == 0)
|
||||
src = ".";
|
||||
host = strrchr(argv[i], '@');
|
||||
|
||||
if (host) {
|
||||
*host++ = 0;
|
||||
host = cleanhostname(host);
|
||||
suser = argv[i];
|
||||
if (*suser == '\0')
|
||||
suser = pwd->pw_name;
|
||||
else if (!okname(suser))
|
||||
continue;
|
||||
if (sport != -1) {
|
||||
addargs(&alist, "-p");
|
||||
addargs(&alist, "%d", sport);
|
||||
}
|
||||
if (suser) {
|
||||
addargs(&alist, "-l");
|
||||
addargs(&alist, "%s", suser);
|
||||
} else {
|
||||
host = cleanhostname(argv[i]);
|
||||
}
|
||||
addargs(&alist, "--");
|
||||
addargs(&alist, "%s", host);
|
||||
|
@ -695,8 +697,7 @@ toremote(char *targ, int argc, char **argv)
|
|||
if (remin == -1) {
|
||||
xasprintf(&bp, "%s -t %s%s", cmd,
|
||||
*targ == '-' ? "-- " : "", targ);
|
||||
host = cleanhostname(thost);
|
||||
if (do_cmd(host, tuser, bp, &remin,
|
||||
if (do_cmd(thost, tuser, tport, bp, &remin,
|
||||
&remout) < 0)
|
||||
exit(1);
|
||||
if (response() < 0)
|
||||
|
@ -706,21 +707,41 @@ toremote(char *targ, int argc, char **argv)
|
|||
source(1, argv + i);
|
||||
}
|
||||
}
|
||||
free(arg);
|
||||
out:
|
||||
free(tuser);
|
||||
free(thost);
|
||||
free(targ);
|
||||
free(suser);
|
||||
free(host);
|
||||
free(src);
|
||||
}
|
||||
|
||||
void
|
||||
tolocal(int argc, char **argv)
|
||||
{
|
||||
char *bp, *host, *src, *suser;
|
||||
char *bp, *host = NULL, *src = NULL, *suser = NULL;
|
||||
arglist alist;
|
||||
int i;
|
||||
int i, r, sport = -1;
|
||||
|
||||
memset(&alist, '\0', sizeof(alist));
|
||||
alist.list = NULL;
|
||||
|
||||
for (i = 0; i < argc - 1; i++) {
|
||||
if (!(src = colon(argv[i]))) { /* Local to local. */
|
||||
free(suser);
|
||||
free(host);
|
||||
free(src);
|
||||
r = parse_uri("scp", argv[i], &suser, &host, &sport, &src);
|
||||
if (r == -1) {
|
||||
++errs;
|
||||
continue;
|
||||
}
|
||||
if (r != 0)
|
||||
parse_user_host_path(argv[i], &suser, &host, &src);
|
||||
if (suser != NULL && !okname(suser)) {
|
||||
++errs;
|
||||
continue;
|
||||
}
|
||||
if (!host) { /* Local to local. */
|
||||
freeargs(&alist);
|
||||
addargs(&alist, "%s", _PATH_CP);
|
||||
if (iamrecursive)
|
||||
|
@ -734,22 +755,10 @@ tolocal(int argc, char **argv)
|
|||
++errs;
|
||||
continue;
|
||||
}
|
||||
*src++ = 0;
|
||||
if (*src == 0)
|
||||
src = ".";
|
||||
if ((host = strrchr(argv[i], '@')) == NULL) {
|
||||
host = argv[i];
|
||||
suser = NULL;
|
||||
} else {
|
||||
*host++ = 0;
|
||||
suser = argv[i];
|
||||
if (*suser == '\0')
|
||||
suser = pwd->pw_name;
|
||||
}
|
||||
host = cleanhostname(host);
|
||||
/* Remote to local. */
|
||||
xasprintf(&bp, "%s -f %s%s",
|
||||
cmd, *src == '-' ? "-- " : "", src);
|
||||
if (do_cmd(host, suser, bp, &remin, &remout) < 0) {
|
||||
if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0) {
|
||||
free(bp);
|
||||
++errs;
|
||||
continue;
|
||||
|
@ -759,6 +768,9 @@ tolocal(int argc, char **argv)
|
|||
(void) close(remin);
|
||||
remin = remout = -1;
|
||||
}
|
||||
free(suser);
|
||||
free(host);
|
||||
free(src);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1275,8 +1287,7 @@ usage(void)
|
|||
{
|
||||
(void) fprintf(stderr,
|
||||
"usage: scp [-346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
|
||||
" [-l limit] [-o ssh_option] [-P port] [-S program]\n"
|
||||
" [[user@]host1:]file1 ... [[user@]host2:]file2\n");
|
||||
" [-l limit] [-o ssh_option] [-P port] [-S program] source ... target\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
|
77
sftp.1
77
sftp.1
|
@ -1,4 +1,4 @@
|
|||
.\" $OpenBSD: sftp.1,v 1.110 2017/05/03 21:49:18 naddy Exp $
|
||||
.\" $OpenBSD: sftp.1,v 1.111 2017/10/21 23:06:24 millert Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2001 Damien Miller. All rights reserved.
|
||||
.\"
|
||||
|
@ -22,7 +22,7 @@
|
|||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd $Mdocdate: May 3 2017 $
|
||||
.Dd $Mdocdate: October 21 2017 $
|
||||
.Dt SFTP 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -44,54 +44,52 @@
|
|||
.Op Fl R Ar num_requests
|
||||
.Op Fl S Ar program
|
||||
.Op Fl s Ar subsystem | sftp_server
|
||||
.Ar host
|
||||
.Ek
|
||||
.Nm sftp
|
||||
.Oo Ar user Ns @ Oc Ns
|
||||
.Ar host Ns Op : Ns Ar
|
||||
.Nm sftp
|
||||
.Oo
|
||||
.Ar user Ns @ Oc Ns
|
||||
.Ar host Ns Oo : Ns Ar dir Ns
|
||||
.Op Ar /
|
||||
.Oc
|
||||
.Nm sftp
|
||||
.Fl b Ar batchfile
|
||||
.Oo Ar user Ns @ Oc Ns Ar host
|
||||
.Ar destination
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is an interactive file transfer program, similar to
|
||||
is a file transfer program, similar to
|
||||
.Xr ftp 1 ,
|
||||
which performs all operations over an encrypted
|
||||
.Xr ssh 1
|
||||
transport.
|
||||
It may also use many features of ssh, such as public key authentication and
|
||||
compression.
|
||||
.Nm
|
||||
connects and logs into the specified
|
||||
.Ar host ,
|
||||
then enters an interactive command mode.
|
||||
.Pp
|
||||
The second usage format will retrieve files automatically if a non-interactive
|
||||
The
|
||||
.Ar destination
|
||||
may be specified either as
|
||||
.Oo Ar user Ns @ Oc Ns Ar host Ns Oo : Ns Ar path Oc
|
||||
or as an sftp URI in the form
|
||||
.No sftp:// Ns Oo Ar user Ns @ Oc Ns Ar host Ns
|
||||
.Oo : Ns Ar port Oc Ns Oo / Ns Ar path Oc .
|
||||
.Pp
|
||||
If the
|
||||
.Ar destination
|
||||
includes a
|
||||
.Ar path
|
||||
and it is not a directory,
|
||||
.Nm
|
||||
will retrieve files automatically if a non-interactive
|
||||
authentication method is used; otherwise it will do so after
|
||||
successful interactive authentication.
|
||||
.Pp
|
||||
The third usage format allows
|
||||
If no
|
||||
.Ar path
|
||||
is specified, or if the
|
||||
.Ar path
|
||||
is a directory,
|
||||
.Nm
|
||||
to start in a remote directory.
|
||||
will log in to the specified
|
||||
.Ar host
|
||||
and enter interactive command mode, changing to the remote directory
|
||||
if one was specified.
|
||||
An optional trailing slash can be used to force the
|
||||
.Ar path
|
||||
to be interpreted as a directory.
|
||||
.Pp
|
||||
The final usage format allows for automated sessions using the
|
||||
.Fl b
|
||||
option.
|
||||
In such cases, it is necessary to configure non-interactive authentication
|
||||
to obviate the need to enter a password at connection time (see
|
||||
.Xr sshd 8
|
||||
and
|
||||
.Xr ssh-keygen 1
|
||||
for details).
|
||||
.Pp
|
||||
Since some usage formats use colon characters to delimit host names from path
|
||||
names, IPv6 addresses must be enclosed in square brackets to avoid ambiguity.
|
||||
Since the destination formats use colon characters to delimit host
|
||||
names from path names or port numbers, IPv6 addresses must be
|
||||
enclosed in square brackets to avoid ambiguity.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
|
@ -121,7 +119,12 @@ Batch mode reads a series of commands from an input
|
|||
instead of
|
||||
.Em stdin .
|
||||
Since it lacks user interaction it should be used in conjunction with
|
||||
non-interactive authentication.
|
||||
non-interactive authentication to obviate the need to enter a password
|
||||
at connection time (see
|
||||
.Xr sshd 8
|
||||
and
|
||||
.Xr ssh-keygen 1
|
||||
for details).
|
||||
A
|
||||
.Ar batchfile
|
||||
of
|
||||
|
|
58
sftp.c
58
sftp.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: sftp.c,v 1.180 2017/06/10 06:33:34 djm Exp $ */
|
||||
/* $OpenBSD: sftp.c,v 1.181 2017/10/21 23:06:24 millert Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||
*
|
||||
|
@ -2301,19 +2301,16 @@ usage(void)
|
|||
"[-i identity_file] [-l limit]\n"
|
||||
" [-o ssh_option] [-P port] [-R num_requests] "
|
||||
"[-S program]\n"
|
||||
" [-s subsystem | sftp_server] host\n"
|
||||
" %s [user@]host[:file ...]\n"
|
||||
" %s [user@]host[:dir[/]]\n"
|
||||
" %s -b batchfile [user@]host\n",
|
||||
__progname, __progname, __progname, __progname);
|
||||
" [-s subsystem | sftp_server] destination\n",
|
||||
__progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int in, out, ch, err;
|
||||
char *host = NULL, *userhost, *cp, *file2 = NULL;
|
||||
int in, out, ch, err, tmp, port = -1;
|
||||
char *host = NULL, *user, *cp, *file2 = NULL;
|
||||
int debug_level = 0, sshver = 2;
|
||||
char *file1 = NULL, *sftp_server = NULL;
|
||||
char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
|
||||
|
@ -2368,7 +2365,9 @@ main(int argc, char **argv)
|
|||
addargs(&args, "-%c", ch);
|
||||
break;
|
||||
case 'P':
|
||||
addargs(&args, "-oPort %s", optarg);
|
||||
port = a2port(optarg);
|
||||
if (port <= 0)
|
||||
fatal("Bad port \"%s\"\n", optarg);
|
||||
break;
|
||||
case 'v':
|
||||
if (debug_level < 3) {
|
||||
|
@ -2451,33 +2450,38 @@ main(int argc, char **argv)
|
|||
if (sftp_direct == NULL) {
|
||||
if (optind == argc || argc > (optind + 2))
|
||||
usage();
|
||||
argv += optind;
|
||||
|
||||
userhost = xstrdup(argv[optind]);
|
||||
file2 = argv[optind+1];
|
||||
|
||||
if ((host = strrchr(userhost, '@')) == NULL)
|
||||
host = userhost;
|
||||
else {
|
||||
*host++ = '\0';
|
||||
if (!userhost[0]) {
|
||||
fprintf(stderr, "Missing username\n");
|
||||
usage();
|
||||
switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
|
||||
case -1:
|
||||
usage();
|
||||
break;
|
||||
case 0:
|
||||
if (tmp != -1)
|
||||
port = tmp;
|
||||
break;
|
||||
default:
|
||||
if (parse_user_host_path(*argv, &user, &host,
|
||||
&file1) == -1) {
|
||||
/* Treat as a plain hostname. */
|
||||
host = xstrdup(*argv);
|
||||
host = cleanhostname(host);
|
||||
}
|
||||
addargs(&args, "-l");
|
||||
addargs(&args, "%s", userhost);
|
||||
break;
|
||||
}
|
||||
file2 = *(argv + 1);
|
||||
|
||||
if ((cp = colon(host)) != NULL) {
|
||||
*cp++ = '\0';
|
||||
file1 = cp;
|
||||
}
|
||||
|
||||
host = cleanhostname(host);
|
||||
if (!*host) {
|
||||
fprintf(stderr, "Missing hostname\n");
|
||||
usage();
|
||||
}
|
||||
|
||||
if (port != -1)
|
||||
addargs(&args, "-oPort %d", port);
|
||||
if (user != NULL) {
|
||||
addargs(&args, "-l");
|
||||
addargs(&args, "%s", user);
|
||||
}
|
||||
addargs(&args, "-oProtocol %d", sshver);
|
||||
|
||||
/* no subsystem if the server-spec contains a '/' */
|
||||
|
|
36
ssh.1
36
ssh.1
|
@ -33,8 +33,8 @@
|
|||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $OpenBSD: ssh.1,v 1.385 2017/10/13 06:45:18 djm Exp $
|
||||
.Dd $Mdocdate: October 13 2017 $
|
||||
.\" $OpenBSD: ssh.1,v 1.386 2017/10/21 23:06:24 millert Exp $
|
||||
.Dd $Mdocdate: October 21 2017 $
|
||||
.Dt SSH 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -52,7 +52,7 @@
|
|||
.Op Fl F Ar configfile
|
||||
.Op Fl I Ar pkcs11
|
||||
.Op Fl i Ar identity_file
|
||||
.Op Fl J Oo Ar user Ns @ Oc Ns Ar host Ns Op : Ns Ar port
|
||||
.Op Fl J Ar destination
|
||||
.Op Fl L Ar address
|
||||
.Op Fl l Ar login_name
|
||||
.Op Fl m Ar mac_spec
|
||||
|
@ -64,7 +64,7 @@
|
|||
.Op Fl S Ar ctl_path
|
||||
.Op Fl W Ar host : Ns Ar port
|
||||
.Op Fl w Ar local_tun Ns Op : Ns Ar remote_tun
|
||||
.Oo Ar user Ns @ Oc Ns Ar hostname
|
||||
.Ar destination
|
||||
.Op Ar command
|
||||
.Ek
|
||||
.Sh DESCRIPTION
|
||||
|
@ -79,15 +79,23 @@ sockets can also be forwarded over the secure channel.
|
|||
.Pp
|
||||
.Nm
|
||||
connects and logs into the specified
|
||||
.Ar hostname
|
||||
(with optional
|
||||
.Ar destination
|
||||
which may be specified as either
|
||||
.Oo Ar user Ns @ Oc Ns Ar hostname
|
||||
where the
|
||||
.Ar user
|
||||
name).
|
||||
is optional, or an ssh URI of the form
|
||||
.No ssh:// Ns Oo Ar user Ns @ Oc Ns Ar hostname Ns Oo : Ns Ar port Oc
|
||||
where the
|
||||
.Ar user
|
||||
and
|
||||
.Ar port
|
||||
are optional.
|
||||
The user must prove
|
||||
his/her identity to the remote machine using one of several methods
|
||||
(see below).
|
||||
.Pp
|
||||
If
|
||||
If a
|
||||
.Ar command
|
||||
is specified,
|
||||
it is executed on the remote host instead of a login shell.
|
||||
|
@ -287,17 +295,11 @@ by appending
|
|||
.Pa -cert.pub
|
||||
to identity filenames.
|
||||
.Pp
|
||||
.It Fl J Xo
|
||||
.Sm off
|
||||
.Op Ar user No @
|
||||
.Ar host
|
||||
.Op : Ar port
|
||||
.Sm on
|
||||
.Xc
|
||||
.It Fl J Ar destination
|
||||
Connect to the target host by first making a
|
||||
.Nm
|
||||
connection to the jump
|
||||
.Ar host
|
||||
connection to the jump host described by
|
||||
.Ar destination
|
||||
and then establishing a TCP forwarding to the ultimate destination from
|
||||
there.
|
||||
Multiple jump hops may be specified separated by comma characters.
|
||||
|
|
56
ssh.c
56
ssh.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh.c,v 1.464 2017/09/21 19:16:53 markus Exp $ */
|
||||
/* $OpenBSD: ssh.c,v 1.465 2017/10/21 23:06:24 millert Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -203,7 +203,7 @@ usage(void)
|
|||
" [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n"
|
||||
" [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n"
|
||||
" [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n"
|
||||
" [user@]hostname [command]\n"
|
||||
" destination [command]\n"
|
||||
);
|
||||
exit(255);
|
||||
}
|
||||
|
@ -846,14 +846,18 @@ main(int ac, char **av)
|
|||
options.control_master = SSHCTL_MASTER_YES;
|
||||
break;
|
||||
case 'p':
|
||||
options.port = a2port(optarg);
|
||||
if (options.port <= 0) {
|
||||
fprintf(stderr, "Bad port '%s'\n", optarg);
|
||||
exit(255);
|
||||
if (options.port == -1) {
|
||||
options.port = a2port(optarg);
|
||||
if (options.port <= 0) {
|
||||
fprintf(stderr, "Bad port '%s'\n",
|
||||
optarg);
|
||||
exit(255);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
options.user = optarg;
|
||||
if (options.user == NULL)
|
||||
options.user = optarg;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
|
@ -933,16 +937,38 @@ main(int ac, char **av)
|
|||
av += optind;
|
||||
|
||||
if (ac > 0 && !host) {
|
||||
if (strrchr(*av, '@')) {
|
||||
int tport;
|
||||
char *tuser;
|
||||
switch (parse_ssh_uri(*av, &tuser, &host, &tport)) {
|
||||
case -1:
|
||||
usage();
|
||||
break;
|
||||
case 0:
|
||||
if (options.user == NULL) {
|
||||
options.user = tuser;
|
||||
tuser = NULL;
|
||||
}
|
||||
free(tuser);
|
||||
if (options.port == -1 && tport != -1)
|
||||
options.port = tport;
|
||||
break;
|
||||
default:
|
||||
p = xstrdup(*av);
|
||||
cp = strrchr(p, '@');
|
||||
if (cp == NULL || cp == p)
|
||||
usage();
|
||||
options.user = p;
|
||||
*cp = '\0';
|
||||
host = xstrdup(++cp);
|
||||
} else
|
||||
host = xstrdup(*av);
|
||||
if (cp != NULL) {
|
||||
if (cp == p)
|
||||
usage();
|
||||
if (options.user == NULL) {
|
||||
options.user = p;
|
||||
p = NULL;
|
||||
}
|
||||
*cp++ = '\0';
|
||||
host = xstrdup(cp);
|
||||
free(p);
|
||||
} else
|
||||
host = p;
|
||||
break;
|
||||
}
|
||||
if (ac > 1 && !opt_terminated) {
|
||||
optind = optreset = 1;
|
||||
goto again;
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $OpenBSD: ssh_config.5,v 1.259 2017/10/18 05:36:59 jmc Exp $
|
||||
.Dd $Mdocdate: October 18 2017 $
|
||||
.\" $OpenBSD: ssh_config.5,v 1.260 2017/10/21 23:06:24 millert Exp $
|
||||
.Dd $Mdocdate: October 21 2017 $
|
||||
.Dt SSH_CONFIG 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -1198,13 +1198,14 @@ For example, the following directive would connect via an HTTP proxy at
|
|||
ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p
|
||||
.Ed
|
||||
.It Cm ProxyJump
|
||||
Specifies one or more jump proxies as
|
||||
Specifies one or more jump proxies as either
|
||||
.Xo
|
||||
.Sm off
|
||||
.Op Ar user No @
|
||||
.Ar host
|
||||
.Op : Ns Ar port
|
||||
.Sm on
|
||||
or an ssh URI
|
||||
.Xc .
|
||||
Multiple proxies may be separated by comma characters and will be visited
|
||||
sequentially.
|
||||
|
|
Loading…
Reference in New Issue