- More large OpenBSD CVS updates:
- [auth.c auth.h servconf.c servconf.h serverloop.c session.c]
[session.h ssh.h sshd.c README.openssh2]
ssh2 server side, see README.openssh2; enable with 'sshd -2'
- [channels.c]
no adjust after close
- [sshd.c compat.c ]
interop w/ latest ssh.com windows client.
diff --git a/ChangeLog b/ChangeLog
index 8573413..976c383 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,15 @@
- Avoid some compiler warnings in fake-get*.c
- Add IPTOS macros for systems which lack them
- Only set define entropy collection macros if they are found
-
+ - More large OpenBSD CVS updates:
+ - [auth.c auth.h servconf.c servconf.h serverloop.c session.c]
+ [session.h ssh.h sshd.c README.openssh2]
+ ssh2 server side, see README.openssh2; enable with 'sshd -2'
+ - [channels.c]
+ no adjust after close
+ - [sshd.c compat.c ]
+ interop w/ latest ssh.com windows client.
+
20000406
- OpenBSD CVS update:
- [channels.c]
diff --git a/README.openssh2 b/README.openssh2
new file mode 100644
index 0000000..59f8cf9
--- /dev/null
+++ b/README.openssh2
@@ -0,0 +1,36 @@
+$Id: README.openssh2,v 1.2 2000/04/06 21:28:22 markus Exp $
+
+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 only
+ secsh-connection: pty+shell or command, flow control works (window adjust)
+ tcp-forwarding: -L works
+ dss: verification works,
+ key database in ~/.ssh/known_hosts with bits == 0 hack
+ dss: signature works, keygen w/ openssl:
+ $ umask 077
+ $ openssl dsaparam 1024 -out dsa1024.pem
+ $ openssl gendsa -out /etc/ssh_dsa_key dsa1024.pem -rand /dev/arandom
+ start sshd with '-2' flag
+ client interops w/ sshd2, lshd
+ server interops w/ ssh2, lsh, ssh.com's Windows client, SecureCRT
+ server supports multiple concurrent sessions (e.g. with SSH.com Windows client)
+todo:
+ re-keying
+ secsh-connection features:
+ tcp-forwarding, agent-fwd, x11-fwd
+ auth other than passwd:
+ pubkey, keyboard-interactive
+ config
+ server-auth w/ old host-keys
+ cleanup
+ advanced key storage?
+ keynote
+ sftp
+
+-markus
+$Date: 2000/04/06 21:28:22 $
diff --git a/auth.c b/auth.c
index 11b5381..e94a86e 100644
--- a/auth.c
+++ b/auth.c
@@ -1,10 +1,11 @@
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
*/
#include "includes.h"
-RCSID("$OpenBSD: auth.c,v 1.1 2000/03/28 21:15:45 markus Exp $");
+RCSID("$OpenBSD: auth.c,v 1.2 2000/04/06 08:55:22 markus Exp $");
#include "xmalloc.h"
#include "rsa.h"
@@ -15,12 +16,17 @@
#include "cipher.h"
#include "mpaux.h"
#include "servconf.h"
+#include "compat.h"
#include "channels.h"
#include "match.h"
+#include "bufaux.h"
+#include "ssh2.h"
+#include "auth.h"
#include "session.h"
#include "dispatch.h"
+
/* import */
extern ServerOptions options;
extern char *forced_command;
@@ -604,3 +610,183 @@
/* Perform session preparation. */
do_authenticated(pw);
}
+
+
+void input_service_request(int type, int plen);
+void input_userauth_request(int type, int plen);
+void ssh2_pty_cleanup(void);
+
+typedef struct Authctxt Authctxt;
+struct Authctxt {
+ char *user;
+ char *service;
+ struct passwd pw;
+ int valid;
+};
+static Authctxt *authctxt = NULL;
+static int userauth_success = 0;
+
+struct passwd*
+auth_get_user(void)
+{
+ return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL;
+}
+struct passwd*
+auth_set_user(char *u, char *s)
+{
+ struct passwd *pw, *copy;
+
+ if (authctxt == NULL) {
+ authctxt = xmalloc(sizeof(*authctxt));
+ authctxt->valid = 0;
+ authctxt->user = xstrdup(u);
+ authctxt->service = xstrdup(s);
+ setproctitle("%s", u);
+ pw = getpwnam(u);
+ if (!pw || !allowed_user(pw)) {
+ log("auth_set_user: bad user %s", u);
+ return NULL;
+ }
+#ifdef USE_PAM
+ start_pam(pw);
+#endif
+ copy = &authctxt->pw;
+ memset(copy, 0, sizeof(*copy));
+ copy->pw_name = xstrdup(pw->pw_name);
+ copy->pw_passwd = xstrdup(pw->pw_passwd);
+ copy->pw_uid = pw->pw_uid;
+ copy->pw_gid = pw->pw_gid;
+ copy->pw_dir = xstrdup(pw->pw_dir);
+ copy->pw_shell = xstrdup(pw->pw_shell);
+ authctxt->valid = 1;
+ } else {
+ if (strcmp(u, authctxt->user) != 0 ||
+ strcmp(s, authctxt->service) != 0) {
+ log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)",
+ u, s, authctxt->user, authctxt->service);
+ return NULL;
+ }
+ }
+ return auth_get_user();
+}
+
+static void
+protocol_error(int type, int plen)
+{
+ log("auth: protocol error: type %d plen %d", type, plen);
+ packet_start(SSH2_MSG_UNIMPLEMENTED);
+ packet_put_int(0);
+ packet_send();
+ packet_write_wait();
+}
+void
+input_service_request(int type, int plen)
+{
+ unsigned int len;
+ int accept = 0;
+ char *service = packet_get_string(&len);
+
+ if (strcmp(service, "ssh-userauth") == 0) {
+ if (!userauth_success) {
+ accept = 1;
+ /* now we can handle user-auth requests */
+ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
+ }
+ }
+ /* XXX all other service requests are denied */
+
+ if (accept) {
+ packet_start(SSH2_MSG_SERVICE_ACCEPT);
+ packet_put_cstring(service);
+ packet_send();
+ packet_write_wait();
+ } else {
+ debug("bad service request %s", service);
+ packet_disconnect("bad service request %s", service);
+ }
+ xfree(service);
+}
+void
+input_userauth_request(int type, int plen)
+{
+ static int try = 0;
+ unsigned int len;
+ int c, authenticated = 0;
+ char *user, *service, *method;
+ struct passwd *pw;
+
+ if (++try == AUTH_FAIL_MAX)
+ packet_disconnect("too many failed userauth_requests");
+
+ user = packet_get_string(&len);
+ service = packet_get_string(&len);
+ method = packet_get_string(&len);
+ debug("userauth-request for user %s service %s method %s", user, service, method);
+
+ /* XXX we only allow the ssh-connection service */
+ pw = auth_set_user(user, service);
+ if (pw && strcmp(service, "ssh-connection")==0) {
+ if (strcmp(method, "none") == 0 && try == 1) {
+#ifdef USE_PAM
+ /* Do PAM auth with password */
+ authenticated = auth_pam_password(pw, "");
+#else /* USE_PAM */
+ /* Try authentication with the password. */
+ authenticated = auth_password(pw, "");
+#endif /* USE_PAM */
+ } else if (strcmp(method, "password") == 0) {
+ char *password;
+ c = packet_get_char();
+ if (c)
+ debug("password change not supported");
+ password = packet_get_string(&len);
+#ifdef USE_PAM
+ /* Do PAM auth with password */
+ authenticated = auth_pam_password(pw, password);
+#else /* USE_PAM */
+ /* Try authentication with the password. */
+ authenticated = auth_password(pw, password);
+#endif /* USE_PAM */
+ memset(password, 0, len);
+ xfree(password);
+ } else if (strcmp(method, "publickey") == 0) {
+ /* XXX TODO */
+ char *pkalg;
+ char *pkblob;
+ c = packet_get_char();
+ pkalg = packet_get_string(&len);
+ pkblob = packet_get_string(&len);
+ xfree(pkalg);
+ xfree(pkblob);
+ }
+ }
+ /* XXX check if other auth methods are needed */
+ if (authenticated) {
+ /* turn off userauth */
+ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
+ /* success! */
+ packet_start(SSH2_MSG_USERAUTH_SUCCESS);
+ packet_send();
+ packet_write_wait();
+ log("userauth success for %s", user);
+ /* now we can break out */
+ userauth_success = 1;
+ } else {
+ packet_start(SSH2_MSG_USERAUTH_FAILURE);
+ packet_put_cstring("password");
+ packet_put_char(0); /* partial success */
+ packet_send();
+ packet_write_wait();
+ }
+ xfree(service);
+ xfree(user);
+ xfree(method);
+}
+void
+do_authentication2()
+{
+ dispatch_init(&protocol_error);
+ dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
+ dispatch_run(DISPATCH_BLOCK, &userauth_success);
+ do_authenticated2();
+}
diff --git a/auth.h b/auth.h
index 8051777..3771e82 100644
--- a/auth.h
+++ b/auth.h
@@ -2,5 +2,9 @@
#define AUTH_H
void do_authentication(void);
+void do_authentication2(void);
+
+struct passwd *
+auth_get_user(void);
#endif
diff --git a/channels.c b/channels.c
index f03cf92..c140b77 100644
--- a/channels.c
+++ b/channels.c
@@ -17,7 +17,7 @@
*/
#include "includes.h"
-RCSID("$Id: channels.c,v 1.22 2000/04/06 02:32:38 damien Exp $");
+RCSID("$Id: channels.c,v 1.23 2000/04/12 08:45:06 damien Exp $");
#include "ssh.h"
#include "packet.h"
@@ -674,7 +674,7 @@
int
channel_check_window(Channel *c, fd_set * readset, fd_set * writeset)
{
- if (!(c->flags & CHAN_CLOSE_SENT) &&
+ if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
c->local_window < c->local_window_max/2 &&
c->local_consumed > 0) {
packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
@@ -837,7 +837,8 @@
c->istate != CHAN_INPUT_WAIT_DRAIN)
continue;
}
- if (compat20 && (c->flags & CHAN_CLOSE_SENT)) {
+ if (compat20 &&
+ (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
debug("channel: %d: no data after CLOSE", c->self);
continue;
}
diff --git a/compat.c b/compat.c
index d09f38c..c183866 100644
--- a/compat.c
+++ b/compat.c
@@ -28,7 +28,7 @@
*/
#include "includes.h"
-RCSID("$Id: compat.c,v 1.5 2000/04/06 02:32:39 damien Exp $");
+RCSID("$Id: compat.c,v 1.6 2000/04/12 08:45:06 damien Exp $");
#include "ssh.h"
#include "packet.h"
@@ -58,9 +58,7 @@
size_t len;
static const char *check[] = {
"2.0.1",
- "2.1.0.beta.9",
- "2.1.0.pre.3",
- "2.1.0.public.beta.1",
+ "2.1.0",
NULL
};
for (i = 0; check[i]; i++) {
diff --git a/servconf.c b/servconf.c
index b08ac67..800c4d5 100644
--- a/servconf.c
+++ b/servconf.c
@@ -12,7 +12,7 @@
*/
#include "includes.h"
-RCSID("$Id: servconf.c,v 1.9 2000/03/09 10:27:51 damien Exp $");
+RCSID("$Id: servconf.c,v 1.10 2000/04/12 08:45:06 damien Exp $");
#include "ssh.h"
#include "servconf.h"
@@ -31,6 +31,7 @@
options->ports_from_cmdline = 0;
options->listen_addrs = NULL;
options->host_key_file = NULL;
+ options->dsa_key_file = NULL;
options->server_key_bits = -1;
options->login_grace_time = -1;
options->key_regeneration_time = -1;
@@ -78,6 +79,8 @@
add_listen_addr(options, NULL);
if (options->host_key_file == NULL)
options->host_key_file = HOST_KEY_FILE;
+ if (options->dsa_key_file == NULL)
+ options->dsa_key_file = DSA_KEY_FILE;
if (options->server_key_bits == -1)
options->server_key_bits = 768;
if (options->login_grace_time == -1)
@@ -159,7 +162,7 @@
sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset,
sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail,
sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
- sIgnoreUserKnownHosts
+ sIgnoreUserKnownHosts, sDSAKeyFile
} ServerOpCodes;
/* Textual representation of the tokens. */
@@ -169,6 +172,7 @@
} keywords[] = {
{ "port", sPort },
{ "hostkey", sHostKeyFile },
+ { "dsakey", sDSAKeyFile },
{ "serverkeybits", sServerKeyBits },
{ "logingracetime", sLoginGraceTime },
{ "keyregenerationinterval", sKeyRegenerationTime },
@@ -338,7 +342,9 @@
break;
case sHostKeyFile:
- charptr = &options->host_key_file;
+ case sDSAKeyFile:
+ charptr = (opcode == sHostKeyFile ) ?
+ &options->host_key_file : &options->dsa_key_file;
cp = strtok(NULL, WHITESPACE);
if (!cp) {
fprintf(stderr, "%s line %d: missing file name.\n",
diff --git a/servconf.h b/servconf.h
index ab5c22c..5ce3f15 100644
--- a/servconf.h
+++ b/servconf.h
@@ -13,7 +13,7 @@
*
*/
-/* RCSID("$Id: servconf.h,v 1.6 2000/01/14 04:45:51 damien Exp $"); */
+/* RCSID("$Id: servconf.h,v 1.7 2000/04/12 08:45:07 damien Exp $"); */
#ifndef SERVCONF_H
#define SERVCONF_H
@@ -32,6 +32,7 @@
char *listen_addr; /* Address on which the server listens. */
struct addrinfo *listen_addrs; /* Addresses on which the server listens. */
char *host_key_file; /* File containing host key. */
+ char *dsa_key_file; /* File containing dsa host key. */
int server_key_bits;/* Size of the server key. */
int login_grace_time; /* Disconnect if no auth in this time
* (sec). */
diff --git a/serverloop.c b/serverloop.c
index 8bf448c..0ea57fa 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -5,6 +5,10 @@
* Created: Sun Sep 10 00:30:37 1995 ylo
* Server main loop for handling the interactive session.
*/
+/*
+ * SSH2 support by Markus Friedl.
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ */
#include "includes.h"
#include "xmalloc.h"
@@ -16,6 +20,8 @@
#include "channels.h"
#include "compat.h"
+#include "ssh2.h"
+#include "session.h"
#include "dispatch.h"
static Buffer stdin_buffer; /* Buffer for stdin data. */
@@ -72,6 +78,15 @@
signal(SIGCHLD, sigchld_handler);
errno = save_errno;
}
+void
+sigchld_handler2(int sig)
+{
+ int save_errno = errno;
+ debug("Received SIGCHLD.");
+ child_terminated = 1;
+ signal(SIGCHLD, sigchld_handler2);
+ errno = save_errno;
+}
/*
* Make packets from buffered stderr data, and buffer it for sending
@@ -154,15 +169,21 @@
* Read packets from the client unless we have too much buffered
* stdin or channel data.
*/
- if (buffer_len(&stdin_buffer) < 4096 &&
- channel_not_very_much_buffered_data())
- FD_SET(connection_in, readset);
+ if (compat20) {
+ // wrong: bad conditionXXX
+ if (channel_not_very_much_buffered_data())
+ FD_SET(connection_in, readset);
+ } else {
+ if (buffer_len(&stdin_buffer) < 4096 &&
+ channel_not_very_much_buffered_data())
+ FD_SET(connection_in, readset);
+ }
/*
* If there is not too much data already buffered going to the
* client, try to get some more data from the program.
*/
- if (packet_not_very_much_data_to_write()) {
+ if (!compat20 && packet_not_very_much_data_to_write()) {
if (!fdout_eof)
FD_SET(fdout, readset);
if (!fderr_eof)
@@ -182,7 +203,7 @@
/* If we have buffered data, try to write some of that data to the
program. */
- if (fdin != -1 && buffer_len(&stdin_buffer) > 0)
+ if (!compat20 && fdin != -1 && buffer_len(&stdin_buffer) > 0)
FD_SET(fdin, writeset);
/* Update the maximum descriptor number if appropriate. */
@@ -204,6 +225,8 @@
tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
tvp = &tv;
}
+ if (tvp!=NULL)
+ debug("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds);
/* Wait for something to happen, or the timeout to expire. */
ret = select(max_fd + 1, readset, writeset, NULL, tvp);
@@ -250,6 +273,9 @@
/* Buffer any received data. */
packet_process_incoming(buf, len);
}
+ if (compat20)
+ return;
+
/* Read and buffer any available stdout data from the program. */
if (!fdout_eof && FD_ISSET(fdout, readset)) {
len = read(fdout, buf, sizeof(buf));
@@ -279,7 +305,7 @@
int len;
/* Write buffered data to program stdin. */
- if (fdin != -1 && FD_ISSET(fdin, writeset)) {
+ if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) {
len = write(fdin, buffer_ptr(&stdin_buffer),
buffer_len(&stdin_buffer));
if (len <= 0) {
@@ -578,6 +604,51 @@
/* NOTREACHED */
}
+void
+server_loop2(void)
+{
+ fd_set readset, writeset;
+ int had_channel = 0;
+ int status;
+ pid_t pid;
+
+ debug("Entering interactive session for SSH2.");
+
+ signal(SIGCHLD, sigchld_handler2);
+ child_terminated = 0;
+ connection_in = packet_get_connection_in();
+ connection_out = packet_get_connection_out();
+ max_fd = connection_in;
+ if (connection_out > max_fd)
+ max_fd = connection_out;
+ server_init_dispatch();
+
+ for (;;) {
+ process_buffered_input_packets();
+ if (!had_channel && channel_still_open())
+ had_channel = 1;
+ if (had_channel && !channel_still_open()) {
+ debug("!channel_still_open.");
+ break;
+ }
+ if (packet_not_very_much_data_to_write())
+ channel_output_poll();
+ wait_until_can_do_something(&readset, &writeset, 0);
+ if (child_terminated) {
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+ session_close_by_pid(pid, status);
+ child_terminated = 0;
+ }
+ channel_after_select(&readset, &writeset);
+ process_input(&readset);
+ process_output(&writeset);
+ }
+ signal(SIGCHLD, SIG_DFL);
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+ session_close_by_pid(pid, status);
+ channel_stop_listening();
+}
+
void
server_input_stdin_data(int type, int plen)
{
@@ -622,6 +693,111 @@
pty_change_window_size(fdin, row, col, xpixel, ypixel);
}
+int
+input_direct_tcpip(void)
+{
+ int sock;
+ char *host, *originator;
+ int host_port, originator_port;
+
+ host = packet_get_string(NULL);
+ host_port = packet_get_int();
+ originator = packet_get_string(NULL);
+ originator_port = packet_get_int();
+ /* XXX check permission */
+ sock = channel_connect_to(host, host_port);
+ xfree(host);
+ xfree(originator);
+ if (sock < 0)
+ return -1;
+ return channel_new("direct-tcpip", SSH_CHANNEL_OPEN,
+ sock, sock, -1, 4*1024, 32*1024, 0, xstrdup("direct-tcpip"));
+}
+
+void
+server_input_channel_open(int type, int plen)
+{
+ Channel *c = NULL;
+ char *ctype;
+ int id;
+ unsigned int len;
+ int rchan;
+ int rmaxpack;
+ int rwindow;
+
+ ctype = packet_get_string(&len);
+ rchan = packet_get_int();
+ rwindow = packet_get_int();
+ rmaxpack = packet_get_int();
+
+ log("channel_input_open: ctype %s rchan %d win %d max %d",
+ ctype, rchan, rwindow, rmaxpack);
+
+ if (strcmp(ctype, "session") == 0) {
+ debug("open session");
+ /*
+ * A server session has no fd to read or write
+ * until a CHANNEL_REQUEST for a shell is made,
+ * so we set the type to SSH_CHANNEL_LARVAL.
+ * Additionally, a callback for handling all
+ * CHANNEL_REQUEST messages is registered.
+ */
+ id = channel_new(ctype, SSH_CHANNEL_LARVAL,
+ -1, -1, -1, 0, 32*1024, 0, xstrdup("server-session"));
+ if (session_open(id) == 1) {
+ channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST,
+ session_input_channel_req, (void *)0);
+ channel_register_cleanup(id, session_close_by_channel);
+ c = channel_lookup(id);
+ } else {
+ debug("session open failed, free channel %d", id);
+ channel_free(id);
+ }
+ } else if (strcmp(ctype, "direct-tcpip") == 0) {
+ debug("open direct-tcpip");
+ id = input_direct_tcpip();
+ if (id >= 0)
+ c = channel_lookup(id);
+ }
+ if (c != NULL) {
+ debug("confirm %s", ctype);
+ c->remote_id = rchan;
+ c->remote_window = rwindow;
+ c->remote_maxpacket = rmaxpack;
+
+ packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
+ packet_put_int(c->remote_id);
+ packet_put_int(c->self);
+ packet_put_int(c->local_window);
+ packet_put_int(c->local_maxpacket);
+ packet_send();
+ } else {
+ debug("failure %s", ctype);
+ packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
+ packet_put_int(rchan);
+ packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
+ packet_put_cstring("bla bla");
+ packet_put_cstring("");
+ packet_send();
+ }
+ xfree(ctype);
+}
+
+void
+server_init_dispatch_20()
+{
+ debug("server_init_dispatch_20");
+ dispatch_init(&dispatch_protocol_error);
+ dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
+ dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
+ dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
+ dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
+ dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open);
+ dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
+ dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
+ dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request);
+ dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
+}
void
server_init_dispatch_13()
{
@@ -648,7 +824,9 @@
void
server_init_dispatch()
{
- if (compat13)
+ if (compat20)
+ server_init_dispatch_20();
+ else if (compat13)
server_init_dispatch_13();
else
server_init_dispatch_15();
diff --git a/session.c b/session.c
index 2128fe3..835a469 100644
--- a/session.c
+++ b/session.c
@@ -2,9 +2,13 @@
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
*/
+/*
+ * SSH2 support by Markus Friedl.
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ */
#include "includes.h"
-RCSID("$OpenBSD: session.c,v 1.1 2000/03/28 21:15:45 markus Exp $");
+RCSID("$OpenBSD: session.c,v 1.2 2000/04/06 08:55:22 markus Exp $");
#include "xmalloc.h"
#include "ssh.h"
@@ -19,6 +23,10 @@
#include "channels.h"
#include "nchan.h"
+#include "bufaux.h"
+#include "ssh2.h"
+#include "auth.h"
+
/* types */
#define TTYSZ 64
@@ -448,9 +456,13 @@
close(pout[1]);
close(perr[1]);
- /* Enter the interactive session. */
- server_loop(pid, pin[1], pout[0], perr[0]);
- /* server_loop has closed pin[1], pout[1], and perr[1]. */
+ if (compat20) {
+ session_set_fds(s, pin[1], pout[0], perr[0]);
+ } else {
+ /* Enter the interactive session. */
+ server_loop(pid, pin[1], pout[0], perr[0]);
+ /* server_loop has closed pin[1], pout[1], and perr[1]. */
+ }
#else /* USE_PIPES */
/* We are the parent. Close the child sides of the socket pairs. */
close(inout[0]);
@@ -460,8 +472,12 @@
* Enter the interactive session. Note: server_loop must be able to
* handle the case that fdin and fdout are the same.
*/
- server_loop(pid, inout[1], inout[1], err[1]);
- /* server_loop has closed inout[1] and err[1]. */
+ if (compat20) {
+ session_set_fds(s, inout[1], inout[1], err[1]);
+ } else {
+ server_loop(pid, inout[1], inout[1], err[1]);
+ /* server_loop has closed inout[1] and err[1]. */
+ }
#endif /* USE_PIPES */
}
@@ -631,9 +647,13 @@
s->ptymaster = ptymaster;
/* Enter interactive session. */
- server_loop(pid, ptyfd, fdout, -1);
- /* server_loop _has_ closed ptyfd and fdout. */
- session_pty_cleanup(s);
+ if (compat20) {
+ session_set_fds(s, ptyfd, fdout, -1);
+ } else {
+ server_loop(pid, ptyfd, fdout, -1);
+ /* server_loop _has_ closed ptyfd and fdout. */
+ session_pty_cleanup(s);
+ }
}
/*
@@ -1126,6 +1146,181 @@
}
}
+int
+session_open(int chanid)
+{
+ Session *s = session_new();
+ debug("session_open: channel %d", chanid);
+ if (s == NULL) {
+ error("no more sessions");
+ return 0;
+ }
+ debug("session_open: session %d: link with channel %d", s->self, chanid);
+ s->chanid = chanid;
+ s->pw = auth_get_user();
+ if (s->pw == NULL)
+ fatal("no user for session %i channel %d",
+ s->self, s->chanid);
+ return 1;
+}
+
+Session *
+session_by_channel(int id)
+{
+ int i;
+ for(i = 0; i < MAX_SESSIONS; i++) {
+ Session *s = &sessions[i];
+ if (s->used && s->chanid == id) {
+ debug("session_by_channel: session %d channel %d", i, id);
+ return s;
+ }
+ }
+ debug("session_by_channel: unknown channel %d", id);
+ session_dump();
+ return NULL;
+}
+
+Session *
+session_by_pid(pid_t pid)
+{
+ int i;
+ debug("session_by_pid: pid %d", pid);
+ for(i = 0; i < MAX_SESSIONS; i++) {
+ Session *s = &sessions[i];
+ if (s->used && s->pid == pid)
+ return s;
+ }
+ error("session_by_pid: unknown pid %d", pid);
+ session_dump();
+ return NULL;
+}
+
+int
+session_window_change_req(Session *s)
+{
+ s->col = packet_get_int();
+ s->row = packet_get_int();
+ s->xpixel = packet_get_int();
+ s->ypixel = packet_get_int();
+ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
+ return 1;
+}
+
+int
+session_pty_req(Session *s)
+{
+ unsigned int len;
+
+ if (s->ttyfd != -1)
+ return -1;
+ s->term = packet_get_string(&len);
+ s->col = packet_get_int();
+ s->row = packet_get_int();
+ s->xpixel = packet_get_int();
+ s->ypixel = packet_get_int();
+
+ if (strcmp(s->term, "") == 0) {
+ xfree(s->term);
+ s->term = NULL;
+ }
+ /* Allocate a pty and open it. */
+ if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) {
+ xfree(s->term);
+ s->term = NULL;
+ s->ptyfd = -1;
+ s->ttyfd = -1;
+ error("session_pty_req: session %d alloc failed", s->self);
+ return -1;
+ }
+ debug("session_pty_req: session %d alloc %s", s->self, s->tty);
+ /*
+ * Add a cleanup function to clear the utmp entry and record logout
+ * time in case we call fatal() (e.g., the connection gets closed).
+ */
+ fatal_add_cleanup(pty_cleanup_proc, (void *)s);
+ pty_setowner(s->pw, s->tty);
+ /* Get window size from the packet. */
+ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
+
+ return 1;
+}
+
+void
+session_input_channel_req(int id, void *arg)
+{
+ unsigned int len;
+ int reply;
+ int success = 0;
+ char *rtype;
+ Session *s;
+ Channel *c;
+
+ rtype = packet_get_string(&len);
+ reply = packet_get_char();
+
+ s = session_by_channel(id);
+ if (s == NULL)
+ fatal("session_input_channel_req: channel %d: no session", id);
+ c = channel_lookup(id);
+ if (c == NULL)
+ fatal("session_input_channel_req: channel %d: bad channel", id);
+
+ debug("session_input_channel_req: session %d channel %d request %s reply %d",
+ s->self, id, rtype, reply);
+
+ /*
+ * a session is in LARVAL state until a shell
+ * or programm is executed
+ */
+ if (c->type == SSH_CHANNEL_LARVAL) {
+ if (strcmp(rtype, "shell") == 0) {
+ if (s->ttyfd == -1)
+ do_exec_no_pty(s, NULL, s->pw);
+ else
+ do_exec_pty(s, NULL, s->pw);
+ success = 1;
+ } else if (strcmp(rtype, "exec") == 0) {
+ char *command = packet_get_string(&len);
+ if (s->ttyfd == -1)
+ do_exec_no_pty(s, command, s->pw);
+ else
+ do_exec_pty(s, command, s->pw);
+ xfree(command);
+ success = 1;
+ } else if (strcmp(rtype, "pty-req") == 0) {
+ if (session_pty_req(s) > 0)
+ success = 1;
+ }
+ }
+ if (strcmp(rtype, "window-change") == 0) {
+ success = session_window_change_req(s);
+ }
+
+ if (reply) {
+ packet_start(success ?
+ SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
+ packet_put_int(c->remote_id);
+ packet_send();
+ }
+ xfree(rtype);
+}
+
+void
+session_set_fds(Session *s, int fdin, int fdout, int fderr)
+{
+ if (!compat20)
+ fatal("session_set_fds: called for proto != 2.0");
+ /*
+ * now that have a child and a pipe to the child,
+ * we can activate our channel and register the fd's
+ */
+ if (s->chanid == -1)
+ fatal("no channel for session %d", s->self);
+ channel_set_fds(s->chanid,
+ fdout, fdin, fderr,
+ fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ);
+}
+
void
session_pty_cleanup(Session *s)
{
@@ -1151,3 +1346,118 @@
if (close(s->ptymaster) < 0)
error("close(s->ptymaster): %s", strerror(errno));
}
+
+void
+session_exit_message(Session *s, int status)
+{
+ Channel *c;
+ if (s == NULL)
+ fatal("session_close: no session");
+ c = channel_lookup(s->chanid);
+ if (c == NULL)
+ fatal("session_close: session %d: no channel %d",
+ s->self, s->chanid);
+ debug("session_exit_message: session %d channel %d pid %d",
+ s->self, s->chanid, s->pid);
+
+ if (WIFEXITED(status)) {
+ channel_request_start(s->chanid,
+ "exit-status", 0);
+ packet_put_int(WEXITSTATUS(status));
+ packet_send();
+ } else if (WIFSIGNALED(status)) {
+ channel_request_start(s->chanid,
+ "exit-signal", 0);
+ packet_put_int(WTERMSIG(status));
+ packet_put_char(WCOREDUMP(status));
+ packet_put_cstring("");
+ packet_put_cstring("");
+ packet_send();
+ } else {
+ /* Some weird exit cause. Just exit. */
+ packet_disconnect("wait returned status %04x.", status);
+ }
+
+ /* disconnect channel */
+ debug("session_exit_message: release channel %d", s->chanid);
+ channel_cancel_cleanup(s->chanid);
+ if (c->istate == CHAN_INPUT_OPEN)
+ chan_read_failed(c);
+ chan_write_failed(c);
+ s->chanid = -1;
+}
+
+void
+session_free(Session *s)
+{
+ debug("session_free: session %d pid %d", s->self, s->pid);
+ if (s->term)
+ xfree(s->term);
+ if (s->display)
+ xfree(s->display);
+ if (s->auth_data)
+ xfree(s->auth_data);
+ if (s->auth_proto)
+ xfree(s->auth_proto);
+ s->used = 0;
+}
+
+void
+session_close(Session *s)
+{
+ session_pty_cleanup(s);
+ session_free(s);
+}
+
+void
+session_close_by_pid(pid_t pid, int status)
+{
+ Session *s = session_by_pid(pid);
+ if (s == NULL) {
+ debug("session_close_by_pid: no session for pid %d", s->pid);
+ return;
+ }
+ if (s->chanid != -1)
+ session_exit_message(s, status);
+ session_close(s);
+}
+
+/*
+ * this is called when a channel dies before
+ * the session 'child' itself dies
+ */
+void
+session_close_by_channel(int id, void *arg)
+{
+ Session *s = session_by_channel(id);
+ if (s == NULL) {
+ debug("session_close_by_channel: no session for channel %d", id);
+ return;
+ }
+ /* disconnect channel */
+ channel_cancel_cleanup(s->chanid);
+ s->chanid = -1;
+
+ debug("session_close_by_channel: channel %d kill %d", id, s->pid);
+ if (s->pid == 0) {
+ /* close session immediately */
+ session_close(s);
+ } else {
+ /* notify child, delay session cleanup */
+ if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0)
+ error("session_close_by_channel: kill %d: %s",
+ s->pid, strerror(errno));
+ }
+}
+
+void
+do_authenticated2(void)
+{
+ /*
+ * Cancel the alarm we set to limit the time taken for
+ * authentication.
+ */
+ alarm(0);
+ log("do_authenticated2");
+ server_loop2();
+}
diff --git a/session.h b/session.h
index 2051b73..a3427bc 100644
--- a/session.h
+++ b/session.h
@@ -4,4 +4,11 @@
/* SSH1 */
void do_authenticated(struct passwd * pw);
+/* SSH2 */
+void do_authenticated2(void);
+int session_open(int id);
+void session_input_channel_req(int id, void *arg);
+void session_close_by_pid(pid_t pid, int status);
+void session_close_by_channel(int id, void *arg);
+
#endif
diff --git a/ssh.h b/ssh.h
index cc8940b..7c5bf9c 100644
--- a/ssh.h
+++ b/ssh.h
@@ -13,7 +13,7 @@
*
*/
-/* RCSID("$Id: ssh.h,v 1.29 2000/04/01 01:09:26 damien Exp $"); */
+/* RCSID("$Id: ssh.h,v 1.30 2000/04/12 08:45:07 damien Exp $"); */
#ifndef SSH_H
#define SSH_H
@@ -90,6 +90,7 @@
#define HOST_KEY_FILE ETCDIR "/ssh_host_key"
#define SERVER_CONFIG_FILE ETCDIR "/sshd_config"
#define HOST_CONFIG_FILE ETCDIR "/ssh_config"
+#define DSA_KEY_FILE ETCDIR "/ssh_dsa_key"
#ifndef SSH_PROGRAM
#define SSH_PROGRAM "/usr/bin/ssh"
@@ -486,6 +487,8 @@
/* Removes a cleanup function to be called at fatal(). */
void fatal_remove_cleanup(void (*proc) (void *context), void *context);
+/* ---- misc */
+
/*
* Expands tildes in the file name. Returns data allocated by xmalloc.
* Warning: this calls getpw*.
@@ -500,6 +503,7 @@
* program).
*/
void server_loop(int pid, int fdin, int fdout, int fderr);
+void server_loop2(void);
/* Client side main loop for the interactive session. */
int client_loop(int have_pty, int escape_char);
diff --git a/sshd.c b/sshd.c
index bb5685d..44782e3 100644
--- a/sshd.c
+++ b/sshd.c
@@ -8,10 +8,13 @@
* information to/from the application to the user client over an encrypted
* connection. This can also handle forwarding of X11, TCP/IP, and authentication
* agent connections.
+ *
+ * SSH2 implementation,
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
*/
#include "includes.h"
-RCSID("$OpenBSD: sshd.c,v 1.97 2000/04/04 21:37:27 markus Exp $");
+RCSID("$OpenBSD: sshd.c,v 1.99 2000/04/07 09:17:39 markus Exp $");
#include "xmalloc.h"
#include "rsa.h"
@@ -25,6 +28,7 @@
#include "compat.h"
#include "buffer.h"
+#include "ssh2.h"
#ifdef HAVE_OPENSSL
# include <openssl/dh.h>
# include <openssl/bn.h>
@@ -39,9 +43,12 @@
# include <ssl/dsa.h>
# include <ssl/rsa.h>
#endif
+#include "kex.h"
#include "key.h"
+#include "dsa.h"
#include "auth.h"
+#include "myproposal.h"
#ifdef LIBWRAP
#include <tcpd.h>
@@ -70,6 +77,9 @@
int IPv4or6 = AF_UNSPEC;
#endif
+/* Flag indicating whether SSH2 is enabled */
+int allow_ssh2 = 0;
+
/*
* Debug mode flag. This can be set on the command line. If debug
* mode is enabled, extra debugging output will be sent to the system
@@ -136,6 +146,7 @@
/* Prototypes for various functions defined later in this file. */
void do_ssh1_kex();
+void do_ssh2_kex();
/*
* Close all listening sockets
@@ -255,6 +266,21 @@
errno = save_errno;
}
+char *
+chop(char *s)
+{
+ char *t = s;
+ while (*t) {
+ if(*t == '\n' || *t == '\r') {
+ *t = '\0';
+ return s;
+ }
+ t++;
+ }
+ return s;
+
+}
+
void
sshd_exchange_identification(int sock_in, int sock_out)
{
@@ -265,7 +291,9 @@
char remote_version[256]; /* Must be at least as big as buf. */
snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
- PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
+ allow_ssh2 ? 1 : PROTOCOL_MAJOR,
+ allow_ssh2 ? 99 : PROTOCOL_MINOR,
+ SSH_VERSION);
server_version_string = xstrdup(buf);
if (client_version_string == NULL) {
@@ -286,7 +314,7 @@
buf[i] = '\n';
buf[i + 1] = 0;
continue;
- /*break; XXX eat \r */
+ //break;
}
if (buf[i] == '\n') {
/* buf[i] == '\n' */
@@ -315,6 +343,8 @@
debug("Client protocol version %d.%d; client software version %.100s",
remote_major, remote_minor, remote_version);
+ compat_datafellows(remote_version);
+
switch(remote_major) {
case 1:
if (remote_minor < 3) {
@@ -324,7 +354,15 @@
/* note that this disables agent-forwarding */
enable_compat13();
}
- break;
+ if (remote_minor != 99)
+ break;
+ /* FALLTHROUGH */
+ case 2:
+ if (allow_ssh2) {
+ enable_compat20();
+ break;
+ }
+ /* FALLTHROUGH */
default:
s = "Protocol major versions differ.\n";
(void) atomicio(write, sock_out, s, strlen(s));
@@ -335,6 +373,8 @@
fatal_cleanup();
break;
}
+ chop(server_version_string);
+ chop(client_version_string);
}
/*
@@ -370,8 +410,11 @@
initialize_server_options(&options);
/* Parse command-line arguments. */
- while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ46")) != EOF) {
+ while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ246")) != EOF) {
switch (opt) {
+ case '2':
+ allow_ssh2 = 1;
+ break;
case '4':
IPv4or6 = AF_INET;
break;
@@ -837,9 +880,14 @@
packet_set_nonblocking();
/* perform the key exchange */
- do_ssh1_kex();
/* authenticate user and start session */
- do_authentication();
+ if (compat20) {
+ do_ssh2_kex();
+ do_authentication2();
+ } else {
+ do_ssh1_kex();
+ do_authentication();
+ }
#ifdef KRB4
/* Cleanup user's ticket cache file. */
@@ -1049,3 +1097,181 @@
packet_send();
packet_write_wait();
}
+
+/*
+ * SSH2 key exchange: diffie-hellman-group1-sha1
+ */
+void
+do_ssh2_kex()
+{
+ Buffer *server_kexinit;
+ Buffer *client_kexinit;
+ int payload_len, dlen;
+ int slen;
+ unsigned int klen, kout;
+ char *ptr;
+ unsigned char *signature = NULL;
+ unsigned char *server_host_key_blob = NULL;
+ unsigned int sbloblen;
+ DH *dh;
+ BIGNUM *dh_client_pub = 0;
+ BIGNUM *shared_secret = 0;
+ int i;
+ unsigned char *kbuf;
+ unsigned char *hash;
+ Kex *kex;
+ Key *server_host_key;
+ char *cprop[PROPOSAL_MAX];
+ char *sprop[PROPOSAL_MAX];
+
+/* KEXINIT */
+
+ debug("Sending KEX init.");
+
+ for (i = 0; i < PROPOSAL_MAX; i++)
+ sprop[i] = xstrdup(myproposal[i]);
+ server_kexinit = kex_init(sprop);
+ packet_start(SSH2_MSG_KEXINIT);
+ packet_put_raw(buffer_ptr(server_kexinit), buffer_len(server_kexinit));
+ packet_send();
+ packet_write_wait();
+
+ debug("done");
+
+ packet_read_expect(&payload_len, SSH2_MSG_KEXINIT);
+
+ /*
+ * save raw KEXINIT payload in buffer. this is used during
+ * computation of the session_id and the session keys.
+ */
+ client_kexinit = xmalloc(sizeof(*client_kexinit));
+ buffer_init(client_kexinit);
+ ptr = packet_get_raw(&payload_len);
+ buffer_append(client_kexinit, ptr, payload_len);
+
+ /* skip cookie */
+ for (i = 0; i < 16; i++)
+ (void) packet_get_char();
+ /* save kex init proposal strings */
+ for (i = 0; i < PROPOSAL_MAX; i++) {
+ cprop[i] = packet_get_string(NULL);
+ debug("got kexinit string: %s", cprop[i]);
+ }
+
+ i = (int) packet_get_char();
+ debug("first kex follow == %d", i);
+ i = packet_get_int();
+ debug("reserved == %d", i);
+
+ debug("done read kexinit");
+ kex = kex_choose_conf(cprop, sprop, 1);
+
+/* KEXDH */
+
+ debug("Wait SSH2_MSG_KEXDH_INIT.");
+ packet_read_expect(&payload_len, SSH2_MSG_KEXDH_INIT);
+
+ /* key, cert */
+ dh_client_pub = BN_new();
+ if (dh_client_pub == NULL)
+ fatal("dh_client_pub == NULL");
+ packet_get_bignum2(dh_client_pub, &dlen);
+
+#ifdef DEBUG_KEXDH
+ fprintf(stderr, "\ndh_client_pub= ");
+ bignum_print(dh_client_pub);
+ fprintf(stderr, "\n");
+ debug("bits %d", BN_num_bits(dh_client_pub));
+#endif
+
+ /* generate DH key */
+ dh = new_dh_group1(); /* XXX depends on 'kex' */
+
+#ifdef DEBUG_KEXDH
+ fprintf(stderr, "\np= ");
+ bignum_print(dh->p);
+ fprintf(stderr, "\ng= ");
+ bignum_print(dh->g);
+ fprintf(stderr, "\npub= ");
+ bignum_print(dh->pub_key);
+ fprintf(stderr, "\n");
+#endif
+
+ klen = DH_size(dh);
+ kbuf = xmalloc(klen);
+ kout = DH_compute_key(kbuf, dh_client_pub, dh);
+
+#ifdef DEBUG_KEXDH
+ debug("shared secret: len %d/%d", klen, kout);
+ fprintf(stderr, "shared secret == ");
+ for (i = 0; i< kout; i++)
+ fprintf(stderr, "%02x", (kbuf[i])&0xff);
+ fprintf(stderr, "\n");
+#endif
+ shared_secret = BN_new();
+
+ BN_bin2bn(kbuf, kout, shared_secret);
+ memset(kbuf, 0, klen);
+ xfree(kbuf);
+
+ server_host_key = dsa_get_serverkey(options.dsa_key_file);
+ dsa_make_serverkey_blob(server_host_key, &server_host_key_blob, &sbloblen);
+
+ /* calc H */ /* XXX depends on 'kex' */
+ hash = kex_hash(
+ client_version_string,
+ server_version_string,
+ buffer_ptr(client_kexinit), buffer_len(client_kexinit),
+ buffer_ptr(server_kexinit), buffer_len(server_kexinit),
+ (char *)server_host_key_blob, sbloblen,
+ dh_client_pub,
+ dh->pub_key,
+ shared_secret
+ );
+ buffer_free(client_kexinit);
+ buffer_free(server_kexinit);
+ xfree(client_kexinit);
+ xfree(server_kexinit);
+#ifdef DEBUG_KEXDH
+ fprintf(stderr, "hash == ");
+ for (i = 0; i< 20; i++)
+ fprintf(stderr, "%02x", (hash[i])&0xff);
+ fprintf(stderr, "\n");
+#endif
+ /* sign H */
+ dsa_sign(server_host_key, &signature, &slen, hash, 20);
+ /* hashlen depends on KEX */
+ key_free(server_host_key);
+
+ /* send server hostkey, DH pubkey 'f' and singed H */
+ packet_start(SSH2_MSG_KEXDH_REPLY);
+ packet_put_string((char *)server_host_key_blob, sbloblen);
+ packet_put_bignum2(dh->pub_key); // f
+ packet_put_string((char *)signature, slen);
+ packet_send();
+ packet_write_wait();
+
+ kex_derive_keys(kex, hash, shared_secret);
+ packet_set_kex(kex);
+
+ /* have keys, free DH */
+ DH_free(dh);
+
+ debug("send SSH2_MSG_NEWKEYS.");
+ packet_start(SSH2_MSG_NEWKEYS);
+ packet_send();
+ packet_write_wait();
+ debug("done: send SSH2_MSG_NEWKEYS.");
+
+ debug("Wait SSH2_MSG_NEWKEYS.");
+ packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS);
+ debug("GOT SSH2_MSG_NEWKEYS.");
+
+ /* send 1st encrypted/maced/compressed message */
+ packet_start(SSH2_MSG_IGNORE);
+ packet_put_cstring("markus");
+ packet_send();
+ packet_write_wait();
+
+ debug("done: KEX2.");
+}