diff options
-rw-r--r-- | usr.bin/ssh/Makefile | 4 | ||||
-rw-r--r-- | usr.bin/ssh/scp.1 | 3 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-client.c | 792 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-client.h | 84 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-common.c | 146 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-common.h | 55 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-int.c | 583 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-int.h | 27 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-server.c | 102 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-server/Makefile | 4 | ||||
-rw-r--r-- | usr.bin/ssh/sftp.1 | 156 | ||||
-rw-r--r-- | usr.bin/ssh/sftp.c | 222 | ||||
-rw-r--r-- | usr.bin/ssh/sftp/Makefile | 19 | ||||
-rw-r--r-- | usr.bin/ssh/ssh.1 | 3 | ||||
-rw-r--r-- | usr.bin/ssh/sshd.8 | 3 |
15 files changed, 2097 insertions, 106 deletions
diff --git a/usr.bin/ssh/Makefile b/usr.bin/ssh/Makefile index 59eed7d4873..eea6f141579 100644 --- a/usr.bin/ssh/Makefile +++ b/usr.bin/ssh/Makefile @@ -1,9 +1,9 @@ -# $OpenBSD: Makefile,v 1.7 2000/12/04 19:24:01 markus Exp $ +# $OpenBSD: Makefile,v 1.8 2001/02/04 11:11:53 djm Exp $ .include <bsd.own.mk> SUBDIR= lib ssh sshd ssh-add ssh-keygen ssh-agent scp sftp-server \ - ssh-keyscan + ssh-keyscan sftp distribution: install -C -o root -g wheel -m 0644 ${.CURDIR}/ssh_config \ diff --git a/usr.bin/ssh/scp.1 b/usr.bin/ssh/scp.1 index 0a2ca1a3445..10e67aa33d1 100644 --- a/usr.bin/ssh/scp.1 +++ b/usr.bin/ssh/scp.1 @@ -9,7 +9,7 @@ .\" .\" Created: Sun May 7 00:14:37 1995 ylo .\" -.\" $OpenBSD: scp.1,v 1.13 2000/10/16 09:38:44 djm Exp $ +.\" $OpenBSD: scp.1,v 1.14 2001/02/04 11:11:53 djm Exp $ .\" .Dd September 25, 1999 .Dt SCP 1 @@ -129,6 +129,7 @@ program in BSD source code from the Regents of the University of California. .Sh SEE ALSO .Xr rcp 1 , +.Xr sftp 1 , .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , diff --git a/usr.bin/ssh/sftp-client.c b/usr.bin/ssh/sftp-client.c new file mode 100644 index 00000000000..458d7364a4f --- /dev/null +++ b/usr.bin/ssh/sftp-client.c @@ -0,0 +1,792 @@ +/* + * Copyright (c) 2001 Damien Miller. 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. + */ + +/* XXX: memleaks */ +/* XXX: signed vs unsigned */ +/* XXX: redesign to allow concurrent overlapped operations */ +/* XXX: we use fatal too much, error may be more appropriate in places */ +/* XXX: copy between two remote sites */ + +#include "includes.h" +RCSID("$OpenBSD: sftp-client.c,v 1.1 2001/02/04 11:11:54 djm Exp $"); + +#include "ssh.h" +#include "buffer.h" +#include "bufaux.h" +#include "getput.h" +#include "xmalloc.h" +#include "log.h" +#include "atomicio.h" +#include "pathnames.h" + +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" + +/* How much data to read/write at at time during copies */ +/* XXX: what should this be? */ +#define COPY_SIZE 8192 + +void +send_msg(int fd, Buffer *m) +{ + int mlen = buffer_len(m); + int len; + Buffer oqueue; + + buffer_init(&oqueue); + buffer_put_int(&oqueue, mlen); + buffer_append(&oqueue, buffer_ptr(m), mlen); + buffer_consume(m, mlen); + + len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue)); + if (len <= 0) + fatal("Couldn't send packet: %s", strerror(errno)); + + buffer_free(&oqueue); +} + +void +get_msg(int fd, Buffer *m) +{ + u_int len, msg_len; + unsigned char buf[4096]; + + len = atomicio(read, fd, buf, 4); + if (len != 4) + fatal("Couldn't read packet: %s", strerror(errno)); + + msg_len = GET_32BIT(buf); + if (msg_len > 256 * 1024) + fatal("Received message too long %d", msg_len); + + while (msg_len) { + len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf))); + if (len <= 0) + fatal("Couldn't read packet: %s", strerror(errno)); + + msg_len -= len; + buffer_append(m, buf, len); + } +} + +void +send_string_request(int fd, u_int id, u_int code, char *s, + u_int len) +{ + Buffer msg; + + buffer_init(&msg); + buffer_put_char(&msg, code); + buffer_put_int(&msg, id); + buffer_put_string(&msg, s, len); + send_msg(fd, &msg); + debug3("Sent message fd %d T:%d I:%d", fd, code, id); + buffer_free(&msg); +} + +void +send_string_attrs_request(int fd, u_int id, u_int code, char *s, + u_int len, Attrib *a) +{ + Buffer msg; + + buffer_init(&msg); + buffer_put_char(&msg, code); + buffer_put_int(&msg, id); + buffer_put_string(&msg, s, len); + encode_attrib(&msg, a); + send_msg(fd, &msg); + debug3("Sent message fd %d T:%d I:%d", fd, code, id); + buffer_free(&msg); +} + +u_int +get_status(int fd, int expected_id) +{ + Buffer msg; + u_int type, id, status; + + buffer_init(&msg); + get_msg(fd, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + if (type != SSH2_FXP_STATUS) + fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d", + SSH2_FXP_STATUS, type); + + status = buffer_get_int(&msg); + buffer_free(&msg); + + debug3("SSH2_FXP_STATUS %d", status); + + return(status); +} + +char * +get_handle(int fd, u_int expected_id, u_int *len) +{ + Buffer msg; + u_int type, id; + char *handle; + + buffer_init(&msg); + get_msg(fd, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + if (type == SSH2_FXP_STATUS) { + int status = buffer_get_int(&msg); + + error("Couldn't get handle: %s", fx2txt(status)); + return(NULL); + } else if (type != SSH2_FXP_HANDLE) + fatal("Expected SSH2_FXP_HANDLE(%d) packet, got %d", + SSH2_FXP_HANDLE, type); + + handle = buffer_get_string(&msg, len); + buffer_free(&msg); + + return(handle); +} + +Attrib * +get_decode_stat(int fd, u_int expected_id) +{ + Buffer msg; + u_int type, id; + Attrib *a; + + buffer_init(&msg); + get_msg(fd, &msg); + + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + debug3("Received stat reply T:%d I:%d", type, id); + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + if (type == SSH2_FXP_STATUS) { + int status = buffer_get_int(&msg); + + error("Couldn't stat remote file: %s", fx2txt(status)); + return(NULL); + } else if (type != SSH2_FXP_ATTRS) { + fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d", + SSH2_FXP_ATTRS, type); + } + a = decode_attrib(&msg); + buffer_free(&msg); + + return(a); +} + +int +do_init(int fd_in, int fd_out) +{ + int type, version; + Buffer msg; + + buffer_init(&msg); + buffer_put_char(&msg, SSH2_FXP_INIT); + buffer_put_int(&msg, SSH2_FILEXFER_VERSION); + send_msg(fd_out, &msg); + + buffer_clear(&msg); + + get_msg(fd_in, &msg); + + /* Expecting a VERSION reply */ + if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { + error("Invalid packet back from SSH2_FXP_INIT (type %d)", + type); + buffer_free(&msg); + return(-1); + } + version = buffer_get_int(&msg); + + debug2("Remote version: %d", version); + + /* Check for extensions */ + while (buffer_len(&msg) > 0) { + char *name = buffer_get_string(&msg, NULL); + char *value = buffer_get_string(&msg, NULL); + + debug2("Init extension: \"%s\"", name); + xfree(name); + xfree(value); + } + + buffer_free(&msg); + return(0); +} + +int +do_close(int fd_in, int fd_out, char *handle, u_int handle_len) +{ + u_int id, status; + Buffer msg; + + buffer_init(&msg); + + id = arc4random(); + buffer_put_char(&msg, SSH2_FXP_CLOSE); + buffer_put_int(&msg, id); + buffer_put_string(&msg, handle, handle_len); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_CLOSE I:%d", id); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't close file: %s", fx2txt(status)); + + buffer_free(&msg); + + return(status); +} + +int +do_ls(int fd_in, int fd_out, char *path) +{ + Buffer msg; + u_int type, id, handle_len, i, expected_id; + char *handle; + + id = arc4random(); + + buffer_init(&msg); + buffer_put_char(&msg, SSH2_FXP_OPENDIR); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, path); + send_msg(fd_out, &msg); + + buffer_clear(&msg); + + handle = get_handle(fd_in, id, &handle_len); + if (handle == NULL) + return(-1); + + for(;;) { + int count; + + expected_id = ++id; + + debug3("Sending SSH2_FXP_READDIR I:%d", id); + + buffer_clear(&msg); + buffer_put_char(&msg, SSH2_FXP_READDIR); + buffer_put_int(&msg, id); + buffer_put_string(&msg, handle, handle_len); + send_msg(fd_out, &msg); + + buffer_clear(&msg); + + get_msg(fd_in, &msg); + + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + debug3("Received reply T:%d I:%d", type, id); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + + if (type == SSH2_FXP_STATUS) { + int status = buffer_get_int(&msg); + + debug3("Received SSH2_FXP_STATUS %d", status); + + if (status == SSH2_FX_EOF) { + break; + } else { + error("Couldn't read directory: %s", + fx2txt(status)); + do_close(fd_in, fd_out, handle, handle_len); + return(NULL); + } + } else if (type != SSH2_FXP_NAME) + fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", + SSH2_FXP_NAME, type); + + count = buffer_get_int(&msg); + debug3("Received %i SSH2_FXP_NAME responses", count); + for(i = 0; i < count; i++) { + char *filename, *longname; + Attrib *a; + + filename = buffer_get_string(&msg, NULL); + longname = buffer_get_string(&msg, NULL); + a = decode_attrib(&msg); + + printf("%s\n", longname); + + xfree(filename); + xfree(longname); + } + } + + buffer_free(&msg); + do_close(fd_in, fd_out, handle, handle_len); + xfree(handle); + + return(0); +} + +int +do_rm(int fd_in, int fd_out, char *path) +{ + u_int status, id; + + debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); + + id = arc4random(); + send_string_request(fd_out, id, SSH2_FXP_REMOVE, path, strlen(path)); + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't delete file: %s", fx2txt(status)); + return(status); +} + +int +do_mkdir(int fd_in, int fd_out, char *path, Attrib *a) +{ + u_int status, id; + + id = arc4random(); + send_string_attrs_request(fd_out, id, SSH2_FXP_MKDIR, path, + strlen(path), a); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't create directory: %s", fx2txt(status)); + + return(status); +} + +int +do_rmdir(int fd_in, int fd_out, char *path) +{ + u_int status, id; + + id = arc4random(); + send_string_request(fd_out, id, SSH2_FXP_RMDIR, path, strlen(path)); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't remove directory: %s", fx2txt(status)); + + return(status); +} + +Attrib * +do_stat(int fd_in, int fd_out, char *path) +{ + u_int id; + + id = arc4random(); + send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path)); + return(get_decode_stat(fd_in, id)); +} + +Attrib * +do_lstat(int fd_in, int fd_out, char *path) +{ + u_int id; + + id = arc4random(); + send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path)); + return(get_decode_stat(fd_in, id)); +} + +Attrib * +do_fstat(int fd_in, int fd_out, char *handle, + u_int handle_len) +{ + u_int id; + + id = arc4random(); + send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len); + return(get_decode_stat(fd_in, id)); +} + +int +do_setstat(int fd_in, int fd_out, char *path, Attrib *a) +{ + u_int status, id; + + id = arc4random(); + send_string_attrs_request(fd_out, id, SSH2_FXP_SETSTAT, path, + strlen(path), a); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't setstat on \"%s\": %s", path, + fx2txt(status)); + + return(status); +} + +int +do_fsetstat(int fd_in, int fd_out, char *handle, u_int handle_len, + Attrib *a) +{ + u_int status, id; + + id = arc4random(); + send_string_attrs_request(fd_out, id, SSH2_FXP_FSETSTAT, handle, + handle_len, a); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't fsetstat: %s", fx2txt(status)); + + return(status); +} + +char * +do_realpath(int fd_in, int fd_out, char *path) +{ + Buffer msg; + u_int type, expected_id, count, id; + char *filename, *longname; + Attrib *a; + + expected_id = id = arc4random(); + send_string_request(fd_out, id, SSH2_FXP_REALPATH, path, + strlen(path)); + + buffer_init(&msg); + + get_msg(fd_in, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + + if (type == SSH2_FXP_STATUS) { + u_int status = buffer_get_int(&msg); + + error("Couldn't canonicalise: %s", fx2txt(status)); + return(NULL); + } else if (type != SSH2_FXP_NAME) + fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", + SSH2_FXP_NAME, type); + + count = buffer_get_int(&msg); + if (count != 1) + fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count); + + filename = buffer_get_string(&msg, NULL); + longname = buffer_get_string(&msg, NULL); + a = decode_attrib(&msg); + + debug3("SSH_FXP_REALPATH %s -> %s", path, filename); + + xfree(longname); + + buffer_free(&msg); + + return(filename); +} + +int +do_rename(int fd_in, int fd_out, char *oldpath, char *newpath) +{ + Buffer msg; + u_int status, id; + + buffer_init(&msg); + + /* Send rename request */ + id = arc4random(); + buffer_put_char(&msg, SSH2_FXP_RENAME); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, oldpath); + buffer_put_cstring(&msg, newpath); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath, + newpath); + buffer_free(&msg); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath, + fx2txt(status)); + + return(status); +} + +int +do_download(int fd_in, int fd_out, char *remote_path, char *local_path, + int pflag) +{ + int local_fd; + u_int expected_id, handle_len, mode, type, id; + u_int64_t offset; + char *handle; + Buffer msg; + Attrib junk, *a; + + a = do_stat(fd_in, fd_out, remote_path); + if (a == NULL) + return(-1); + + /* XXX: should we preserve set[ug]id? */ + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + mode = S_IWRITE | (a->perm & 0777); + else + mode = 0666; + + local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode); + if (local_fd == -1) { + error("Couldn't open local file \"%s\" for writing: %s", + local_path, strerror(errno)); + return(errno); + } + + /* Override umask and utimes if asked */ + if (pflag && fchmod(local_fd, mode) == -1) + error("Couldn't set mode on \"%s\": %s", local_path, + strerror(errno)); + if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { + struct timeval tv; + + tv.tv_sec = a->atime; + tv.tv_usec = a->mtime; + if (utimes(local_path, &tv) == -1) + error("Can't set times on \"%s\": %s", local_path, + strerror(errno)); + } + + buffer_init(&msg); + + /* Send open request */ + id = arc4random(); + buffer_put_char(&msg, SSH2_FXP_OPEN); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, remote_path); + buffer_put_int(&msg, SSH2_FXF_READ); + attrib_clear(&junk); /* Send empty attributes */ + encode_attrib(&msg, &junk); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path); + + handle = get_handle(fd_in, id, &handle_len); + if (handle == NULL) { + buffer_free(&msg); + close(local_fd); + return(-1); + } + + /* Read from remote and write to local */ + offset = 0; + for(;;) { + u_int len; + char *data; + + expected_id = ++id; + + buffer_clear(&msg); + buffer_put_char(&msg, SSH2_FXP_READ); + buffer_put_int(&msg, id); + buffer_put_string(&msg, handle, handle_len); + buffer_put_int64(&msg, offset); + buffer_put_int(&msg, COPY_SIZE); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u", + id, offset, COPY_SIZE); + + buffer_clear(&msg); + + get_msg(fd_in, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + debug3("Received reply T:%d I:%d", type, id); + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + if (type == SSH2_FXP_STATUS) { + int status = buffer_get_int(&msg); + + if (status == SSH2_FX_EOF) + break; + else { + error("Couldn't read from remote " + "file \"%s\" : %s", remote_path, + fx2txt(status)); + do_close(fd_in, fd_out, handle, handle_len); + xfree(handle); + close(local_fd); + buffer_free(&msg); + return(status); + } + } else if (type != SSH2_FXP_DATA) { + fatal("Expected SSH2_FXP_DATA(%d) packet, got %d", + SSH2_FXP_DATA, type); + } + + data = buffer_get_string(&msg, &len); + if (len > COPY_SIZE) + fatal("Received more data than asked for %d > %d", + len, COPY_SIZE); + + debug3("In read loop, got %d offset %lld", len, offset); + if (atomicio(write, local_fd, data, len) != len) { + error("Couldn't write to \"%s\": %s", local_path, + strerror(errno)); + do_close(fd_in, fd_out, handle, handle_len); + xfree(handle); + close(local_fd); + xfree(data); + buffer_free(&msg); + return(-1); + } + + offset += len; + xfree(data); + } + xfree(handle); + buffer_free(&msg); + close(local_fd); + + return(do_close(fd_in, fd_out, handle, handle_len)); +} + +int +do_upload(int fd_in, int fd_out, char *local_path, char *remote_path, + int pflag) +{ + int local_fd; + u_int handle_len, id; + u_int64_t offset; + char *handle; + Buffer msg; + struct stat sb; + Attrib a; + + if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { + error("Couldn't open local file \"%s\" for reading: %s", + local_path, strerror(errno)); + return(-1); + } + if (fstat(local_fd, &sb) == -1) { + error("Couldn't fstat local file \"%s\": %s", + local_path, strerror(errno)); + close(local_fd); + return(-1); + } + stat_to_attrib(&sb, &a); + + a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; + a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; + a.perm &= 0777; + if (!pflag) + a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; + + buffer_init(&msg); + + /* Send open request */ + id = arc4random(); + buffer_put_char(&msg, SSH2_FXP_OPEN); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, remote_path); + buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); + encode_attrib(&msg, &a); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path); + + buffer_clear(&msg); + + handle = get_handle(fd_in, id, &handle_len); + if (handle == NULL) { + close(local_fd); + buffer_free(&msg); + return(-1); + } + + /* Override umask and utimes if asked */ + if (pflag) + do_fsetstat(fd_in, fd_out, handle, handle_len, &a); + + /* Read from local and write to remote */ + offset = 0; + for(;;) { + int len; + char data[COPY_SIZE]; + u_int status; + + /* + * Can't use atomicio here because it returns 0 on EOF, thus losing + * the last block of the file + */ + do + len = read(local_fd, data, COPY_SIZE); + while ((len == -1) && (errno == EINTR || errno == EAGAIN)); + + if (len == -1) + fatal("Couldn't read from \"%s\": %s", local_path, + strerror(errno)); + if (len == 0) + break; + + buffer_clear(&msg); + buffer_put_char(&msg, SSH2_FXP_WRITE); + buffer_put_int(&msg, ++id); + buffer_put_string(&msg, handle, handle_len); + buffer_put_int64(&msg, offset); + buffer_put_string(&msg, data, len); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u", + id, offset, len); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) { + error("Couldn't write to remote file \"%s\": %s", + remote_path, fx2txt(status)); + do_close(fd_in, fd_out, handle, handle_len); + xfree(handle); + close(local_fd); + return(-1); + } + debug3("In write loop, got %d offset %lld", len, offset); + + offset += len; + } + xfree(handle); + buffer_free(&msg); + + if (close(local_fd) == -1) { + error("Couldn't close local file \"%s\": %s", local_path, + strerror(errno)); + do_close(fd_in, fd_out, handle, handle_len); + return(-1); + } + + return(do_close(fd_in, fd_out, handle, handle_len)); +} diff --git a/usr.bin/ssh/sftp-client.h b/usr.bin/ssh/sftp-client.h new file mode 100644 index 00000000000..838b46b0b3a --- /dev/null +++ b/usr.bin/ssh/sftp-client.h @@ -0,0 +1,84 @@ +/* $OpenBSD: sftp-client.h,v 1.1 2001/02/04 11:11:54 djm Exp $ */ + +/* + * Copyright (c) 2001 Damien Miller. 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. + */ + +/* Client side of SSH2 filexfer protocol */ + +/* Initialiase a SSH filexfer connection */ +int do_init(int fd_in, int fd_out); + +/* Close file referred to by 'handle' */ +int do_close(int fd_in, int fd_out, char *handle, u_int handle_len); + +/* List contents of directory 'path' to stdout */ +int do_ls(int fd_in, int fd_out, char *path); + +/* Delete file 'path' */ +int do_rm(int fd_in, int fd_out, char *path); + +/* Create directory 'path' */ +int do_mkdir(int fd_in, int fd_out, char *path, Attrib *a); + +/* Remove directory 'path' */ +int do_rmdir(int fd_in, int fd_out, char *path); + +/* Get file attributes of 'path' (follows symlinks) */ +Attrib *do_stat(int fd_in, int fd_out, char *path); + +/* Get file attributes of 'path' (does not follow symlinks) */ +Attrib *do_lstat(int fd_in, int fd_out, char *path); + +/* Get file attributes of open file 'handle' */ +Attrib *do_fstat(int fd_in, int fd_out, char *handle, + u_int handle_len); + +/* Set file attributes of 'path' */ +int do_setstat(int fd_in, int fd_out, char *path, Attrib *a); + +/* Set file attributes of open file 'handle' */ +int do_fsetstat(int fd_in, int fd_out, char *handle, + u_int handle_len, Attrib *a); + +/* Canonicalise 'path' - caller must free result */ +char *do_realpath(int fd_in, int fd_out, char *path); + +/* Rename 'oldpath' to 'newpath' */ +int do_rename(int fd_in, int fd_out, char *oldpath, char *newpath); + +/* XXX: add callbacks to do_download/do_upload so we can do progress meter */ + +/* + * Download 'remote_path' to 'local_path'. Preserve permissions and times + * if 'pflag' is set + */ +int do_download(int fd_in, int fd_out, char *remote_path, char *local_path, + int pflag); + +/* + * Upload 'local_path' to 'remote_path'. Preserve permissions and times + * if 'pflag' is set + */ +int do_upload(int fd_in, int fd_out, char *local_path, char *remote_path, + int pflag); diff --git a/usr.bin/ssh/sftp-common.c b/usr.bin/ssh/sftp-common.c new file mode 100644 index 00000000000..aed9b339a60 --- /dev/null +++ b/usr.bin/ssh/sftp-common.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Damien Miller. 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" +RCSID("$OpenBSD: sftp-common.c,v 1.1 2001/02/04 11:11:54 djm Exp $"); + +#include "buffer.h" +#include "bufaux.h" +#include "getput.h" +#include "log.h" +#include "xmalloc.h" + +#include "sftp.h" +#include "sftp-common.h" + +void +attrib_clear(Attrib *a) +{ + a->flags = 0; + a->size = 0; + a->uid = 0; + a->gid = 0; + a->perm = 0; + a->atime = 0; + a->mtime = 0; +} + +void +stat_to_attrib(struct stat *st, Attrib *a) +{ + attrib_clear(a); + a->flags = 0; + a->flags |= SSH2_FILEXFER_ATTR_SIZE; + a->size = st->st_size; + a->flags |= SSH2_FILEXFER_ATTR_UIDGID; + a->uid = st->st_uid; + a->gid = st->st_gid; + a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + a->perm = st->st_mode; + a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME; + a->atime = st->st_atime; + a->mtime = st->st_mtime; +} + +Attrib * +decode_attrib(Buffer *b) +{ + static Attrib a; + attrib_clear(&a); + a.flags = buffer_get_int(b); + if (a.flags & SSH2_FILEXFER_ATTR_SIZE) + a.size = buffer_get_int64(b); + if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { + a.uid = buffer_get_int(b); + a.gid = buffer_get_int(b); + } + if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + a.perm = buffer_get_int(b); + if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + a.atime = buffer_get_int(b); + a.mtime = buffer_get_int(b); + } + /* vendor-specific extensions */ + if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) { + char *type, *data; + int i, count; + count = buffer_get_int(b); + for (i = 0; i < count; i++) { + type = buffer_get_string(b, NULL); + data = buffer_get_string(b, NULL); + debug3("Got file attribute \"%s\"", type); + xfree(type); + xfree(data); + } + } + return &a; +} + +void +encode_attrib(Buffer *b, Attrib *a) +{ + buffer_put_int(b, a->flags); + if (a->flags & SSH2_FILEXFER_ATTR_SIZE) + buffer_put_int64(b, a->size); + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + buffer_put_int(b, a->uid); + buffer_put_int(b, a->gid); + } + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + buffer_put_int(b, a->perm); + if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + buffer_put_int(b, a->atime); + buffer_put_int(b, a->mtime); + } +} + +const char * +fx2txt(int status) +{ + switch (status) { + case SSH2_FX_OK: + return("No Error"); + case SSH2_FX_EOF: + return("End of File"); + case SSH2_FX_NO_SUCH_FILE: + return("No Such File"); + case SSH2_FX_PERMISSION_DENIED: + return("Permission Denied"); + case SSH2_FX_FAILURE: + return("Failure"); + case SSH2_FX_BAD_MESSAGE: + return("Bad message"); + case SSH2_FX_NO_CONNECTION: + return("No connection"); + case SSH2_FX_CONNECTION_LOST: + return("Connection lost"); + case SSH2_FX_OP_UNSUPPORTED: + return("Operation unsupported"); + default: + return("Unknown status"); + }; + /* NOTREACHED */ +} + diff --git a/usr.bin/ssh/sftp-common.h b/usr.bin/ssh/sftp-common.h new file mode 100644 index 00000000000..6dc1a32f8fd --- /dev/null +++ b/usr.bin/ssh/sftp-common.h @@ -0,0 +1,55 @@ +/* $OpenBSD: sftp-common.h,v 1.1 2001/02/04 11:11:54 djm Exp $ */ + +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Damien Miller. 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. + */ + +typedef struct Attrib Attrib; + +/* File attributes */ +struct Attrib { + u_int32_t flags; + u_int64_t size; + u_int32_t uid; + u_int32_t gid; + u_int32_t perm; + u_int32_t atime; + u_int32_t mtime; +}; + +/* Clear contents of attributes structure */ +void attrib_clear(Attrib *a); + +/* Convert from struct stat to filexfer attribs */ +void stat_to_attrib(struct stat *st, Attrib *a); + +/* Decode attributes in buffer */ +Attrib *decode_attrib(Buffer *b); + +/* Encode attributes to buffer */ +void encode_attrib(Buffer *b, Attrib *a); + +/* Convert from SSH2_FX_ status to text error message */ +const char *fx2txt(int status); + diff --git a/usr.bin/ssh/sftp-int.c b/usr.bin/ssh/sftp-int.c new file mode 100644 index 00000000000..f050c098ebe --- /dev/null +++ b/usr.bin/ssh/sftp-int.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2001 Damien Miller. 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. + */ + +/* XXX: finish implementation of all commands */ +/* XXX: do fnmatch() instead of using raw pathname */ +/* XXX: recursive operations */ + +#include "includes.h" +RCSID("$OpenBSD: sftp-int.c,v 1.1 2001/02/04 11:11:54 djm Exp $"); + +#include "buffer.h" +#include "xmalloc.h" +#include "log.h" +#include "pathnames.h" + +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" +#include "sftp-int.h" + +/* Seperators for interactive commands */ +#define WHITESPACE " \t\r\n" + +/* Commands for interactive mode */ +#define I_CHDIR 1 +#define I_CHGRP 2 +#define I_CHMOD 3 +#define I_CHOWN 4 +#define I_GET 5 +#define I_HELP 6 +#define I_LCHDIR 7 +#define I_LLS 8 +#define I_LMKDIR 9 +#define I_LPWD 10 +#define I_LS 11 +#define I_LUMASK 12 +#define I_MKDIR 13 +#define I_PUT 14 +#define I_PWD 15 +#define I_QUIT 16 +#define I_RENAME 17 +#define I_RM 18 +#define I_RMDIR 19 +#define I_SHELL 20 + +struct CMD { + const int n; + const char *c; +}; + +const struct CMD cmds[] = { + { I_CHDIR, "CD" }, + { I_CHDIR, "CHDIR" }, + { I_CHDIR, "LCD" }, + { I_CHGRP, "CHGRP" }, + { I_CHMOD, "CHMOD" }, + { I_CHOWN, "CHOWN" }, + { I_HELP, "HELP" }, + { I_GET, "GET" }, + { I_LCHDIR, "LCHDIR" }, + { I_LLS, "LLS" }, + { I_LMKDIR, "LMKDIR" }, + { I_LPWD, "LPWD" }, + { I_LS, "LS" }, + { I_LUMASK, "LUMASK" }, + { I_MKDIR, "MKDIR" }, + { I_PUT, "PUT" }, + { I_PWD, "PWD" }, + { I_QUIT, "EXIT" }, + { I_QUIT, "QUIT" }, + { I_RENAME, "RENAME" }, + { I_RMDIR, "RMDIR" }, + { I_RM, "RM" }, + { I_SHELL, "!" }, + { -1, NULL} +}; + +void +help(void) +{ + printf("Available commands:\n"); + printf("CD path Change remote directory to 'path'\n"); + printf("LCD path Change local directory to 'path'\n"); + printf("CHGRP grp path Change group of file 'path' to 'grp'\n"); + printf("CHMOD mode path Change permissions of file 'path' to 'mode'\n"); + printf("CHOWN own path Change owner of file 'path' to 'own'\n"); + printf("HELP Display this help text\n"); + printf("GET remote-path [local-path] Download file\n"); + printf("LLS [ls options] [path] Display local directory listing\n"); + printf("LMKDIR path Create local directory\n"); + printf("LPWD Print local working directory\n"); + printf("LS [path] Display remote directory listing\n"); + printf("LUMASK umask Set local umask to 'umask'\n"); + printf("MKDIR path Create remote directory\n"); + printf("PUT local-path [remote-path] Upload file\n"); + printf("PWD Display remote working directory\n"); + printf("EXIT Quit sftp\n"); + printf("QUIT Quit sftp\n"); + printf("RENAME oldpath newpath Rename remote file\n"); + printf("RMDIR path Remove remote directory\n"); + printf("RM path Delete remote file\n"); + printf("!command Execute 'command' in local shell\n"); + printf("! Escape to local shell\n"); +} + +void +local_do_shell(const char *args) +{ + int ret, status; + char *shell; + pid_t pid; + + if (!*args) + args = NULL; + + if ((shell = getenv("SHELL")) == NULL) + shell = _PATH_BSHELL; + + if ((pid = fork()) == -1) + fatal("Couldn't fork: %s", strerror(errno)); + + if (pid == 0) { + /* XXX: child has pipe fds to ssh subproc open - issue? */ + if (args) { + debug3("Executing %s -c \"%s\"", shell, args); + ret = execl(shell, shell, "-c", args, NULL); + } else { + debug3("Executing %s", shell); + ret = execl(shell, shell, NULL); + } + fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, + strerror(errno)); + _exit(1); + } + if (waitpid(pid, &status, 0) == -1) + fatal("Couldn't wait for child: %s", strerror(errno)); + if (!WIFEXITED(status)) + error("Shell exited abormally"); + else if (WEXITSTATUS(status)) + error("Shell exited with status %d", WEXITSTATUS(status)); +} + +void +local_do_ls(const char *args) +{ + if (!args || !*args) + local_do_shell("ls"); + else { + char *buf = xmalloc(8 + strlen(args) + 1); + + /* XXX: quoting - rip quoting code from ftp? */ + sprintf(buf, "/bin/ls %s", args); + local_do_shell(buf); + } +} + +char * +make_absolute(char *p, char *pwd) +{ + char buf[2048]; + + /* Derelativise */ + if (p && p[0] != '/') { + snprintf(buf, sizeof(buf), "%s/%s", pwd, p); + xfree(p); + p = xstrdup(buf); + } + + return(p); +} + +int +parse_getput_flags(const char **cpp, int *pflag) +{ + const char *cp = *cpp; + + /* Check for flags */ + if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { + switch (*cp) { + case 'P': + *pflag = 1; + break; + default: + error("Invalid flag -%c", *cp); + return(-1); + } + cp += 2; + *cpp = cp + strspn(cp, WHITESPACE); + } + + return(0); +} + +int +get_pathname(const char **cpp, char **path) +{ + const char *quot, *cp = *cpp; + int i; + + cp += strspn(cp, WHITESPACE); + if (!*cp) { + *cpp = cp; + *path = NULL; + return(0); + } + + /* Check for quoted filenames */ + if (*cp == '\"' || *cp == '\'') { + quot = cp++; + for(i = 0; cp[i] && cp[i] != *quot; i++) + ; + if (!cp[i]) { + error("Unterminated quote"); + *path = NULL; + return(-1); + } + if (i == 0) { + error("Empty quotes"); + *path = NULL; + return(-1); + } + *path = xmalloc(i + 1); + memcpy(*path, cp, i); + (*path)[i] = '\0'; + cp += i + 1; + *cpp = cp + strspn(cp, WHITESPACE); + return(0); + } + + /* Read to end of filename */ + for(i = 0; cp[i] && cp[i] != ' '; i++) + ; + + *path = xmalloc(i + 1); + memcpy(*path, cp, i); + (*path)[i] = '\0'; + cp += i; + *cpp = cp + strspn(cp, WHITESPACE); + + return(0); +} + +int +infer_path(const char *p, char **ifp) +{ + char *cp; + + debug("XXX: P = \"%s\"", p); + + cp = strrchr(p, '/'); + + if (cp == NULL) { + *ifp = xstrdup(p); + return(0); + } + + if (!cp[1]) { + error("Invalid path"); + return(-1); + } + + *ifp = xstrdup(cp + 1); + return(0); +} + +int +parse_args(const char **cpp, int *pflag, unsigned long *n_arg, + char **path1, char **path2) +{ + const char *cmd, *cp = *cpp; + int i, cmdnum; + + /* Skip leading whitespace */ + cp = cp + strspn(cp, WHITESPACE); + + /* Ignore blank lines */ + if (!*cp) + return(-1); + + /* Figure out which command we have */ + for(i = 0; cmds[i].c; i++) { + int cmdlen = strlen(cmds[i].c); + + /* Check for command followed by whitespace */ + if (!strncasecmp(cp, cmds[i].c, cmdlen) && + strchr(WHITESPACE, cp[cmdlen])) { + cp += cmdlen; + cp = cp + strspn(cp, WHITESPACE); + break; + } + } + cmdnum = cmds[i].n; + cmd = cmds[i].c; + + /* Special case */ + if (*cp == '!') { + cp++; + cmdnum = I_SHELL; + } else if (cmdnum == -1) { + error("Invalid command."); + return(-1); + } + + /* Get arguments and parse flags */ + *pflag = *n_arg = 0; + *path1 = *path2 = NULL; + switch (cmdnum) { + case I_GET: + case I_PUT: + if (parse_getput_flags(&cp, pflag)) + return(-1); + /* Get first pathname (mandatory) */ + if (get_pathname(&cp, path1)) + return(-1); + if (*path1 == NULL) { + error("You must specify at least one path after a " + "%s command.", cmd); + return(-1); + } + /* Try to get second pathname (optional) */ + if (get_pathname(&cp, path2)) + return(-1); + /* Otherwise try to guess it from first path */ + if (*path2 == NULL && infer_path(*path1, path2)) + return(-1); + break; + case I_RENAME: + /* Get first pathname (mandatory) */ + if (get_pathname(&cp, path1)) + return(-1); + if (get_pathname(&cp, path2)) + return(-1); + if (!*path1 || !*path2) { + error("You must specify two paths after a %s " + "command.", cmd); + return(-1); + } + break; + case I_RM: + case I_MKDIR: + case I_RMDIR: + case I_CHDIR: + case I_LCHDIR: + case I_LMKDIR: + /* Get pathname (mandatory) */ + if (get_pathname(&cp, path1)) + return(-1); + if (*path1 == NULL) { + error("You must specify a path after a %s command.", + cmd); + return(-1); + } + break; + case I_LS: + /* Path is optional */ + if (get_pathname(&cp, path1)) + return(-1); + break; + case I_LLS: + case I_SHELL: + /* Uses the rest of the line */ + break; + case I_LUMASK: + case I_CHMOD: + case I_CHOWN: + case I_CHGRP: + /* Get numeric arg (mandatory) */ + if (*cp < '0' && *cp > '9') { + error("You must supply a numeric argument " + "to the %s command.", cmd); + return(-1); + } + *n_arg = strtoul(cp, (char**)&cp, 0); + if (!*cp || !strchr(WHITESPACE, *cp)) { + error("You must supply a numeric argument " + "to the %s command.", cmd); + return(-1); + } + cp += strspn(cp, WHITESPACE); + + /* Get pathname (mandatory) */ + if (get_pathname(&cp, path1)) + return(-1); + if (*path1 == NULL) { + error("You must specify a path after a %s command.", + cmd); + return(-1); + } + break; + case I_QUIT: + case I_PWD: + case I_LPWD: + case I_HELP: + break; + default: + fatal("Command not implemented"); + } + + *cpp = cp; + + return(cmdnum); +} + +int +parse_dispatch_command(int in, int out, const char *cmd, char **pwd) +{ + char *path1, *path2; + int pflag, cmdnum; + unsigned long n_arg; + Attrib a, *aa; + char path_buf[PATH_MAX]; + + path1 = path2 = NULL; + cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2); + + /* Perform command */ + switch (cmdnum) { + case -1: + break; + case I_GET: + path1 = make_absolute(path1, *pwd); + do_download(in, out, path1, path2, pflag); + break; + case I_PUT: + path2 = make_absolute(path2, *pwd); + do_upload(in, out, path1, path2, pflag); + break; + case I_RENAME: + path1 = make_absolute(path1, *pwd); + path2 = make_absolute(path2, *pwd); + do_rename(in, out, path1, path2); + break; + case I_RM: + path1 = make_absolute(path1, *pwd); + do_rm(in, out, path1); + break; + case I_MKDIR: + path1 = make_absolute(path1, *pwd); + attrib_clear(&a); + a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + a.perm = 0777; + do_mkdir(in, out, path1, &a); + break; + case I_RMDIR: + path1 = make_absolute(path1, *pwd); + do_rmdir(in, out, path1); + break; + case I_CHDIR: + path1 = make_absolute(path1, *pwd); + xfree(*pwd); + *pwd = do_realpath(in, out, path1); + break; + case I_LS: + path1 = make_absolute(path1, *pwd); + do_ls(in, out, path1?path1:*pwd); + break; + case I_LCHDIR: + if (chdir(path1) == -1) + error("Couldn't change local directory to " + "\"%s\": %s", path1, strerror(errno)); + break; + case I_LMKDIR: + if (mkdir(path1, 0777) == -1) + error("Couldn't create local directory to " + "\"%s\": %s", path1, strerror(errno)); + break; + case I_LLS: + local_do_ls(cmd); + break; + case I_SHELL: + local_do_shell(cmd); + break; + case I_LUMASK: + umask(n_arg); + break; + case I_CHMOD: + path1 = make_absolute(path1, *pwd); + attrib_clear(&a); + a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + a.perm = n_arg; + do_setstat(in, out, path1, &a); + case I_CHOWN: + path1 = make_absolute(path1, *pwd); + aa = do_stat(in, out, path1); + if (!aa->flags & SSH2_FILEXFER_ATTR_UIDGID) { + error("Can't get current ownership of " + "remote file \"%s\"", path1); + break; + } + aa->uid = n_arg; + do_setstat(in, out, path1, aa); + break; + case I_CHGRP: + path1 = make_absolute(path1, *pwd); + aa = do_stat(in, out, path1); + if (!aa->flags & SSH2_FILEXFER_ATTR_UIDGID) { + error("Can't get current ownership of " + "remote file \"%s\"", path1); + break; + } + aa->gid = n_arg; + do_setstat(in, out, path1, aa); + break; + case I_PWD: + printf("Remote working directory: %s\n", *pwd); + break; + case I_LPWD: + if (!getcwd(path_buf, sizeof(path_buf))) + error("Couldn't get local cwd: %s\n", + strerror(errno)); + else + printf("Local working directory: %s\n", + path_buf); + break; + case I_QUIT: + return(-1); + case I_HELP: + help(); + break; + default: + fatal("%d is not implemented", cmdnum); + } + + if (path1) + xfree(path1); + if (path2) + xfree(path2); + + return(0); +} + +void +interactive_loop(int fd_in, int fd_out) +{ + char *pwd; + char cmd[2048]; + + pwd = do_realpath(fd_in, fd_out, "."); + if (pwd == NULL) + fatal("Need cwd"); + + setlinebuf(stdout); + setlinebuf(stdin); + + for(;;) { + char *cp; + + printf("sftp> "); + + /* XXX: use libedit */ + if (fgets(cmd, sizeof(cmd), stdin) == NULL) { + printf("\n"); + break; + } + cp = strrchr(cmd, '\n'); + if (cp) + *cp = '\0'; + if (parse_dispatch_command(fd_in, fd_out, cmd, &pwd)) + break; + } + xfree(pwd); +} diff --git a/usr.bin/ssh/sftp-int.h b/usr.bin/ssh/sftp-int.h new file mode 100644 index 00000000000..234d8003bc5 --- /dev/null +++ b/usr.bin/ssh/sftp-int.h @@ -0,0 +1,27 @@ +/* $OpenBSD: sftp-int.h,v 1.1 2001/02/04 11:11:54 djm Exp $ */ + +/* + * Copyright (c) 2001 Damien Miller. 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. + */ + +void interactive_loop(int fd_in, int fd_out); diff --git a/usr.bin/ssh/sftp-server.c b/usr.bin/ssh/sftp-server.c index 30c93354713..e6952c6ddce 100644 --- a/usr.bin/ssh/sftp-server.c +++ b/usr.bin/ssh/sftp-server.c @@ -22,7 +22,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" -RCSID("$OpenBSD: sftp-server.c,v 1.14 2001/01/21 19:05:56 markus Exp $"); +RCSID("$OpenBSD: sftp-server.c,v 1.15 2001/02/04 11:11:54 djm Exp $"); #include "buffer.h" #include "bufaux.h" @@ -31,6 +31,7 @@ RCSID("$OpenBSD: sftp-server.c,v 1.14 2001/01/21 19:05:56 markus Exp $"); #include "xmalloc.h" #include "sftp.h" +#include "sftp-common.h" /* helper */ #define get_int64() buffer_get_int64(&iqueue); @@ -44,22 +45,9 @@ Buffer oqueue; /* portable attibutes, etc. */ -typedef struct Attrib Attrib; typedef struct Stat Stat; -struct Attrib -{ - u_int32_t flags; - u_int64_t size; - u_int32_t uid; - u_int32_t gid; - u_int32_t perm; - u_int32_t atime; - u_int32_t mtime; -}; - -struct Stat -{ +struct Stat { char *name; char *long_name; Attrib attrib; @@ -116,90 +104,6 @@ flags_from_portable(int pflags) return flags; } -void -attrib_clear(Attrib *a) -{ - a->flags = 0; - a->size = 0; - a->uid = 0; - a->gid = 0; - a->perm = 0; - a->atime = 0; - a->mtime = 0; -} - -Attrib * -decode_attrib(Buffer *b) -{ - static Attrib a; - attrib_clear(&a); - a.flags = buffer_get_int(b); - if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { - a.size = buffer_get_int64(b); - } - if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { - a.uid = buffer_get_int(b); - a.gid = buffer_get_int(b); - } - if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { - a.perm = buffer_get_int(b); - } - if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { - a.atime = buffer_get_int(b); - a.mtime = buffer_get_int(b); - } - /* vendor-specific extensions */ - if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) { - char *type, *data; - int i, count; - count = buffer_get_int(b); - for (i = 0; i < count; i++) { - type = buffer_get_string(b, NULL); - data = buffer_get_string(b, NULL); - xfree(type); - xfree(data); - } - } - return &a; -} - -void -encode_attrib(Buffer *b, Attrib *a) -{ - buffer_put_int(b, a->flags); - if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { - buffer_put_int64(b, a->size); - } - if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { - buffer_put_int(b, a->uid); - buffer_put_int(b, a->gid); - } - if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { - buffer_put_int(b, a->perm); - } - if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { - buffer_put_int(b, a->atime); - buffer_put_int(b, a->mtime); - } -} - -void -stat_to_attrib(struct stat *st, Attrib *a) -{ - attrib_clear(a); - a->flags = 0; - a->flags |= SSH2_FILEXFER_ATTR_SIZE; - a->size = st->st_size; - a->flags |= SSH2_FILEXFER_ATTR_UIDGID; - a->uid = st->st_uid; - a->gid = st->st_gid; - a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; - a->perm = st->st_mode; - a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME; - a->atime = st->st_atime; - a->mtime = st->st_mtime; -} - Attrib * get_attrib(void) { diff --git a/usr.bin/ssh/sftp-server/Makefile b/usr.bin/ssh/sftp-server/Makefile index 4a37fe8e9c8..82640afa204 100644 --- a/usr.bin/ssh/sftp-server/Makefile +++ b/usr.bin/ssh/sftp-server/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.3 2001/01/29 01:58:20 niklas Exp $ +# $OpenBSD: Makefile,v 1.4 2001/02/04 11:11:56 djm Exp $ .PATH: ${.CURDIR}/.. @@ -10,7 +10,7 @@ BINMODE?=555 BINDIR= /usr/libexec MAN= sftp-server.8 -SRCS= sftp-server.c log-server.c +SRCS= sftp-server.c sftp-common.c log-server.c .include <bsd.prog.mk> diff --git a/usr.bin/ssh/sftp.1 b/usr.bin/ssh/sftp.1 new file mode 100644 index 00000000000..59206b65444 --- /dev/null +++ b/usr.bin/ssh/sftp.1 @@ -0,0 +1,156 @@ +.\" $OpenBSD: sftp.1,v 1.1 2001/02/04 11:11:54 djm Exp $ +.\" +.\" Copyright (c) 2001 Damien Miller. 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. +.\" +.Dd Febuary 4, 2001 +.Dt SFTP 1 +.Os +.Sh NAME +.Nm sftp +.Nd Secure file tranfer program +.Sh SYNOPSIS +.Nm sftp +.Op Fl v Li | Fl C +.Op Fl o Ar ssh_option +.Op Ar hostname | user@hostname +.Sh DESCRIPTION +.Nm +is an interactive file transfer program, similar to +.Xr ftp 1 , +which performs all operations over an encrypted +.Xr ssh 1 +transport. +It may also use many features of ssh, such as public key authentication and +compression. +.Nm +connects and logs into the specified +.Ar hostname +then enters an interactive command mode. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl C +Enables compression (via ssh's +.Fl C +flag) +.It Fl v +Raise logging level. This option is also passed to ssh. +.It Fl o Ar ssh_option +Specify an option to be directly passed to +.Xr ssh 1 . +.El +.Sh INTERACTIVE COMMANDS +Once in interactive mode +.Nm , +understands a set of commands similar to those of +.Xr ftp 1 . +Commands are case insensitive. +.Bl -tag -width Ds +.It Ic CD Ar path +Change remote directory to +.Ar path +.It Ic LCD Ar path +Change local directory to +.Ar path +.It Ic CHGRP Ar grp Ar path +Change group of file +.Ar path to +.Ar grp . +.Ar grp +must be numeric. +.It Ic CHMOD Ar mode Ar path +Change permissions of file +.Ar path to +.Ar mode +.It Ic CHOWN Ar own Ar path +Change owner of file +.Ar path to +.Ar own . +.Ar own +must be a numeric UID. +.It Ic HELP +Display help text +.It Ic GET Ar remote-file Op Ar local-file +Retrieve the +.Ar remote-file +and store it on the local machine. +If the local +file name is not specified, it is given the same name it has on the +remote machine. +.It Ic LLS Op Ar ls-options Op Ar path +Display local directory listing of either +.Ar path +or current directory if +.Ar path +was not specified. +.It Ic LMKDIR Ar path +Create local directory specified by +.Ar path +.It Ic LPWD +Print local working directory +.It Ic LS Op Ar path +Display remote directory listing of either +.Ar path +or current directory, is +.Ar path not specified. +.It Ic LUMASK Ar umask +Set local umask to +.Ar umask +.It Ic MKDIR Ar path +Create remote directory specified by +.Ar path +.It Ic PUT local-file Op Ar remote-file +Upload +.Ar local-file +and store it on the remote machine. If the local file name is not specified, +it is given the same name it has on the local machine. +.It Ic PWD +Display remote working directory +.It Ic EXIT +Quit sftp +.It Ic QUIT +Quit sftp +.It Ic RENAME Ar oldpath Ar newpath +Rename remote file from +.Ar oldpath +to +.Ar newpath +.It Ic RMDIR Ar path +Remove remote directory specified by +.Ar path +.It Ic RM Ar path +Delete remote file specified by +.Ar path +.It Ic ! Ar command +Execute +.Ar command +in local shell +.It Ic ! +Escape to local shell +.Sh AUTHORS +Damien Miller <djm@mindrot.org> +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-keygen 1 , +.Xr sshd 8 diff --git a/usr.bin/ssh/sftp.c b/usr.bin/ssh/sftp.c new file mode 100644 index 00000000000..0dca12d8507 --- /dev/null +++ b/usr.bin/ssh/sftp.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2001 Damien Miller. 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" + +RCSID("$OpenBSD: sftp.c,v 1.1 2001/02/04 11:11:54 djm Exp $"); + +/* XXX: commandline mode */ +/* XXX: copy between two remote hosts (commandline) */ +/* XXX: short-form remote directory listings (like 'ls -C') */ + +#include "buffer.h" +#include "xmalloc.h" +#include "log.h" +#include "pathnames.h" + +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" +#include "sftp-int.h" + +void +connect_to_server(char **args, int *in, int *out, pid_t *sshpid) +{ + int c_in, c_out; +#ifdef USE_PIPES + int pin[2], pout[2]; + if ((pipe(pin) == -1) || (pipe(pout) == -1)) + fatal("pipe: %s", strerror(errno)); + *in = pin[0]; + *out = pout[1]; + c_in = pout[0]; + c_out = pin[1]; +#else /* USE_PIPES */ + int inout[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) + fatal("socketpair: %s", strerror(errno)); + *in = *out = inout[0]; + c_in = c_out = inout[1]; +#endif /* USE_PIPES */ + + if ((*sshpid = fork()) == -1) + fatal("fork: %s", strerror(errno)); + else if (*sshpid == 0) { + if ((dup2(c_in, STDIN_FILENO) == -1) || + (dup2(c_out, STDOUT_FILENO) == -1)) { + fprintf(stderr, "dup2: %s\n", strerror(errno)); + exit(1); + } + close(*in); + close(*out); + close(c_in); + close(c_out); + execv(_PATH_SSH_PROGRAM, args); + fprintf(stderr, "exec: %s", strerror(errno)); + exit(1); + } + + close(c_in); + close(c_out); +} + +char ** +make_ssh_args(char *add_arg) +{ + static char **args = NULL; + static int nargs = 0; + char debug_buf[4096]; + int i; + + /* Init args array */ + if (args == NULL) { + nargs = 4; + i = 0; + args = xmalloc(sizeof(*args) * nargs); + args[i++] = "ssh"; + args[i++] = "-oProtocol=2"; + args[i++] = "-s"; + args[i++] = NULL; + } + + /* If asked to add args, then do so and return */ + if (add_arg) { + i = nargs++ - 1; + args = xrealloc(args, sizeof(*args) * nargs); + args[i++] = add_arg; + args[i++] = NULL; + return(NULL); + } + + /* Otherwise finish up and return the arg array */ + make_ssh_args("sftp"); + + /* XXX: overflow - doesn't grow debug_buf */ + debug_buf[0] = '\0'; + for(i = 0; args[i]; i++) { + if (i) + strlcat(debug_buf, " ", sizeof(debug_buf)); + + strlcat(debug_buf, args[i], sizeof(debug_buf)); + } + debug("SSH args \"%s\"", debug_buf); + + return(args); +} + +void +usage(void) +{ + fprintf(stderr, "usage: sftp [-vC] [-osshopt=value] [user@]host\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + int in, out, i, debug_level, compress_flag; + pid_t sshpid; + char *cp; + LogLevel ll; + + debug_level = compress_flag = 0; + for(i = 1; i < argc && argv[i][0] == '-'; i++) { + if (!strcmp(argv[i], "-v")) + debug_level = MIN(3, debug_level + 1); + else if (!strcmp(argv[i], "-C")) + compress_flag = 1; + else if (!strncmp(argv[i], "-o", 2)) { + make_ssh_args(argv[i]); + } else { + fprintf(stderr, "Unknown option \"%s\"\n", argv[i]); + usage(); + } + } + + if (i == argc || argc > (i + 1)) + usage(); + + if ((cp = strchr(argv[i], '@')) == NULL) + cp = argv[i]; + else { + *cp = '\0'; + if (!argv[i][0]) { + fprintf(stderr, "Missing username\n"); + usage(); + } + make_ssh_args("-l"); + make_ssh_args(argv[i]); + cp++; + } + + if (!*cp) { + fprintf(stderr, "Missing hostname\n"); + usage(); + } + + /* Set up logging and debug '-d' arguments to ssh */ + ll = SYSLOG_LEVEL_INFO; + switch (debug_level) { + case 1: + ll = SYSLOG_LEVEL_DEBUG1; + make_ssh_args("-v"); + break; + case 2: + ll = SYSLOG_LEVEL_DEBUG2; + make_ssh_args("-v"); + make_ssh_args("-v"); + break; + case 3: + ll = SYSLOG_LEVEL_DEBUG3; + make_ssh_args("-v"); + make_ssh_args("-v"); + make_ssh_args("-v"); + break; + } + + if (compress_flag) + make_ssh_args("-C"); + + log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); + + make_ssh_args(cp); + + fprintf(stderr, "Connecting to %s...\n", cp); + + connect_to_server(make_ssh_args(NULL), &in, &out, &sshpid); + + do_init(in, out); + + interactive_loop(in, out); + + close(in); + close(out); + + if (kill(sshpid, SIGHUP) == -1) + fatal("Couldn't terminate ssh process: %s", strerror(errno)); + + /* XXX: wait? */ + + exit(0); +} diff --git a/usr.bin/ssh/sftp/Makefile b/usr.bin/ssh/sftp/Makefile new file mode 100644 index 00000000000..096a0cb3cd8 --- /dev/null +++ b/usr.bin/ssh/sftp/Makefile @@ -0,0 +1,19 @@ +# $OpenBSD: Makefile,v 1.1 2001/02/04 11:11:56 djm Exp $ + +.PATH: ${.CURDIR}/.. + +PROG= sftp +BINOWN= root + +BINMODE?=555 + +BINDIR= /usr/bin +MAN= sftp.1 + +SRCS= sftp.c sftp-client.c sftp-int.c sftp-common.c log-client.c + +.include <bsd.prog.mk> + +LDADD+= -lcrypto +DPADD+= ${LIBCRYPTO} + diff --git a/usr.bin/ssh/ssh.1 b/usr.bin/ssh/ssh.1 index 3e0eba392d9..99fb8c7cd5c 100644 --- a/usr.bin/ssh/ssh.1 +++ b/usr.bin/ssh/ssh.1 @@ -34,7 +34,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.82 2001/02/03 17:03:05 stevesk Exp $ +.\" $OpenBSD: ssh.1,v 1.83 2001/02/04 11:11:55 djm Exp $ .Dd September 25, 1999 .Dt SSH 1 .Os @@ -1271,6 +1271,7 @@ protocol versions 1.5 and 2.0. .Xr rlogin 1 , .Xr rsh 1 , .Xr scp 1 , +.Xr sftp 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , diff --git a/usr.bin/ssh/sshd.8 b/usr.bin/ssh/sshd.8 index 9396af8ee3c..c71ecb2a894 100644 --- a/usr.bin/ssh/sshd.8 +++ b/usr.bin/ssh/sshd.8 @@ -34,7 +34,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.89 2001/02/03 10:43:09 markus Exp $ +.\" $OpenBSD: sshd.8,v 1.90 2001/02/04 11:11:55 djm Exp $ .Dd September 25, 1999 .Dt SSHD 8 .Os @@ -1087,6 +1087,7 @@ Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. .Sh SEE ALSO .Xr scp 1 , +.Xr sftp 1 , .Xr sftp-server 8 , .Xr ssh 1 , .Xr ssh-add 1 , |