summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormillert@openbsd.org <millert@openbsd.org>2017-10-21 23:06:24 +0000
committerDamien Miller <djm@mindrot.org>2017-10-23 16:10:08 +1100
commit887669ef032d63cf07f53cada216fa8a0c9a7d72 (patch)
tree089b20255da21a489d7bc796a8ee86bd0b8f028f
parentd27bff293cfeb2252f4c7a58babe5ad3262c6c98 (diff)
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
-rw-r--r--misc.c297
-rw-r--r--misc.h5
-rw-r--r--readconf.c54
-rw-r--r--readconf.h3
-rw-r--r--scp.141
-rw-r--r--scp.c199
-rw-r--r--sftp.177
-rw-r--r--sftp.c58
-rw-r--r--ssh.136
-rw-r--r--ssh.c56
-rw-r--r--ssh_config.57
11 files changed, 582 insertions, 251 deletions
diff --git a/misc.c b/misc.c
index 05950a471..d4d0e44a5 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.c,v 1.113 2017/08/18 05:48:04 djm Exp $ */ 1/* $OpenBSD: misc.c,v 1.114 2017/10/21 23:06:24 millert Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved. 4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved.
@@ -395,11 +395,12 @@ put_host_port(const char *host, u_short port)
395 * Search for next delimiter between hostnames/addresses and ports. 395 * Search for next delimiter between hostnames/addresses and ports.
396 * Argument may be modified (for termination). 396 * Argument may be modified (for termination).
397 * Returns *cp if parsing succeeds. 397 * Returns *cp if parsing succeeds.
398 * *cp is set to the start of the next delimiter, if one was found. 398 * *cp is set to the start of the next field, if one was found.
399 * The delimiter char, if present, is stored in delim.
399 * If this is the last field, *cp is set to NULL. 400 * If this is the last field, *cp is set to NULL.
400 */ 401 */
401char * 402static char *
402hpdelim(char **cp) 403hpdelim2(char **cp, char *delim)
403{ 404{
404 char *s, *old; 405 char *s, *old;
405 406
@@ -422,6 +423,8 @@ hpdelim(char **cp)
422 423
423 case ':': 424 case ':':
424 case '/': 425 case '/':
426 if (delim != NULL)
427 *delim = *s;
425 *s = '\0'; /* terminate */ 428 *s = '\0'; /* terminate */
426 *cp = s + 1; 429 *cp = s + 1;
427 break; 430 break;
@@ -434,6 +437,12 @@ hpdelim(char **cp)
434} 437}
435 438
436char * 439char *
440hpdelim(char **cp)
441{
442 return hpdelim2(cp, NULL);
443}
444
445char *
437cleanhostname(char *host) 446cleanhostname(char *host)
438{ 447{
439 if (*host == '[' && host[strlen(host) - 1] == ']') { 448 if (*host == '[' && host[strlen(host) - 1] == ']') {
@@ -467,6 +476,75 @@ colon(char *cp)
467} 476}
468 477
469/* 478/*
479 * Parse a [user@]host:[path] string.
480 * Caller must free returned user, host and path.
481 * Any of the pointer return arguments may be NULL (useful for syntax checking).
482 * If user was not specified then *userp will be set to NULL.
483 * If host was not specified then *hostp will be set to NULL.
484 * If path was not specified then *pathp will be set to ".".
485 * Returns 0 on success, -1 on failure.
486 */
487int
488parse_user_host_path(const char *s, char **userp, char **hostp, char **pathp)
489{
490 char *user = NULL, *host = NULL, *path = NULL;
491 char *sdup, *tmp;
492 int ret = -1;
493
494 if (userp != NULL)
495 *userp = NULL;
496 if (hostp != NULL)
497 *hostp = NULL;
498 if (pathp != NULL)
499 *pathp = NULL;
500
501 sdup = tmp = xstrdup(s);
502
503 /* Check for remote syntax: [user@]host:[path] */
504 if ((tmp = colon(sdup)) == NULL)
505 goto out;
506
507 /* Extract optional path */
508 *tmp++ = '\0';
509 if (*tmp == '\0')
510 tmp = ".";
511 path = xstrdup(tmp);
512
513 /* Extract optional user and mandatory host */
514 tmp = strrchr(sdup, '@');
515 if (tmp != NULL) {
516 *tmp++ = '\0';
517 host = xstrdup(cleanhostname(tmp));
518 if (*sdup != '\0')
519 user = xstrdup(sdup);
520 } else {
521 host = xstrdup(cleanhostname(sdup));
522 user = NULL;
523 }
524
525 /* Success */
526 if (userp != NULL) {
527 *userp = user;
528 user = NULL;
529 }
530 if (hostp != NULL) {
531 *hostp = host;
532 host = NULL;
533 }
534 if (pathp != NULL) {
535 *pathp = path;
536 path = NULL;
537 }
538 ret = 0;
539out:
540 free(sdup);
541 free(user);
542 free(host);
543 free(path);
544 return ret;
545}
546
547/*
470 * Parse a [user@]host[:port] string. 548 * Parse a [user@]host[:port] string.
471 * Caller must free returned user and host. 549 * Caller must free returned user and host.
472 * Any of the pointer return arguments may be NULL (useful for syntax checking). 550 * Any of the pointer return arguments may be NULL (useful for syntax checking).
@@ -491,7 +569,7 @@ parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
491 if ((sdup = tmp = strdup(s)) == NULL) 569 if ((sdup = tmp = strdup(s)) == NULL)
492 return -1; 570 return -1;
493 /* Extract optional username */ 571 /* Extract optional username */
494 if ((cp = strchr(tmp, '@')) != NULL) { 572 if ((cp = strrchr(tmp, '@')) != NULL) {
495 *cp = '\0'; 573 *cp = '\0';
496 if (*tmp == '\0') 574 if (*tmp == '\0')
497 goto out; 575 goto out;
@@ -527,6 +605,168 @@ parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
527 return ret; 605 return ret;
528} 606}
529 607
608/*
609 * Converts a two-byte hex string to decimal.
610 * Returns the decimal value or -1 for invalid input.
611 */
612static int
613hexchar(const char *s)
614{
615 unsigned char result[2];
616 int i;
617
618 for (i = 0; i < 2; i++) {
619 if (s[i] >= '0' && s[i] <= '9')
620 result[i] = (unsigned char)(s[i] - '0');
621 else if (s[i] >= 'a' && s[i] <= 'f')
622 result[i] = (unsigned char)(s[i] - 'a') + 10;
623 else if (s[i] >= 'A' && s[i] <= 'F')
624 result[i] = (unsigned char)(s[i] - 'A') + 10;
625 else
626 return -1;
627 }
628 return (result[0] << 4) | result[1];
629}
630
631/*
632 * Decode an url-encoded string.
633 * Returns a newly allocated string on success or NULL on failure.
634 */
635static char *
636urldecode(const char *src)
637{
638 char *ret, *dst;
639 int ch;
640
641 ret = xmalloc(strlen(src) + 1);
642 for (dst = ret; *src != '\0'; src++) {
643 switch (*src) {
644 case '+':
645 *dst++ = ' ';
646 break;
647 case '%':
648 if (!isxdigit((unsigned char)src[1]) ||
649 !isxdigit((unsigned char)src[2]) ||
650 (ch = hexchar(src + 1)) == -1) {
651 free(ret);
652 return NULL;
653 }
654 *dst++ = ch;
655 src += 2;
656 break;
657 default:
658 *dst++ = *src;
659 break;
660 }
661 }
662 *dst = '\0';
663
664 return ret;
665}
666
667/*
668 * Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI.
669 * See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04
670 * Either user or path may be url-encoded (but not host or port).
671 * Caller must free returned user, host and path.
672 * Any of the pointer return arguments may be NULL (useful for syntax checking)
673 * but the scheme must always be specified.
674 * If user was not specified then *userp will be set to NULL.
675 * If port was not specified then *portp will be -1.
676 * If path was not specified then *pathp will be set to NULL.
677 * Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri.
678 */
679int
680parse_uri(const char *scheme, const char *uri, char **userp, char **hostp,
681 int *portp, char **pathp)
682{
683 char *uridup, *cp, *tmp, ch;
684 char *user = NULL, *host = NULL, *path = NULL;
685 int port = -1, ret = -1;
686 size_t len;
687
688 len = strlen(scheme);
689 if (strncmp(uri, scheme, len) != 0 || strncmp(uri + len, "://", 3) != 0)
690 return 1;
691 uri += len + 3;
692
693 if (userp != NULL)
694 *userp = NULL;
695 if (hostp != NULL)
696 *hostp = NULL;
697 if (portp != NULL)
698 *portp = -1;
699 if (pathp != NULL)
700 *pathp = NULL;
701
702 uridup = tmp = xstrdup(uri);
703
704 /* Extract optional ssh-info (username + connection params) */
705 if ((cp = strchr(tmp, '@')) != NULL) {
706 char *delim;
707
708 *cp = '\0';
709 /* Extract username and connection params */
710 if ((delim = strchr(tmp, ';')) != NULL) {
711 /* Just ignore connection params for now */
712 *delim = '\0';
713 }
714 if (*tmp == '\0') {
715 /* Empty username */
716 goto out;
717 }
718 if ((user = urldecode(tmp)) == NULL)
719 goto out;
720 tmp = cp + 1;
721 }
722
723 /* Extract mandatory hostname */
724 if ((cp = hpdelim2(&tmp, &ch)) == NULL || *cp == '\0')
725 goto out;
726 host = xstrdup(cleanhostname(cp));
727 if (!valid_domain(host, 0, NULL))
728 goto out;
729
730 if (tmp != NULL && *tmp != '\0') {
731 if (ch == ':') {
732 /* Convert and verify port. */
733 if ((cp = strchr(tmp, '/')) != NULL)
734 *cp = '\0';
735 if ((port = a2port(tmp)) <= 0)
736 goto out;
737 tmp = cp ? cp + 1 : NULL;
738 }
739 if (tmp != NULL && *tmp != '\0') {
740 /* Extract optional path */
741 if ((path = urldecode(tmp)) == NULL)
742 goto out;
743 }
744 }
745
746 /* Success */
747 if (userp != NULL) {
748 *userp = user;
749 user = NULL;
750 }
751 if (hostp != NULL) {
752 *hostp = host;
753 host = NULL;
754 }
755 if (portp != NULL)
756 *portp = port;
757 if (pathp != NULL) {
758 *pathp = path;
759 path = NULL;
760 }
761 ret = 0;
762 out:
763 free(uridup);
764 free(user);
765 free(host);
766 free(path);
767 return ret;
768}
769
530/* function to assist building execv() arguments */ 770/* function to assist building execv() arguments */
531void 771void
532addargs(arglist *args, char *fmt, ...) 772addargs(arglist *args, char *fmt, ...)
@@ -1743,3 +1983,50 @@ child_set_env(char ***envp, u_int *envsizep, const char *name,
1743 snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); 1983 snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
1744} 1984}
1745 1985
1986/*
1987 * Check and optionally lowercase a domain name, also removes trailing '.'
1988 * Returns 1 on success and 0 on failure, storing an error message in errstr.
1989 */
1990int
1991valid_domain(char *name, int makelower, const char **errstr)
1992{
1993 size_t i, l = strlen(name);
1994 u_char c, last = '\0';
1995 static char errbuf[256];
1996
1997 if (l == 0) {
1998 strlcpy(errbuf, "empty domain name", sizeof(errbuf));
1999 goto bad;
2000 }
2001 if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) {
2002 snprintf(errbuf, sizeof(errbuf), "domain name \"%.100s\" "
2003 "starts with invalid character", name);
2004 goto bad;
2005 }
2006 for (i = 0; i < l; i++) {
2007 c = tolower((u_char)name[i]);
2008 if (makelower)
2009 name[i] = (char)c;
2010 if (last == '.' && c == '.') {
2011 snprintf(errbuf, sizeof(errbuf), "domain name "
2012 "\"%.100s\" contains consecutive separators", name);
2013 goto bad;
2014 }
2015 if (c != '.' && c != '-' && !isalnum(c) &&
2016 c != '_') /* technically invalid, but common */ {
2017 snprintf(errbuf, sizeof(errbuf), "domain name "
2018 "\"%.100s\" contains invalid characters", name);
2019 goto bad;
2020 }
2021 last = c;
2022 }
2023 if (name[l - 1] == '.')
2024 name[l - 1] = '\0';
2025 if (errstr != NULL)
2026 *errstr = NULL;
2027 return 1;
2028bad:
2029 if (errstr != NULL)
2030 *errstr = errbuf;
2031 return 0;
2032}
diff --git a/misc.h b/misc.h
index 153d11375..b6f502b3e 100644
--- a/misc.h
+++ b/misc.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.h,v 1.63 2017/08/18 05:48:04 djm Exp $ */ 1/* $OpenBSD: misc.h,v 1.64 2017/10/21 23:06:24 millert Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -54,7 +54,9 @@ char *put_host_port(const char *, u_short);
54char *hpdelim(char **); 54char *hpdelim(char **);
55char *cleanhostname(char *); 55char *cleanhostname(char *);
56char *colon(char *); 56char *colon(char *);
57int parse_user_host_path(const char *, char **, char **, char **);
57int parse_user_host_port(const char *, char **, char **, int *); 58int parse_user_host_port(const char *, char **, char **, int *);
59int parse_uri(const char *, const char *, char **, char **, int *, char **);
58long convtime(const char *); 60long convtime(const char *);
59char *tilde_expand_filename(const char *, uid_t); 61char *tilde_expand_filename(const char *, uid_t);
60char *percent_expand(const char *, ...) __attribute__((__sentinel__)); 62char *percent_expand(const char *, ...) __attribute__((__sentinel__));
@@ -66,6 +68,7 @@ time_t monotime(void);
66double monotime_double(void); 68double monotime_double(void);
67void lowercase(char *s); 69void lowercase(char *s);
68int unix_listener(const char *, int, int); 70int unix_listener(const char *, int, int);
71int valid_domain(char *, int, const char **);
69 72
70void sock_set_v6only(int); 73void sock_set_v6only(int);
71 74
diff --git a/readconf.c b/readconf.c
index f63894f9c..63baa7d78 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.c,v 1.279 2017/09/21 19:16:53 markus Exp $ */ 1/* $OpenBSD: readconf.c,v 1.280 2017/10/21 23:06:24 millert Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * 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,
683 return result; 683 return result;
684} 684}
685 685
686/* Check and prepare a domain name: removes trailing '.' and lowercases */
687static void
688valid_domain(char *name, const char *filename, int linenum)
689{
690 size_t i, l = strlen(name);
691 u_char c, last = '\0';
692
693 if (l == 0)
694 fatal("%s line %d: empty hostname suffix", filename, linenum);
695 if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0]))
696 fatal("%s line %d: hostname suffix \"%.100s\" "
697 "starts with invalid character", filename, linenum, name);
698 for (i = 0; i < l; i++) {
699 c = tolower((u_char)name[i]);
700 name[i] = (char)c;
701 if (last == '.' && c == '.')
702 fatal("%s line %d: hostname suffix \"%.100s\" contains "
703 "consecutive separators", filename, linenum, name);
704 if (c != '.' && c != '-' && !isalnum(c) &&
705 c != '_') /* technically invalid, but common */
706 fatal("%s line %d: hostname suffix \"%.100s\" contains "
707 "invalid characters", filename, linenum, name);
708 last = c;
709 }
710 if (name[l - 1] == '.')
711 name[l - 1] = '\0';
712}
713
714/* 686/*
715 * Returns the number of the token pointed to by cp or oBadOption. 687 * Returns the number of the token pointed to by cp or oBadOption.
716 */ 688 */
@@ -1562,7 +1534,11 @@ parse_keytypes:
1562 case oCanonicalDomains: 1534 case oCanonicalDomains:
1563 value = options->num_canonical_domains != 0; 1535 value = options->num_canonical_domains != 0;
1564 while ((arg = strdelim(&s)) != NULL && *arg != '\0') { 1536 while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
1565 valid_domain(arg, filename, linenum); 1537 const char *errstr;
1538 if (!valid_domain(arg, 1, &errstr)) {
1539 fatal("%s line %d: %s", filename, linenum,
1540 errstr);
1541 }
1566 if (!*activep || value) 1542 if (!*activep || value)
1567 continue; 1543 continue;
1568 if (options->num_canonical_domains >= MAX_CANON_DOMAINS) 1544 if (options->num_canonical_domains >= MAX_CANON_DOMAINS)
@@ -2294,11 +2270,13 @@ parse_jump(const char *s, Options *o, int active)
2294 2270
2295 if (first) { 2271 if (first) {
2296 /* First argument and configuration is active */ 2272 /* First argument and configuration is active */
2297 if (parse_user_host_port(cp, &user, &host, &port) != 0) 2273 if (parse_ssh_uri(cp, &user, &host, &port) == -1 ||
2274 parse_user_host_port(cp, &user, &host, &port) != 0)
2298 goto out; 2275 goto out;
2299 } else { 2276 } else {
2300 /* Subsequent argument or inactive configuration */ 2277 /* Subsequent argument or inactive configuration */
2301 if (parse_user_host_port(cp, NULL, NULL, NULL) != 0) 2278 if (parse_ssh_uri(cp, NULL, NULL, NULL) == -1 ||
2279 parse_user_host_port(cp, NULL, NULL, NULL) != 0)
2302 goto out; 2280 goto out;
2303 } 2281 }
2304 first = 0; /* only check syntax for subsequent hosts */ 2282 first = 0; /* only check syntax for subsequent hosts */
@@ -2323,6 +2301,18 @@ parse_jump(const char *s, Options *o, int active)
2323 return ret; 2301 return ret;
2324} 2302}
2325 2303
2304int
2305parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp)
2306{
2307 char *path;
2308 int r;
2309
2310 r = parse_uri("ssh", uri, userp, hostp, portp, &path);
2311 if (r == 0 && path != NULL)
2312 r = -1; /* path not allowed */
2313 return r;
2314}
2315
2326/* XXX the following is a near-vebatim copy from servconf.c; refactor */ 2316/* XXX the following is a near-vebatim copy from servconf.c; refactor */
2327static const char * 2317static const char *
2328fmt_multistate_int(int val, const struct multistate *m) 2318fmt_multistate_int(int val, const struct multistate *m)
diff --git a/readconf.h b/readconf.h
index 22fe5c187..34aad83cf 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.h,v 1.123 2017/09/03 23:33:13 djm Exp $ */ 1/* $OpenBSD: readconf.h,v 1.124 2017/10/21 23:06:24 millert Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -204,6 +204,7 @@ int read_config_file(const char *, struct passwd *, const char *,
204 const char *, Options *, int); 204 const char *, Options *, int);
205int parse_forward(struct Forward *, const char *, int, int); 205int parse_forward(struct Forward *, const char *, int, int);
206int parse_jump(const char *, Options *, int); 206int parse_jump(const char *, Options *, int);
207int parse_ssh_uri(const char *, char **, char **, int *);
207int default_ssh_port(void); 208int default_ssh_port(void);
208int option_clear_or_none(const char *); 209int option_clear_or_none(const char *);
209void dump_client_config(Options *o, const char *host); 210void dump_client_config(Options *o, const char *host);
diff --git a/scp.1 b/scp.1
index 76ce33361..92b63f6f4 100644
--- a/scp.1
+++ b/scp.1
@@ -8,9 +8,9 @@
8.\" 8.\"
9.\" Created: Sun May 7 00:14:37 1995 ylo 9.\" Created: Sun May 7 00:14:37 1995 ylo
10.\" 10.\"
11.\" $OpenBSD: scp.1,v 1.74 2017/05/03 21:49:18 naddy Exp $ 11.\" $OpenBSD: scp.1,v 1.75 2017/10/21 23:06:24 millert Exp $
12.\" 12.\"
13.Dd $Mdocdate: May 3 2017 $ 13.Dd $Mdocdate: October 21 2017 $
14.Dt SCP 1 14.Dt SCP 1
15.Os 15.Os
16.Sh NAME 16.Sh NAME
@@ -27,20 +27,8 @@
27.Op Fl o Ar ssh_option 27.Op Fl o Ar ssh_option
28.Op Fl P Ar port 28.Op Fl P Ar port
29.Op Fl S Ar program 29.Op Fl S Ar program
30.Sm off 30.Ar source ...
31.Oo 31.Ar target
32.Op Ar user No @
33.Ar host1 :
34.Oc Ar file1
35.Sm on
36.Ar ...
37.Sm off
38.Oo
39.Op Ar user No @
40.Ar host2 :
41.Oc Ar file2
42.Sm on
43.Ek
44.Sh DESCRIPTION 32.Sh DESCRIPTION
45.Nm 33.Nm
46copies files between hosts on a network. 34copies files between hosts on a network.
@@ -53,15 +41,30 @@ same security as
53will ask for passwords or passphrases if they are needed for 41will ask for passwords or passphrases if they are needed for
54authentication. 42authentication.
55.Pp 43.Pp
56File names may contain a user and host specification to indicate 44The
57that the file is to be copied to/from that host. 45.Ar target
46and
47.Ar destination
48may be specified as a local pathname, a remote host with optional path
49in the form
50.Oo Ar user Ns @ Oc Ns Ar host Ns : Ns Oo Ar path Oc ,
51or an scp URI in the form
52.No scp:// Ns Oo Ar user Ns @ Oc Ns Ar host Ns
53.Oo : Ns Ar port Oc Ns Oo / Ns Ar path Oc .
58Local file names can be made explicit using absolute or relative pathnames 54Local file names can be made explicit using absolute or relative pathnames
59to avoid 55to avoid
60.Nm 56.Nm
61treating file names containing 57treating file names containing
62.Sq :\& 58.Sq :\&
63as host specifiers. 59as host specifiers.
64Copies between two remote hosts are also permitted. 60.Pp
61When copying between two remote hosts, if the URI format is used, a
62.Ar port
63may only be specified on the
64.Ar target
65if the
66.Fl 3
67option is used.
65.Pp 68.Pp
66The options are as follows: 69The options are as follows:
67.Bl -tag -width Ds 70.Bl -tag -width Ds
diff --git a/scp.c b/scp.c
index a533eb097..2103a54ea 100644
--- a/scp.c
+++ b/scp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: scp.c,v 1.192 2017/05/31 09:15:42 deraadt Exp $ */ 1/* $OpenBSD: scp.c,v 1.193 2017/10/21 23:06:24 millert Exp $ */
2/* 2/*
3 * scp - secure remote copy. This is basically patched BSD rcp which 3 * scp - secure remote copy. This is basically patched BSD rcp which
4 * uses ssh to do the data transfer (instead of using rcmd). 4 * uses ssh to do the data transfer (instead of using rcmd).
@@ -112,6 +112,7 @@
112#endif 112#endif
113 113
114#include "xmalloc.h" 114#include "xmalloc.h"
115#include "ssh.h"
115#include "atomicio.h" 116#include "atomicio.h"
116#include "pathnames.h" 117#include "pathnames.h"
117#include "log.h" 118#include "log.h"
@@ -123,8 +124,8 @@ extern char *__progname;
123 124
124#define COPY_BUFLEN 16384 125#define COPY_BUFLEN 16384
125 126
126int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 127int do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout);
127int do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout); 128int do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout);
128 129
129/* Struct for addargs */ 130/* Struct for addargs */
130arglist args; 131arglist args;
@@ -149,6 +150,9 @@ int showprogress = 1;
149 */ 150 */
150int throughlocal = 0; 151int throughlocal = 0;
151 152
153/* Non-standard port to use for the ssh connection or -1. */
154int sshport = -1;
155
152/* This is the program to execute for the secured connection. ("ssh" or -S) */ 156/* This is the program to execute for the secured connection. ("ssh" or -S) */
153char *ssh_program = _PATH_SSH_PROGRAM; 157char *ssh_program = _PATH_SSH_PROGRAM;
154 158
@@ -231,7 +235,7 @@ do_local_cmd(arglist *a)
231 */ 235 */
232 236
233int 237int
234do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 238do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout)
235{ 239{
236 int pin[2], pout[2], reserved[2]; 240 int pin[2], pout[2], reserved[2];
237 241
@@ -241,6 +245,9 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
241 ssh_program, host, 245 ssh_program, host,
242 remuser ? remuser : "(unspecified)", cmd); 246 remuser ? remuser : "(unspecified)", cmd);
243 247
248 if (port == -1)
249 port = sshport;
250
244 /* 251 /*
245 * Reserve two descriptors so that the real pipes won't get 252 * Reserve two descriptors so that the real pipes won't get
246 * descriptors 0 and 1 because that will screw up dup2 below. 253 * 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)
274 close(pout[1]); 281 close(pout[1]);
275 282
276 replacearg(&args, 0, "%s", ssh_program); 283 replacearg(&args, 0, "%s", ssh_program);
284 if (port != -1) {
285 addargs(&args, "-p");
286 addargs(&args, "%d", port);
287 }
277 if (remuser != NULL) { 288 if (remuser != NULL) {
278 addargs(&args, "-l"); 289 addargs(&args, "-l");
279 addargs(&args, "%s", remuser); 290 addargs(&args, "%s", remuser);
@@ -305,7 +316,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
305 * This way the input and output of two commands can be connected. 316 * This way the input and output of two commands can be connected.
306 */ 317 */
307int 318int
308do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) 319do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout)
309{ 320{
310 pid_t pid; 321 pid_t pid;
311 int status; 322 int status;
@@ -316,6 +327,9 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout)
316 ssh_program, host, 327 ssh_program, host,
317 remuser ? remuser : "(unspecified)", cmd); 328 remuser ? remuser : "(unspecified)", cmd);
318 329
330 if (port == -1)
331 port = sshport;
332
319 /* Fork a child to execute the command on the remote host using ssh. */ 333 /* Fork a child to execute the command on the remote host using ssh. */
320 pid = fork(); 334 pid = fork();
321 if (pid == 0) { 335 if (pid == 0) {
@@ -323,6 +337,10 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout)
323 dup2(fdout, 1); 337 dup2(fdout, 1);
324 338
325 replacearg(&args, 0, "%s", ssh_program); 339 replacearg(&args, 0, "%s", ssh_program);
340 if (port != -1) {
341 addargs(&args, "-p");
342 addargs(&args, "%d", port);
343 }
326 if (remuser != NULL) { 344 if (remuser != NULL) {
327 addargs(&args, "-l"); 345 addargs(&args, "-l");
328 addargs(&args, "%s", remuser); 346 addargs(&args, "%s", remuser);
@@ -367,14 +385,14 @@ void rsource(char *, struct stat *);
367void sink(int, char *[]); 385void sink(int, char *[]);
368void source(int, char *[]); 386void source(int, char *[]);
369void tolocal(int, char *[]); 387void tolocal(int, char *[]);
370void toremote(char *, int, char *[]); 388void toremote(int, char *[]);
371void usage(void); 389void usage(void);
372 390
373int 391int
374main(int argc, char **argv) 392main(int argc, char **argv)
375{ 393{
376 int ch, fflag, tflag, status, n; 394 int ch, fflag, tflag, status, n;
377 char *targ, **newargv; 395 char **newargv;
378 const char *errstr; 396 const char *errstr;
379 extern char *optarg; 397 extern char *optarg;
380 extern int optind; 398 extern int optind;
@@ -430,10 +448,9 @@ main(int argc, char **argv)
430 addargs(&args, "%s", optarg); 448 addargs(&args, "%s", optarg);
431 break; 449 break;
432 case 'P': 450 case 'P':
433 addargs(&remote_remote_args, "-p"); 451 sshport = a2port(optarg);
434 addargs(&remote_remote_args, "%s", optarg); 452 if (sshport <= 0)
435 addargs(&args, "-p"); 453 fatal("bad port \"%s\"\n", optarg);
436 addargs(&args, "%s", optarg);
437 break; 454 break;
438 case 'B': 455 case 'B':
439 addargs(&remote_remote_args, "-oBatchmode=yes"); 456 addargs(&remote_remote_args, "-oBatchmode=yes");
@@ -533,8 +550,8 @@ main(int argc, char **argv)
533 550
534 (void) signal(SIGPIPE, lostconn); 551 (void) signal(SIGPIPE, lostconn);
535 552
536 if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 553 if (colon(argv[argc - 1])) /* Dest is remote host. */
537 toremote(targ, argc, argv); 554 toremote(argc, argv);
538 else { 555 else {
539 if (targetshouldbedirectory) 556 if (targetshouldbedirectory)
540 verifydir(argv[argc - 1]); 557 verifydir(argv[argc - 1]);
@@ -590,71 +607,65 @@ do_times(int fd, int verb, const struct stat *sb)
590} 607}
591 608
592void 609void
593toremote(char *targ, int argc, char **argv) 610toremote(int argc, char **argv)
594{ 611{
595 char *bp, *host, *src, *suser, *thost, *tuser, *arg; 612 char *suser = NULL, *host = NULL, *src = NULL;
613 char *bp, *tuser, *thost, *targ;
614 int sport = -1, tport = -1;
596 arglist alist; 615 arglist alist;
597 int i; 616 int i, r;
598 u_int j; 617 u_int j;
599 618
600 memset(&alist, '\0', sizeof(alist)); 619 memset(&alist, '\0', sizeof(alist));
601 alist.list = NULL; 620 alist.list = NULL;
602 621
603 *targ++ = 0; 622 /* Parse target */
604 if (*targ == 0) 623 r = parse_uri("scp", argv[argc - 1], &tuser, &thost, &tport, &targ);
605 targ = "."; 624 if (r == -1)
606 625 goto out; /* invalid URI */
607 arg = xstrdup(argv[argc - 1]); 626 if (r != 0) {
608 if ((thost = strrchr(arg, '@'))) { 627 if (parse_user_host_path(argv[argc - 1], &tuser, &thost,
609 /* user@host */ 628 &targ) == -1)
610 *thost++ = 0; 629 goto out;
611 tuser = arg;
612 if (*tuser == '\0')
613 tuser = NULL;
614 } else {
615 thost = arg;
616 tuser = NULL;
617 }
618
619 if (tuser != NULL && !okname(tuser)) {
620 free(arg);
621 return;
622 } 630 }
631 if (tuser != NULL && !okname(tuser))
632 goto out;
623 633
634 /* Parse source files */
624 for (i = 0; i < argc - 1; i++) { 635 for (i = 0; i < argc - 1; i++) {
625 src = colon(argv[i]); 636 free(suser);
626 if (src && throughlocal) { /* extended remote to remote */ 637 free(host);
627 *src++ = 0; 638 free(src);
628 if (*src == 0) 639 r = parse_uri("scp", argv[i], &suser, &host, &sport, &src);
629 src = "."; 640 if (r == -1)
630 host = strrchr(argv[i], '@'); 641 continue; /* invalid URI */
631 if (host) { 642 if (r != 0)
632 *host++ = 0; 643 parse_user_host_path(argv[i], &suser, &host, &src);
633 host = cleanhostname(host); 644 if (suser != NULL && !okname(suser)) {
634 suser = argv[i]; 645 ++errs;
635 if (*suser == '\0') 646 continue;
636 suser = pwd->pw_name; 647 }
637 else if (!okname(suser)) 648 if (host && throughlocal) { /* extended remote to remote */
638 continue;
639 } else {
640 host = cleanhostname(argv[i]);
641 suser = NULL;
642 }
643 xasprintf(&bp, "%s -f %s%s", cmd, 649 xasprintf(&bp, "%s -f %s%s", cmd,
644 *src == '-' ? "-- " : "", src); 650 *src == '-' ? "-- " : "", src);
645 if (do_cmd(host, suser, bp, &remin, &remout) < 0) 651 if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0)
646 exit(1); 652 exit(1);
647 free(bp); 653 free(bp);
648 host = cleanhostname(thost);
649 xasprintf(&bp, "%s -t %s%s", cmd, 654 xasprintf(&bp, "%s -t %s%s", cmd,
650 *targ == '-' ? "-- " : "", targ); 655 *targ == '-' ? "-- " : "", targ);
651 if (do_cmd2(host, tuser, bp, remin, remout) < 0) 656 if (do_cmd2(thost, tuser, tport, bp, remin, remout) < 0)
652 exit(1); 657 exit(1);
653 free(bp); 658 free(bp);
654 (void) close(remin); 659 (void) close(remin);
655 (void) close(remout); 660 (void) close(remout);
656 remin = remout = -1; 661 remin = remout = -1;
657 } else if (src) { /* standard remote to remote */ 662 } else if (host) { /* standard remote to remote */
663 if (tport != -1 && tport != SSH_DEFAULT_PORT) {
664 /* This would require the remote support URIs */
665 fatal("target port not supported with two "
666 "remote hosts without the -3 option");
667 }
668
658 freeargs(&alist); 669 freeargs(&alist);
659 addargs(&alist, "%s", ssh_program); 670 addargs(&alist, "%s", ssh_program);
660 addargs(&alist, "-x"); 671 addargs(&alist, "-x");
@@ -664,23 +675,14 @@ toremote(char *targ, int argc, char **argv)
664 addargs(&alist, "%s", 675 addargs(&alist, "%s",
665 remote_remote_args.list[j]); 676 remote_remote_args.list[j]);
666 } 677 }
667 *src++ = 0; 678
668 if (*src == 0) 679 if (sport != -1) {
669 src = "."; 680 addargs(&alist, "-p");
670 host = strrchr(argv[i], '@'); 681 addargs(&alist, "%d", sport);
671 682 }
672 if (host) { 683 if (suser) {
673 *host++ = 0;
674 host = cleanhostname(host);
675 suser = argv[i];
676 if (*suser == '\0')
677 suser = pwd->pw_name;
678 else if (!okname(suser))
679 continue;
680 addargs(&alist, "-l"); 684 addargs(&alist, "-l");
681 addargs(&alist, "%s", suser); 685 addargs(&alist, "%s", suser);
682 } else {
683 host = cleanhostname(argv[i]);
684 } 686 }
685 addargs(&alist, "--"); 687 addargs(&alist, "--");
686 addargs(&alist, "%s", host); 688 addargs(&alist, "%s", host);
@@ -695,8 +697,7 @@ toremote(char *targ, int argc, char **argv)
695 if (remin == -1) { 697 if (remin == -1) {
696 xasprintf(&bp, "%s -t %s%s", cmd, 698 xasprintf(&bp, "%s -t %s%s", cmd,
697 *targ == '-' ? "-- " : "", targ); 699 *targ == '-' ? "-- " : "", targ);
698 host = cleanhostname(thost); 700 if (do_cmd(thost, tuser, tport, bp, &remin,
699 if (do_cmd(host, tuser, bp, &remin,
700 &remout) < 0) 701 &remout) < 0)
701 exit(1); 702 exit(1);
702 if (response() < 0) 703 if (response() < 0)
@@ -706,21 +707,41 @@ toremote(char *targ, int argc, char **argv)
706 source(1, argv + i); 707 source(1, argv + i);
707 } 708 }
708 } 709 }
709 free(arg); 710out:
711 free(tuser);
712 free(thost);
713 free(targ);
714 free(suser);
715 free(host);
716 free(src);
710} 717}
711 718
712void 719void
713tolocal(int argc, char **argv) 720tolocal(int argc, char **argv)
714{ 721{
715 char *bp, *host, *src, *suser; 722 char *bp, *host = NULL, *src = NULL, *suser = NULL;
716 arglist alist; 723 arglist alist;
717 int i; 724 int i, r, sport = -1;
718 725
719 memset(&alist, '\0', sizeof(alist)); 726 memset(&alist, '\0', sizeof(alist));
720 alist.list = NULL; 727 alist.list = NULL;
721 728
722 for (i = 0; i < argc - 1; i++) { 729 for (i = 0; i < argc - 1; i++) {
723 if (!(src = colon(argv[i]))) { /* Local to local. */ 730 free(suser);
731 free(host);
732 free(src);
733 r = parse_uri("scp", argv[i], &suser, &host, &sport, &src);
734 if (r == -1) {
735 ++errs;
736 continue;
737 }
738 if (r != 0)
739 parse_user_host_path(argv[i], &suser, &host, &src);
740 if (suser != NULL && !okname(suser)) {
741 ++errs;
742 continue;
743 }
744 if (!host) { /* Local to local. */
724 freeargs(&alist); 745 freeargs(&alist);
725 addargs(&alist, "%s", _PATH_CP); 746 addargs(&alist, "%s", _PATH_CP);
726 if (iamrecursive) 747 if (iamrecursive)
@@ -734,22 +755,10 @@ tolocal(int argc, char **argv)
734 ++errs; 755 ++errs;
735 continue; 756 continue;
736 } 757 }
737 *src++ = 0; 758 /* Remote to local. */
738 if (*src == 0)
739 src = ".";
740 if ((host = strrchr(argv[i], '@')) == NULL) {
741 host = argv[i];
742 suser = NULL;
743 } else {
744 *host++ = 0;
745 suser = argv[i];
746 if (*suser == '\0')
747 suser = pwd->pw_name;
748 }
749 host = cleanhostname(host);
750 xasprintf(&bp, "%s -f %s%s", 759 xasprintf(&bp, "%s -f %s%s",
751 cmd, *src == '-' ? "-- " : "", src); 760 cmd, *src == '-' ? "-- " : "", src);
752 if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 761 if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0) {
753 free(bp); 762 free(bp);
754 ++errs; 763 ++errs;
755 continue; 764 continue;
@@ -759,6 +768,9 @@ tolocal(int argc, char **argv)
759 (void) close(remin); 768 (void) close(remin);
760 remin = remout = -1; 769 remin = remout = -1;
761 } 770 }
771 free(suser);
772 free(host);
773 free(src);
762} 774}
763 775
764void 776void
@@ -1275,8 +1287,7 @@ usage(void)
1275{ 1287{
1276 (void) fprintf(stderr, 1288 (void) fprintf(stderr,
1277 "usage: scp [-346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" 1289 "usage: scp [-346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
1278 " [-l limit] [-o ssh_option] [-P port] [-S program]\n" 1290 " [-l limit] [-o ssh_option] [-P port] [-S program] source ... target\n");
1279 " [[user@]host1:]file1 ... [[user@]host2:]file2\n");
1280 exit(1); 1291 exit(1);
1281} 1292}
1282 1293
diff --git a/sftp.1 b/sftp.1
index c218376fb..49f7febf1 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: sftp.1,v 1.110 2017/05/03 21:49:18 naddy Exp $ 1.\" $OpenBSD: sftp.1,v 1.111 2017/10/21 23:06:24 millert Exp $
2.\" 2.\"
3.\" Copyright (c) 2001 Damien Miller. All rights reserved. 3.\" Copyright (c) 2001 Damien Miller. All rights reserved.
4.\" 4.\"
@@ -22,7 +22,7 @@
22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 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. 23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24.\" 24.\"
25.Dd $Mdocdate: May 3 2017 $ 25.Dd $Mdocdate: October 21 2017 $
26.Dt SFTP 1 26.Dt SFTP 1
27.Os 27.Os
28.Sh NAME 28.Sh NAME
@@ -44,54 +44,52 @@
44.Op Fl R Ar num_requests 44.Op Fl R Ar num_requests
45.Op Fl S Ar program 45.Op Fl S Ar program
46.Op Fl s Ar subsystem | sftp_server 46.Op Fl s Ar subsystem | sftp_server
47.Ar host 47.Ar destination
48.Ek
49.Nm sftp
50.Oo Ar user Ns @ Oc Ns
51.Ar host Ns Op : Ns Ar
52.Nm sftp
53.Oo
54.Ar user Ns @ Oc Ns
55.Ar host Ns Oo : Ns Ar dir Ns
56.Op Ar /
57.Oc
58.Nm sftp
59.Fl b Ar batchfile
60.Oo Ar user Ns @ Oc Ns Ar host
61.Sh DESCRIPTION 48.Sh DESCRIPTION
62.Nm 49.Nm
63is an interactive file transfer program, similar to 50is a file transfer program, similar to
64.Xr ftp 1 , 51.Xr ftp 1 ,
65which performs all operations over an encrypted 52which performs all operations over an encrypted
66.Xr ssh 1 53.Xr ssh 1
67transport. 54transport.
68It may also use many features of ssh, such as public key authentication and 55It may also use many features of ssh, such as public key authentication and
69compression. 56compression.
70.Nm
71connects and logs into the specified
72.Ar host ,
73then enters an interactive command mode.
74.Pp 57.Pp
75The second usage format will retrieve files automatically if a non-interactive 58The
59.Ar destination
60may be specified either as
61.Oo Ar user Ns @ Oc Ns Ar host Ns Oo : Ns Ar path Oc
62or as an sftp URI in the form
63.No sftp:// Ns Oo Ar user Ns @ Oc Ns Ar host Ns
64.Oo : Ns Ar port Oc Ns Oo / Ns Ar path Oc .
65.Pp
66If the
67.Ar destination
68includes a
69.Ar path
70and it is not a directory,
71.Nm
72will retrieve files automatically if a non-interactive
76authentication method is used; otherwise it will do so after 73authentication method is used; otherwise it will do so after
77successful interactive authentication. 74successful interactive authentication.
78.Pp 75.Pp
79The third usage format allows 76If no
77.Ar path
78is specified, or if the
79.Ar path
80is a directory,
80.Nm 81.Nm
81to start in a remote directory. 82will log in to the specified
82.Pp 83.Ar host
83The final usage format allows for automated sessions using the 84and enter interactive command mode, changing to the remote directory
84.Fl b 85if one was specified.
85option. 86An optional trailing slash can be used to force the
86In such cases, it is necessary to configure non-interactive authentication 87.Ar path
87to obviate the need to enter a password at connection time (see 88to be interpreted as a directory.
88.Xr sshd 8
89and
90.Xr ssh-keygen 1
91for details).
92.Pp 89.Pp
93Since some usage formats use colon characters to delimit host names from path 90Since the destination formats use colon characters to delimit host
94names, IPv6 addresses must be enclosed in square brackets to avoid ambiguity. 91names from path names or port numbers, IPv6 addresses must be
92enclosed in square brackets to avoid ambiguity.
95.Pp 93.Pp
96The options are as follows: 94The options are as follows:
97.Bl -tag -width Ds 95.Bl -tag -width Ds
@@ -121,7 +119,12 @@ Batch mode reads a series of commands from an input
121instead of 119instead of
122.Em stdin . 120.Em stdin .
123Since it lacks user interaction it should be used in conjunction with 121Since it lacks user interaction it should be used in conjunction with
124non-interactive authentication. 122non-interactive authentication to obviate the need to enter a password
123at connection time (see
124.Xr sshd 8
125and
126.Xr ssh-keygen 1
127for details).
125A 128A
126.Ar batchfile 129.Ar batchfile
127of 130of
diff --git a/sftp.c b/sftp.c
index 67110f738..9aee2fafe 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.180 2017/06/10 06:33:34 djm Exp $ */ 1/* $OpenBSD: sftp.c,v 1.181 2017/10/21 23:06:24 millert Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -2301,19 +2301,16 @@ usage(void)
2301 "[-i identity_file] [-l limit]\n" 2301 "[-i identity_file] [-l limit]\n"
2302 " [-o ssh_option] [-P port] [-R num_requests] " 2302 " [-o ssh_option] [-P port] [-R num_requests] "
2303 "[-S program]\n" 2303 "[-S program]\n"
2304 " [-s subsystem | sftp_server] host\n" 2304 " [-s subsystem | sftp_server] destination\n",
2305 " %s [user@]host[:file ...]\n" 2305 __progname);
2306 " %s [user@]host[:dir[/]]\n"
2307 " %s -b batchfile [user@]host\n",
2308 __progname, __progname, __progname, __progname);
2309 exit(1); 2306 exit(1);
2310} 2307}
2311 2308
2312int 2309int
2313main(int argc, char **argv) 2310main(int argc, char **argv)
2314{ 2311{
2315 int in, out, ch, err; 2312 int in, out, ch, err, tmp, port = -1;
2316 char *host = NULL, *userhost, *cp, *file2 = NULL; 2313 char *host = NULL, *user, *cp, *file2 = NULL;
2317 int debug_level = 0, sshver = 2; 2314 int debug_level = 0, sshver = 2;
2318 char *file1 = NULL, *sftp_server = NULL; 2315 char *file1 = NULL, *sftp_server = NULL;
2319 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 2316 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
@@ -2368,7 +2365,9 @@ main(int argc, char **argv)
2368 addargs(&args, "-%c", ch); 2365 addargs(&args, "-%c", ch);
2369 break; 2366 break;
2370 case 'P': 2367 case 'P':
2371 addargs(&args, "-oPort %s", optarg); 2368 port = a2port(optarg);
2369 if (port <= 0)
2370 fatal("Bad port \"%s\"\n", optarg);
2372 break; 2371 break;
2373 case 'v': 2372 case 'v':
2374 if (debug_level < 3) { 2373 if (debug_level < 3) {
@@ -2451,33 +2450,38 @@ main(int argc, char **argv)
2451 if (sftp_direct == NULL) { 2450 if (sftp_direct == NULL) {
2452 if (optind == argc || argc > (optind + 2)) 2451 if (optind == argc || argc > (optind + 2))
2453 usage(); 2452 usage();
2453 argv += optind;
2454 2454
2455 userhost = xstrdup(argv[optind]); 2455 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2456 file2 = argv[optind+1]; 2456 case -1:
2457 2457 usage();
2458 if ((host = strrchr(userhost, '@')) == NULL) 2458 break;
2459 host = userhost; 2459 case 0:
2460 else { 2460 if (tmp != -1)
2461 *host++ = '\0'; 2461 port = tmp;
2462 if (!userhost[0]) { 2462 break;
2463 fprintf(stderr, "Missing username\n"); 2463 default:
2464 usage(); 2464 if (parse_user_host_path(*argv, &user, &host,
2465 &file1) == -1) {
2466 /* Treat as a plain hostname. */
2467 host = xstrdup(*argv);
2468 host = cleanhostname(host);
2465 } 2469 }
2466 addargs(&args, "-l"); 2470 break;
2467 addargs(&args, "%s", userhost);
2468 }
2469
2470 if ((cp = colon(host)) != NULL) {
2471 *cp++ = '\0';
2472 file1 = cp;
2473 } 2471 }
2472 file2 = *(argv + 1);
2474 2473
2475 host = cleanhostname(host);
2476 if (!*host) { 2474 if (!*host) {
2477 fprintf(stderr, "Missing hostname\n"); 2475 fprintf(stderr, "Missing hostname\n");
2478 usage(); 2476 usage();
2479 } 2477 }
2480 2478
2479 if (port != -1)
2480 addargs(&args, "-oPort %d", port);
2481 if (user != NULL) {
2482 addargs(&args, "-l");
2483 addargs(&args, "%s", user);
2484 }
2481 addargs(&args, "-oProtocol %d", sshver); 2485 addargs(&args, "-oProtocol %d", sshver);
2482 2486
2483 /* no subsystem if the server-spec contains a '/' */ 2487 /* no subsystem if the server-spec contains a '/' */
diff --git a/ssh.1 b/ssh.1
index 92092df1e..310f34cc9 100644
--- a/ssh.1
+++ b/ssh.1
@@ -33,8 +33,8 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: ssh.1,v 1.385 2017/10/13 06:45:18 djm Exp $ 36.\" $OpenBSD: ssh.1,v 1.386 2017/10/21 23:06:24 millert Exp $
37.Dd $Mdocdate: October 13 2017 $ 37.Dd $Mdocdate: October 21 2017 $
38.Dt SSH 1 38.Dt SSH 1
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -52,7 +52,7 @@
52.Op Fl F Ar configfile 52.Op Fl F Ar configfile
53.Op Fl I Ar pkcs11 53.Op Fl I Ar pkcs11
54.Op Fl i Ar identity_file 54.Op Fl i Ar identity_file
55.Op Fl J Oo Ar user Ns @ Oc Ns Ar host Ns Op : Ns Ar port 55.Op Fl J Ar destination
56.Op Fl L Ar address 56.Op Fl L Ar address
57.Op Fl l Ar login_name 57.Op Fl l Ar login_name
58.Op Fl m Ar mac_spec 58.Op Fl m Ar mac_spec
@@ -64,7 +64,7 @@
64.Op Fl S Ar ctl_path 64.Op Fl S Ar ctl_path
65.Op Fl W Ar host : Ns Ar port 65.Op Fl W Ar host : Ns Ar port
66.Op Fl w Ar local_tun Ns Op : Ns Ar remote_tun 66.Op Fl w Ar local_tun Ns Op : Ns Ar remote_tun
67.Oo Ar user Ns @ Oc Ns Ar hostname 67.Ar destination
68.Op Ar command 68.Op Ar command
69.Ek 69.Ek
70.Sh DESCRIPTION 70.Sh DESCRIPTION
@@ -79,15 +79,23 @@ sockets can also be forwarded over the secure channel.
79.Pp 79.Pp
80.Nm 80.Nm
81connects and logs into the specified 81connects and logs into the specified
82.Ar hostname 82.Ar destination
83(with optional 83which may be specified as either
84.Oo Ar user Ns @ Oc Ns Ar hostname
85where the
86.Ar user
87is optional, or an ssh URI of the form
88.No ssh:// Ns Oo Ar user Ns @ Oc Ns Ar hostname Ns Oo : Ns Ar port Oc
89where the
84.Ar user 90.Ar user
85name). 91and
92.Ar port
93are optional.
86The user must prove 94The user must prove
87his/her identity to the remote machine using one of several methods 95his/her identity to the remote machine using one of several methods
88(see below). 96(see below).
89.Pp 97.Pp
90If 98If a
91.Ar command 99.Ar command
92is specified, 100is specified,
93it is executed on the remote host instead of a login shell. 101it is executed on the remote host instead of a login shell.
@@ -287,17 +295,11 @@ by appending
287.Pa -cert.pub 295.Pa -cert.pub
288to identity filenames. 296to identity filenames.
289.Pp 297.Pp
290.It Fl J Xo 298.It Fl J Ar destination
291.Sm off
292.Op Ar user No @
293.Ar host
294.Op : Ar port
295.Sm on
296.Xc
297Connect to the target host by first making a 299Connect to the target host by first making a
298.Nm 300.Nm
299connection to the jump 301connection to the jump host described by
300.Ar host 302.Ar destination
301and then establishing a TCP forwarding to the ultimate destination from 303and then establishing a TCP forwarding to the ultimate destination from
302there. 304there.
303Multiple jump hops may be specified separated by comma characters. 305Multiple jump hops may be specified separated by comma characters.
diff --git a/ssh.c b/ssh.c
index ae37432bd..213c35e77 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh.c,v 1.464 2017/09/21 19:16:53 markus Exp $ */ 1/* $OpenBSD: ssh.c,v 1.465 2017/10/21 23:06:24 millert Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -203,7 +203,7 @@ usage(void)
203" [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n" 203" [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n"
204" [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n" 204" [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n"
205" [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n" 205" [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n"
206" [user@]hostname [command]\n" 206" destination [command]\n"
207 ); 207 );
208 exit(255); 208 exit(255);
209} 209}
@@ -846,14 +846,18 @@ main(int ac, char **av)
846 options.control_master = SSHCTL_MASTER_YES; 846 options.control_master = SSHCTL_MASTER_YES;
847 break; 847 break;
848 case 'p': 848 case 'p':
849 options.port = a2port(optarg); 849 if (options.port == -1) {
850 if (options.port <= 0) { 850 options.port = a2port(optarg);
851 fprintf(stderr, "Bad port '%s'\n", optarg); 851 if (options.port <= 0) {
852 exit(255); 852 fprintf(stderr, "Bad port '%s'\n",
853 optarg);
854 exit(255);
855 }
853 } 856 }
854 break; 857 break;
855 case 'l': 858 case 'l':
856 options.user = optarg; 859 if (options.user == NULL)
860 options.user = optarg;
857 break; 861 break;
858 862
859 case 'L': 863 case 'L':
@@ -933,16 +937,38 @@ main(int ac, char **av)
933 av += optind; 937 av += optind;
934 938
935 if (ac > 0 && !host) { 939 if (ac > 0 && !host) {
936 if (strrchr(*av, '@')) { 940 int tport;
941 char *tuser;
942 switch (parse_ssh_uri(*av, &tuser, &host, &tport)) {
943 case -1:
944 usage();
945 break;
946 case 0:
947 if (options.user == NULL) {
948 options.user = tuser;
949 tuser = NULL;
950 }
951 free(tuser);
952 if (options.port == -1 && tport != -1)
953 options.port = tport;
954 break;
955 default:
937 p = xstrdup(*av); 956 p = xstrdup(*av);
938 cp = strrchr(p, '@'); 957 cp = strrchr(p, '@');
939 if (cp == NULL || cp == p) 958 if (cp != NULL) {
940 usage(); 959 if (cp == p)
941 options.user = p; 960 usage();
942 *cp = '\0'; 961 if (options.user == NULL) {
943 host = xstrdup(++cp); 962 options.user = p;
944 } else 963 p = NULL;
945 host = xstrdup(*av); 964 }
965 *cp++ = '\0';
966 host = xstrdup(cp);
967 free(p);
968 } else
969 host = p;
970 break;
971 }
946 if (ac > 1 && !opt_terminated) { 972 if (ac > 1 && !opt_terminated) {
947 optind = optreset = 1; 973 optind = optreset = 1;
948 goto again; 974 goto again;
diff --git a/ssh_config.5 b/ssh_config.5
index 96e6904bc..c04701044 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -33,8 +33,8 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: ssh_config.5,v 1.259 2017/10/18 05:36:59 jmc Exp $ 36.\" $OpenBSD: ssh_config.5,v 1.260 2017/10/21 23:06:24 millert Exp $
37.Dd $Mdocdate: October 18 2017 $ 37.Dd $Mdocdate: October 21 2017 $
38.Dt SSH_CONFIG 5 38.Dt SSH_CONFIG 5
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -1198,13 +1198,14 @@ For example, the following directive would connect via an HTTP proxy at
1198ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p 1198ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p
1199.Ed 1199.Ed
1200.It Cm ProxyJump 1200.It Cm ProxyJump
1201Specifies one or more jump proxies as 1201Specifies one or more jump proxies as either
1202.Xo 1202.Xo
1203.Sm off 1203.Sm off
1204.Op Ar user No @ 1204.Op Ar user No @
1205.Ar host 1205.Ar host
1206.Op : Ns Ar port 1206.Op : Ns Ar port
1207.Sm on 1207.Sm on
1208or an ssh URI
1208.Xc . 1209.Xc .
1209Multiple proxies may be separated by comma characters and will be visited 1210Multiple proxies may be separated by comma characters and will be visited
1210sequentially. 1211sequentially.