diff options
author | millert@openbsd.org <millert@openbsd.org> | 2017-10-21 23:06:24 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2017-10-23 16:10:08 +1100 |
commit | 887669ef032d63cf07f53cada216fa8a0c9a7d72 (patch) | |
tree | 089b20255da21a489d7bc796a8ee86bd0b8f028f /scp.c | |
parent | d27bff293cfeb2252f4c7a58babe5ad3262c6c98 (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
Diffstat (limited to 'scp.c')
-rw-r--r-- | scp.c | 199 |
1 files changed, 105 insertions, 94 deletions
@@ -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 | ||
126 | int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); | 127 | int do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout); |
127 | int do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout); | 128 | int do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout); |
128 | 129 | ||
129 | /* Struct for addargs */ | 130 | /* Struct for addargs */ |
130 | arglist args; | 131 | arglist args; |
@@ -149,6 +150,9 @@ int showprogress = 1; | |||
149 | */ | 150 | */ |
150 | int throughlocal = 0; | 151 | int throughlocal = 0; |
151 | 152 | ||
153 | /* Non-standard port to use for the ssh connection or -1. */ | ||
154 | int 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) */ |
153 | char *ssh_program = _PATH_SSH_PROGRAM; | 157 | char *ssh_program = _PATH_SSH_PROGRAM; |
154 | 158 | ||
@@ -231,7 +235,7 @@ do_local_cmd(arglist *a) | |||
231 | */ | 235 | */ |
232 | 236 | ||
233 | int | 237 | int |
234 | do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | 238 | do_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 | */ |
307 | int | 318 | int |
308 | do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) | 319 | do_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 *); | |||
367 | void sink(int, char *[]); | 385 | void sink(int, char *[]); |
368 | void source(int, char *[]); | 386 | void source(int, char *[]); |
369 | void tolocal(int, char *[]); | 387 | void tolocal(int, char *[]); |
370 | void toremote(char *, int, char *[]); | 388 | void toremote(int, char *[]); |
371 | void usage(void); | 389 | void usage(void); |
372 | 390 | ||
373 | int | 391 | int |
374 | main(int argc, char **argv) | 392 | main(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 | ||
592 | void | 609 | void |
593 | toremote(char *targ, int argc, char **argv) | 610 | toremote(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); | 710 | out: |
711 | free(tuser); | ||
712 | free(thost); | ||
713 | free(targ); | ||
714 | free(suser); | ||
715 | free(host); | ||
716 | free(src); | ||
710 | } | 717 | } |
711 | 718 | ||
712 | void | 719 | void |
713 | tolocal(int argc, char **argv) | 720 | tolocal(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 | ||
764 | void | 776 | void |
@@ -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 | ||