X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/dc3c8261f1530c7926d4fded06a7ee58ec010b73..02105c79d5cbe02d41294d7eeeb603b94706bbcc:/scp.c diff --git a/scp.c b/scp.c index 5537ef33..3527f2b1 100644 --- a/scp.c +++ b/scp.c @@ -1,11 +1,15 @@ /* - * scp.c - Scp (Secure Copy) client for PuTTY. - * Joris van Rantwijk, Simon Tatham + * scp.c - Scp (Secure Copy) client for PuTTY. + * Joris van Rantwijk, Simon Tatham * - * This is mainly based on ssh-1.2.26/scp.c by Timo Rinne & Tatu Ylonen. - * They, in turn, used stuff from BSD rcp. - * - * Adaptations to enable connecting a GUI by L. Gunnarsson - Sept 2000 + * This is mainly based on ssh-1.2.26/scp.c by Timo Rinne & Tatu Ylonen. + * They, in turn, used stuff from BSD rcp. + * + * (SGT, 2001-09-10: Joris van Rantwijk assures me that although + * this file as originally submitted was inspired by, and + * _structurally_ based on, ssh-1.2.26's scp.c, there wasn't any + * actual code duplicated, so the above comment shouldn't give rise + * to licensing issues.) */ #include @@ -45,6 +49,9 @@ #define WM_STATS_ELAPSED ( WM_APP_BASE+405 ) #define WM_RET_ERR_CNT ( WM_APP_BASE+406 ) #define WM_LS_RET_ERR_CNT ( WM_APP_BASE+407 ) +#define WM_STATS_DONE ( WM_APP_BASE+408 ) +#define WM_STATS_ETA ( WM_APP_BASE+409 ) +#define WM_STATS_RATEBS ( WM_APP_BASE+410 ) static int list = 0; static int verbose = 0; @@ -61,6 +68,9 @@ static int errs = 0; #define NAME_STR_MAX 2048 static char statname[NAME_STR_MAX + 1]; static unsigned long statsize = 0; +static unsigned long statdone = 0; +static unsigned long stateta = 0; +static unsigned long statratebs = 0; static int statperct = 0; static unsigned long statelapsed = 0; static int gui_mode = 0; @@ -75,7 +85,9 @@ static void tell_char(FILE * stream, char c); static void tell_str(FILE * stream, char *str); static void tell_user(FILE * stream, char *fmt, ...); static void gui_update_stats(char *name, unsigned long size, - int percentage, unsigned long elapsed); + int percentage, unsigned long elapsed, + unsigned long done, unsigned long eta, + unsigned long ratebs); /* * The maximum amount of queued data we accept before we stop and @@ -83,11 +95,7 @@ static void gui_update_stats(char *name, unsigned long size, */ #define MAX_SCP_BUFSIZE 16384 -void logevent(char *string) -{ -} - -void ldisc_send(char *buf, int len) +void ldisc_send(char *buf, int len, int interactive) { /* * This is only here because of the calls to ldisc_send(NULL, @@ -98,122 +106,6 @@ void ldisc_send(char *buf, int len) assert(len == 0); } -void verify_ssh_host_key(char *host, int port, char *keytype, - char *keystr, char *fingerprint) -{ - int ret; - HANDLE hin; - DWORD savemode, i; - - static const char absentmsg[] = - "The server's host key is not cached in the registry. You\n" - "have no guarantee that the server is the computer you\n" - "think it is.\n" - "The server's key fingerprint is:\n" - "%s\n" - "If you trust this host, enter \"y\" to add the key to\n" - "PuTTY's cache and carry on connecting.\n" - "If you want to carry on connecting just once, without\n" - "adding the key to the cache, enter \"n\".\n" - "If you do not trust this host, press Return to abandon the\n" - "connection.\n" - "Store key in cache? (y/n) "; - - static const char wrongmsg[] = - "WARNING - POTENTIAL SECURITY BREACH!\n" - "The server's host key does not match the one PuTTY has\n" - "cached in the registry. This means that either the\n" - "server administrator has changed the host key, or you\n" - "have actually connected to another computer pretending\n" - "to be the server.\n" - "The new key fingerprint is:\n" - "%s\n" - "If you were expecting this change and trust the new key,\n" - "enter \"y\" to update PuTTY's cache and continue connecting.\n" - "If you want to carry on connecting but without updating\n" - "the cache, enter \"n\".\n" - "If you want to abandon the connection completely, press\n" - "Return to cancel. Pressing Return is the ONLY guaranteed\n" - "safe choice.\n" - "Update cached key? (y/n, Return cancels connection) "; - - static const char abandoned[] = "Connection abandoned.\n"; - - char line[32]; - - /* - * Verify the key against the registry. - */ - ret = verify_host_key(host, port, keytype, keystr); - - if (ret == 0) /* success - key matched OK */ - return; - - if (ret == 2) { /* key was different */ - fprintf(stderr, wrongmsg, fingerprint); - fflush(stderr); - } - if (ret == 1) { /* key was absent */ - fprintf(stderr, absentmsg, fingerprint); - fflush(stderr); - } - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') { - if (line[0] == 'y' || line[0] == 'Y') - store_host_key(host, port, keytype, keystr); - } else { - fprintf(stderr, abandoned); - exit(0); - } -} - -/* - * Ask whether the selected cipher is acceptable (since it was - * below the configured 'warn' threshold). - * cs: 0 = both ways, 1 = client->server, 2 = server->client - */ -void askcipher(char *ciphername, int cs) -{ - HANDLE hin; - DWORD savemode, i; - - static const char msg[] = - "The first %scipher supported by the server is\n" - "%s, which is below the configured warning threshold.\n" - "Continue with connection? (y/n) "; - static const char abandoned[] = "Connection abandoned.\n"; - - char line[32]; - - fprintf(stderr, msg, - (cs == 0) ? "" : - (cs == 1) ? "client-to-server " : - "server-to-client ", - ciphername); - fflush(stderr); - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] == 'y' || line[0] == 'Y') { - return; - } else { - fprintf(stderr, abandoned); - exit(0); - } -} - /* GUI Adaptation - Sept 2000 */ static void send_msg(HWND h, UINT message, WPARAM wParam) { @@ -253,7 +145,9 @@ static void tell_user(FILE * stream, char *fmt, ...) } static void gui_update_stats(char *name, unsigned long size, - int percentage, unsigned long elapsed) + int percentage, unsigned long elapsed, + unsigned long done, unsigned long eta, + unsigned long ratebs) { unsigned int i; @@ -268,6 +162,18 @@ static void gui_update_stats(char *name, unsigned long size, send_msg((HWND) atoi(gui_hwnd), WM_STATS_SIZE, (WPARAM) size); statsize = size; } + if (statdone != done) { + send_msg((HWND) atoi(gui_hwnd), WM_STATS_DONE, (WPARAM) done); + statdone = done; + } + if (stateta != eta) { + send_msg((HWND) atoi(gui_hwnd), WM_STATS_ETA, (WPARAM) eta); + stateta = eta; + } + if (statratebs != ratebs) { + send_msg((HWND) atoi(gui_hwnd), WM_STATS_RATEBS, (WPARAM) ratebs); + statratebs = ratebs; + } if (statelapsed != elapsed) { send_msg((HWND) atoi(gui_hwnd), WM_STATS_ELAPSED, (WPARAM) elapsed); @@ -304,7 +210,7 @@ void fatalbox(char *fmt, ...) 0 /*lParam */ ))SleepEx(1000, TRUE); } - exit(1); + cleanup_exit(1); } void connection_fatal(char *fmt, ...) { @@ -327,7 +233,7 @@ void connection_fatal(char *fmt, ...) 0 /*lParam */ ))SleepEx(1000, TRUE); } - exit(1); + cleanup_exit(1); } /* @@ -362,6 +268,8 @@ int from_backend(int is_stderr, char *data, int datalen) unsigned char *p = (unsigned char *) data; unsigned len = (unsigned) datalen; + assert(len > 0); + /* * stderr data is just spouted to local stderr and otherwise * ignored. @@ -371,8 +279,6 @@ int from_backend(int is_stderr, char *data, int datalen) return 0; } - inbuf_head = 0; - /* * If this is before the real session begins, just return. */ @@ -498,61 +404,7 @@ static void bump(char *fmt, ...) 0 /*lParam */ ))SleepEx(1000, TRUE); } - exit(1); -} - -static int get_line(const char *prompt, char *str, int maxlen, int is_pw) -{ - HANDLE hin, hout; - DWORD savemode, newmode, i; - - if (is_pw && password) { - static int tried_once = 0; - - if (tried_once) { - return 0; - } else { - strncpy(str, password, maxlen); - str[maxlen - 1] = '\0'; - tried_once = 1; - return 1; - } - } - - /* GUI Adaptation - Sept 2000 */ - if (gui_mode) { - if (maxlen > 0) - str[0] = '\0'; - } else { - hin = GetStdHandle(STD_INPUT_HANDLE); - hout = GetStdHandle(STD_OUTPUT_HANDLE); - if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) - bump("Cannot get standard input/output handles"); - - GetConsoleMode(hin, &savemode); - newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT; - if (is_pw) - newmode &= ~ENABLE_ECHO_INPUT; - else - newmode |= ENABLE_ECHO_INPUT; - SetConsoleMode(hin, newmode); - - WriteFile(hout, prompt, strlen(prompt), &i, NULL); - ReadFile(hin, str, maxlen - 1, &i, NULL); - - SetConsoleMode(hin, savemode); - - if ((int) i > maxlen) - i = maxlen - 1; - else - i = i - 2; - str[i] = '\0'; - - if (is_pw) - WriteFile(hout, "\r\n", 2, &i, NULL); - } - - return 1; + cleanup_exit(1); } /* @@ -576,6 +428,32 @@ static void do_cmd(char *host, char *user, char *cmd) cfg.port = 22; } + /* + * Trim leading whitespace off the hostname if it's there. + */ + { + int space = strspn(cfg.host, " \t"); + memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space); + } + + /* See if host is of the form user@host */ + if (cfg.host[0] != '\0') { + char *atsign = strchr(cfg.host, '@'); + /* Make sure we're not overflowing the user field */ + if (atsign) { + if (atsign - cfg.host < sizeof cfg.username) { + strncpy(cfg.username, cfg.host, atsign - cfg.host); + cfg.username[atsign - cfg.host] = '\0'; + } + memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1)); + } + } + + /* + * Trim a colon suffix off the hostname if it's there. + */ + cfg.host[strcspn(cfg.host, ":")] = '\0'; + /* Set username */ if (user != NULL && user[0] != '\0') { strncpy(cfg.username, user, sizeof(cfg.username) - 1); @@ -600,6 +478,15 @@ static void do_cmd(char *host, char *user, char *cmd) cfg.port = portnumber; /* + * Disable scary things which shouldn't be enabled for simple + * things like SCP and SFTP: agent forwarding, port forwarding, + * X forwarding. + */ + cfg.x11_forward = 0; + cfg.agentfwd = 0; + cfg.portfwd[0] = cfg.portfwd[1] = '\0'; + + /* * Attempt to start the SFTP subsystem as a first choice, * falling back to the provided scp command if that fails. */ @@ -611,7 +498,7 @@ static void do_cmd(char *host, char *user, char *cmd) back = &ssh_backend; - err = back->init(cfg.host, cfg.port, &realhost); + err = back->init(cfg.host, cfg.port, &realhost, 0); if (err != NULL) bump("ssh_init: %s", err); ssh_scp_init(); @@ -631,26 +518,29 @@ static void print_stats(char *name, unsigned long size, unsigned long done, char etastr[10]; int pct; int len; + int elap; - /* GUI Adaptation - Sept 2000 */ - if (gui_mode) - gui_update_stats(name, size, (int) (100 * (done * 1.0 / size)), - (unsigned long) difftime(now, start)); - else { - if (now > start) - ratebs = (float) done / (now - start); - else - ratebs = (float) done; + elap = (unsigned long) difftime(now, start); - if (ratebs < 1.0) - eta = size - done; - else - eta = (unsigned long) ((size - done) / ratebs); - sprintf(etastr, "%02ld:%02ld:%02ld", - eta / 3600, (eta % 3600) / 60, eta % 60); + if (now > start) + ratebs = (float) done / elap; + else + ratebs = (float) done; + + if (ratebs < 1.0) + eta = size - done; + else + eta = (unsigned long) ((size - done) / ratebs); + sprintf(etastr, "%02ld:%02ld:%02ld", + eta / 3600, (eta % 3600) / 60, eta % 60); - pct = (int) (100.0 * (float) done / size); + pct = (int) (100 * (done * 1.0 / size)); + if (gui_mode) + /* GUI Adaptation - Sept 2000 */ + gui_update_stats(name, size, pct, elap, done, eta, + (unsigned long) ratebs); + else { len = printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%", name, done / 1024, ratebs / 1024.0, etastr, pct); if (len < prev_stats_len) @@ -858,6 +748,12 @@ void scp_source_setup(char *target, int shouldbedir) */ struct fxp_attrs attrs; + if (!fxp_init()) { + tell_user(stderr, "unable to initialise SFTP: %s", fxp_error()); + errs++; + return 1; + } + if (!fxp_stat(target, &attrs) || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) scp_sftp_targetisdir = 0; @@ -1069,6 +965,12 @@ int scp_sink_setup(char *source, int preserve, int recursive) { if (using_sftp) { char *newsource; + + if (!fxp_init()) { + tell_user(stderr, "unable to initialise SFTP: %s", fxp_error()); + errs++; + return 1; + } /* * It's possible that the source string we've been given * contains a wildcard. If so, we must split the directory @@ -2165,7 +2067,7 @@ static void usage(void) printf (" -gui hWnd GUI mode with the windows handle for receiving messages\n"); #endif - exit(1); + cleanup_exit(1); } /* @@ -2178,7 +2080,7 @@ int main(int argc, char *argv[]) default_protocol = PROT_TELNET; flags = FLAG_STDERR; - ssh_get_line = &get_line; + ssh_get_line = &console_get_line; init_winsock(); sk_init(); @@ -2193,15 +2095,18 @@ int main(int argc, char *argv[]) preserve = 1; else if (strcmp(argv[i], "-q") == 0) statistics = 0; + else if (strcmp(argv[i], "-batch") == 0) + console_batch_mode = 1; else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0) usage(); else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc) portnumber = atoi(argv[++i]); else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc) - password = argv[++i]; + console_password = argv[++i]; else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) { gui_hwnd = argv[++i]; gui_mode = 1; + console_batch_mode = TRUE; } else if (strcmp(argv[i], "-ls") == 0) list = 1; else if (strcmp(argv[i], "-unsafe") == 0)