diff options
Diffstat (limited to 'scp.c')
-rw-r--r-- | scp.c | 222 |
1 files changed, 130 insertions, 92 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.195 2018/02/10 06:15:12 djm 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 | ||
@@ -239,7 +243,7 @@ do_local_cmd(arglist *a) | |||
239 | */ | 243 | */ |
240 | 244 | ||
241 | int | 245 | int |
242 | do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | 246 | do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) |
243 | { | 247 | { |
244 | int pin[2], pout[2], reserved[2]; | 248 | int pin[2], pout[2], reserved[2]; |
245 | 249 | ||
@@ -249,6 +253,9 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | |||
249 | ssh_program, host, | 253 | ssh_program, host, |
250 | remuser ? remuser : "(unspecified)", cmd); | 254 | remuser ? remuser : "(unspecified)", cmd); |
251 | 255 | ||
256 | if (port == -1) | ||
257 | port = sshport; | ||
258 | |||
252 | /* | 259 | /* |
253 | * Reserve two descriptors so that the real pipes won't get | 260 | * Reserve two descriptors so that the real pipes won't get |
254 | * descriptors 0 and 1 because that will screw up dup2 below. | 261 | * descriptors 0 and 1 because that will screw up dup2 below. |
@@ -282,6 +289,10 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | |||
282 | close(pout[1]); | 289 | close(pout[1]); |
283 | 290 | ||
284 | replacearg(&args, 0, "%s", ssh_program); | 291 | replacearg(&args, 0, "%s", ssh_program); |
292 | if (port != -1) { | ||
293 | addargs(&args, "-p"); | ||
294 | addargs(&args, "%d", port); | ||
295 | } | ||
285 | if (remuser != NULL) { | 296 | if (remuser != NULL) { |
286 | addargs(&args, "-l"); | 297 | addargs(&args, "-l"); |
287 | addargs(&args, "%s", remuser); | 298 | addargs(&args, "%s", remuser); |
@@ -313,7 +324,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | |||
313 | * This way the input and output of two commands can be connected. | 324 | * This way the input and output of two commands can be connected. |
314 | */ | 325 | */ |
315 | int | 326 | int |
316 | do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) | 327 | do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout) |
317 | { | 328 | { |
318 | pid_t pid; | 329 | pid_t pid; |
319 | int status; | 330 | int status; |
@@ -324,6 +335,9 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) | |||
324 | ssh_program, host, | 335 | ssh_program, host, |
325 | remuser ? remuser : "(unspecified)", cmd); | 336 | remuser ? remuser : "(unspecified)", cmd); |
326 | 337 | ||
338 | if (port == -1) | ||
339 | port = sshport; | ||
340 | |||
327 | /* Fork a child to execute the command on the remote host using ssh. */ | 341 | /* Fork a child to execute the command on the remote host using ssh. */ |
328 | pid = fork(); | 342 | pid = fork(); |
329 | if (pid == 0) { | 343 | if (pid == 0) { |
@@ -331,6 +345,10 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) | |||
331 | dup2(fdout, 1); | 345 | dup2(fdout, 1); |
332 | 346 | ||
333 | replacearg(&args, 0, "%s", ssh_program); | 347 | replacearg(&args, 0, "%s", ssh_program); |
348 | if (port != -1) { | ||
349 | addargs(&args, "-p"); | ||
350 | addargs(&args, "%d", port); | ||
351 | } | ||
334 | if (remuser != NULL) { | 352 | if (remuser != NULL) { |
335 | addargs(&args, "-l"); | 353 | addargs(&args, "-l"); |
336 | addargs(&args, "%s", remuser); | 354 | addargs(&args, "%s", remuser); |
@@ -375,14 +393,14 @@ void rsource(char *, struct stat *); | |||
375 | void sink(int, char *[]); | 393 | void sink(int, char *[]); |
376 | void source(int, char *[]); | 394 | void source(int, char *[]); |
377 | void tolocal(int, char *[]); | 395 | void tolocal(int, char *[]); |
378 | void toremote(char *, int, char *[]); | 396 | void toremote(int, char *[]); |
379 | void usage(void); | 397 | void usage(void); |
380 | 398 | ||
381 | int | 399 | int |
382 | main(int argc, char **argv) | 400 | main(int argc, char **argv) |
383 | { | 401 | { |
384 | int ch, fflag, tflag, status, n; | 402 | int ch, fflag, tflag, status, n; |
385 | char *targ, **newargv; | 403 | char **newargv; |
386 | const char *errstr; | 404 | const char *errstr; |
387 | extern char *optarg; | 405 | extern char *optarg; |
388 | extern int optind; | 406 | extern int optind; |
@@ -408,6 +426,8 @@ main(int argc, char **argv) | |||
408 | addargs(&args, "-oForwardAgent=no"); | 426 | addargs(&args, "-oForwardAgent=no"); |
409 | addargs(&args, "-oPermitLocalCommand=no"); | 427 | addargs(&args, "-oPermitLocalCommand=no"); |
410 | addargs(&args, "-oClearAllForwardings=yes"); | 428 | addargs(&args, "-oClearAllForwardings=yes"); |
429 | addargs(&args, "-oRemoteCommand=none"); | ||
430 | addargs(&args, "-oRequestTTY=no"); | ||
411 | 431 | ||
412 | fflag = tflag = 0; | 432 | fflag = tflag = 0; |
413 | while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) | 433 | while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) |
@@ -438,10 +458,9 @@ main(int argc, char **argv) | |||
438 | addargs(&args, "%s", optarg); | 458 | addargs(&args, "%s", optarg); |
439 | break; | 459 | break; |
440 | case 'P': | 460 | case 'P': |
441 | addargs(&remote_remote_args, "-p"); | 461 | sshport = a2port(optarg); |
442 | addargs(&remote_remote_args, "%s", optarg); | 462 | if (sshport <= 0) |
443 | addargs(&args, "-p"); | 463 | fatal("bad port \"%s\"\n", optarg); |
444 | addargs(&args, "%s", optarg); | ||
445 | break; | 464 | break; |
446 | case 'B': | 465 | case 'B': |
447 | addargs(&remote_remote_args, "-oBatchmode=yes"); | 466 | addargs(&remote_remote_args, "-oBatchmode=yes"); |
@@ -541,8 +560,8 @@ main(int argc, char **argv) | |||
541 | 560 | ||
542 | (void) signal(SIGPIPE, lostconn); | 561 | (void) signal(SIGPIPE, lostconn); |
543 | 562 | ||
544 | if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ | 563 | if (colon(argv[argc - 1])) /* Dest is remote host. */ |
545 | toremote(targ, argc, argv); | 564 | toremote(argc, argv); |
546 | else { | 565 | else { |
547 | if (targetshouldbedirectory) | 566 | if (targetshouldbedirectory) |
548 | verifydir(argv[argc - 1]); | 567 | verifydir(argv[argc - 1]); |
@@ -597,72 +616,90 @@ do_times(int fd, int verb, const struct stat *sb) | |||
597 | return (response()); | 616 | return (response()); |
598 | } | 617 | } |
599 | 618 | ||
619 | static int | ||
620 | parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp, | ||
621 | char **pathp) | ||
622 | { | ||
623 | int r; | ||
624 | |||
625 | r = parse_uri("scp", uri, userp, hostp, portp, pathp); | ||
626 | if (r == 0 && *pathp == NULL) | ||
627 | *pathp = xstrdup("."); | ||
628 | return r; | ||
629 | } | ||
630 | |||
600 | void | 631 | void |
601 | toremote(char *targ, int argc, char **argv) | 632 | toremote(int argc, char **argv) |
602 | { | 633 | { |
603 | char *bp, *host, *src, *suser, *thost, *tuser, *arg; | 634 | char *suser = NULL, *host = NULL, *src = NULL; |
635 | char *bp, *tuser, *thost, *targ; | ||
636 | int sport = -1, tport = -1; | ||
604 | arglist alist; | 637 | arglist alist; |
605 | int i; | 638 | int i, r; |
606 | u_int j; | 639 | u_int j; |
607 | 640 | ||
608 | memset(&alist, '\0', sizeof(alist)); | 641 | memset(&alist, '\0', sizeof(alist)); |
609 | alist.list = NULL; | 642 | alist.list = NULL; |
610 | 643 | ||
611 | *targ++ = 0; | 644 | /* Parse target */ |
612 | if (*targ == 0) | 645 | r = parse_scp_uri(argv[argc - 1], &tuser, &thost, &tport, &targ); |
613 | targ = "."; | 646 | if (r == -1) { |
614 | 647 | fmprintf(stderr, "%s: invalid uri\n", argv[argc - 1]); | |
615 | arg = xstrdup(argv[argc - 1]); | 648 | ++errs; |
616 | if ((thost = strrchr(arg, '@'))) { | 649 | goto out; |
617 | /* user@host */ | 650 | } |
618 | *thost++ = 0; | 651 | if (r != 0) { |
619 | tuser = arg; | 652 | if (parse_user_host_path(argv[argc - 1], &tuser, &thost, |
620 | if (*tuser == '\0') | 653 | &targ) == -1) { |
621 | tuser = NULL; | 654 | fmprintf(stderr, "%s: invalid target\n", argv[argc - 1]); |
622 | } else { | 655 | ++errs; |
623 | thost = arg; | 656 | goto out; |
624 | tuser = NULL; | 657 | } |
625 | } | 658 | } |
626 | |||
627 | if (tuser != NULL && !okname(tuser)) { | 659 | if (tuser != NULL && !okname(tuser)) { |
628 | free(arg); | 660 | ++errs; |
629 | return; | 661 | goto out; |
630 | } | 662 | } |
631 | 663 | ||
664 | /* Parse source files */ | ||
632 | for (i = 0; i < argc - 1; i++) { | 665 | for (i = 0; i < argc - 1; i++) { |
633 | src = colon(argv[i]); | 666 | free(suser); |
634 | if (src && throughlocal) { /* extended remote to remote */ | 667 | free(host); |
635 | *src++ = 0; | 668 | free(src); |
636 | if (*src == 0) | 669 | r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); |
637 | src = "."; | 670 | if (r == -1) { |
638 | host = strrchr(argv[i], '@'); | 671 | fmprintf(stderr, "%s: invalid uri\n", argv[i]); |
639 | if (host) { | 672 | ++errs; |
640 | *host++ = 0; | 673 | continue; |
641 | host = cleanhostname(host); | 674 | } |
642 | suser = argv[i]; | 675 | if (r != 0) { |
643 | if (*suser == '\0') | 676 | parse_user_host_path(argv[i], &suser, &host, &src); |
644 | suser = pwd->pw_name; | 677 | } |
645 | else if (!okname(suser)) | 678 | if (suser != NULL && !okname(suser)) { |
646 | continue; | 679 | ++errs; |
647 | } else { | 680 | continue; |
648 | host = cleanhostname(argv[i]); | 681 | } |
649 | suser = NULL; | 682 | if (host && throughlocal) { /* extended remote to remote */ |
650 | } | ||
651 | xasprintf(&bp, "%s -f %s%s", cmd, | 683 | xasprintf(&bp, "%s -f %s%s", cmd, |
652 | *src == '-' ? "-- " : "", src); | 684 | *src == '-' ? "-- " : "", src); |
653 | if (do_cmd(host, suser, bp, &remin, &remout) < 0) | 685 | if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0) |
654 | exit(1); | 686 | exit(1); |
655 | free(bp); | 687 | free(bp); |
656 | host = cleanhostname(thost); | ||
657 | xasprintf(&bp, "%s -t %s%s", cmd, | 688 | xasprintf(&bp, "%s -t %s%s", cmd, |
658 | *targ == '-' ? "-- " : "", targ); | 689 | *targ == '-' ? "-- " : "", targ); |
659 | if (do_cmd2(host, tuser, bp, remin, remout) < 0) | 690 | if (do_cmd2(thost, tuser, tport, bp, remin, remout) < 0) |
660 | exit(1); | 691 | exit(1); |
661 | free(bp); | 692 | free(bp); |
662 | (void) close(remin); | 693 | (void) close(remin); |
663 | (void) close(remout); | 694 | (void) close(remout); |
664 | remin = remout = -1; | 695 | remin = remout = -1; |
665 | } else if (src) { /* standard remote to remote */ | 696 | } else if (host) { /* standard remote to remote */ |
697 | if (tport != -1 && tport != SSH_DEFAULT_PORT) { | ||
698 | /* This would require the remote support URIs */ | ||
699 | fatal("target port not supported with two " | ||
700 | "remote hosts without the -3 option"); | ||
701 | } | ||
702 | |||
666 | freeargs(&alist); | 703 | freeargs(&alist); |
667 | addargs(&alist, "%s", ssh_program); | 704 | addargs(&alist, "%s", ssh_program); |
668 | addargs(&alist, "-x"); | 705 | addargs(&alist, "-x"); |
@@ -672,23 +709,14 @@ toremote(char *targ, int argc, char **argv) | |||
672 | addargs(&alist, "%s", | 709 | addargs(&alist, "%s", |
673 | remote_remote_args.list[j]); | 710 | remote_remote_args.list[j]); |
674 | } | 711 | } |
675 | *src++ = 0; | 712 | |
676 | if (*src == 0) | 713 | if (sport != -1) { |
677 | src = "."; | 714 | addargs(&alist, "-p"); |
678 | host = strrchr(argv[i], '@'); | 715 | addargs(&alist, "%d", sport); |
679 | 716 | } | |
680 | if (host) { | 717 | if (suser) { |
681 | *host++ = 0; | ||
682 | host = cleanhostname(host); | ||
683 | suser = argv[i]; | ||
684 | if (*suser == '\0') | ||
685 | suser = pwd->pw_name; | ||
686 | else if (!okname(suser)) | ||
687 | continue; | ||
688 | addargs(&alist, "-l"); | 718 | addargs(&alist, "-l"); |
689 | addargs(&alist, "%s", suser); | 719 | addargs(&alist, "%s", suser); |
690 | } else { | ||
691 | host = cleanhostname(argv[i]); | ||
692 | } | 720 | } |
693 | addargs(&alist, "--"); | 721 | addargs(&alist, "--"); |
694 | addargs(&alist, "%s", host); | 722 | addargs(&alist, "%s", host); |
@@ -703,8 +731,7 @@ toremote(char *targ, int argc, char **argv) | |||
703 | if (remin == -1) { | 731 | if (remin == -1) { |
704 | xasprintf(&bp, "%s -t %s%s", cmd, | 732 | xasprintf(&bp, "%s -t %s%s", cmd, |
705 | *targ == '-' ? "-- " : "", targ); | 733 | *targ == '-' ? "-- " : "", targ); |
706 | host = cleanhostname(thost); | 734 | if (do_cmd(thost, tuser, tport, bp, &remin, |
707 | if (do_cmd(host, tuser, bp, &remin, | ||
708 | &remout) < 0) | 735 | &remout) < 0) |
709 | exit(1); | 736 | exit(1); |
710 | if (response() < 0) | 737 | if (response() < 0) |
@@ -714,21 +741,42 @@ toremote(char *targ, int argc, char **argv) | |||
714 | source(1, argv + i); | 741 | source(1, argv + i); |
715 | } | 742 | } |
716 | } | 743 | } |
717 | free(arg); | 744 | out: |
745 | free(tuser); | ||
746 | free(thost); | ||
747 | free(targ); | ||
748 | free(suser); | ||
749 | free(host); | ||
750 | free(src); | ||
718 | } | 751 | } |
719 | 752 | ||
720 | void | 753 | void |
721 | tolocal(int argc, char **argv) | 754 | tolocal(int argc, char **argv) |
722 | { | 755 | { |
723 | char *bp, *host, *src, *suser; | 756 | char *bp, *host = NULL, *src = NULL, *suser = NULL; |
724 | arglist alist; | 757 | arglist alist; |
725 | int i; | 758 | int i, r, sport = -1; |
726 | 759 | ||
727 | memset(&alist, '\0', sizeof(alist)); | 760 | memset(&alist, '\0', sizeof(alist)); |
728 | alist.list = NULL; | 761 | alist.list = NULL; |
729 | 762 | ||
730 | for (i = 0; i < argc - 1; i++) { | 763 | for (i = 0; i < argc - 1; i++) { |
731 | if (!(src = colon(argv[i]))) { /* Local to local. */ | 764 | free(suser); |
765 | free(host); | ||
766 | free(src); | ||
767 | r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); | ||
768 | if (r == -1) { | ||
769 | fmprintf(stderr, "%s: invalid uri\n", argv[i]); | ||
770 | ++errs; | ||
771 | continue; | ||
772 | } | ||
773 | if (r != 0) | ||
774 | parse_user_host_path(argv[i], &suser, &host, &src); | ||
775 | if (suser != NULL && !okname(suser)) { | ||
776 | ++errs; | ||
777 | continue; | ||
778 | } | ||
779 | if (!host) { /* Local to local. */ | ||
732 | freeargs(&alist); | 780 | freeargs(&alist); |
733 | addargs(&alist, "%s", _PATH_CP); | 781 | addargs(&alist, "%s", _PATH_CP); |
734 | if (iamrecursive) | 782 | if (iamrecursive) |
@@ -742,22 +790,10 @@ tolocal(int argc, char **argv) | |||
742 | ++errs; | 790 | ++errs; |
743 | continue; | 791 | continue; |
744 | } | 792 | } |
745 | *src++ = 0; | 793 | /* Remote to local. */ |
746 | if (*src == 0) | ||
747 | src = "."; | ||
748 | if ((host = strrchr(argv[i], '@')) == NULL) { | ||
749 | host = argv[i]; | ||
750 | suser = NULL; | ||
751 | } else { | ||
752 | *host++ = 0; | ||
753 | suser = argv[i]; | ||
754 | if (*suser == '\0') | ||
755 | suser = pwd->pw_name; | ||
756 | } | ||
757 | host = cleanhostname(host); | ||
758 | xasprintf(&bp, "%s -f %s%s", | 794 | xasprintf(&bp, "%s -f %s%s", |
759 | cmd, *src == '-' ? "-- " : "", src); | 795 | cmd, *src == '-' ? "-- " : "", src); |
760 | if (do_cmd(host, suser, bp, &remin, &remout) < 0) { | 796 | if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0) { |
761 | free(bp); | 797 | free(bp); |
762 | ++errs; | 798 | ++errs; |
763 | continue; | 799 | continue; |
@@ -767,6 +803,9 @@ tolocal(int argc, char **argv) | |||
767 | (void) close(remin); | 803 | (void) close(remin); |
768 | remin = remout = -1; | 804 | remin = remout = -1; |
769 | } | 805 | } |
806 | free(suser); | ||
807 | free(host); | ||
808 | free(src); | ||
770 | } | 809 | } |
771 | 810 | ||
772 | void | 811 | void |
@@ -1283,8 +1322,7 @@ usage(void) | |||
1283 | { | 1322 | { |
1284 | (void) fprintf(stderr, | 1323 | (void) fprintf(stderr, |
1285 | "usage: scp [-346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" | 1324 | "usage: scp [-346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" |
1286 | " [-l limit] [-o ssh_option] [-P port] [-S program]\n" | 1325 | " [-l limit] [-o ssh_option] [-P port] [-S program] source ... target\n"); |
1287 | " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); | ||
1288 | exit(1); | 1326 | exit(1); |
1289 | } | 1327 | } |
1290 | 1328 | ||