From b5e300c254e530ade2b4ca65a3cd13cf6a41675b Mon Sep 17 00:00:00 2001 From: djm Date: Tue, 5 Sep 2000 02:34:53 +0000 Subject: [PATCH] 20000905 - (djm) Import OpenBSD CVS changes - markus@cvs.openbsd.org 2000/08/31 15:52:24 [Makefile sshd.8 sshd_config sftp-server.8 sftp-server.c] implement a SFTP server. interops with sftp2, scp2 and the windows client from ssh.com - markus@cvs.openbsd.org 2000/08/31 15:56:03 [README.openssh2] sync - markus@cvs.openbsd.org 2000/08/31 16:05:42 [session.c] Wall - markus@cvs.openbsd.org 2000/08/31 16:09:34 [authfd.c ssh-agent.c] add a flag to SSH2_AGENTC_SIGN_REQUEST for future extensions - deraadt@cvs.openbsd.org 2000/09/01 09:25:13 [scp.1 scp.c] cleanup and fix -S support; stevesk@sweden.hp.com - markus@cvs.openbsd.org 2000/09/01 16:29:32 [sftp-server.c] portability fixes - markus@cvs.openbsd.org 2000/09/01 16:32:41 [sftp-server.c] fix cast; mouring@pconline.com - itojun@cvs.openbsd.org 2000/09/03 09:23:28 [ssh-add.1 ssh.1] add missing .El against .Bl. - markus@cvs.openbsd.org 2000/09/04 13:03:41 [session.c] missing close; ok theo - markus@cvs.openbsd.org 2000/09/04 13:07:21 [session.c] fix get_last_login_time order; from andre@van-veen.de - markus@cvs.openbsd.org 2000/09/04 13:10:09 [sftp-server.c] more cast fixes; from mouring@pconline.com - markus@cvs.openbsd.org 2000/09/04 13:06:04 [session.c] set SSH_ORIGINAL_COMMAND; from Leakin@dfw.nostrum.com, bet@rahul.net - (djm) Cleanup after import. Fix sftp-server compilation, Makefile --- ChangeLog | 40 ++ Makefile.in | 18 +- README.openssh2 | 13 +- authfd.c | 3 +- configure.in | 2 +- scp.1 | 13 +- scp.c | 4 +- session.c | 35 +- sftp-server.8 | 33 ++ sftp-server.c | 1078 +++++++++++++++++++++++++++++++++++++++++++++++ ssh-add.1 | 3 +- ssh-agent.c | 5 +- ssh.1 | 2 + sshd.8 | 6 + sshd_config | 3 +- 15 files changed, 1216 insertions(+), 42 deletions(-) create mode 100644 sftp-server.8 create mode 100644 sftp-server.c diff --git a/ChangeLog b/ChangeLog index da17a9c1..9aa9423c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,43 @@ +20000905 + - (djm) Import OpenBSD CVS changes + - markus@cvs.openbsd.org 2000/08/31 15:52:24 + [Makefile sshd.8 sshd_config sftp-server.8 sftp-server.c] + implement a SFTP server. interops with sftp2, scp2 and the windows + client from ssh.com + - markus@cvs.openbsd.org 2000/08/31 15:56:03 + [README.openssh2] + sync + - markus@cvs.openbsd.org 2000/08/31 16:05:42 + [session.c] + Wall + - markus@cvs.openbsd.org 2000/08/31 16:09:34 + [authfd.c ssh-agent.c] + add a flag to SSH2_AGENTC_SIGN_REQUEST for future extensions + - deraadt@cvs.openbsd.org 2000/09/01 09:25:13 + [scp.1 scp.c] + cleanup and fix -S support; stevesk@sweden.hp.com + - markus@cvs.openbsd.org 2000/09/01 16:29:32 + [sftp-server.c] + portability fixes + - markus@cvs.openbsd.org 2000/09/01 16:32:41 + [sftp-server.c] + fix cast; mouring@pconline.com + - itojun@cvs.openbsd.org 2000/09/03 09:23:28 + [ssh-add.1 ssh.1] + add missing .El against .Bl. + - markus@cvs.openbsd.org 2000/09/04 13:03:41 + [session.c] + missing close; ok theo + - markus@cvs.openbsd.org 2000/09/04 13:07:21 + [session.c] + fix get_last_login_time order; from andre@van-veen.de + - markus@cvs.openbsd.org 2000/09/04 13:10:09 + [sftp-server.c] + more cast fixes; from mouring@pconline.com + - markus@cvs.openbsd.org 2000/09/04 13:06:04 + [session.c] + set SSH_ORIGINAL_COMMAND; from Leakin@dfw.nostrum.com, bet@rahul.net + - (djm) Cleanup after import. Fix sftp-server compilation, Makefile 20000903 - (djm) Fix Redhat init script diff --git a/Makefile.in b/Makefile.in index 4ceef704..7eb84ce6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -15,8 +15,8 @@ DESTDIR= VPATH=@srcdir@ SSH_PROGRAM=@bindir@/ssh -ASKPASS_LOCATION=@libexecdir@/ssh -ASKPASS_PROGRAM=$(ASKPASS_LOCATION)/ssh-askpass +LIBEXEC=@libexecdir@/ssh +ASKPASS_PROGRAM=$(LIBEXEC)/ssh-askpass CC=@CC@ LD=@LD@ @@ -32,7 +32,7 @@ LDFLAGS=-L. @LDFLAGS@ INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@ -TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS) +TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp sftp-server $(EXTRA_TARGETS) LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o util.o uuencode.o xmalloc.o @@ -42,13 +42,13 @@ SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o log-client.o readconf.o SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-rhosts.o auth-options.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o loginrec.o servconf.o serverloop.o md5crypt.o session.o -TROFFMAN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8 -CATMAN = scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh.0 sshd.0 +TROFFMAN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8 sftp-server.8 +CATMAN = scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh.0 sshd.0 sftp-server.0 MANPAGES = @MANTYPE@ CONFIGFILES=sshd_config ssh_config -PATHSUBS = -D/etc/ssh_config=$(sysconfdir)/ssh_config -D/etc/known_hosts=$(sysconfdir)/ssh_known_hosts -D/etc/sshd_config=$(sysconfdir)/sshd_config -D/etc/shosts.equiv=$(sysconfdir)/shosts.equiv -D/etc/ssh_host_key=$(sysconfdir)/ssh_host_key -D/var/run/sshd.pid=$(piddir)/sshd.pid +PATHSUBS = -D/etc/ssh_config=$(sysconfdir)/ssh_config -D/etc/known_hosts=$(sysconfdir)/ssh_known_hosts -D/etc/sshd_config=$(sysconfdir)/sshd_config -D/usr/libexec=$(LIBEXEC) -D/etc/shosts.equiv=$(sysconfdir)/shosts.equiv -D/etc/ssh_host_key=$(sysconfdir)/ssh_host_key -D/var/run/sshd.pid=$(piddir)/sshd.pid FIXPATHSCMD = $(PERL) $(srcdir)/fixpaths $(PATHSUBS) @@ -86,6 +86,9 @@ ssh-agent: libopenbsd-compat.a libssh.a ssh-agent.o log-client.o ssh-keygen: libopenbsd-compat.a libssh.a ssh-keygen.o log-client.o $(LD) -o $@ ssh-keygen.o log-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) +sftp-server: libopenbsd-compat.a libssh.a sftp-server.o log-server.o + $(LD) -o $@ sftp-server.o log-server.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + # test driver for the loginrec code - not built by default logintest: logintest.o libopenbsd-compat.a libssh.a log-client.o loginrec.o $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh log-client.o $(LIBS) @@ -123,18 +126,21 @@ install-files: ./mkinstalldirs $(DESTDIR)$(mandir) ./mkinstalldirs $(DESTDIR)$(mandir)/$(mansubdir)1 ./mkinstalldirs $(DESTDIR)$(mandir)/$(mansubdir)8 + ./mkinstalldirs $(DESTDIR)$(LIBEXEC) $(INSTALL) -m 4755 -s ssh $(DESTDIR)$(bindir)/ssh $(INSTALL) -m 0755 -s scp $(DESTDIR)$(bindir)/scp $(INSTALL) -m 0755 -s ssh-add $(DESTDIR)$(bindir)/ssh-add $(INSTALL) -m 0755 -s ssh-agent $(DESTDIR)$(bindir)/ssh-agent $(INSTALL) -m 0755 -s ssh-keygen $(DESTDIR)$(bindir)/ssh-keygen $(INSTALL) -m 0755 -s sshd $(DESTDIR)$(sbindir)/sshd + $(INSTALL) -m 0755 -s sftp-server $(DESTDIR)$(LIBEXEC)/sftp-server $(INSTALL) -m 644 ssh.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 $(INSTALL) -m 644 scp.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 $(INSTALL) -m 644 ssh-add.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 $(INSTALL) -m 644 ssh-agent.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1 $(INSTALL) -m 644 ssh-keygen.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 $(INSTALL) -m 644 sshd.[08].out $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 + $(INSTALL) -m 644 sftp-server.[08].out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 -rm -f $(DESTDIR)$(bindir)/slogin ln -s ssh $(DESTDIR)$(bindir)/slogin -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 diff --git a/README.openssh2 b/README.openssh2 index 9c034aeb..01837bd1 100644 --- a/README.openssh2 +++ b/README.openssh2 @@ -15,30 +15,27 @@ howto: works: secsh-transport: works w/o rekey - proposal exchange, i.e. different enc/mac/comp per direction - encryption: blowfish-cbc, 3des-cbc, arcfour, cast128-cbc - mac: hmac-md5, hmac-sha1, (hmac-ripemd160) - compression: zlib, none secsh-userauth: passwd and pubkey with DSA secsh-connection: pty+shell or command, flow control works (window adjust) tcp-forwarding: -L works, -R incomplete x11-fwd dss/dsa: host key database in ~/.ssh/known_hosts2 + ssh-agent: supports SSH1-RSA and ssh-dss keys client interops w/ sshd2, lshd server interops w/ ssh2, lsh, ssh.com's Windows client, SecureCRT, F-Secure SSH Client 4.0, SecureFX (secure ftp) server supports multiple concurrent sessions (e.g. with SSH.com Windows client) + server supports SFTP (interops with ssh.com's windows, sftp2, scp2) todo: - re-keying + RE-KEYING secsh-connection features: - tcp-forwarding, agent-fwd + complete tcp-forwarding, agent-fwd auth other than passwd, and DSA-pubkey: - keyboard-interactive, (PGP-pubkey?) + keyboard-interactive, (PGP-pubkey?), kerberos config server-auth w/ old host-keys cleanup advanced key storage? keynote - sftp -markus $Date$ diff --git a/authfd.c b/authfd.c index d9427d37..6c40afc6 100644 --- a/authfd.c +++ b/authfd.c @@ -17,7 +17,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: authfd.c,v 1.25 2000/08/19 21:34:42 markus Exp $"); +RCSID("$OpenBSD: authfd.c,v 1.26 2000/08/31 22:09:34 markus Exp $"); #include "ssh.h" #include "rsa.h" @@ -359,6 +359,7 @@ ssh_agent_sign(AuthenticationConnection *auth, buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST); buffer_put_string(&msg, blob, blen); buffer_put_string(&msg, data, datalen); + buffer_put_int(&msg, 0); /* flags, unused */ xfree(blob); if (ssh_request_reply(auth, &msg, &msg) == 0) { diff --git a/configure.in b/configure.in index 33b82895..b1dd1d06 100644 --- a/configure.in +++ b/configure.in @@ -235,7 +235,7 @@ fi AC_CHECK_HEADERS(bstring.h endian.h floatingpoint.h lastlog.h limits.h login.h login_cap.h maillock.h netdb.h netgroup.h netinet/in_systm.h paths.h poll.h pty.h shadow.h security/pam_appl.h sys/bitypes.h sys/bsdtty.h sys/cdefs.h sys/poll.h sys/select.h sys/stat.h sys/stropts.h sys/sysmacros.h sys/time.h sys/ttcompat.h stddef.h time.h ttyent.h usersec.h util.h utmp.h utmpx.h) dnl Checks for library functions. -AC_CHECK_FUNCS(arc4random atexit b64_ntop bcopy bindresvport_af clock freeaddrinfo gai_strerror getaddrinfo getnameinfo getrusage getttyent inet_aton inet_ntoa innetgr login_getcapbool md5_crypt memmove mkdtemp on_exit openpty rresvport_af setenv seteuid setlogin setproctitle setreuid sigaction sigvec snprintf strerror strlcat strlcpy strsep vsnprintf vhangup _getpty __b64_ntop) +AC_CHECK_FUNCS(arc4random atexit b64_ntop bcopy bindresvport_af clock freeaddrinfo futimes gai_strerror getaddrinfo getnameinfo getrusage getttyent inet_aton inet_ntoa innetgr login_getcapbool md5_crypt memmove mkdtemp on_exit openpty rresvport_af setenv seteuid setlogin setproctitle setreuid sigaction sigvec snprintf strerror strlcat strlcpy strsep vsnprintf vhangup _getpty __b64_ntop) dnl Checks for time functions AC_CHECK_FUNCS(gettimeofday time) dnl Checks for libutil functions diff --git a/scp.1 b/scp.1 index 54ca0f4b..9ae2ab64 100644 --- a/scp.1 +++ b/scp.1 @@ -20,6 +20,7 @@ .Sh SYNOPSIS .Nm scp .Op Fl pqrvC46 +.Op Fl S Ar program .Op Fl P Ar port .Op Fl c Ar cipher .Op Fl i Ar identity_file @@ -68,11 +69,6 @@ This option is directly passed to .It Fl p Preserves modification times, access times, and modes from the original file. -.It Fl S -Name of program to use for the encrypted connection. -The program must understand -.Xr ssh 1 -options. .It Fl r Recursively copy entire directories. .It Fl v @@ -103,9 +99,10 @@ because .Fl p is already reserved for preserving the times and modes of the file in .Xr rcp 1 . -.It Fl S -Name of program to use for the encrypted connection. The program must -understand +.It Fl S Ar program +Name of +.Ar program +to use for the encrypted connection. The program must understand .Xr ssh 1 options. .It Fl 4 diff --git a/scp.c b/scp.c index 3b356a9c..33bd0a5f 100644 --- a/scp.c +++ b/scp.c @@ -47,7 +47,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: scp.c,v 1.36 2000/08/24 21:46:59 deraadt Exp $"); +RCSID("$OpenBSD: scp.c,v 1.37 2000/09/01 15:25:13 deraadt Exp $"); #include "ssh.h" #include "xmalloc.h" @@ -262,7 +262,7 @@ main(argc, argv) extern int optind; fflag = tflag = 0; - while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S")) != EOF) + while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:")) != EOF) switch (ch) { /* User-visible flags. */ case '4': diff --git a/session.c b/session.c index 3678b8f0..d5faf4cf 100644 --- a/session.c +++ b/session.c @@ -8,7 +8,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.31 2000/08/28 03:50:54 deraadt Exp $"); +RCSID("$OpenBSD: session.c,v 1.35 2000/09/04 19:07:21 markus Exp $"); #include "xmalloc.h" #include "ssh.h" @@ -113,6 +113,9 @@ extern int startup_pipe; /* Local Xauthority file. */ static char *xauthfile; +/* original command from peer. */ +char *original_command = NULL; + /* data */ #define MAX_SESSIONS 10 Session sessions[MAX_SESSIONS]; @@ -177,7 +180,7 @@ void do_authenticated(struct passwd * pw) { Session *s; - int type; + int type, fd; int compression_level = 0, enable_compression_after_reply = 0; int have_pty = 0; char *command; @@ -332,7 +335,9 @@ do_authenticated(struct passwd * pw) break; } strlcat(xauthfile, "/cookies", MAXPATHLEN); - open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + if (fd >= 0) + close(fd); restore_uid(); fatal_add_cleanup(xauthfile_cleanup_proc, NULL); success = 1; @@ -377,6 +382,7 @@ do_authenticated(struct passwd * pw) packet_integrity_check(plen, 0, type); } if (forced_command != NULL) { + original_command = command; command = forced_command; debug("Forced command '%.500s'", forced_command); } @@ -638,6 +644,7 @@ do_login(Session *s) FILE *f; char *time_string; char buf[256]; + char hostname[MAXHOSTNAMELEN]; socklen_t fromlen; struct sockaddr_storage from; struct stat st; @@ -659,6 +666,10 @@ do_login(Session *s) } } + /* Get the time and hostname when the user last logged in. */ + last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, + hostname, sizeof(hostname)); + /* Record that there was a login on that tty from the remote host. */ record_login(pid, s->tty, pw->pw_name, pw->pw_uid, get_remote_name_or_ip(), (struct sockaddr *)&from); @@ -680,12 +691,6 @@ do_login(Session *s) printf("%s\n", aixloginmsg); #endif /* WITH_AIXAUTHENTICATE */ - /* - * Get the time when the user last logged in. 'buf' will be set - * to contain the hostname the last login was from. - */ - last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, - buf, sizeof(buf)); if (last_login_time != 0) { time_string = ctime(&last_login_time); if (strchr(time_string, '\n')) @@ -911,7 +916,7 @@ do_child(const char *command, struct passwd * pw, const char *term, const char *display, const char *auth_proto, const char *auth_data, const char *ttyname) { - const char *shell, *hostname, *cp = NULL; + const char *shell, *hostname = NULL, *cp = NULL; char buf[256]; char cmd[1024]; FILE *f = NULL; @@ -1089,6 +1094,9 @@ do_child(const char *command, struct passwd * pw, const char *term, child_set_env(&env, &envsize, "TERM", term); if (display) child_set_env(&env, &envsize, "DISPLAY", display); + if (original_command) + child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", + original_command); #ifdef _AIX { @@ -1511,6 +1519,7 @@ session_subsystem_req(Session *s) int session_x11_req(Session *s) { + int fd; if (no_x11_forwarding_flag) { debug("X11 forwarding disabled in user configuration file."); return 0; @@ -1555,7 +1564,9 @@ session_x11_req(Session *s) return 0; } strlcat(xauthfile, "/cookies", MAXPATHLEN); - open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + if (fd >= 0) + close(fd); restore_uid(); fatal_add_cleanup(xauthfile_cleanup_proc, s); return 1; @@ -1582,7 +1593,7 @@ session_exec_req(Session *s) char *command = packet_get_string(&len); packet_done(); if (forced_command) { - xfree(command); + original_command = command; command = forced_command; debug("Forced command '%.500s'", forced_command); } diff --git a/sftp-server.8 b/sftp-server.8 new file mode 100644 index 00000000..85720a08 --- /dev/null +++ b/sftp-server.8 @@ -0,0 +1,33 @@ +.\" $OpenBSD: sftp-server.8,v 1.1 2000/08/31 21:52:23 markus Exp $ +.Dd August 30, 2000 +.Dt SFTP-SERVER 8 +.Os +.Sh NAME +.Nm sftp-server +.Nd SFTP server subsystem +.Sh SYNOPSIS +.Nm sftp-server +.Sh DESCRIPTION +.Nm +is a program that speaks the server side of SFTP protocol +to stdout and expects client requests from stdin. +.Nm +is not intended to be called directly, but from +.Xr sshd 8 +using the +.Cm Subsystem +option. +See +.Xr sshd 8 +for more information. +.Sh HISTORY +.Nm +first appeared in +.Ox 2.8 . +.Sh AUTHOR +Markus Friedl +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-keygen 1 , +.Xr sshd 8 , diff --git a/sftp-server.c b/sftp-server.c new file mode 100644 index 00000000..39cecac5 --- /dev/null +++ b/sftp-server.c @@ -0,0 +1,1078 @@ +/* + * Copyright (c) 2000 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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-server.c,v 1.4 2000/09/04 19:10:08 markus Exp $"); + +#include "ssh.h" +#include "buffer.h" +#include "bufaux.h" +#include "getput.h" +#include "xmalloc.h" + +/* version */ +#define SSH_FILEXFER_VERSION 2 + +/* client to server */ +#define SSH_FXP_INIT 1 +#define SSH_FXP_OPEN 3 +#define SSH_FXP_CLOSE 4 +#define SSH_FXP_READ 5 +#define SSH_FXP_WRITE 6 +#define SSH_FXP_LSTAT 7 +#define SSH_FXP_FSTAT 8 +#define SSH_FXP_SETSTAT 9 +#define SSH_FXP_FSETSTAT 10 +#define SSH_FXP_OPENDIR 11 +#define SSH_FXP_READDIR 12 +#define SSH_FXP_REMOVE 13 +#define SSH_FXP_MKDIR 14 +#define SSH_FXP_RMDIR 15 +#define SSH_FXP_REALPATH 16 +#define SSH_FXP_STAT 17 +#define SSH_FXP_RENAME 18 + +/* server to client */ +#define SSH_FXP_VERSION 2 +#define SSH_FXP_STATUS 101 +#define SSH_FXP_HANDLE 102 +#define SSH_FXP_DATA 103 +#define SSH_FXP_NAME 104 +#define SSH_FXP_ATTRS 105 + +/* portable open modes */ +#define SSH_FXF_READ 0x01 +#define SSH_FXF_WRITE 0x02 +#define SSH_FXF_APPEND 0x04 +#define SSH_FXF_CREAT 0x08 +#define SSH_FXF_TRUNC 0x10 +#define SSH_FXF_EXCL 0x20 + +/* attributes */ +#define SSH_FXA_HAVE_SIZE 0x01 +#define SSH_FXA_HAVE_UGID 0x02 +#define SSH_FXA_HAVE_PERM 0x04 +#define SSH_FXA_HAVE_TIME 0x08 + +/* status messages */ +#define SSH_FX_OK 0x00 +#define SSH_FX_EOF 0x01 +#define SSH_FX_NO_SUCH_FILE 0x02 +#define SSH_FX_PERMISSION_DENIED 0x03 +#define SSH_FX_FAILURE 0x04 +#define SSH_FX_BAD_MESSAGE 0x05 +#define SSH_FX_NO_CONNECTION 0x06 +#define SSH_FX_CONNECTION_LOST 0x07 + + +/* helper */ +#define get_int() buffer_get_int(&iqueue); +#define get_string(lenp) buffer_get_string(&iqueue, lenp); +#define TRACE log + +/* input and output queue */ +Buffer iqueue; +Buffer oqueue; + +/* portable attibutes, etc. */ + +typedef struct Attrib Attrib; +typedef struct Stat Stat; + +struct Attrib +{ + u_int32_t flags; + u_int32_t size_high; + u_int32_t size_low; + 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 +{ + char *name; + char *long_name; + Attrib attrib; +}; + +int +errno_to_portable(int unixerrno) +{ + int ret = 0; + switch (unixerrno) { + case 0: + ret = SSH_FX_OK; + break; + case ENOENT: + case ENOTDIR: + case EBADF: + case ELOOP: + ret = SSH_FX_NO_SUCH_FILE; + break; + case EPERM: + case EACCES: + case EFAULT: + ret = SSH_FX_PERMISSION_DENIED; + break; + case ENAMETOOLONG: + case EINVAL: + ret = SSH_FX_BAD_MESSAGE; + break; + default: + ret = SSH_FX_FAILURE; + break; + } + return ret; +} + +int +flags_from_portable(int pflags) +{ + int flags = 0; + if (pflags & SSH_FXF_READ && + pflags & SSH_FXF_WRITE) { + flags = O_RDWR; + } else if (pflags & SSH_FXF_READ) { + flags = O_RDONLY; + } else if (pflags & SSH_FXF_WRITE) { + flags = O_WRONLY; + } + if (pflags & SSH_FXF_CREAT) + flags |= O_CREAT; + if (pflags & SSH_FXF_TRUNC) + flags |= O_TRUNC; + if (pflags & SSH_FXF_EXCL) + flags |= O_EXCL; + return flags; +} + +void +attrib_clear(Attrib *a) +{ + a->flags = 0; + a->size_low = 0; + a->size_high = 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 = get_int(); + if (a.flags & SSH_FXA_HAVE_SIZE) { + a.size_high = get_int(); + a.size_low = get_int(); + a.size = (((u_int64_t) a.size_high) << 32) + a.size_low; + } + if (a.flags & SSH_FXA_HAVE_UGID) { + a.uid = get_int(); + a.gid = get_int(); + } + if (a.flags & SSH_FXA_HAVE_PERM) { + a.perm = get_int(); + } + if (a.flags & SSH_FXA_HAVE_TIME) { + a.atime = get_int(); + a.mtime = get_int(); + } + return &a; +} + +void +encode_attrib(Buffer *b, Attrib *a) +{ + buffer_put_int(b, a->flags); + if (a->flags & SSH_FXA_HAVE_SIZE) { + buffer_put_int(b, a->size_high); + buffer_put_int(b, a->size_low); + } + if (a->flags & SSH_FXA_HAVE_UGID) { + buffer_put_int(b, a->uid); + buffer_put_int(b, a->gid); + } + if (a->flags & SSH_FXA_HAVE_PERM) { + buffer_put_int(b, a->perm); + } + if (a->flags & SSH_FXA_HAVE_TIME) { + buffer_put_int(b, a->atime); + buffer_put_int(b, a->mtime); + } +} + +Attrib * +stat_to_attrib(struct stat *st) +{ + static Attrib a; + attrib_clear(&a); + a.flags = 0; + a.flags |= SSH_FXA_HAVE_SIZE; + a.size = st->st_size; + a.size_low = a.size; + a.size_high = (u_int32_t) (a.size >> 32); + a.flags |= SSH_FXA_HAVE_UGID; + a.uid = st->st_uid; + a.gid = st->st_gid; + a.flags |= SSH_FXA_HAVE_PERM; + a.perm = st->st_mode; + a.flags |= SSH_FXA_HAVE_TIME; + a.atime = st->st_atime; + a.mtime = st->st_mtime; + return &a; +} + +Attrib * +get_attrib(void) +{ + return decode_attrib(&iqueue); +} + +/* handle handles */ + +typedef struct Handle Handle; +struct Handle { + int use; + DIR *dirp; + int fd; + char *name; +}; +enum { + HANDLE_UNUSED, + HANDLE_DIR, + HANDLE_FILE +}; +Handle handles[100]; + +void +handle_init(void) +{ + int i; + for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) + handles[i].use = HANDLE_UNUSED; +} + +int +handle_new(int use, char *name, int fd, DIR *dirp) +{ + int i; + for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) { + if (handles[i].use == HANDLE_UNUSED) { + handles[i].use = use; + handles[i].dirp = dirp; + handles[i].fd = fd; + handles[i].name = name; + return i; + } + } + return -1; +} + +int +handle_is_ok(int i, int type) +{ + return i >= 0 && i < sizeof(handles)/sizeof(Handle) && handles[i].use == type; +} + +int +handle_to_string(int handle, char **stringp, int *hlenp) +{ + char buf[1024]; + if (stringp == NULL || hlenp == NULL) + return -1; + snprintf(buf, sizeof buf, "%d", handle); + *stringp = xstrdup(buf); + *hlenp = strlen(*stringp); + return 0; +} + +int +handle_from_string(char *handle, int hlen) +{ +/* XXX OVERFLOW ? */ + char *ep; + long lval = strtol(handle, &ep, 10); + int val = lval; + if (*ep != '\0') + return -1; + if (handle_is_ok(val, HANDLE_FILE) || + handle_is_ok(val, HANDLE_DIR)) + return val; + return -1; +} + +char * +handle_to_name(int handle) +{ + if (handle_is_ok(handle, HANDLE_DIR)|| + handle_is_ok(handle, HANDLE_FILE)) + return handles[handle].name; + return NULL; +} + +DIR * +handle_to_dir(int handle) +{ + if (handle_is_ok(handle, HANDLE_DIR)) + return handles[handle].dirp; + return NULL; +} + +int +handle_to_fd(int handle) +{ + if (handle_is_ok(handle, HANDLE_FILE)) + return handles[handle].fd; + return -1; +} + +int +handle_close(int handle) +{ + int ret = -1; + if (handle_is_ok(handle, HANDLE_FILE)) { + ret = close(handles[handle].fd); + handles[handle].use = HANDLE_UNUSED; + } else if (handle_is_ok(handle, HANDLE_DIR)) { + ret = closedir(handles[handle].dirp); + handles[handle].use = HANDLE_UNUSED; + } else { + errno = ENOENT; + } + return ret; +} + +int +get_handle(void) +{ + char *handle; + int hlen, val; + handle = get_string(&hlen); + val = handle_from_string(handle, hlen); + xfree(handle); + return val; +} + +/* send replies */ + +void +send_msg(Buffer *m) +{ + int mlen = buffer_len(m); + buffer_put_int(&oqueue, mlen); + buffer_append(&oqueue, buffer_ptr(m), mlen); + buffer_consume(m, mlen); +} + +void +send_status(u_int32_t id, u_int32_t error) +{ + Buffer msg; + TRACE("sent status id %d error %d", id, error); + buffer_init(&msg); + buffer_put_char(&msg, SSH_FXP_STATUS); + buffer_put_int(&msg, id); + buffer_put_int(&msg, error); + send_msg(&msg); + buffer_free(&msg); +} +void +send_data_or_handle(char type, u_int32_t id, char *data, int dlen) +{ + Buffer msg; + buffer_init(&msg); + buffer_put_char(&msg, type); + buffer_put_int(&msg, id); + buffer_put_string(&msg, data, dlen); + send_msg(&msg); + buffer_free(&msg); +} + +void +send_data(u_int32_t id, char *data, int dlen) +{ + TRACE("sent data id %d len %d", id, dlen); + send_data_or_handle(SSH_FXP_DATA, id, data, dlen); +} + +void +send_handle(u_int32_t id, int handle) +{ + char *string; + int hlen; + handle_to_string(handle, &string, &hlen); + TRACE("sent handle id %d handle %d", id, handle); + send_data_or_handle(SSH_FXP_HANDLE, id, string, hlen); + xfree(string); +} + +void +send_names(u_int32_t id, int count, Stat *stats) +{ + Buffer msg; + int i; + buffer_init(&msg); + buffer_put_char(&msg, SSH_FXP_NAME); + buffer_put_int(&msg, id); + buffer_put_int(&msg, count); + TRACE("sent names id %d count %d", id, count); + for (i = 0; i < count; i++) { + buffer_put_cstring(&msg, stats[i].name); + buffer_put_cstring(&msg, stats[i].long_name); + encode_attrib(&msg, &stats[i].attrib); + } + send_msg(&msg); + buffer_free(&msg); +} + +void +send_attrib(u_int32_t id, Attrib *a) +{ + Buffer msg; + TRACE("sent attrib id %d have 0x%x", id, a->flags); + buffer_init(&msg); + buffer_put_char(&msg, SSH_FXP_ATTRS); + buffer_put_int(&msg, id); + encode_attrib(&msg, a); + send_msg(&msg); + buffer_free(&msg); +} + +/* parse incoming */ + +void +process_init(void) +{ + Buffer msg; + int version = buffer_get_int(&iqueue); + + TRACE("client version %d", version); + buffer_init(&msg); + buffer_put_char(&msg, SSH_FXP_VERSION); + buffer_put_int(&msg, SSH_FILEXFER_VERSION); + send_msg(&msg); + buffer_free(&msg); +} + +void +process_open(void) +{ + u_int32_t id, pflags; + Attrib *a; + char *name; + int handle, fd, flags, mode, status = SSH_FX_FAILURE; + + id = get_int(); + name = get_string(NULL); + pflags = get_int(); + a = get_attrib(); + flags = flags_from_portable(pflags); + mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm : 0666; + TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode); + fd = open(name, flags, mode); + if (fd < 0) { + status = errno_to_portable(errno); + } else { + handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL); + if (handle < 0) { + close(fd); + } else { + send_handle(id, handle); + status = SSH_FX_OK; + } + } + if (status != SSH_FX_OK) + send_status(id, status); + xfree(name); +} + +void +process_close(void) +{ + u_int32_t id; + int handle, ret, status = SSH_FX_FAILURE; + + id = get_int(); + handle = get_handle(); + TRACE("close id %d handle %d", id, handle); + ret = handle_close(handle); + status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; + send_status(id, status); +} + +void +process_read(void) +{ + char buf[64*1024]; + u_int32_t id, off_high, off_low, len; + int handle, fd, ret, status = SSH_FX_FAILURE; + u_int64_t off; + + id = get_int(); + handle = get_handle(); + off_high = get_int(); + off_low = get_int(); + len = get_int(); + + off = (((u_int64_t) off_high) << 32) + off_low; + TRACE("read id %d handle %d off %qd len %d", id, handle, off, len); + if (len > sizeof buf) { + len = sizeof buf; + log("read change len %d", len); + } + fd = handle_to_fd(handle); + if (fd >= 0) { + if (lseek(fd, off, SEEK_SET) < 0) { + error("process_read: seek failed"); + status = errno_to_portable(errno); + } else { + ret = read(fd, buf, len); + if (ret < 0) { + status = errno_to_portable(errno); + } else if (ret == 0) { + status = SSH_FX_EOF; + } else { + send_data(id, buf, ret); + status = SSH_FX_OK; + } + } + } + if (status != SSH_FX_OK) + send_status(id, status); +} + +void +process_write(void) +{ + u_int32_t id, off_high, off_low; + u_int64_t off; + int len; + int handle, fd, ret, status = SSH_FX_FAILURE; + char *data; + + id = get_int(); + handle = get_handle(); + off_high = get_int(); + off_low = get_int(); + data = get_string(&len); + + off = (((u_int64_t) off_high) << 32) + off_low; + TRACE("write id %d handle %d off %qd len %d", id, handle, off, len); + fd = handle_to_fd(handle); + if (fd >= 0) { + if (lseek(fd, off, SEEK_SET) < 0) { + status = errno_to_portable(errno); + error("process_write: seek failed"); + } else { +/* XXX ATOMICIO ? */ + ret = write(fd, data, len); + if (ret == -1) { + error("process_write: write failed"); + status = errno_to_portable(errno); + } else if (ret == len) { + status = SSH_FX_OK; + } else { + log("nothing at all written"); + } + } + } + send_status(id, status); + xfree(data); +} + +void +process_do_stat(int do_lstat) +{ + Attrib *a; + struct stat st; + u_int32_t id; + char *name; + int ret, status = SSH_FX_FAILURE; + + id = get_int(); + name = get_string(NULL); + TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name); + ret = do_lstat ? lstat(name, &st) : stat(name, &st); + if (ret < 0) { + status = errno_to_portable(errno); + } else { + a = stat_to_attrib(&st); + send_attrib(id, a); + status = SSH_FX_OK; + } + if (status != SSH_FX_OK) + send_status(id, status); + xfree(name); +} + +void +process_stat(void) +{ + process_do_stat(0); +} + +void +process_lstat(void) +{ + process_do_stat(1); +} + +void +process_fstat(void) +{ + Attrib *a; + struct stat st; + u_int32_t id; + int fd, ret, handle, status = SSH_FX_FAILURE; + + id = get_int(); + handle = get_handle(); + TRACE("fstat id %d handle %d", id, handle); + fd = handle_to_fd(handle); + if (fd >= 0) { + ret = fstat(fd, &st); + if (ret < 0) { + status = errno_to_portable(errno); + } else { + a = stat_to_attrib(&st); + send_attrib(id, a); + status = SSH_FX_OK; + } + } + if (status != SSH_FX_OK) + send_status(id, status); +} + +struct timeval * +attrib_to_tv(Attrib *a) +{ + static struct timeval tv[2]; + tv[0].tv_sec = a->atime; + tv[0].tv_usec = 0; + tv[1].tv_sec = a->mtime; + tv[1].tv_usec = 0; + return tv; +} + +void +process_setstat(void) +{ + Attrib *a; + u_int32_t id; + char *name; + int ret; + int status = SSH_FX_OK; + + id = get_int(); + name = get_string(NULL); + a = get_attrib(); + TRACE("setstat id %d name %s", id, name); + if (a->flags & SSH_FXA_HAVE_PERM) { + ret = chmod(name, a->perm & 0777); + if (ret == -1) + status = errno_to_portable(errno); + } + if (a->flags & SSH_FXA_HAVE_TIME) { + ret = utimes(name, attrib_to_tv(a)); + if (ret == -1) + status = errno_to_portable(errno); + } + send_status(id, status); + xfree(name); +} + +void +process_fsetstat(void) +{ + Attrib *a; + u_int32_t id; + int handle, fd, ret; + int status = SSH_FX_OK; + char *name = NULL; + + id = get_int(); + handle = get_handle(); + a = get_attrib(); + TRACE("fsetstat id %d handle %d", id, handle); + fd = handle_to_fd(handle); + name = handle_to_name(handle); + if ((fd < 0) || (name == NULL)) { + status = SSH_FX_FAILURE; + } else { + if (a->flags & SSH_FXA_HAVE_PERM) { + ret = fchmod(fd, a->perm & 0777); + if (ret == -1) + status = errno_to_portable(errno); + } + if (a->flags & SSH_FXA_HAVE_TIME) { +#ifdef HAVE_FUTIMES + ret = futimes(fd, attrib_to_tv(a)); +#else + ret = utimes(name, attrib_to_tv(a)); +#endif + if (ret == -1) + status = errno_to_portable(errno); + } + } + send_status(id, status); +} + +void +process_opendir(void) +{ + DIR *dirp = NULL; + char *path; + int handle, status = SSH_FX_FAILURE; + u_int32_t id; + + id = get_int(); + path = get_string(NULL); + TRACE("opendir id %d path %s", id, path); + dirp = opendir(path); + if (dirp == NULL) { + status = errno_to_portable(errno); + } else { + handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp); + if (handle < 0) { + closedir(dirp); + } else { + send_handle(id, handle); + status = SSH_FX_OK; + } + + } + if (status != SSH_FX_OK) + send_status(id, status); + xfree(path); +} + +char * +ls_file(char *name, struct stat *st) +{ + char buf[1024]; + snprintf(buf, sizeof buf, "0%o %d %d %qd %d %s", + st->st_mode, st->st_uid, st->st_gid, (long long)st->st_size,(int) st->st_mtime, + name); + return xstrdup(buf); +} + +void +process_readdir(void) +{ + DIR *dirp; + struct dirent *dp; + char *path; + int handle; + u_int32_t id; + + id = get_int(); + handle = get_handle(); + TRACE("readdir id %d handle %d", id, handle); + dirp = handle_to_dir(handle); + path = handle_to_name(handle); + if (dirp == NULL || path == NULL) { + send_status(id, SSH_FX_FAILURE); + } else { + Attrib *a; + struct stat st; + char pathname[1024]; + Stat *stats; + int nstats = 10, count = 0, i; + stats = xmalloc(nstats * sizeof(Stat)); + while ((dp = readdir(dirp)) != NULL) { + if (count >= nstats) { + nstats *= 2; + stats = xrealloc(stats, nstats * sizeof(Stat)); + } +/* XXX OVERFLOW ? */ + snprintf(pathname, sizeof pathname, + "%s/%s", path, dp->d_name); + if (lstat(pathname, &st) < 0) + continue; + a = stat_to_attrib(&st); + stats[count].attrib = *a; + stats[count].name = xstrdup(dp->d_name); + stats[count].long_name = ls_file(dp->d_name, &st); + count++; + /* send up to 100 entries in one message */ + if (count == 100) + break; + } + send_names(id, count, stats); + for(i = 0; i < count; i++) { + xfree(stats[i].name); + xfree(stats[i].long_name); + } + xfree(stats); + } +} + +void +process_remove(void) +{ + char *name; + u_int32_t id; + int status = SSH_FX_FAILURE; + int ret; + + id = get_int(); + name = get_string(NULL); + TRACE("remove id %d name %s", id, name); + ret = remove(name); + status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; + send_status(id, status); + xfree(name); +} + +void +process_mkdir(void) +{ + Attrib *a; + u_int32_t id; + char *name; + int ret, mode, status = SSH_FX_FAILURE; + + id = get_int(); + name = get_string(NULL); + a = get_attrib(); + mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm & 0777 : 0777; + TRACE("mkdir id %d name %s mode 0%o", id, name, mode); + ret = mkdir(name, mode); + status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; + send_status(id, status); + xfree(name); +} + +void +process_rmdir(void) +{ + u_int32_t id; + char *name; + int ret, status; + + id = get_int(); + name = get_string(NULL); + TRACE("rmdir id %d name %s", id, name); + ret = rmdir(name); + status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; + send_status(id, status); + xfree(name); +} + +void +process_realpath(void) +{ + char resolvedname[MAXPATHLEN]; + u_int32_t id; + char *path; + + id = get_int(); + path = get_string(NULL); + TRACE("realpath id %d path %s", id, path); + if (realpath(path, resolvedname) == NULL) { + send_status(id, errno_to_portable(errno)); + } else { + Stat s; + attrib_clear(&s.attrib); + s.name = s.long_name = resolvedname; + send_names(id, 1, &s); + } + xfree(path); +} + +void +process_rename(void) +{ + u_int32_t id; + char *oldpath, *newpath; + int ret, status; + + id = get_int(); + oldpath = get_string(NULL); + newpath = get_string(NULL); + TRACE("rename id %d old %s new %s", id, oldpath, newpath); + ret = rename(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; + send_status(id, status); + xfree(oldpath); + xfree(newpath); +} + + +/* stolen from ssh-agent */ + +void +process(void) +{ + unsigned int msg_len; + unsigned int type; + unsigned char *cp; + + if (buffer_len(&iqueue) < 5) + return; /* Incomplete message. */ + cp = (unsigned char *) buffer_ptr(&iqueue); + msg_len = GET_32BIT(cp); + if (msg_len > 256 * 1024) { + error("bad message "); + exit(11); + } + if (buffer_len(&iqueue) < msg_len + 4) + return; + buffer_consume(&iqueue, 4); + type = buffer_get_char(&iqueue); + switch (type) { + case SSH_FXP_INIT: + process_init(); + break; + case SSH_FXP_OPEN: + process_open(); + break; + case SSH_FXP_CLOSE: + process_close(); + break; + case SSH_FXP_READ: + process_read(); + break; + case SSH_FXP_WRITE: + process_write(); + break; + case SSH_FXP_LSTAT: + process_lstat(); + break; + case SSH_FXP_FSTAT: + process_fstat(); + break; + case SSH_FXP_SETSTAT: + process_setstat(); + break; + case SSH_FXP_FSETSTAT: + process_fsetstat(); + break; + case SSH_FXP_OPENDIR: + process_opendir(); + break; + case SSH_FXP_READDIR: + process_readdir(); + break; + case SSH_FXP_REMOVE: + process_remove(); + break; + case SSH_FXP_MKDIR: + process_mkdir(); + break; + case SSH_FXP_RMDIR: + process_rmdir(); + break; + case SSH_FXP_REALPATH: + process_realpath(); + break; + case SSH_FXP_STAT: + process_stat(); + break; + case SSH_FXP_RENAME: + process_rename(); + break; + default: + error("Unknown message %d", type); + break; + } +} + +int +main(int ac, char **av) +{ + fd_set rset, wset; + int in, out, max; + size_t len, olen; + + handle_init(); + + in = dup(STDIN_FILENO); + out = dup(STDOUT_FILENO); + + max = 0; + if (in > max) + max = in; + if (out > max) + max = out; + + buffer_init(&iqueue); + buffer_init(&oqueue); + + for (;;) { + FD_ZERO(&rset); + FD_ZERO(&wset); + + FD_SET(in, &rset); + olen = buffer_len(&oqueue); + if (olen > 0) + FD_SET(out, &wset); + + if (select(max+1, &rset, &wset, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + exit(2); + } + + /* copy stdin to iqueue */ + if (FD_ISSET(in, &rset)) { + char buf[4*4096]; + len = read(in, buf, sizeof buf); + if (len == 0) { + debug("read eof"); + exit(0); + } else if (len < 0) { + error("read error"); + exit(1); + } else { + buffer_append(&iqueue, buf, len); + } + } + /* send oqueue to stdout */ + if (FD_ISSET(out, &wset)) { + len = write(out, buffer_ptr(&oqueue), olen); + if (len < 0) { + error("write error"); + exit(1); + } else { + buffer_consume(&oqueue, len); + } + } + /* process requests from client */ + process(); + } +} diff --git a/ssh-add.1 b/ssh-add.1 index b5dae10d..f0c31a27 100644 --- a/ssh-add.1 +++ b/ssh-add.1 @@ -65,7 +65,7 @@ This is the default file added by when no other files have been specified. .It Pa $HOME/.ssh/id_dsa Contains the DSA authentication identity of the user. -.Pp +.El .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev "DISPLAY" and "SSH_ASKPASS" @@ -91,6 +91,7 @@ or related script. may be necessary to redirect the input from .Pa /dev/null to make this work.) +.El .Sh AUTHOR Tatu Ylonen .Pp diff --git a/ssh-agent.c b/ssh-agent.c index 56b81a78..0bc4722b 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.33 2000/08/19 21:34:43 markus Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.34 2000/08/31 22:09:34 markus Exp $ */ /* * Author: Tatu Ylonen @@ -12,7 +12,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: ssh-agent.c,v 1.33 2000/08/19 21:34:43 markus Exp $"); +RCSID("$OpenBSD: ssh-agent.c,v 1.34 2000/08/31 22:09:34 markus Exp $"); #include "ssh.h" #include "rsa.h" @@ -219,6 +219,7 @@ process_sign_request2(SocketEntry *e) blob = buffer_get_string(&e->input, &blen); data = buffer_get_string(&e->input, &dlen); + buffer_get_int(&e->input); /* flags, unused */ key = dsa_key_from_blob(blob, blen); if (key != NULL) { diff --git a/ssh.1 b/ssh.1 index 58bbb701..7c04c0e9 100644 --- a/ssh.1 +++ b/ssh.1 @@ -946,6 +946,7 @@ Specifies the location of the program. The default is .Pa /usr/X11R6/bin/xauth . +.El .Sh ENVIRONMENT .Nm will normally set the following environment variables: @@ -1189,6 +1190,7 @@ above. .It Pa libcrypto.so.X.1 A version of this library which includes support for the RSA algorithm is required for proper operation. +.El .Sh AUTHOR OpenSSH is a derivative of the original (free) ssh 1.2.12 release by Tatu Ylonen, diff --git a/sshd.8 b/sshd.8 index b5c2aeff..aedaa031 100644 --- a/sshd.8 +++ b/sshd.8 @@ -589,6 +589,11 @@ The default is .It Cm Subsystem Configures an external subsystem (e.g. file transfer daemon). Arguments should be a subsystem name and a command to execute upon subsystem request. +The command +.Xr sftp-server 8 +implements the +.Dq sftp +file transfer subsystem. By default no subsystems are defined. Note that this option applies to protocol version 2 only. .It Cm SyslogFacility @@ -1031,6 +1036,7 @@ Niels Provos, Theo de Raadt, and Dug Song. The support for SSH protocol 2 was written by Markus Friedl. .Sh SEE ALSO .Xr scp 1 , +.Xr sftp-server 8 , .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , diff --git a/sshd_config b/sshd_config index b89b19fc..43d2e8ea 100644 --- a/sshd_config +++ b/sshd_config @@ -50,5 +50,6 @@ PermitEmptyPasswords no CheckMail no #UseLogin no -#Subsystem sftp /usr/local/sbin/sftpd +# Uncomment if you want to enable sftp +#Subsystem sftp /usr/libexec/sftp-server #MaxStartups 10:30:60 -- 2.45.1