summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2000-09-05 13:34:53 +1100
committerDamien Miller <djm@mindrot.org>2000-09-05 13:34:53 +1100
commit7b28dc5eb0b4d766ddbf5c1955de7e4edbe50e7c (patch)
treea3f8409e421134c543a49851c5cfd9d62a0455d5
parent123cbe8e86b1f6e4c4dc016e76dcac1616971089 (diff)
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
-rw-r--r--ChangeLog40
-rw-r--r--Makefile.in18
-rw-r--r--README.openssh217
-rw-r--r--authfd.c3
-rw-r--r--configure.in2
-rw-r--r--scp.115
-rw-r--r--scp.c4
-rw-r--r--session.c35
-rw-r--r--sftp-server.833
-rw-r--r--sftp-server.c1078
-rw-r--r--ssh-add.15
-rw-r--r--ssh-agent.c5
-rw-r--r--ssh.14
-rw-r--r--sshd.88
-rw-r--r--sshd_config3
15 files changed, 1222 insertions, 48 deletions
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 12c90aa3..df737c69 100644
--- a/README.openssh2
+++ b/README.openssh2
@@ -1,4 +1,4 @@
-$Id: README.openssh2,v 1.8 2000/05/07 18:30:03 markus Exp $
+$Id: README.openssh2,v 1.9 2000/08/31 21:56:03 markus Exp $
howto:
1) generate server key:
@@ -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: 2000/05/07 18:30:03 $
+$Date: 2000/08/31 21:56:03 $
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 b7b24762..51ef34c8 100644
--- a/scp.1
+++ b/scp.1
@@ -9,7 +9,7 @@
.\"
.\" Created: Sun May 7 00:14:37 1995 ylo
.\"
-.\" $Id: scp.1,v 1.9 2000/08/23 00:46:24 djm Exp $
+.\" $Id: scp.1,v 1.10 2000/09/05 02:34:54 djm Exp $
.\"
.Dd September 25, 1999
.Dt 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 <markus@openbsd.org>
+.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 8e9d33bf..0e6930ab 100644
--- a/ssh-add.1
+++ b/ssh-add.1
@@ -9,7 +9,7 @@
.\"
.\" Created: Sat Apr 22 23:55:14 1995 ylo
.\"
-.\" $Id: ssh-add.1,v 1.15 2000/08/29 00:33:51 djm Exp $
+.\" $Id: ssh-add.1,v 1.16 2000/09/05 02:34:54 djm Exp $
.\"
.Dd September 25, 1999
.Dt 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 <ylo@cs.hut.fi>
.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 <ylo@cs.hut.fi>
@@ -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 67dc3df7..ca208871 100644
--- a/ssh.1
+++ b/ssh.1
@@ -9,7 +9,7 @@
.\"
.\" Created: Sat Apr 22 21:55:14 1995 ylo
.\"
-.\" $Id: ssh.1,v 1.30 2000/08/29 00:33:51 djm Exp $
+.\" $Id: ssh.1,v 1.31 2000/09/05 02:34:54 djm Exp $
.\"
.Dd September 25, 1999
.Dt 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 d710baad..0402748d 100644
--- a/sshd.8
+++ b/sshd.8
@@ -9,7 +9,7 @@
.\"
.\" Created: Sat Apr 22 21:55:14 1995 ylo
.\"
-.\" $Id: sshd.8,v 1.27 2000/08/29 00:33:51 djm Exp $
+.\" $Id: sshd.8,v 1.28 2000/09/05 02:34:54 djm Exp $
.\"
.Dd September 25, 1999
.Dt 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