# # old_revision [701f8835d5e1759b9b73c6fd7d1eab708f3b7882] # # add_file ".mt-attrs" # # add_file "ssh-xfer.c" # # patch ".mt-attrs" # from [] # to [2255fea673e2fa649896aa80a39fe72cb4fb966d] # # patch "Makefile.in" # from [2d0423fea68d8a3004e32b7e262dfa36c0391924] # to [504fc9e5e067b3e8580f567a93dc758a63fe552d] # # patch "authfd.c" # from [eff279634e8dcd5cc9b6c64213a8d36a68092538] # to [b05e152abae99780d98530a215db09c602c17711] # # patch "authfd.h" # from [298895eee53b2c4cbf05fd16f58d3070e402ed12] # to [bbbda6d15a0409823787cb25fef1a7404d0b9132] # # patch "ssh-agent.c" # from [e198575e6448e6f553ccbad54bb7cc4900f9d2eb] # to [bcc34251bc107b9daa1a7f6283c807aca3479876] # # patch "ssh-xfer.c" # from [] # to [a0ec393c7c91687f1e8f452ad46dbe82a6f27bec] # ============================================================ --- Makefile.in 2d0423fea68d8a3004e32b7e262dfa36c0391924 +++ Makefile.in 504fc9e5e067b3e8580f567a93dc758a63fe552d @@ -59,7 +59,7 @@ INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@ INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@ -TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT) +TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-xfer$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT) LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o buffer.o \ canohost.o channels.o cipher.o cipher-acss.o cipher-aes.o \ @@ -143,6 +143,9 @@ ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) +ssh-xfer$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-xfer.o + $(LD) -o $@ ssh-xfer.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o $(LD) -o $@ ssh-agent.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) @@ -248,6 +251,7 @@ $(INSTALL) -m 0755 $(STRIP_OPT) ssh $(DESTDIR)$(bindir)/ssh $(INSTALL) -m 0755 $(STRIP_OPT) scp $(DESTDIR)$(bindir)/scp $(INSTALL) -m 0755 $(STRIP_OPT) ssh-add $(DESTDIR)$(bindir)/ssh-add + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-xfer $(DESTDIR)$(bindir)/ssh-xfer $(INSTALL) -m 0755 $(STRIP_OPT) ssh-agent $(DESTDIR)$(bindir)/ssh-agent $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keygen $(DESTDIR)$(bindir)/ssh-keygen $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keyscan $(DESTDIR)$(bindir)/ssh-keyscan @@ -349,6 +353,7 @@ -rm -f $(DESTDIR)$(bindir)/ssh$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/scp$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/ssh-add$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/ssh-xfer$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) ============================================================ --- authfd.c eff279634e8dcd5cc9b6c64213a8d36a68092538 +++ authfd.c b05e152abae99780d98530a215db09c602c17711 @@ -112,6 +112,27 @@ } static int +ssh_request_noreply(AuthenticationConnection *auth, Buffer *request) +{ + int l; + u_int len; + char buf[1024]; + + /* Get the length of the message, and format it in the buffer. */ + len = buffer_len(request); + PUT_32BIT(buf, len); + + /* Send the length and then the packet to the agent. */ + if (atomicio(vwrite, auth->fd, buf, 4) != 4 || + atomicio(vwrite, auth->fd, buffer_ptr(request), + buffer_len(request)) != buffer_len(request)) { + error("Error writing to authentication socket."); + return 0; + } + return 1; +} + +static int ssh_request_reply(AuthenticationConnection *auth, Buffer *request, Buffer *reply) { int l; @@ -139,7 +160,7 @@ if (l == -1 && (errno == EAGAIN || errno == EINTR)) continue; if (l <= 0) { - error("Error reading response length from authentication socket."); + error("Error reading response length from authentication socket: %s", strerror(errno)); return 0; } len -= l; @@ -150,6 +171,7 @@ if (len > 256 * 1024) fatal("Authentication response too long: %u", len); + /* Read the rest of the response in to the buffer. */ buffer_clear(reply); while (len > 0) { @@ -160,7 +182,8 @@ if (l == -1 && (errno == EAGAIN || errno == EINTR)) continue; if (l <= 0) { - error("Error reading response from authentication socket."); + error("Error reading response from authentication socket: %s", + strerror(errno)); return 0; } buffer_append(reply, buf, l); @@ -669,3 +692,63 @@ /* NOTREACHED */ return 0; } + +int +send_xferreq(AuthenticationConnection *auth, char* filename, u_int size, + int havesize) { + + Buffer request, reply; + int ret; + + buffer_init(&request); + buffer_init(&reply); + + buffer_put_char(&request, SSH_AGENTC_XFER_REQUEST); + buffer_put_string(&request, filename, strlen(filename)); + buffer_put_int(&request, size); + buffer_put_int(&request, havesize); + + fprintf(stderr, "Sending file\n"); + ret = ssh_request_reply(auth, &request, &reply); + + buffer_free(&reply); + buffer_free(&request); + return ret; +} + +int send_xferdata(AuthenticationConnection *auth, char* buf, u_int size) { + + Buffer request; + int ret; + + buffer_init(&request); + + buffer_put_char(&request, SSH_AGENTC_XFER_DATA); + buffer_put_string(&request, buf, size); + + fprintf(stderr, "."); + ret = ssh_request_noreply(auth, &request); + + buffer_free(&request); + return ret; +} + +int send_xferdone(AuthenticationConnection *auth) { + + Buffer request, reply; + int ret; + + buffer_init(&request); + buffer_init(&reply); + + buffer_put_char(&request, SSH_AGENTC_XFER_DONE); + + fprintf(stderr, " done"); + ret = ssh_request_reply(auth, &request, &reply); + fprintf(stderr, ".\n"); + + buffer_free(&reply); + buffer_free(&request); + return ret; +} + ============================================================ --- authfd.h 298895eee53b2c4cbf05fd16f58d3070e402ed12 +++ authfd.h bbbda6d15a0409823787cb25fef1a7404d0b9132 @@ -57,6 +57,12 @@ /* extended failure messages */ #define SSH2_AGENT_FAILURE 30 +/* matt's filexfer-over-agent */ +#define SSH_AGENTC_XFER_REQUEST 90 +#define SSH_AGENTC_XFER_DONE 91 +#define SSH_AGENTC_XFER_DATA 92 + + /* additional error code for ssh.com's ssh-agent2 */ #define SSH_COM_AGENT2_FAILURE 102 @@ -94,4 +100,10 @@ ssh_agent_sign(AuthenticationConnection *, Key *, u_char **, u_int *, u_char *, u_int); +int +send_xferreq(AuthenticationConnection *auth, char* filename, u_int size, + int havesize); +int send_xferdata(AuthenticationConnection *auth, char* buf, u_int size); +int send_xferdone(AuthenticationConnection *auth); + #endif /* AUTHFD_H */ ============================================================ --- ssh-agent.c e198575e6448e6f553ccbad54bb7cc4900f9d2eb +++ ssh-agent.c bcc34251bc107b9daa1a7f6283c807aca3479876 @@ -52,7 +52,11 @@ #include "log.h" #include "readpass.h" #include "misc.h" +#include "tildexpand.h" +/* for basename */ +#include + #ifdef SMARTCARD #include "scard.h" #endif @@ -61,18 +65,33 @@ #include /* For prctl() and PR_SET_DUMPABLE */ #endif +#define XFER_DEST_DIR "~/Desktop" + typedef enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } sock_type; +/* matt */ typedef struct { + + char* filename; + unsigned int size; + unsigned int havesize; /* A flag if we have a filesize */ + unsigned int progress; /* How far we've got */ + char filepath[MAXPATHLEN]; int fd; + +} FileXFer; + +typedef struct { + int fd; sock_type type; Buffer input; Buffer output; Buffer request; + FileXFer xferdata; /* matt */ } SocketEntry; u_int sockets_alloc = 0; @@ -125,6 +144,10 @@ buffer_free(&e->input); buffer_free(&e->output); buffer_free(&e->request); + if (e->xferdata.filename) { + xfree(e->xferdata.filename); + e->xferdata.filename = NULL; + } } static void @@ -193,6 +216,156 @@ return (ret); } +static void +process_xferdone(SocketEntry *e) { + + /* HACKY */ + debug("process_xferdone"); + buffer_clear(&e->request); + + buffer_put_int(&e->output, 1); + if (e->xferdata.filename != NULL) { + xfree(e->xferdata.filename); + e->xferdata.filename = NULL; + buffer_put_char(&e->output, SSH_AGENT_SUCCESS); + } else { + buffer_put_char(&e->output, SSH_AGENT_FAILURE); + } + debug("process_xferdone done"); +} + +static void +process_xfer(SocketEntry *e) { + + char* full_filename = NULL; + char* filename = NULL; + char* filepath = NULL; + char* destdir = NULL; + int i, fd; + + full_filename = buffer_get_string(&e->request, NULL); + e->xferdata.size = buffer_get_int(&e->request); + e->xferdata.havesize = buffer_get_int(&e->request); + + if (strstr(full_filename, "..") != NULL) { + logit("ssh-xfer remote side tried to send a filename with '..' in it. Bad remote-side!"); + goto fail; + } + + filename = xstrdup(basename(full_filename)); + xfree(full_filename); + full_filename = NULL; + + debug("process_xfer: read data: file '%s' size '%d'", filename, + e->xferdata.size); + + destdir = tilde_expand_filename(XFER_DEST_DIR, getuid()); + + e->xferdata.fd = -1; + + /* TODO! matt - make it configurable*/ + filepath = e->xferdata.filepath; + snprintf(filepath, MAXPATHLEN, "%s/%s", destdir, filename); + + fd = open(filepath, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | + S_IWUSR); + + if (fd < 0) { + for (i = 0; i < 1000; i++) { + snprintf(filepath, MAXPATHLEN, "%s/%s.%d", destdir, filename, i); + + fd = open(filepath, O_CREAT | O_WRONLY | O_EXCL, + S_IRUSR | S_IWUSR); + if (fd >= 0) { + break; + } + + } + } + + if (fd < 0) { + debug("failed opening file %s: %s", filepath, strerror(errno)); + goto fail; + } + + debug("filepath is '%s'", filepath); + e->xferdata.filename = filename; + + e->xferdata.fd = fd; + e->xferdata.progress = 0; + + buffer_clear(&e->request); + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, SSH_AGENT_SUCCESS); + debug("finish process: success"); + return; + +fail: + if (filename) { + xfree(filename); + } + buffer_clear(&e->request); + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, SSH_AGENT_FAILURE); + debug("finish process: fail"); + +} + +static void +process_xferdata(SocketEntry *e) { + + unsigned int size; + int val; + + debug("enter process_xferdata"); + if (e->xferdata.filename == NULL || e->xferdata.fd < 0) { + logit("Client tried xferdata with unopened socket: fd %d file %s", + e->xferdata.fd, e->xferdata.filename); + close_socket(e); + return; + } + + size = buffer_get_int(&e->request); + + /* We fail if: + * - we had a filesize (havesize == 1) and this write would go past the end + * or + * - the buffer doesn't have enough data for this chunk size */ + if ( (e->xferdata.havesize == 1 + && (size + e->xferdata.progress > e->xferdata.size)) + || buffer_len(&e->request) < size ) { + + /* Past end of file */ + logit("ssh-xfer write past end of file: size %d progress %d filesize %d buflen %d", size, e->xferdata.progress, e->xferdata.size, buffer_len(&e->request)); + goto finishup; + } + + val = write(e->xferdata.fd, buffer_ptr(&e->request), size); + if (val < 0) { + logit("failed write for fd %d: %s", e->xferdata.fd, strerror(errno)); + goto finishup; + } + + e->xferdata.progress += size; + + buffer_clear(&e->request); + /* + No output is written, that way we don't have problems with + latency/windowing etc. The SSH channel layer should handle all of that. + */ + close(e->xferdata.fd); + debug("process_xferdata: success"); + return; + +finishup: + close(e->xferdata.fd); + unlink(e->xferdata.filepath); + debug("process_xferdata: fail"); + +} + + + /* send list of supported public keys to 'client' */ static void process_request_identities(SocketEntry *e, int version) @@ -682,7 +855,8 @@ /* dispatch incoming messages */ -static void +/* Returns 0 on finished, or 1 on "call me again please" */ +static int process_message(SocketEntry *e) { u_int msg_len, type; @@ -692,15 +866,15 @@ reaper(); if (buffer_len(&e->input) < 5) - return; /* Incomplete message. */ + return 0; /* Incomplete message. */ cp = buffer_ptr(&e->input); msg_len = GET_32BIT(cp); if (msg_len > 256 * 1024) { close_socket(e); - return; + return 0; } if (buffer_len(&e->input) < msg_len + 4) - return; + return 0; /* move the current input to e->request */ buffer_consume(&e->input, 4); @@ -723,7 +897,7 @@ buffer_put_int(&e->output, 1); buffer_put_char(&e->output, SSH_AGENT_FAILURE); } - return; + return 1; } debug("type %d", type); @@ -775,6 +949,15 @@ process_remove_smartcard_key(e); break; #endif /* SMARTCARD */ + case SSH_AGENTC_XFER_REQUEST: + process_xfer(e); + break; + case SSH_AGENTC_XFER_DATA: + process_xferdata(e); + break; + case SSH_AGENTC_XFER_DONE: + process_xferdone(e); + break; default: /* Unknown message. Respond with failure. */ error("Unknown message %d", type); @@ -783,6 +966,7 @@ buffer_put_char(&e->output, SSH_AGENT_FAILURE); break; } + return 1; } static void @@ -803,6 +987,7 @@ buffer_init(&sockets[i].output); buffer_init(&sockets[i].request); sockets[i].type = type; + sockets[i].xferdata.filename = NULL; return; } old_alloc = sockets_alloc; @@ -862,6 +1047,8 @@ case AUTH_SOCKET: case AUTH_CONNECTION: FD_SET(sockets[i].fd, *fdrp); + debug("fdset, output len (%d) = %d", sockets[i].fd, + buffer_len(&sockets[i].output)); if (buffer_len(&sockets[i].output) > 0) FD_SET(sockets[i].fd, *fdwp); break; @@ -883,6 +1070,8 @@ uid_t euid; gid_t egid; + debug("after select"); + for (i = 0; i < sockets_alloc; i++) switch (sockets[i].type) { case AUTH_UNUSED: @@ -914,6 +1103,7 @@ } break; case AUTH_CONNECTION: + debug("AUTH_CONNECTION"); if (buffer_len(&sockets[i].output) > 0 && FD_ISSET(sockets[i].fd, writeset)) { do { @@ -931,11 +1121,13 @@ } buffer_consume(&sockets[i].output, len); } + if (FD_ISSET(sockets[i].fd, readset)) { do { len = read(sockets[i].fd, buf, sizeof(buf)); if (len == -1 && (errno == EAGAIN || errno == EINTR)) + //debug("read EINTR"); continue; break; } while (1); @@ -944,7 +1136,7 @@ break; } buffer_append(&sockets[i].input, buf, len); - process_message(&sockets[i]); + while ( process_message(&sockets[i]) == 1) {}; } break; default: @@ -1233,6 +1425,7 @@ while (1) { prepare_select(&readsetp, &writesetp, &max_fd, &nalloc); + if (select(max_fd + 1, readsetp, writesetp, NULL, NULL) < 0) { if (errno == EINTR) continue; ============================================================ --- ssh-xfer.c +++ ssh-xfer.c a0ec393c7c91687f1e8f452ad46dbe82a6f27bec @@ -0,0 +1,165 @@ +/* + * ssh-xfer.c (c) 2004 Matt Johnston matt at ucc.asn.au + * + * Based on ssh-add.c which copyright as below. + * + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Adds an identity to the authentication server, or removes an identity. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 implementation, + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include "ssh.h" +#include "log.h" +#include "xmalloc.h" +#include "key.h" +#include "authfd.h" +#include "authfile.h" +#include "pathnames.h" +#include "readpass.h" +#include "misc.h" + +/* for basename */ +#include + +#ifdef HAVE___PROGNAME +extern char *__progname; +#else +char *__progname; +#endif + + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [-]\n", __progname); + fprintf(stderr, "If '-' is given as the second argument, indicates the naming to use,\nwith data taken from standard input.\n"); + +} + +int +main(int argc, char **argv) +{ + AuthenticationConnection *ac = NULL; + int ret; + + char* filename = NULL; + unsigned int filesize; + unsigned int isfile; + int filefd = -1; + int len; + char readbuf[8000]; /* If it's more than about this, the Pageant won't + work.... */ + + struct stat sb; + + __progname = ssh_get_progname(argv[0]); + + if (argc < 2 || argc > 3) { + usage(); + exit(1); + } + + /* At first, get a connection to the authentication agent. */ + ac = ssh_get_authentication_connection(); + if (ac == NULL) { + fprintf(stderr, "Could not open a connection to your authentication agent.\n"); + exit(2); + } + + filename = argv[1]; + + if (argc == 2) { + isfile = 1; + filefd = open(filename, O_RDONLY, 0); + if (filefd < 0) { + fprintf(stderr, "Failed opening file '%s': %s\n", + filename, strerror(errno)); + exit(1); + } + + /* Get the filesize */ + if (fstat(filefd, &sb) < 0) { + fprintf(stderr, "Failed getting size of file: %s\n", + strerror(errno)); + exit(1); + } + filesize = sb.st_size; + + } else { + if (strcmp(argv[2], "-") != 0) { + usage(); + exit(1); + } + isfile = 0; + filefd = STDIN_FILENO; + } + + if (send_xferreq(ac, basename(filename), filesize, isfile) == 0) { + fprintf(stderr, "Agent responded with failure message.\n"); + exit(1); + } + + /* The sending loop */ + for (;;) { + len = read(filefd, readbuf, sizeof(readbuf)); + if (len < 0) { + if (errno == EINTR) { + continue; /* try again */ + } + fprintf(stderr, "Error reading file: %s\n", strerror(errno)); + ret = 2; + goto done; + } + + if (len == 0) { + close(filefd); + send_xferdone(ac); + ret = 0; + goto done; + } + + ret = send_xferdata(ac, readbuf, len); + if (ret == 0) { + fprintf(stderr, "Error sending file."); + ret = 3; + goto done; + } + } + +done: + close(filefd); + ssh_close_authentication_connection(ac); + return ret; +}