]> andersk Git - openssh.git/commitdiff
- Big OpenBSD CVS update (mainly beginnings of SSH2 infrastructure)
authordamien <damien>
Sat, 1 Apr 2000 01:09:21 +0000 (01:09 +0000)
committerdamien <damien>
Sat, 1 Apr 2000 01:09:21 +0000 (01:09 +0000)
   - [auth.c session.c sshd.c auth.h]
     split sshd.c -> auth.c session.c sshd.c plus cleanup and goto-removal
   - [bufaux.c bufaux.h]
     support ssh2 bignums
   - [channels.c channels.h clientloop.c sshd.c nchan.c nchan.h packet.c]
     [readconf.c ssh.c ssh.h serverloop.c]
     replace big switch() with function tables (prepare for ssh2)
   - [ssh2.h]
     ssh2 message type codes
   - [sshd.8]
     reorder Xr to avoid cutting
   - [serverloop.c]
     close(fdin) if fdin != fdout, shutdown otherwise, ok theo@
   - [channels.c]
     missing close
     allow bigger packets
   - [cipher.c cipher.h]
     support ssh2 ciphers
   - [compress.c]
     cleanup, less code
   - [dispatch.c dispatch.h]
     function tables for different message types
   - [log-server.c]
     do not log() if debuggin to stderr
     rename a cpp symbol, to avoid param.h collision
   - [mpaux.c]
     KNF
   - [nchan.c]
     sync w/ channels.c

29 files changed:
ChangeLog
Makefile.in
auth.c [new file with mode: 0644]
auth.h [new file with mode: 0644]
bufaux.c
bufaux.h
channels.c
channels.h
cipher.c
cipher.h
clientloop.c
compress.c
dispatch.c [new file with mode: 0644]
dispatch.h [new file with mode: 0644]
log-server.c
mpaux.c
nchan.c
nchan.h
packet.c
pty.h
readconf.c
serverloop.c
session.c [new file with mode: 0644]
session.h [new file with mode: 0644]
ssh.c
ssh.h
ssh2.h [new file with mode: 0644]
sshd.8
sshd.c

index 2462417dc906b0dc1d614ac80c25d3d9874578f1..585686e2af11a64a9986c0148c0b8b59e3950dbe 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+20000401
+ - Big OpenBSD CVS update (mainly beginnings of SSH2 infrastructure)
+   - [auth.c session.c sshd.c auth.h]
+     split sshd.c -> auth.c session.c sshd.c plus cleanup and goto-removal
+   - [bufaux.c bufaux.h]
+     support ssh2 bignums
+   - [channels.c channels.h clientloop.c sshd.c nchan.c nchan.h packet.c]
+     [readconf.c ssh.c ssh.h serverloop.c]
+     replace big switch() with function tables (prepare for ssh2)
+   - [ssh2.h]
+     ssh2 message type codes
+   - [sshd.8]
+     reorder Xr to avoid cutting
+   - [serverloop.c]
+     close(fdin) if fdin != fdout, shutdown otherwise, ok theo@
+   - [channels.c]
+     missing close
+     allow bigger packets
+   - [cipher.c cipher.h]
+     support ssh2 ciphers
+   - [compress.c]
+     cleanup, less code
+   - [dispatch.c dispatch.h]
+     function tables for different message types
+   - [log-server.c]
+     do not log() if debuggin to stderr
+     rename a cpp symbol, to avoid param.h collision
+   - [mpaux.c]
+     KNF
+   - [nchan.c]
+     sync w/ channels.c
+
 20000326
  - Better tests for OpenSSL w/ RSAref
  - Added replacement setenv() function from OpenBSD libc. Suggested by 
index 6fee608dfd0c632bcc286543124b67e8ad13c96c..43870d5f94e89d4ffae3e9069f56c90a8d9bdc2f 100644 (file)
@@ -31,11 +31,11 @@ LDFLAGS=-L. @LDFLAGS@
 
 TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS)
 
-LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hostfile.o key.o log.o match.o mpaux.o nchan.o packet.o radix.o random.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o 
+LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hostfile.o key.o log.o match.o mpaux.o nchan.o packet.o radix.o random.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o 
 
 SSHOBJS= ssh.o sshconnect.o log-client.o readconf.o clientloop.o
 
-SSHDOBJS= sshd.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o bsd-login.o md5crypt.o
+SSHDOBJS= sshd.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o bsd-login.o md5crypt.o session.o auth.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
diff --git a/auth.c b/auth.c
new file mode 100644 (file)
index 0000000..11b5381
--- /dev/null
+++ b/auth.c
@@ -0,0 +1,606 @@
+/*
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ */
+
+#include "includes.h"
+RCSID("$OpenBSD: auth.c,v 1.1 2000/03/28 21:15:45 markus Exp $");
+
+#include "xmalloc.h"
+#include "rsa.h"
+#include "ssh.h"
+#include "pty.h"
+#include "packet.h"
+#include "buffer.h"
+#include "cipher.h"
+#include "mpaux.h"
+#include "servconf.h"
+#include "channels.h"
+#include "match.h"
+
+#include "session.h"
+#include "dispatch.h"
+
+/* import */
+extern ServerOptions options;
+extern char *forced_command;
+
+/*
+ * Check if the user is allowed to log in via ssh. If user is listed in
+ * DenyUsers or user's primary group is listed in DenyGroups, false will
+ * be returned. If AllowUsers isn't empty and user isn't listed there, or
+ * if AllowGroups isn't empty and user isn't listed there, false will be
+ * returned. 
+ * If the user's shell is not executable, false will be returned.
+ * Otherwise true is returned. 
+ */
+static int
+allowed_user(struct passwd * pw)
+{
+       struct stat st;
+       struct group *grp;
+       int i;
+#ifdef WITH_AIXAUTHENTICATE
+       char *loginmsg;
+#endif /* WITH_AIXAUTHENTICATE */
+
+       /* Shouldn't be called if pw is NULL, but better safe than sorry... */
+       if (!pw)
+               return 0;
+
+       /* deny if shell does not exists or is not executable */
+       if (stat(pw->pw_shell, &st) != 0)
+               return 0;
+       if (!((st.st_mode & S_IFREG) && (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP))))
+               return 0;
+
+       /* Return false if user is listed in DenyUsers */
+       if (options.num_deny_users > 0) {
+               if (!pw->pw_name)
+                       return 0;
+               for (i = 0; i < options.num_deny_users; i++)
+                       if (match_pattern(pw->pw_name, options.deny_users[i]))
+                               return 0;
+       }
+       /* Return false if AllowUsers isn't empty and user isn't listed there */
+       if (options.num_allow_users > 0) {
+               if (!pw->pw_name)
+                       return 0;
+               for (i = 0; i < options.num_allow_users; i++)
+                       if (match_pattern(pw->pw_name, options.allow_users[i]))
+                               break;
+               /* i < options.num_allow_users iff we break for loop */
+               if (i >= options.num_allow_users)
+                       return 0;
+       }
+       /* Get the primary group name if we need it. Return false if it fails */
+       if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
+               grp = getgrgid(pw->pw_gid);
+               if (!grp)
+                       return 0;
+
+               /* Return false if user's group is listed in DenyGroups */
+               if (options.num_deny_groups > 0) {
+                       if (!grp->gr_name)
+                               return 0;
+                       for (i = 0; i < options.num_deny_groups; i++)
+                               if (match_pattern(grp->gr_name, options.deny_groups[i]))
+                                       return 0;
+               }
+               /*
+                * Return false if AllowGroups isn't empty and user's group
+                * isn't listed there
+                */
+               if (options.num_allow_groups > 0) {
+                       if (!grp->gr_name)
+                               return 0;
+                       for (i = 0; i < options.num_allow_groups; i++)
+                               if (match_pattern(grp->gr_name, options.allow_groups[i]))
+                                       break;
+                       /* i < options.num_allow_groups iff we break for
+                          loop */
+                       if (i >= options.num_allow_groups)
+                               return 0;
+               }
+       }
+
+#ifdef WITH_AIXAUTHENTICATE
+       if (loginrestrictions(pw->pw_name,S_LOGIN,NULL,&loginmsg) != 0)
+               return 0;
+#endif /* WITH_AIXAUTHENTICATE */
+
+       /* We found no reason not to let this user try to log on... */
+       return 1;
+}
+
+/*
+ * convert ssh auth msg type into description
+ */
+char *
+get_authname(int type)
+{
+       static char buf[1024];
+       switch (type) {
+       case SSH_CMSG_AUTH_PASSWORD:
+               return "password";
+       case SSH_CMSG_AUTH_RSA:
+               return "rsa";
+       case SSH_CMSG_AUTH_RHOSTS_RSA:
+               return "rhosts-rsa";
+       case SSH_CMSG_AUTH_RHOSTS:
+               return "rhosts";
+#ifdef KRB4
+       case SSH_CMSG_AUTH_KERBEROS:
+               return "kerberos";
+#endif
+#ifdef SKEY
+       case SSH_CMSG_AUTH_TIS_RESPONSE:
+               return "s/key";
+#endif
+       }
+       snprintf(buf, sizeof buf, "bad-auth-msg-%d", type);
+       return buf;
+}
+
+#define AUTH_FAIL_MAX 6
+#define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2)
+#define AUTH_FAIL_MSG "Too many authentication failures for %.100s"
+
+/*
+ * The user does not exist or access is denied,
+ * but fake indication that authentication is needed.
+ */
+void
+do_fake_authloop1(char *user)
+{
+       int attempt = 0;
+
+       log("Faking authloop for illegal user %.200s from %.200s port %d",
+           user,
+           get_remote_ipaddr(),
+           get_remote_port());
+
+#ifdef WITH_AIXAUTHENTICATE 
+               if (strncmp(get_authname(type),"password",
+                   strlen(get_authname(type))) == 0)
+                       loginfailed(pw->pw_name,get_canonical_hostname(),"ssh");
+#endif /* WITH_AIXAUTHENTICATE */
+
+       /* Indicate that authentication is needed. */
+       packet_start(SSH_SMSG_FAILURE);
+       packet_send();
+       packet_write_wait();
+
+       /*
+        * Keep reading packets, and always respond with a failure.  This is
+        * to avoid disclosing whether such a user really exists.
+        */
+       for (attempt = 1;; attempt++) {
+               /* Read a packet.  This will not return if the client disconnects. */
+               int plen;
+#ifndef SKEY
+               (void)packet_read(&plen);
+#else /* SKEY */
+               int type = packet_read(&plen);
+               unsigned int dlen;
+               char *password, *skeyinfo;
+               /* Try to send a fake s/key challenge. */
+               if (options.skey_authentication == 1 &&
+                   (skeyinfo = skey_fake_keyinfo(user)) != NULL) {
+                       password = NULL;
+                       if (type == SSH_CMSG_AUTH_TIS) {
+                               packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
+                               packet_put_string(skeyinfo, strlen(skeyinfo));
+                               packet_send();
+                               packet_write_wait();
+                               continue;
+                       } else if (type == SSH_CMSG_AUTH_PASSWORD &&
+                                  options.password_authentication &&
+                                  (password = packet_get_string(&dlen)) != NULL &&
+                                  dlen == 5 &&
+                                  strncasecmp(password, "s/key", 5) == 0 ) {
+                               packet_send_debug(skeyinfo);
+                       }
+                       if (password != NULL)
+                               xfree(password);
+               }
+#endif
+               if (attempt > AUTH_FAIL_MAX)
+                       packet_disconnect(AUTH_FAIL_MSG, user);
+
+               /*
+                * Send failure.  This should be indistinguishable from a
+                * failed authentication.
+                */
+               packet_start(SSH_SMSG_FAILURE);
+               packet_send();
+               packet_write_wait();
+       }
+       /* NOTREACHED */
+       abort();
+}
+
+/*
+ * read packets and try to authenticate local user *pw.
+ * return if authentication is successfull
+ */
+void
+do_authloop(struct passwd * pw)
+{
+       int attempt = 0;
+       unsigned int bits;
+       RSA *client_host_key;
+       BIGNUM *n;
+       char *client_user = NULL, *password = NULL;
+       char user[1024];
+       unsigned int dlen;
+       int plen, nlen, elen;
+       unsigned int ulen;
+       int type = 0;
+       void (*authlog) (const char *fmt,...) = verbose;
+
+       /* Indicate that authentication is needed. */
+       packet_start(SSH_SMSG_FAILURE);
+       packet_send();
+       packet_write_wait();
+
+       for (attempt = 1;; attempt++) {
+               int authenticated = 0;
+               strlcpy(user, "", sizeof user);
+
+               /* Get a packet from the client. */
+               type = packet_read(&plen);
+
+               /* Process the packet. */
+               switch (type) {
+#ifdef AFS
+               case SSH_CMSG_HAVE_KERBEROS_TGT:
+                       if (!options.kerberos_tgt_passing) {
+                               /* packet_get_all(); */
+                               verbose("Kerberos tgt passing disabled.");
+                               break;
+                       } else {
+                               /* Accept Kerberos tgt. */
+                               char *tgt = packet_get_string(&dlen);
+                               packet_integrity_check(plen, 4 + dlen, type);
+                               if (!auth_kerberos_tgt(pw, tgt))
+                                       verbose("Kerberos tgt REFUSED for %s", pw->pw_name);
+                               xfree(tgt);
+                       }
+                       continue;
+
+               case SSH_CMSG_HAVE_AFS_TOKEN:
+                       if (!options.afs_token_passing || !k_hasafs()) {
+                               /* packet_get_all(); */
+                               verbose("AFS token passing disabled.");
+                               break;
+                       } else {
+                               /* Accept AFS token. */
+                               char *token_string = packet_get_string(&dlen);
+                               packet_integrity_check(plen, 4 + dlen, type);
+                               if (!auth_afs_token(pw, token_string))
+                                       verbose("AFS token REFUSED for %s", pw->pw_name);
+                               xfree(token_string);
+                       }
+                       continue;
+#endif /* AFS */
+#ifdef KRB4
+               case SSH_CMSG_AUTH_KERBEROS:
+                       if (!options.kerberos_authentication) {
+                               /* packet_get_all(); */
+                               verbose("Kerberos authentication disabled.");
+                               break;
+                       } else {
+                               /* Try Kerberos v4 authentication. */
+                               KTEXT_ST auth;
+                               char *tkt_user = NULL;
+                               char *kdata = packet_get_string((unsigned int *) &auth.length);
+                               packet_integrity_check(plen, 4 + auth.length, type);
+
+                               if (auth.length < MAX_KTXT_LEN)
+                                       memcpy(auth.dat, kdata, auth.length);
+                               xfree(kdata);
+
+                               authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user);
+
+                               if (authenticated) {
+                                       snprintf(user, sizeof user, " tktuser %s", tkt_user);
+                                       xfree(tkt_user);
+                               }
+                       }
+                       break;
+#endif /* KRB4 */
+
+               case SSH_CMSG_AUTH_RHOSTS:
+                       if (!options.rhosts_authentication) {
+                               verbose("Rhosts authentication disabled.");
+                               break;
+                       }
+                       /*
+                        * Get client user name.  Note that we just have to
+                        * trust the client; this is one reason why rhosts
+                        * authentication is insecure. (Another is
+                        * IP-spoofing on a local network.)
+                        */
+                       client_user = packet_get_string(&ulen);
+                       packet_integrity_check(plen, 4 + ulen, type);
+
+                       /* Try to authenticate using /etc/hosts.equiv and
+                          .rhosts. */
+                       authenticated = auth_rhosts(pw, client_user);
+
+                       snprintf(user, sizeof user, " ruser %s", client_user);
+                       break;
+
+               case SSH_CMSG_AUTH_RHOSTS_RSA:
+                       if (!options.rhosts_rsa_authentication) {
+                               verbose("Rhosts with RSA authentication disabled.");
+                               break;
+                       }
+                       /*
+                        * Get client user name.  Note that we just have to
+                        * trust the client; root on the client machine can
+                        * claim to be any user.
+                        */
+                       client_user = packet_get_string(&ulen);
+
+                       /* Get the client host key. */
+                       client_host_key = RSA_new();
+                       if (client_host_key == NULL)
+                               fatal("RSA_new failed");
+                       client_host_key->e = BN_new();
+                       client_host_key->n = BN_new();
+                       if (client_host_key->e == NULL || client_host_key->n == NULL)
+                               fatal("BN_new failed");
+                       bits = packet_get_int();
+                       packet_get_bignum(client_host_key->e, &elen);
+                       packet_get_bignum(client_host_key->n, &nlen);
+
+                       if (bits != BN_num_bits(client_host_key->n))
+                               error("Warning: keysize mismatch for client_host_key: "
+                                     "actual %d, announced %d", BN_num_bits(client_host_key->n), bits);
+                       packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type);
+
+                       authenticated = auth_rhosts_rsa(pw, client_user, client_host_key);
+                       RSA_free(client_host_key);
+
+                       snprintf(user, sizeof user, " ruser %s", client_user);
+                       break;
+
+               case SSH_CMSG_AUTH_RSA:
+                       if (!options.rsa_authentication) {
+                               verbose("RSA authentication disabled.");
+                               break;
+                       }
+                       /* RSA authentication requested. */
+                       n = BN_new();
+                       packet_get_bignum(n, &nlen);
+                       packet_integrity_check(plen, nlen, type);
+                       authenticated = auth_rsa(pw, n);
+                       BN_clear_free(n);
+                       break;
+
+               case SSH_CMSG_AUTH_PASSWORD:
+                       if (!options.password_authentication) {
+                               verbose("Password authentication disabled.");
+                               break;
+                       }
+                       /*
+                        * Read user password.  It is in plain text, but was
+                        * transmitted over the encrypted channel so it is
+                        * not visible to an outside observer.
+                        */
+                       password = packet_get_string(&dlen);
+                       packet_integrity_check(plen, 4 + dlen, type);
+
+#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, strlen(password));
+                       xfree(password);
+                       break;
+
+#ifdef SKEY
+               case SSH_CMSG_AUTH_TIS:
+                       debug("rcvd SSH_CMSG_AUTH_TIS");
+                       if (options.skey_authentication == 1) {
+                               char *skeyinfo = skey_keyinfo(pw->pw_name);
+                               if (skeyinfo == NULL) {
+                                       debug("generating fake skeyinfo for %.100s.", pw->pw_name);
+                                       skeyinfo = skey_fake_keyinfo(pw->pw_name);
+                               }
+                               if (skeyinfo != NULL) {
+                                       /* we send our s/key- in tis-challenge messages */
+                                       debug("sending challenge '%s'", skeyinfo);
+                                       packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
+                                       packet_put_string(skeyinfo, strlen(skeyinfo));
+                                       packet_send();
+                                       packet_write_wait();
+                                       continue;
+                               }
+                       }
+                       break;
+               case SSH_CMSG_AUTH_TIS_RESPONSE:
+                       debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE");
+                       if (options.skey_authentication == 1) {
+                               char *response = packet_get_string(&dlen);
+                               debug("skey response == '%s'", response);
+                               packet_integrity_check(plen, 4 + dlen, type);
+                               authenticated = (skey_haskey(pw->pw_name) == 0 &&
+                                                skey_passcheck(pw->pw_name, response) != -1);
+                               xfree(response);
+                       }
+                       break;
+#else
+               case SSH_CMSG_AUTH_TIS:
+                       /* TIS Authentication is unsupported */
+                       log("TIS authentication unsupported.");
+                       break;
+#endif
+
+               default:
+                       /*
+                        * Any unknown messages will be ignored (and failure
+                        * returned) during authentication.
+                        */
+                       log("Unknown message during authentication: type %d", type);
+                       break;
+               }
+
+               /*
+                * Check if the user is logging in as root and root logins
+                * are disallowed.
+                * Note that root login is allowed for forced commands.
+                */
+               if (authenticated && pw->pw_uid == 0 && !options.permit_root_login) {
+                       if (forced_command) {
+                               log("Root login accepted for forced command.");
+                       } else {
+                               authenticated = 0;
+                               log("ROOT LOGIN REFUSED FROM %.200s",
+                                   get_canonical_hostname());
+                       }
+               }
+
+               /* Raise logging level */
+               if (authenticated ||
+                   attempt == AUTH_FAIL_LOG ||
+                   type == SSH_CMSG_AUTH_PASSWORD)
+                       authlog = log;
+
+               authlog("%s %s for %.200s from %.200s port %d%s",
+                       authenticated ? "Accepted" : "Failed",
+                       get_authname(type),
+                       pw->pw_uid == 0 ? "ROOT" : pw->pw_name,
+                       get_remote_ipaddr(),
+                       get_remote_port(),
+                       user);
+
+#ifdef USE_PAM
+               if (authenticated) {
+                       if (!do_pam_account(pw->pw_name, client_user)) {
+                               if (client_user != NULL) {
+                                       xfree(client_user);
+                                       client_user = NULL;
+                               }
+                               do_fake_authloop1(pw->pw_name);
+                       }
+                       return;
+               }
+#else /* USE_PAM */
+               if (authenticated) {
+                       return;
+               }
+#endif /* USE_PAM */
+
+               if (client_user != NULL) {
+                       xfree(client_user);
+                       client_user = NULL;
+               }
+
+               if (attempt > AUTH_FAIL_MAX)
+                       packet_disconnect(AUTH_FAIL_MSG, pw->pw_name);
+
+               /* Send a message indicating that the authentication attempt failed. */
+               packet_start(SSH_SMSG_FAILURE);
+               packet_send();
+               packet_write_wait();
+       }
+}
+
+/*
+ * Performs authentication of an incoming connection.  Session key has already
+ * been exchanged and encryption is enabled.
+ */
+void
+do_authentication()
+{
+       struct passwd *pw, pwcopy;
+       int plen;
+       unsigned int ulen;
+       char *user;
+#ifdef WITH_AIXAUTHENTICATE
+       char *loginmsg;
+#endif /* WITH_AIXAUTHENTICATE */
+
+       /* Get the name of the user that we wish to log in as. */
+       packet_read_expect(&plen, SSH_CMSG_USER);
+
+       /* Get the user name. */
+       user = packet_get_string(&ulen);
+       packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER);
+
+       setproctitle("%s", user);
+
+#ifdef AFS
+       /* If machine has AFS, set process authentication group. */
+       if (k_hasafs()) {
+               k_setpag();
+               k_unlog();
+       }
+#endif /* AFS */
+
+       /* Verify that the user is a valid user. */
+       pw = getpwnam(user);
+       if (!pw || !allowed_user(pw))
+               do_fake_authloop1(user);
+       xfree(user);
+
+       /* Take a copy of the returned structure. */
+       memset(&pwcopy, 0, sizeof(pwcopy));
+       pwcopy.pw_name = xstrdup(pw->pw_name);
+       pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
+       pwcopy.pw_uid = pw->pw_uid;
+       pwcopy.pw_gid = pw->pw_gid;
+       pwcopy.pw_dir = xstrdup(pw->pw_dir);
+       pwcopy.pw_shell = xstrdup(pw->pw_shell);
+       pw = &pwcopy;
+
+#ifdef USE_PAM
+       start_pam(pw);
+#endif
+
+       /*
+        * If we are not running as root, the user must have the same uid as
+        * the server.
+        */
+       if (getuid() != 0 && pw->pw_uid != getuid())
+               packet_disconnect("Cannot change user when server not running as root.");
+
+       debug("Attempting authentication for %.100s.", pw->pw_name);
+
+       /* If the user has no password, accept authentication immediately. */
+       if (options.password_authentication &&
+#ifdef KRB4
+           (!options.kerberos_authentication || options.kerberos_or_local_passwd) &&
+#endif /* KRB4 */
+#ifdef USE_PAM
+           auth_pam_password(pw, "")) {
+#else /* USE_PAM */
+           auth_password(pw, "")) {
+#endif /* USE_PAM */
+               /* Authentication with empty password succeeded. */
+               log("Login for user %s from %.100s, accepted without authentication.",
+                   pw->pw_name, get_remote_ipaddr());
+       } else {
+               /* Loop until the user has been authenticated or the
+                  connection is closed, do_authloop() returns only if
+                  authentication is successfull */
+               do_authloop(pw);
+       }
+
+       /* The user has been authenticated and accepted. */
+#ifdef WITH_AIXAUTHENTICATE
+       loginsuccess(user,get_canonical_hostname(),"ssh",&loginmsg);
+#endif /* WITH_AIXAUTHENTICATE */
+       packet_start(SSH_SMSG_SUCCESS);
+       packet_send();
+       packet_write_wait();
+
+       /* Perform session preparation. */
+       do_authenticated(pw);
+}
diff --git a/auth.h b/auth.h
new file mode 100644 (file)
index 0000000..8051777
--- /dev/null
+++ b/auth.h
@@ -0,0 +1,6 @@
+#ifndef AUTH_H
+#define AUTH_H
+
+void   do_authentication(void);
+
+#endif
index 422584bd2a94e9d79b3039662099fad0f691342d..aa95cb3f06811e8bb2f7e97a24f74d89a27bbf1b 100644 (file)
--- a/bufaux.c
+++ b/bufaux.c
@@ -12,6 +12,8 @@
  * Auxiliary functions for storing and retrieving various data types to/from
  * Buffers.
  *
+ * SSH2 packet format added by Markus Friedl
+ *
  */
 
 #include "includes.h"
@@ -82,6 +84,50 @@ buffer_get_bignum(Buffer *buffer, BIGNUM *value)
        return 2 + bytes;
 }
 
+/*
+ * Stores an BIGNUM in the buffer in SSH2 format.
+ */
+void
+buffer_put_bignum2(Buffer *buffer, BIGNUM *value)
+{
+       int bytes = BN_num_bytes(value) + 1;
+       unsigned char *buf = xmalloc(bytes);
+       int oi;
+       int hasnohigh = 0;
+       buf[0] = '\0';
+       /* Get the value of in binary */
+       oi = BN_bn2bin(value, buf+1);
+       if (oi != bytes-1)
+               fatal("buffer_put_bignum: BN_bn2bin() failed: oi %d != bin_size %d",
+                     oi, bytes);
+       hasnohigh = (buf[1] & 0x80) ? 0 : 1;
+       if (value->neg) {
+               /**XXX should be two's-complement */
+               int i, carry;
+               unsigned char *uc = buf;
+               log("negativ!");
+               for(i = bytes-1, carry = 1; i>=0; i--) {
+                       uc[i] ^= 0xff;
+                       if(carry)
+                               carry = !++uc[i];
+               }
+       }
+       buffer_put_string(buffer, buf+hasnohigh, bytes-hasnohigh);
+       memset(buf, 0, bytes);
+       xfree(buf);
+}
+
+int
+buffer_get_bignum2(Buffer *buffer, BIGNUM *value)
+{
+       /**XXX should be two's-complement */
+       int len;
+       unsigned char *bin = (unsigned char *)buffer_get_string(buffer, (unsigned int *)&len);
+       BN_bin2bn(bin, len, value);
+       xfree(bin);
+       return len;
+}
+
 /*
  * Returns an integer from the buffer (4 bytes, msb first).
  */
@@ -142,6 +188,11 @@ buffer_put_string(Buffer *buffer, const void *buf, unsigned int len)
        buffer_put_int(buffer, len);
        buffer_append(buffer, buf, len);
 }
+void 
+buffer_put_cstring(Buffer *buffer, const char *s)
+{
+       buffer_put_string(buffer, s, strlen(s));
+}
 
 /*
  * Returns a character from the buffer (0 - 255).
index c19b3a9796f7f8749e0ca7a788e29114d19fb22d..ce5442efedb5ede1a1426388cf513ba3cf3fba92 100644 (file)
--- a/bufaux.h
+++ b/bufaux.h
  * by (bits+7)/8 bytes of binary data, msb first.
  */
 void    buffer_put_bignum(Buffer * buffer, BIGNUM * value);
+void    buffer_put_bignum2(Buffer * buffer, BIGNUM * value);
 
 /* Retrieves an BIGNUM from the buffer. */
 int     buffer_get_bignum(Buffer * buffer, BIGNUM * value);
+int    buffer_get_bignum2(Buffer *buffer, BIGNUM * value);
 
 /* Returns an integer from the buffer (4 bytes, msb first). */
 unsigned int buffer_get_int(Buffer * buffer);
@@ -51,5 +53,6 @@ char   *buffer_get_string(Buffer * buffer, unsigned int *length_ptr);
 
 /* Stores and arbitrary binary string in the buffer. */
 void    buffer_put_string(Buffer * buffer, const void *buf, unsigned int len);
+void   buffer_put_cstring(Buffer *buffer, const char *s);
 
 #endif                         /* BUFAUX_H */
index 16e3df3980ef3a64f6f18e7da858636e5bf0d3a5..0becd4ca0555e75315e929811c30475a326240ed 100644 (file)
@@ -37,6 +37,10 @@ RCSID("$Id$");
 /* Max len of agent socket */
 #define MAX_SOCKET_NAME 100
 
+/* default buffer for tcp-fwd-channel */
+#define CHAN_WINDOW_DEFAULT      (8*1024)
+#define CHAN_PACKET_DEFAULT     (CHAN_WINDOW_DEFAULT/2)
+
 /*
  * Pointer to an array containing all allocated channels.  The array is
  * dynamically extended as needed.
@@ -81,8 +85,9 @@ unsigned int x11_fake_data_len;
  * network (which might be behind a firewall).
  */
 typedef struct {
-       char *host;             /* Host name. */
-       u_short port;           /* Port number. */
+       char *host_to_connect;          /* Connect to 'host'. */
+       u_short port_to_connect;        /* Connect to 'port'. */
+       u_short listen_port;            /* Remote side should listen port number. */
 } ForwardPermission;
 
 /* List of all permitted host/port pairs to connect. */
@@ -119,20 +124,43 @@ channel_permit_all_opens()
        all_opens_permitted = 1;
 }
 
+/* lookup channel by id */
+
+Channel *
+channel_lookup(int id)
+{
+       Channel *c;
+       if (id < 0 && id > channels_alloc) {
+               log("channel_lookup: %d: bad id", id);
+               return NULL;
+       }
+       c = &channels[id];
+       if (c->type == SSH_CHANNEL_FREE) {
+               log("channel_lookup: %d: bad id: channel free", id);
+               return NULL;
+       }
+       return c;
+}
+
 /*
  * Allocate a new channel object and set its type and socket. This will cause
  * remote_name to be freed.
  */
 
 int 
-channel_allocate(int type, int sock, char *remote_name)
+channel_new(char *ctype, int type, int rfd, int wfd, int efd,
+    int window, int maxpack, int extended_usage, char *remote_name)
 {
        int i, found;
        Channel *c;
 
        /* Update the maximum file descriptor value. */
-       if (sock > channel_max_fd_value)
-               channel_max_fd_value = sock;
+       if (rfd > channel_max_fd_value)
+               channel_max_fd_value = rfd;
+       if (wfd > channel_max_fd_value)
+               channel_max_fd_value = wfd;
+       if (efd > channel_max_fd_value)
+               channel_max_fd_value = efd;
        /* XXX set close-on-exec -markus */
 
        /* Do initial allocation if this is the first call. */
@@ -167,388 +195,514 @@ channel_allocate(int type, int sock, char *remote_name)
        c = &channels[found];
        buffer_init(&c->input);
        buffer_init(&c->output);
+       buffer_init(&c->extended);
        chan_init_iostates(c);
        c->self = found;
        c->type = type;
-       c->sock = sock;
+       c->ctype = ctype;
+       c->local_window = window;
+       c->local_window_max = window;
+       c->local_consumed = 0;
+       c->local_maxpacket = maxpack;
+       c->remote_window = 0;
+       c->remote_maxpacket = 0;
+       c->rfd = rfd;
+       c->wfd = wfd;
+       c->sock = (rfd == wfd) ? rfd : -1;
+       c->efd = efd;
+       c->extended_usage = extended_usage;
        c->remote_id = -1;
        c->remote_name = remote_name;
+       c->remote_window = 0;
+       c->remote_maxpacket = 0;
+       c->cb_fn = NULL;
+       c->cb_arg = NULL;
+       c->cb_event = 0;
+       c->dettach_user = NULL;
        debug("channel %d: new [%s]", found, remote_name);
        return found;
 }
+int 
+channel_allocate(int type, int sock, char *remote_name)
+{
+       return channel_new("", type, sock, sock, -1, 0, 0, 0, remote_name);
+}
 
 /* Free the channel and close its socket. */
 
 void 
-channel_free(int channel)
+channel_free(int id)
 {
-       if (channel < 0 || channel >= channels_alloc ||
-           channels[channel].type == SSH_CHANNEL_FREE)
-               packet_disconnect("channel free: bad local channel %d", channel);
-
-       if (compat13)
-               shutdown(channels[channel].sock, SHUT_RDWR);
-       close(channels[channel].sock);
-       buffer_free(&channels[channel].input);
-       buffer_free(&channels[channel].output);
-       channels[channel].type = SSH_CHANNEL_FREE;
-       if (channels[channel].remote_name) {
-               xfree(channels[channel].remote_name);
-               channels[channel].remote_name = NULL;
+       Channel *c = channel_lookup(id);
+       if (c == NULL)
+               packet_disconnect("channel free: bad local channel %d", id);
+       debug("channel_free: channel %d: status: %s", id, channel_open_message());
+       if (c->sock != -1) {
+               shutdown(c->sock, SHUT_RDWR);
+               close(c->sock);
+       }
+       buffer_free(&c->input);
+       buffer_free(&c->output);
+       buffer_free(&c->extended);
+       c->type = SSH_CHANNEL_FREE;
+       if (c->remote_name) {
+               xfree(c->remote_name);
+               c->remote_name = NULL;
        }
 }
 
 /*
- * This is called just before select() to add any bits relevant to channels
- * in the select bitmasks.
+ * 'channel_pre*' are called just before select() to add any bits relevant to
+ * channels in the select bitmasks.
+ */
+/*
+ * 'channel_post*': perform any appropriate operations for channels which
+ * have events pending.
  */
+typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset);
+chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
+chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
 
-void 
-channel_prepare_select(fd_set * readset, fd_set * writeset)
+void
+channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset)
 {
-       int i;
-       Channel *ch;
-       unsigned char *ucp;
-       unsigned int proto_len, data_len;
+       FD_SET(c->sock, readset);
+}
 
-       for (i = 0; i < channels_alloc; i++) {
-               ch = &channels[i];
-redo:
-               switch (ch->type) {
-               case SSH_CHANNEL_X11_LISTENER:
-               case SSH_CHANNEL_PORT_LISTENER:
-               case SSH_CHANNEL_AUTH_SOCKET:
-                       FD_SET(ch->sock, readset);
-                       break;
+void
+channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       if (buffer_len(&c->input) < packet_get_maxsize())
+               FD_SET(c->sock, readset);
+       if (buffer_len(&c->output) > 0)
+               FD_SET(c->sock, writeset);
+}
 
-               case SSH_CHANNEL_OPEN:
-                       if (compat13) {
-                               if (buffer_len(&ch->input) < packet_get_maxsize())
-                                       FD_SET(ch->sock, readset);
-                               if (buffer_len(&ch->output) > 0)
-                                       FD_SET(ch->sock, writeset);
-                               break;
-                       }
-                       /* test whether sockets are 'alive' for read/write */
-                       if (ch->istate == CHAN_INPUT_OPEN)
-                               if (buffer_len(&ch->input) < packet_get_maxsize())
-                                       FD_SET(ch->sock, readset);
-                       if (ch->ostate == CHAN_OUTPUT_OPEN ||
-                           ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
-                               if (buffer_len(&ch->output) > 0) {
-                                       FD_SET(ch->sock, writeset);
-                               } else if (ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
-                                       chan_obuf_empty(ch);
-                               }
-                       }
-                       break;
+void
+channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       /* test whether sockets are 'alive' for read/write */
+       if (c->istate == CHAN_INPUT_OPEN)
+               if (buffer_len(&c->input) < packet_get_maxsize())
+                       FD_SET(c->sock, readset);
+       if (c->ostate == CHAN_OUTPUT_OPEN ||
+           c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
+               if (buffer_len(&c->output) > 0) {
+                       FD_SET(c->sock, writeset);
+               } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
+                       chan_obuf_empty(c);
+               }
+       }
+}
 
-               case SSH_CHANNEL_INPUT_DRAINING:
-                       if (!compat13)
-                               fatal("cannot happen: IN_DRAIN");
-                       if (buffer_len(&ch->input) == 0) {
-                               packet_start(SSH_MSG_CHANNEL_CLOSE);
-                               packet_put_int(ch->remote_id);
-                               packet_send();
-                               ch->type = SSH_CHANNEL_CLOSED;
-                               debug("Closing channel %d after input drain.", ch->self);
-                               break;
-                       }
-                       break;
+void
+channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       if (buffer_len(&c->input) == 0) {
+               packet_start(SSH_MSG_CHANNEL_CLOSE);
+               packet_put_int(c->remote_id);
+               packet_send();
+               c->type = SSH_CHANNEL_CLOSED;
+               debug("Closing channel %d after input drain.", c->self);
+       }
+}
 
-               case SSH_CHANNEL_OUTPUT_DRAINING:
-                       if (!compat13)
-                               fatal("cannot happen: OUT_DRAIN");
-                       if (buffer_len(&ch->output) == 0) {
-                               channel_free(i);
-                               break;
-                       }
-                       FD_SET(ch->sock, writeset);
-                       break;
+void
+channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       if (buffer_len(&c->output) == 0)
+               channel_free(c->self);
+       else 
+               FD_SET(c->sock, writeset);
+}
 
-               case SSH_CHANNEL_X11_OPEN:
-                       /*
-                        * This is a special state for X11 authentication
-                        * spoofing.  An opened X11 connection (when
-                        * authentication spoofing is being done) remains in
-                        * this state until the first packet has been
-                        * completely read.  The authentication data in that
-                        * packet is then substituted by the real data if it
-                        * matches the fake data, and the channel is put into
-                        * normal mode.
-                        */
-                       /* Check if the fixed size part of the packet is in buffer. */
-                       if (buffer_len(&ch->output) < 12)
-                               break;
+/*
+ * This is a special state for X11 authentication spoofing.  An opened X11
+ * connection (when authentication spoofing is being done) remains in this
+ * state until the first packet has been completely read.  The authentication
+ * data in that packet is then substituted by the real data if it matches the
+ * fake data, and the channel is put into normal mode.
+ */
+int
+x11_open_helper(Channel *c)
+{
+       unsigned char *ucp;
+       unsigned int proto_len, data_len;
 
-                       /* Parse the lengths of variable-length fields. */
-                       ucp = (unsigned char *) buffer_ptr(&ch->output);
-                       if (ucp[0] == 0x42) {   /* Byte order MSB first. */
-                               proto_len = 256 * ucp[6] + ucp[7];
-                               data_len = 256 * ucp[8] + ucp[9];
-                       } else if (ucp[0] == 0x6c) {    /* Byte order LSB first. */
-                               proto_len = ucp[6] + 256 * ucp[7];
-                               data_len = ucp[8] + 256 * ucp[9];
-                       } else {
-                               debug("Initial X11 packet contains bad byte order byte: 0x%x",
-                                     ucp[0]);
-                               ch->type = SSH_CHANNEL_OPEN;
-                               goto reject;
-                       }
+       /* Check if the fixed size part of the packet is in buffer. */
+       if (buffer_len(&c->output) < 12)
+               return 0;
+
+       /* Parse the lengths of variable-length fields. */
+       ucp = (unsigned char *) buffer_ptr(&c->output);
+       if (ucp[0] == 0x42) {   /* Byte order MSB first. */
+               proto_len = 256 * ucp[6] + ucp[7];
+               data_len = 256 * ucp[8] + ucp[9];
+       } else if (ucp[0] == 0x6c) {    /* Byte order LSB first. */
+               proto_len = ucp[6] + 256 * ucp[7];
+               data_len = ucp[8] + 256 * ucp[9];
+       } else {
+               debug("Initial X11 packet contains bad byte order byte: 0x%x",
+                     ucp[0]);
+               return -1;
+       }
 
-                       /* Check if the whole packet is in buffer. */
-                       if (buffer_len(&ch->output) <
-                           12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
-                               break;
+       /* Check if the whole packet is in buffer. */
+       if (buffer_len(&c->output) <
+           12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
+               return 0;
 
-                       /* Check if authentication protocol matches. */
-                       if (proto_len != strlen(x11_saved_proto) ||
-                           memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
-                               debug("X11 connection uses different authentication protocol.");
-                               ch->type = SSH_CHANNEL_OPEN;
-                               goto reject;
-                       }
-                       /* Check if authentication data matches our fake data. */
-                       if (data_len != x11_fake_data_len ||
-                           memcmp(ucp + 12 + ((proto_len + 3) & ~3),
-                               x11_fake_data, x11_fake_data_len) != 0) {
-                               debug("X11 auth data does not match fake data.");
-                               ch->type = SSH_CHANNEL_OPEN;
-                               goto reject;
-                       }
-                       /* Check fake data length */
-                       if (x11_fake_data_len != x11_saved_data_len) {
-                               error("X11 fake_data_len %d != saved_data_len %d",
-                                 x11_fake_data_len, x11_saved_data_len);
-                               ch->type = SSH_CHANNEL_OPEN;
-                               goto reject;
-                       }
-                       /*
-                        * Received authentication protocol and data match
-                        * our fake data. Substitute the fake data with real
-                        * data.
-                        */
-                       memcpy(ucp + 12 + ((proto_len + 3) & ~3),
-                              x11_saved_data, x11_saved_data_len);
+       /* Check if authentication protocol matches. */
+       if (proto_len != strlen(x11_saved_proto) ||
+           memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
+               debug("X11 connection uses different authentication protocol.");
+               return -1;
+       }
+       /* Check if authentication data matches our fake data. */
+       if (data_len != x11_fake_data_len ||
+           memcmp(ucp + 12 + ((proto_len + 3) & ~3),
+               x11_fake_data, x11_fake_data_len) != 0) {
+               debug("X11 auth data does not match fake data.");
+               return -1;
+       }
+       /* Check fake data length */
+       if (x11_fake_data_len != x11_saved_data_len) {
+               error("X11 fake_data_len %d != saved_data_len %d",
+                   x11_fake_data_len, x11_saved_data_len);
+               return -1;
+       }
+       /*
+        * Received authentication protocol and data match
+        * our fake data. Substitute the fake data with real
+        * data.
+        */
+       memcpy(ucp + 12 + ((proto_len + 3) & ~3),
+           x11_saved_data, x11_saved_data_len);
+       return 1;
+}
 
-                       /* Start normal processing for the channel. */
-                       ch->type = SSH_CHANNEL_OPEN;
-                       goto redo;
+void
+channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       int ret = x11_open_helper(c);
+       if (ret == 1) {
+               /* Start normal processing for the channel. */
+               c->type = SSH_CHANNEL_OPEN;
+       } else if (ret == -1) {
+               /*
+                * We have received an X11 connection that has bad
+                * authentication information.
+                */
+               log("X11 connection rejected because of wrong authentication.\r\n");
+               buffer_clear(&c->input);
+               buffer_clear(&c->output);
+               close(c->sock);
+               c->sock = -1;
+               c->type = SSH_CHANNEL_CLOSED;
+               packet_start(SSH_MSG_CHANNEL_CLOSE);
+               packet_put_int(c->remote_id);
+               packet_send();
+       }
+}
 
-       reject:
-                       /*
-                        * We have received an X11 connection that has bad
-                        * authentication information.
-                        */
-                       log("X11 connection rejected because of wrong authentication.\r\n");
-                       buffer_clear(&ch->input);
-                       buffer_clear(&ch->output);
-                       if (compat13) {
-                               close(ch->sock);
-                               ch->sock = -1;
-                               ch->type = SSH_CHANNEL_CLOSED;
-                               packet_start(SSH_MSG_CHANNEL_CLOSE);
-                               packet_put_int(ch->remote_id);
-                               packet_send();
-                       } else {
-                               debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate);
-                               chan_read_failed(ch);
-                               chan_write_failed(ch);
-                               debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate);
-                       }
-                       break;
+void
+channel_pre_x11_open_15(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       int ret = x11_open_helper(c);
+       if (ret == 1) {
+               c->type = SSH_CHANNEL_OPEN;
+       } else if (ret == -1) {
+               debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
+               chan_read_failed(c);
+               chan_write_failed(c);
+               debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
+       }
+}
 
-               case SSH_CHANNEL_FREE:
-               default:
-                       continue;
+/* This is our fake X11 server socket. */
+void
+channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       struct sockaddr addr;
+       int newsock, newch;
+       socklen_t addrlen;
+       char buf[16384], *remote_hostname;
+
+       if (FD_ISSET(c->sock, readset)) {
+               debug("X11 connection requested.");
+               addrlen = sizeof(addr);
+               newsock = accept(c->sock, &addr, &addrlen);
+               if (newsock < 0) {
+                       error("accept: %.100s", strerror(errno));
+                       return;
                }
+               remote_hostname = get_remote_hostname(newsock);
+               snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
+               remote_hostname, get_peer_port(newsock));
+               xfree(remote_hostname);
+               newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
+                                        xstrdup(buf));
+               packet_start(SSH_SMSG_X11_OPEN);
+               packet_put_int(newch);
+               if (have_hostname_in_open)
+                       packet_put_string(buf, strlen(buf));
+               packet_send();
        }
 }
 
 /*
- * After select, perform any appropriate operations for channels which have
- * events pending.
+ * This socket is listening for connections to a forwarded TCP/IP port.
  */
+void
+channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       struct sockaddr addr;
+       int newsock, newch;
+       socklen_t addrlen;
+       char buf[1024], *remote_hostname;
+       int remote_port;
+
+       if (FD_ISSET(c->sock, readset)) {
+               debug("Connection to port %d forwarding "
+                   "to %.100s port %d requested.",
+                   c->listening_port, c->path, c->host_port);
+               addrlen = sizeof(addr);
+               newsock = accept(c->sock, &addr, &addrlen);
+               if (newsock < 0) {
+                       error("accept: %.100s", strerror(errno));
+                       return;
+               }
+               remote_hostname = get_remote_hostname(newsock);
+               remote_port = get_peer_port(newsock);
+               snprintf(buf, sizeof buf,
+                   "listen port %d for %.100s port %d, "
+                   "connect from %.200s port %d",
+                   c->listening_port, c->path, c->host_port,
+                   remote_hostname, remote_port);
+               newch = channel_new("direct-tcpip",
+                   SSH_CHANNEL_OPENING, newsock, newsock, -1,
+                   c->local_window_max, c->local_maxpacket,
+                   0, xstrdup(buf));
+
+               packet_start(SSH_MSG_PORT_OPEN);
+               packet_put_int(newch);
+               packet_put_string(c->path, strlen(c->path));
+               packet_put_int(c->host_port);
+               if (have_hostname_in_open) {
+                       packet_put_string(buf, strlen(buf));
+               }
+               packet_send();
+               xfree(remote_hostname);
+       }
+}
 
-void 
-channel_after_select(fd_set * readset, fd_set * writeset)
+/*
+ * This is the authentication agent socket listening for connections from
+ * clients.
+ */
+void
+channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset)
 {
        struct sockaddr addr;
-       int newsock, i, newch, len;
+       int newsock, newch;
        socklen_t addrlen;
-       Channel *ch;
-       char buf[16384], *remote_hostname;
 
-       /* Loop over all channels... */
-       for (i = 0; i < channels_alloc; i++) {
-               ch = &channels[i];
-               switch (ch->type) {
-               case SSH_CHANNEL_X11_LISTENER:
-                       /* This is our fake X11 server socket. */
-                       if (FD_ISSET(ch->sock, readset)) {
-                               debug("X11 connection requested.");
-                               addrlen = sizeof(addr);
-                               newsock = accept(ch->sock, &addr, &addrlen);
-                               if (newsock < 0) {
-                                       error("accept: %.100s", strerror(errno));
-                                       break;
-                               }
-                               remote_hostname = get_remote_hostname(newsock);
-                               snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
-                               remote_hostname, get_peer_port(newsock));
-                               xfree(remote_hostname);
-                               newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
-                                                        xstrdup(buf));
-                               packet_start(SSH_SMSG_X11_OPEN);
-                               packet_put_int(newch);
-                               if (have_hostname_in_open)
-                                       packet_put_string(buf, strlen(buf));
-                               packet_send();
-                       }
-                       break;
+       if (FD_ISSET(c->sock, readset)) {
+               addrlen = sizeof(addr);
+               newsock = accept(c->sock, &addr, &addrlen);
+               if (newsock < 0) {
+                       error("accept from auth socket: %.100s", strerror(errno));
+                       return;
+               }
+               newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
+                   xstrdup("accepted auth socket"));
+               packet_start(SSH_SMSG_AGENT_OPEN);
+               packet_put_int(newch);
+               packet_send();
+       }
+}
 
-               case SSH_CHANNEL_PORT_LISTENER:
-                       /*
-                        * This socket is listening for connections to a
-                        * forwarded TCP/IP port.
-                        */
-                       if (FD_ISSET(ch->sock, readset)) {
-                               debug("Connection to port %d forwarding to %.100s port %d requested.",
-                                     ch->listening_port, ch->path, ch->host_port);
-                               addrlen = sizeof(addr);
-                               newsock = accept(ch->sock, &addr, &addrlen);
-                               if (newsock < 0) {
-                                       error("accept: %.100s", strerror(errno));
-                                       break;
-                               }
-                               remote_hostname = get_remote_hostname(newsock);
-                               snprintf(buf, sizeof buf, "listen port %d for %.100s port %d, connect from %.200s port %d",
-                                        ch->listening_port, ch->path, ch->host_port,
-                               remote_hostname, get_peer_port(newsock));
-                               xfree(remote_hostname);
-                               newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
-                                                        xstrdup(buf));
-                               packet_start(SSH_MSG_PORT_OPEN);
-                               packet_put_int(newch);
-                               packet_put_string(ch->path, strlen(ch->path));
-                               packet_put_int(ch->host_port);
-                               if (have_hostname_in_open)
-                                       packet_put_string(buf, strlen(buf));
-                               packet_send();
+int
+channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       char buf[16*1024];
+       int len;
+
+       if (c->rfd != -1 &&
+           FD_ISSET(c->rfd, readset)) {
+               len = read(c->rfd, buf, sizeof(buf));
+               if (len <= 0) {
+                       debug("channel %d: read<0 rfd %d len %d",
+                           c->self, c->rfd, len);
+                       if (compat13) {
+                               buffer_consume(&c->output, buffer_len(&c->output));
+                               c->type = SSH_CHANNEL_INPUT_DRAINING;
+                               debug("Channel %d status set to input draining.", c->self);
+                       } else {
+                               chan_read_failed(c);
                        }
-                       break;
-
-               case SSH_CHANNEL_AUTH_SOCKET:
-                       /*
-                        * This is the authentication agent socket listening
-                        * for connections from clients.
-                        */
-                       if (FD_ISSET(ch->sock, readset)) {
-                               addrlen = sizeof(addr);
-                               newsock = accept(ch->sock, &addr, &addrlen);
-                               if (newsock < 0) {
-                                       error("accept from auth socket: %.100s", strerror(errno));
-                                       break;
-                               }
-                               newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
-                                       xstrdup("accepted auth socket"));
-                               packet_start(SSH_SMSG_AGENT_OPEN);
-                               packet_put_int(newch);
-                               packet_send();
+                       return -1;
+               }
+               buffer_append(&c->input, buf, len);
+       }
+       return 1;
+}
+int
+channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       int len;
+
+       /* Send buffered output data to the socket. */
+       if (c->wfd != -1 &&
+           FD_ISSET(c->wfd, writeset) &&
+           buffer_len(&c->output) > 0) {
+               len = write(c->wfd, buffer_ptr(&c->output),
+                           buffer_len(&c->output));
+               if (len <= 0) {
+                       if (compat13) {
+                               buffer_consume(&c->output, buffer_len(&c->output));
+                               debug("Channel %d status set to input draining.", c->self);
+                               c->type = SSH_CHANNEL_INPUT_DRAINING;
+                       } else {
+                               chan_write_failed(c);
                        }
-                       break;
+                       return -1;
+               }
+               buffer_consume(&c->output, len);
+       }
+       return 1;
+}
 
-               case SSH_CHANNEL_OPEN:
-                       /*
-                        * This is an open two-way communication channel. It
-                        * is not of interest to us at this point what kind
-                        * of data is being transmitted.
-                        */
+void
+channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       channel_handle_rfd(c, readset, writeset);
+       channel_handle_wfd(c, readset, writeset);
+}
 
-                       /*
-                        * Read available incoming data and append it to
-                        * buffer; shutdown socket, if read or write failes
-                        */
-                       if (FD_ISSET(ch->sock, readset)) {
-                               len = read(ch->sock, buf, sizeof(buf));
-                               if (len <= 0) {
-                                       if (compat13) {
-                                               buffer_consume(&ch->output, buffer_len(&ch->output));
-                                               ch->type = SSH_CHANNEL_INPUT_DRAINING;
-                                               debug("Channel %d status set to input draining.", i);
-                                       } else {
-                                               chan_read_failed(ch);
-                                       }
-                                       break;
-                               }
-                               buffer_append(&ch->input, buf, len);
-                       }
-                       /* Send buffered output data to the socket. */
-                       if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) {
-                               len = write(ch->sock, buffer_ptr(&ch->output),
-                                           buffer_len(&ch->output));
-                               if (len <= 0) {
-                                       if (compat13) {
-                                               buffer_consume(&ch->output, buffer_len(&ch->output));
-                                               debug("Channel %d status set to input draining.", i);
-                                               ch->type = SSH_CHANNEL_INPUT_DRAINING;
-                                       } else {
-                                               chan_write_failed(ch);
-                                       }
-                                       break;
-                               }
-                               buffer_consume(&ch->output, len);
-                       }
-                       break;
+void
+channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
+{
+       int len;
+       /* Send buffered output data to the socket. */
+       if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {
+               len = write(c->sock, buffer_ptr(&c->output),
+                           buffer_len(&c->output));
+               if (len <= 0)
+                       buffer_consume(&c->output, buffer_len(&c->output));
+               else
+                       buffer_consume(&c->output, len);
+       }
+}
 
-               case SSH_CHANNEL_OUTPUT_DRAINING:
-                       if (!compat13)
-                               fatal("cannot happen: OUT_DRAIN");
-                       /* Send buffered output data to the socket. */
-                       if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) {
-                               len = write(ch->sock, buffer_ptr(&ch->output),
-                                           buffer_len(&ch->output));
-                               if (len <= 0)
-                                       buffer_consume(&ch->output, buffer_len(&ch->output));
-                               else
-                                       buffer_consume(&ch->output, len);
-                       }
-                       break;
+void
+channel_handler_init_13(void)
+{
+       channel_pre[SSH_CHANNEL_OPEN] =                 &channel_pre_open_13;
+       channel_pre[SSH_CHANNEL_X11_OPEN] =             &channel_pre_x11_open_13;
+       channel_pre[SSH_CHANNEL_X11_LISTENER] =         &channel_pre_listener;
+       channel_pre[SSH_CHANNEL_PORT_LISTENER] =        &channel_pre_listener;
+       channel_pre[SSH_CHANNEL_AUTH_SOCKET] =          &channel_pre_listener;
+       channel_pre[SSH_CHANNEL_INPUT_DRAINING] =       &channel_pre_input_draining;
+       channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] =      &channel_pre_output_draining;
+
+       channel_post[SSH_CHANNEL_OPEN] =                &channel_post_open_1;
+       channel_post[SSH_CHANNEL_X11_LISTENER] =        &channel_post_x11_listener;
+       channel_post[SSH_CHANNEL_PORT_LISTENER] =       &channel_post_port_listener;
+       channel_post[SSH_CHANNEL_AUTH_SOCKET] =         &channel_post_auth_listener;
+       channel_post[SSH_CHANNEL_OUTPUT_DRAINING] =     &channel_post_output_drain_13;
+}
 
-               case SSH_CHANNEL_X11_OPEN:
-               case SSH_CHANNEL_FREE:
-               default:
+void
+channel_handler_init_15(void)
+{
+       channel_pre[SSH_CHANNEL_OPEN] =                 &channel_pre_open_15;
+       channel_pre[SSH_CHANNEL_X11_OPEN] =             &channel_pre_x11_open_15;
+       channel_pre[SSH_CHANNEL_X11_LISTENER] =         &channel_pre_listener;
+       channel_pre[SSH_CHANNEL_PORT_LISTENER] =        &channel_pre_listener;
+       channel_pre[SSH_CHANNEL_AUTH_SOCKET] =          &channel_pre_listener;
+
+       channel_post[SSH_CHANNEL_X11_LISTENER] =        &channel_post_x11_listener;
+       channel_post[SSH_CHANNEL_PORT_LISTENER] =       &channel_post_port_listener;
+       channel_post[SSH_CHANNEL_AUTH_SOCKET] =         &channel_post_auth_listener;
+       channel_post[SSH_CHANNEL_OPEN] =                &channel_post_open_1;
+}
+
+void
+channel_handler_init(void)
+{
+       int i;
+       for(i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
+               channel_pre[i] = NULL;
+               channel_post[i] = NULL;
+       }
+       if (compat13)
+               channel_handler_init_13();
+       else
+               channel_handler_init_15();
+}
+
+void 
+channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset)
+{
+       static int did_init = 0;
+       int i;
+       Channel *c;
+
+       if (!did_init) {
+               channel_handler_init();
+               did_init = 1;
+       }
+       for (i = 0; i < channels_alloc; i++) {
+               c = &channels[i];
+               if (c->type == SSH_CHANNEL_FREE)
                        continue;
-               }
+               if (ftab[c->type] == NULL)
+                       continue;
+               (*ftab[c->type])(c, readset, writeset);
+               if (!compat13)
+                       chan_delete_if_full_closed(c);
        }
 }
 
+void 
+channel_prepare_select(fd_set * readset, fd_set * writeset)
+{
+       channel_handler(channel_pre, readset, writeset);
+}
+
+void 
+channel_after_select(fd_set * readset, fd_set * writeset)
+{
+       channel_handler(channel_post, readset, writeset);
+}
+
 /* If there is data to send to the connection, send some of it now. */
 
 void 
 channel_output_poll()
 {
        int len, i;
-       Channel *ch;
+       Channel *c;
 
        for (i = 0; i < channels_alloc; i++) {
-               ch = &channels[i];
+               c = &channels[i];
 
                /* We are only interested in channels that can have buffered incoming data. */
                if (compat13) {
-                       if (ch->type != SSH_CHANNEL_OPEN &&
-                           ch->type != SSH_CHANNEL_INPUT_DRAINING)
+                       if (c->type != SSH_CHANNEL_OPEN &&
+                           c->type != SSH_CHANNEL_INPUT_DRAINING)
                                continue;
                } else {
-                       if (ch->type != SSH_CHANNEL_OPEN)
+                       if (c->type != SSH_CHANNEL_OPEN)
                                continue;
-                       if (ch->istate != CHAN_INPUT_OPEN &&
-                           ch->istate != CHAN_INPUT_WAIT_DRAIN)
+                       if (c->istate != CHAN_INPUT_OPEN &&
+                           c->istate != CHAN_INPUT_WAIT_DRAIN)
                                continue;
                }
 
                /* Get the amount of buffered data for this channel. */
-               len = buffer_len(&ch->input);
+               len = buffer_len(&c->input);
                if (len > 0) {
                        /* Send some data for the other side over the secure connection. */
                        if (packet_is_interactive()) {
@@ -556,22 +710,26 @@ channel_output_poll()
                                        len = 512;
                        } else {
                                /* Keep the packets at reasonable size. */
-                               if (len > packet_get_maxsize()/2)
+                               if (len > packet_get_maxsize())
                                        len = packet_get_maxsize()/2;
                        }
-                       packet_start(SSH_MSG_CHANNEL_DATA);
-                       packet_put_int(ch->remote_id);
-                       packet_put_string(buffer_ptr(&ch->input), len);
-                       packet_send();
-                       buffer_consume(&ch->input, len);
-               } else if (ch->istate == CHAN_INPUT_WAIT_DRAIN) {
+                       if (len > 0) {
+                               packet_start(SSH_MSG_CHANNEL_DATA);
+                               packet_put_int(c->remote_id);
+                               packet_put_string(buffer_ptr(&c->input), len);
+                               packet_send();
+                               buffer_consume(&c->input, len);
+                               c->remote_window -= len;
+debug("channel %d: send data len %d", c->self, len);
+                       }
+               } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
                        if (compat13)
                                fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
                        /*
                         * input-buffer is empty and read-socket shutdown:
                         * tell peer, that we will not send more data: send IEOF
                         */
-                       chan_ibuf_empty(ch);
+                       chan_ibuf_empty(c);
                }
        }
 }
@@ -583,35 +741,33 @@ channel_output_poll()
  */
 
 void 
-channel_input_data(int payload_len)
+channel_input_data(int type, int plen)
 {
        int id;
        char *data;
        unsigned int data_len;
-       Channel *ch;
+       Channel *c;
 
        /* Get the channel number and verify it. */
        id = packet_get_int();
-       if (id < 0 || id >= channels_alloc)
+       c = channel_lookup(id);
+       if (c == NULL)
                packet_disconnect("Received data for nonexistent channel %d.", id);
-       ch = &channels[id];
-
-       if (ch->type == SSH_CHANNEL_FREE)
-               packet_disconnect("Received data for free channel %d.", ch->self);
 
        /* Ignore any data for non-open channels (might happen on close) */
-       if (ch->type != SSH_CHANNEL_OPEN &&
-           ch->type != SSH_CHANNEL_X11_OPEN)
+       if (c->type != SSH_CHANNEL_OPEN &&
+           c->type != SSH_CHANNEL_X11_OPEN)
                return;
 
        /* same for protocol 1.5 if output end is no longer open */
-       if (!compat13 && ch->ostate != CHAN_OUTPUT_OPEN)
+       if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN)
                return;
 
        /* Get the data. */
        data = packet_get_string(&data_len);
-       packet_integrity_check(payload_len, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA);
-       buffer_append(&ch->output, data, data_len);
+
+       packet_integrity_check(plen, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA);
+       buffer_append(&c->output, data, data_len);
        xfree(data);
 }
 
@@ -624,45 +780,60 @@ int
 channel_not_very_much_buffered_data()
 {
        unsigned int i;
-       Channel *ch;
+       Channel *c;
 
        for (i = 0; i < channels_alloc; i++) {
-               ch = &channels[i];
-               if (ch->type == SSH_CHANNEL_OPEN) {
-                       if (buffer_len(&ch->input) > packet_get_maxsize())
+               c = &channels[i];
+               if (c->type == SSH_CHANNEL_OPEN) {
+                       if (buffer_len(&c->input) > packet_get_maxsize()) {
+                               debug("channel %d: big input buffer %d",
+                                   c->self, buffer_len(&c->input));
                                return 0;
-                       if (buffer_len(&ch->output) > packet_get_maxsize())
+                       }
+                       if (buffer_len(&c->output) > packet_get_maxsize()) {
+                               debug("channel %d: big output buffer %d",
+                                   c->self, buffer_len(&c->output));
                                return 0;
+                       }
                }
        }
        return 1;
 }
 
-/* This is called after receiving CHANNEL_CLOSE/IEOF. */
+void 
+channel_input_ieof(int type, int plen)
+{
+       int id;
+       Channel *c;
+
+       packet_integrity_check(plen, 4, type);
+
+       id = packet_get_int();
+       c = channel_lookup(id);
+       if (c == NULL)
+               packet_disconnect("Received ieof for nonexistent channel %d.", id);
+       chan_rcvd_ieof(c);
+}
 
 void 
-channel_input_close()
+channel_input_close(int type, int plen)
 {
-       int channel;
+       int id;
+       Channel *c;
 
-       /* Get the channel number and verify it. */
-       channel = packet_get_int();
-       if (channel < 0 || channel >= channels_alloc ||
-           channels[channel].type == SSH_CHANNEL_FREE)
-               packet_disconnect("Received data for nonexistent channel %d.", channel);
-
-       if (!compat13) {
-               /* proto version 1.5 overloads CLOSE with IEOF */
-               chan_rcvd_ieof(&channels[channel]);
-               return;
-       }
+       packet_integrity_check(plen, 4, type);
+
+       id = packet_get_int();
+       c = channel_lookup(id);
+       if (c == NULL)
+               packet_disconnect("Received close for nonexistent channel %d.", id);
 
        /*
         * Send a confirmation that we have closed the channel and no more
         * data is coming for it.
         */
        packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION);
-       packet_put_int(channels[channel].remote_id);
+       packet_put_int(c->remote_id);
        packet_send();
 
        /*
@@ -672,81 +843,80 @@ channel_input_close()
         * no-one to receive the confirmation.  The channel gets freed when
         * the confirmation arrives.
         */
-       if (channels[channel].type != SSH_CHANNEL_CLOSED) {
+       if (c->type != SSH_CHANNEL_CLOSED) {
                /*
                 * Not a closed channel - mark it as draining, which will
                 * cause it to be freed later.
                 */
-               buffer_consume(&channels[channel].input,
-                              buffer_len(&channels[channel].input));
-               channels[channel].type = SSH_CHANNEL_OUTPUT_DRAINING;
+               buffer_consume(&c->input, buffer_len(&c->input));
+               c->type = SSH_CHANNEL_OUTPUT_DRAINING;
        }
 }
 
-/* This is called after receiving CHANNEL_CLOSE_CONFIRMATION/OCLOSE. */
-
+/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
 void 
-channel_input_close_confirmation()
+channel_input_oclose(int type, int plen)
 {
-       int channel;
-
-       /* Get the channel number and verify it. */
-       channel = packet_get_int();
-       if (channel < 0 || channel >= channels_alloc)
-               packet_disconnect("Received close confirmation for out-of-range channel %d.",
-                                 channel);
-
-       if (!compat13) {
-               /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
-               chan_rcvd_oclose(&channels[channel]);
-               return;
-       }
-       if (channels[channel].type != SSH_CHANNEL_CLOSED)
-               packet_disconnect("Received close confirmation for non-closed channel %d (type %d).",
-                                 channel, channels[channel].type);
-
-       /* Free the channel. */
-       channel_free(channel);
+       int id = packet_get_int();
+       Channel *c = channel_lookup(id);
+       packet_integrity_check(plen, 4, type);
+       if (c == NULL)
+               packet_disconnect("Received oclose for nonexistent channel %d.", id);
+       chan_rcvd_oclose(c);
 }
 
-/* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */
+void 
+channel_input_close_confirmation(int type, int plen)
+{
+       int id = packet_get_int();
+       Channel *c = channel_lookup(id);
+
+       if (c == NULL)
+               packet_disconnect("Received close confirmation for "
+                   "out-of-range channel %d.", id);
+       if (c->type != SSH_CHANNEL_CLOSED)
+               packet_disconnect("Received close confirmation for "
+                   "non-closed channel %d (type %d).", id, c->type);
+       channel_free(c->self);
+}
 
 void 
-channel_input_open_confirmation()
+channel_input_open_confirmation(int type, int plen)
 {
-       int channel, remote_channel;
+       int id, remote_id;
+       Channel *c;
 
-       /* Get the channel number and verify it. */
-       channel = packet_get_int();
-       if (channel < 0 || channel >= channels_alloc ||
-           channels[channel].type != SSH_CHANNEL_OPENING)
-               packet_disconnect("Received open confirmation for non-opening channel %d.",
-                                 channel);
+       packet_integrity_check(plen, 4 + 4, type);
 
-       /* Get remote side's id for this channel. */
-       remote_channel = packet_get_int();
+       id = packet_get_int();
+       c = channel_lookup(id);
 
+       if (c==NULL || c->type != SSH_CHANNEL_OPENING)
+               packet_disconnect("Received open confirmation for "
+                   "non-opening channel %d.", id);
+       remote_id = packet_get_int();
        /* Record the remote channel number and mark that the channel is now open. */
-       channels[channel].remote_id = remote_channel;
-       channels[channel].type = SSH_CHANNEL_OPEN;
+       c->remote_id = remote_id;
+       c->type = SSH_CHANNEL_OPEN;
 }
 
-/* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */
-
 void 
-channel_input_open_failure()
+channel_input_open_failure(int type, int plen)
 {
-       int channel;
+       int id;
+       Channel *c;
 
-       /* Get the channel number and verify it. */
-       channel = packet_get_int();
-       if (channel < 0 || channel >= channels_alloc ||
-           channels[channel].type != SSH_CHANNEL_OPENING)
-               packet_disconnect("Received open failure for non-opening channel %d.",
-                                 channel);
+       packet_integrity_check(plen, 4, type);
+
+       id = packet_get_int();
+       c = channel_lookup(id);
+
+       if (c==NULL || c->type != SSH_CHANNEL_OPENING)
+               packet_disconnect("Received open failure for "
+                   "non-opening channel %d.", id);
 
        /* Free the channel.  This will also close the socket. */
-       channel_free(channel);
+       channel_free(id);
 }
 
 /*
@@ -859,15 +1029,16 @@ channel_open_message()
                case SSH_CHANNEL_X11_OPEN:
                case SSH_CHANNEL_INPUT_DRAINING:
                case SSH_CHANNEL_OUTPUT_DRAINING:
-                       snprintf(buf, sizeof buf, "  #%d %.300s (t%d r%d i%d/%d o%d/%d)\r\n",
+                       snprintf(buf, sizeof buf, "  #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n",
                            c->self, c->remote_name,
                            c->type, c->remote_id,
                            c->istate, buffer_len(&c->input),
-                           c->ostate, buffer_len(&c->output));
+                           c->ostate, buffer_len(&c->output),
+                           c->rfd, c->wfd);
                        buffer_append(&buffer, buf, strlen(buf));
                        continue;
                default:
-                       fatal("channel_still_open: bad channel type %d", c->type);
+                       fatal("channel_open_message: bad channel type %d", c->type);
                        /* NOTREACHED */
                }
        }
@@ -950,8 +1121,11 @@ channel_request_local_forwarding(u_short port, const char *host,
                        continue;
                }
                /* Allocate a channel number for the socket. */
-               ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock,
-                   xstrdup("port listener"));
+               ch = channel_new(
+                   "port listener", SSH_CHANNEL_PORT_LISTENER,
+                   sock, sock, -1,
+                   CHAN_WINDOW_DEFAULT, CHAN_PACKET_DEFAULT,
+                   0, xstrdup("port listener"));
                strlcpy(channels[ch].path, host, sizeof(channels[ch].path));
                channels[ch].host_port = host_port;
                channels[ch].listening_port = port;
@@ -968,26 +1142,26 @@ channel_request_local_forwarding(u_short port, const char *host,
  */
 
 void 
-channel_request_remote_forwarding(u_short port, const char *host,
-                                 u_short remote_port)
+channel_request_remote_forwarding(u_short listen_port, const char *host_to_connect,
+                                 u_short port_to_connect)
 {
        int payload_len;
        /* Record locally that connection to this host/port is permitted. */
        if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
                fatal("channel_request_remote_forwarding: too many forwards");
 
-       permitted_opens[num_permitted_opens].host = xstrdup(host);
-       permitted_opens[num_permitted_opens].port = remote_port;
+       permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
+       permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
+       permitted_opens[num_permitted_opens].listen_port = listen_port;
        num_permitted_opens++;
 
        /* Send the forward request to the remote side. */
        packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
-       packet_put_int(port);
-       packet_put_string(host, strlen(host));
-       packet_put_int(remote_port);
+       packet_put_int(port_to_connect);
+       packet_put_string(host_to_connect, strlen(host_to_connect));
+       packet_put_int(listen_port);
        packet_send();
        packet_write_wait();
-
        /*
         * Wait for response from the remote side.  It will send a disconnect
         * message on failure, and we will never see it here.
@@ -1029,63 +1203,14 @@ channel_input_port_forward_request(int is_root)
        xfree(hostname);
 }
 
-/*
- * This is called after receiving PORT_OPEN message.  This attempts to
- * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION
- * or CHANNEL_OPEN_FAILURE.
- */
-
-void 
-channel_input_port_open(int payload_len)
+/* XXX move to aux.c */
+int
+channel_connect_to(const char *host, u_short host_port)
 {
-       int remote_channel, sock = 0, newch, i;
-       u_short host_port;
-       char *host, *originator_string;
-       unsigned int host_len, originator_len;
        struct addrinfo hints, *ai, *aitop;
        char ntop[NI_MAXHOST], strport[NI_MAXSERV];
        int gaierr;
-
-       /* Get remote channel number. */
-       remote_channel = packet_get_int();
-
-       /* Get host name to connect to. */
-       host = packet_get_string(&host_len);
-
-       /* Get port to connect to. */
-       host_port = packet_get_int();
-
-       /* Get remote originator name. */
-       if (have_hostname_in_open) {
-               originator_string = packet_get_string(&originator_len);
-               originator_len += 4;    /* size of packet_int */
-       } else {
-               originator_string = xstrdup("unknown (remote did not supply name)");
-               originator_len = 0;     /* no originator supplied */
-       }
-
-       packet_integrity_check(payload_len,
-                              4 + 4 + host_len + 4 + originator_len,
-                              SSH_MSG_PORT_OPEN);
-
-       /* Check if opening that port is permitted. */
-       if (!all_opens_permitted) {
-               /* Go trough all permitted ports. */
-               for (i = 0; i < num_permitted_opens; i++)
-                       if (permitted_opens[i].port == host_port &&
-                           strcmp(permitted_opens[i].host, host) == 0)
-                               break;
-
-               /* Check if we found the requested port among those permitted. */
-               if (i >= num_permitted_opens) {
-                       /* The port is not permitted. */
-                       log("Received request to connect to %.100s:%d, but the request was denied.",
-                           host, host_port);
-                       packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
-                       packet_put_int(remote_channel);
-                       packet_send();
-               }
-       }
+       int sock = -1;
 
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = IPv4or6;
@@ -1093,15 +1218,14 @@ channel_input_port_open(int payload_len)
        snprintf(strport, sizeof strport, "%d", host_port);
        if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
                error("%.100s: unknown host (%s)", host, gai_strerror(gaierr));
-               goto fail;
+               return -1;
        }
-
        for (ai = aitop; ai; ai = ai->ai_next) {
                if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
                        continue;
                if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
                    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
-                       error("channel_input_port_open: getnameinfo failed");
+                       error("channel_connect_to: getnameinfo failed");
                        continue;
                }
                /* Create the socket. */
@@ -1121,37 +1245,82 @@ channel_input_port_open(int payload_len)
 
        }
        freeaddrinfo(aitop);
-
        if (!ai) {
                error("connect %.100s port %d: failed.", host, host_port);      
-               goto fail;
+               return -1;
        }
+       /* success */
+       return sock;
+}
 
-       /* Successful connection. */
+/*
+ * This is called after receiving PORT_OPEN message.  This attempts to
+ * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION
+ * or CHANNEL_OPEN_FAILURE.
+ */
 
-       /* Allocate a channel for this connection. */
-       newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string);
-       channels[newch].remote_id = remote_channel;
+void 
+channel_input_port_open(int type, int plen)
+{
+       u_short host_port;
+       char *host, *originator_string;
+       int remote_channel, sock = -1, newch, i, denied;
+       unsigned int host_len, originator_len;
 
-       /* Send a confirmation to the remote host. */
-       packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
-       packet_put_int(remote_channel);
-       packet_put_int(newch);
-       packet_send();
+       /* Get remote channel number. */
+       remote_channel = packet_get_int();
 
-       /* Free the argument string. */
-       xfree(host);
+       /* Get host name to connect to. */
+       host = packet_get_string(&host_len);
 
-       return;
+       /* Get port to connect to. */
+       host_port = packet_get_int();
 
-fail:
-       /* Free the argument string. */
-       xfree(host);
+       /* Get remote originator name. */
+       if (have_hostname_in_open) {
+               originator_string = packet_get_string(&originator_len);
+               originator_len += 4;    /* size of packet_int */
+       } else {
+               originator_string = xstrdup("unknown (remote did not supply name)");
+               originator_len = 0;     /* no originator supplied */
+       }
 
-       /* Send refusal to the remote host. */
-       packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
-       packet_put_int(remote_channel);
-       packet_send();
+       packet_integrity_check(plen,
+           4 + 4 + host_len + 4 + originator_len, SSH_MSG_PORT_OPEN);
+
+       /* Check if opening that port is permitted. */
+       denied = 0;
+       if (!all_opens_permitted) {
+               /* Go trough all permitted ports. */
+               for (i = 0; i < num_permitted_opens; i++)
+                       if (permitted_opens[i].port_to_connect == host_port &&
+                           strcmp(permitted_opens[i].host_to_connect, host) == 0)
+                               break;
+
+               /* Check if we found the requested port among those permitted. */
+               if (i >= num_permitted_opens) {
+                       /* The port is not permitted. */
+                       log("Received request to connect to %.100s:%d, but the request was denied.",
+                           host, host_port);
+                       denied = 1;
+               }
+       }
+       sock = denied ? -1 : channel_connect_to(host, host_port);
+       if (sock > 0) {
+               /* Allocate a channel for this connection. */
+               newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string);
+               channels[newch].remote_id = remote_channel;
+
+               packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+               packet_put_int(remote_channel);
+               packet_put_int(newch);
+               packet_send();
+       } else {
+               packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
+               packet_put_int(remote_channel);
+               packet_send();
+       }
+       xfree(host);
 }
 
 /*
@@ -1336,7 +1505,7 @@ connect_local_xsocket(unsigned int dnr)
  */
 
 void 
-x11_input_open(int payload_len)
+x11_input_open(int type, int plen)
 {
        int remote_channel, display_number, sock = 0, newch;
        const char *display;
@@ -1359,7 +1528,7 @@ x11_input_open(int payload_len)
        }
 
        debug("Received X11 open request.");
-       packet_integrity_check(payload_len, 4 + remote_len, SSH_SMSG_X11_OPEN);
+       packet_integrity_check(plen, 4 + remote_len, SSH_SMSG_X11_OPEN);
 
        /* Try to open a socket for the local X server. */
        display = getenv("DISPLAY");
@@ -1425,19 +1594,18 @@ x11_input_open(int payload_len)
                sock = socket(ai->ai_family, SOCK_STREAM, 0);
                if (sock < 0) {
                        debug("socket: %.100s", strerror(errno));
-               continue;
-       }
-       /* Connect it to the display. */
-       if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
-               debug("connect %.100s port %d: %.100s", buf, 6000 + display_number, 
-                   strerror(errno));
-               close(sock);
-               continue;
+                       continue;
+               }
+               /* Connect it to the display. */
+               if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
+                       debug("connect %.100s port %d: %.100s", buf,
+                           6000 + display_number, strerror(errno));
+                       close(sock);
+                       continue;
+               }
+               /* Success */
+               break;
        }
-       /* Success */
-       break;
-
-       } /* (ai = aitop, ai; ai = ai->ai_next) */
        freeaddrinfo(aitop);
        if (!ai) {
                error("connect %.100s port %d: %.100s", buf, 6000 + display_number, 
@@ -1625,11 +1793,13 @@ auth_input_request_forwarding(struct passwd * pw)
 /* This is called to process an SSH_SMSG_AGENT_OPEN message. */
 
 void 
-auth_input_open_request()
+auth_input_open_request(int type, int plen)
 {
        int remch, sock, newch;
        char *dummyname;
 
+       packet_integrity_check(plen, 4, type);
+
        /* Read the remote channel number from the message. */
        remch = packet_get_int();
 
index 335ed4106d17d1325c36a2ebb5849d356e9626e5..11af5ba02117edb36a6b0968a4d29842a110a0a6 100644 (file)
 #define SSH_CHANNEL_OPENING            3       /* waiting for confirmation */
 #define SSH_CHANNEL_OPEN               4       /* normal open two-way channel */
 #define SSH_CHANNEL_CLOSED             5       /* waiting for close confirmation */
-/*     SSH_CHANNEL_AUTH_FD             6          authentication fd */
-#define SSH_CHANNEL_AUTH_SOCKET                7       /* authentication socket */
-/*     SSH_CHANNEL_AUTH_SOCKET_FD      8          connection to auth socket */
-#define SSH_CHANNEL_X11_OPEN           9       /* reading first X11 packet */
-#define SSH_CHANNEL_INPUT_DRAINING     10      /* sending remaining data to conn */
-#define SSH_CHANNEL_OUTPUT_DRAINING    11      /* sending remaining data to app */
+#define SSH_CHANNEL_AUTH_SOCKET                6       /* authentication socket */
+#define SSH_CHANNEL_X11_OPEN           7       /* reading first X11 packet */
+#define SSH_CHANNEL_INPUT_DRAINING     8       /* sending remaining data to conn */
+#define SSH_CHANNEL_OUTPUT_DRAINING    9       /* sending remaining data to app */
+#define SSH_CHANNEL_LARVAL             10      /* larval session */
+#define SSH_CHANNEL_MAX_TYPE           11
 
 /*
  * Data structure for channel data.  This is iniailized in channel_allocate
  * and cleared in channel_free.
  */
+typedef void channel_callback_fn(int id, void *arg);
 
 typedef struct Channel {
        int     type;           /* channel type/state */
@@ -29,15 +30,192 @@ typedef struct Channel {
        /* peer can be reached over encrypted connection, via packet-sent */
        int     istate;         /* input from channel (state of receive half) */
        int     ostate;         /* output to channel  (state of transmit half) */
-       int     sock;           /* data socket, linked to this channel */
+       int     rfd;            /* read fd */
+       int     wfd;            /* write fd */
+       int     efd;            /* extended fd */
+       int     sock;           /* sock fd */
        Buffer  input;          /* data read from socket, to be sent over
                                 * encrypted connection */
        Buffer  output;         /* data received over encrypted connection for
                                 * send on socket */
+       Buffer  extended;
        char    path[200];      /* path for unix domain sockets, or host name
                                 * for forwards */
        int     listening_port; /* port being listened for forwards */
        int     host_port;      /* remote port to connect for forwards */
        char   *remote_name;    /* remote hostname */
+
+       int     remote_window;
+       int     remote_maxpacket;
+       int     local_window;
+       int     local_window_max;
+       int     local_consumed;
+       int     local_maxpacket;
+       int     extended_usage;
+
+       char   *ctype;          /* type */
+
+       // callback
+       channel_callback_fn     *cb_fn;
+       void    *cb_arg;
+       int     cb_event;
+       channel_callback_fn     *dettach_user;
 }       Channel;
+
+#define CHAN_EXTENDED_IGNORE           0
+#define CHAN_EXTENDED_READ             1
+#define CHAN_EXTENDED_WRITE            2
+
+void   channel_open(int id);
+Channel        *channel_lookup(int id);
+
+int
+channel_new(char *ctype, int type, int rfd, int wfd, int efd,
+    int window, int maxpack, int extended_usage, char *remote_name);
+
+void   channel_input_close(int type, int plen);
+void   channel_input_close_confirmation(int type, int plen);
+void   channel_input_data(int type, int plen);
+void   channel_input_ieof(int type, int plen);
+void   channel_input_oclose(int type, int plen);
+void   channel_input_open_confirmation(int type, int plen);
+void   channel_input_open_failure(int type, int plen);
+void   channel_input_port_open(int type, int plen);
+void   channel_input_open(int type, int plen);
+
+/* Sets specific protocol options. */
+void    channel_set_options(int hostname_in_open);
+
+/*
+ * Allocate a new channel object and set its type and socket.  Remote_name
+ * must have been allocated with xmalloc; this will free it when the channel
+ * is freed.
+ */
+int     channel_allocate(int type, int sock, char *remote_name);
+
+/* Free the channel and close its socket. */
+void    channel_free(int channel);
+
+/* Add any bits relevant to channels in select bitmasks. */
+void    channel_prepare_select(fd_set * readset, fd_set * writeset);
+
+/*
+ * After select, perform any appropriate operations for channels which have
+ * events pending.
+ */
+void    channel_after_select(fd_set * readset, fd_set * writeset);
+
+/* If there is data to send to the connection, send some of it now. */
+void    channel_output_poll(void);
+
+/* Returns true if no channel has too much buffered data. */
+int     channel_not_very_much_buffered_data(void);
+
+/* This closes any sockets that are listening for connections; this removes
+   any unix domain sockets. */
+void    channel_stop_listening(void);
+
+/*
+ * Closes the sockets of all channels.  This is used to close extra file
+ * descriptors after a fork.
+ */
+void    channel_close_all(void);
+
+/* Returns the maximum file descriptor number used by the channels. */
+int     channel_max_fd(void);
+
+/* Returns true if there is still an open channel over the connection. */
+int     channel_still_open(void);
+
+/*
+ * Returns a string containing a list of all open channels.  The list is
+ * suitable for displaying to the user.  It uses crlf instead of newlines.
+ * The caller should free the string with xfree.
+ */
+char   *channel_open_message(void);
+
+/*
+ * Initiate forwarding of connections to local port "port" through the secure
+ * channel to host:port from remote side.  This never returns if there was an
+ * error.
+ */
+void 
+channel_request_local_forwarding(u_short port, const char *host,
+    u_short remote_port, int gateway_ports);
+
+/*
+ * Initiate forwarding of connections to port "port" on remote host through
+ * the secure channel to host:port from local side.  This never returns if
+ * there was an error.  This registers that open requests for that port are
+ * permitted.
+ */
+void 
+channel_request_remote_forwarding(u_short port, const char *host,
+    u_short remote_port);
+
+/*
+ * Permits opening to any host/port in SSH_MSG_PORT_OPEN.  This is usually
+ * called by the server, because the user could connect to any port anyway,
+ * and the server has no way to know but to trust the client anyway.
+ */
+void    channel_permit_all_opens(void);
+
+/*
+ * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
+ * listening for the port, and sends back a success reply (or disconnect
+ * message if there was an error).  This never returns if there was an error.
+ */
+void    channel_input_port_forward_request(int is_root);
+
+/*
+ * Creates a port for X11 connections, and starts listening for it. Returns
+ * the display name, or NULL if an error was encountered.
+ */
+char   *x11_create_display(int screen);
+
+/*
+ * Creates an internet domain socket for listening for X11 connections.
+ * Returns a suitable value for the DISPLAY variable, or NULL if an error
+ * occurs.
+ */
+char   *x11_create_display_inet(int screen, int x11_display_offset);
+
+/*
+ * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
+ * the remote channel number.  We should do whatever we want, and respond
+ * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
+ */
+void    x11_input_open(int type, int plen);
+
+/*
+ * Requests forwarding of X11 connections.  This should be called on the
+ * client only.
+ */
+void    x11_request_forwarding(void);
+
+/*
+ * Requests forwarding for X11 connections, with authentication spoofing.
+ * This should be called in the client only.
+ */
+void    x11_request_forwarding_with_spoofing(const char *proto, const char *data);
+
+/* Sends a message to the server to request authentication fd forwarding. */
+void    auth_request_forwarding(void);
+
+/*
+ * Returns the name of the forwarded authentication socket.  Returns NULL if
+ * there is no forwarded authentication socket.  The returned value points to
+ * a static buffer.
+ */
+char   *auth_get_socket_name(void);
+
+/*
+ * This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server.
+ * This starts forwarding authentication requests.
+ */
+void    auth_input_request_forwarding(struct passwd * pw);
+
+/* This is called to process an SSH_SMSG_AGENT_OPEN message. */
+void    auth_input_open_request(int type, int plen);
+
 #endif
index ea1a473dc78200339e833938d752b389d3d38b84..21d543d3626736d3a87cf3f679df2dc909989c9e 100644 (file)
--- a/cipher.c
+++ b/cipher.c
@@ -122,7 +122,12 @@ static char *cipher_names[] =
        "3des",
        "tss",
        "rc4",
-       "blowfish"
+       "blowfish",
+       "reserved",
+       "blowfish-cbc",
+       "3des-cbc",
+       "arcfour",
+       "cast128-cbc"
 };
 
 /*
@@ -137,6 +142,10 @@ cipher_mask()
        unsigned int mask = 0;
        mask |= 1 << SSH_CIPHER_3DES;           /* Mandatory */
        mask |= 1 << SSH_CIPHER_BLOWFISH;
+       mask |= 1 << SSH_CIPHER_BLOWFISH_CBC;
+       mask |= 1 << SSH_CIPHER_3DES_CBC;
+       mask |= 1 << SSH_CIPHER_ARCFOUR;
+       mask |= 1 << SSH_CIPHER_CAST128_CBC;
        return mask;
 }
 
@@ -233,16 +242,84 @@ cipher_set_key(CipherContext *context, int cipher,
                break;
 
        case SSH_CIPHER_BLOWFISH:
+               if (keylen < 16)
+                       error("Key length %d is insufficient for blowfish.", keylen);
                BF_set_key(&context->u.bf.key, keylen, padded);
                memset(context->u.bf.iv, 0, 8);
                break;
 
+       case SSH_CIPHER_3DES_CBC:
+       case SSH_CIPHER_BLOWFISH_CBC:
+       case SSH_CIPHER_ARCFOUR:
+       case SSH_CIPHER_CAST128_CBC:
+               fatal("cipher_set_key: illegal cipher: %s", cipher_name(cipher));
+               break;
+
        default:
                fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher));
        }
        memset(padded, 0, sizeof(padded));
 }
 
+
+void 
+cipher_set_key_iv(CipherContext * context, int cipher,
+    const unsigned char *key, int keylen, 
+    const unsigned char *iv, int ivlen)
+{
+       /* Set cipher type. */
+       context->type = cipher;
+
+       /* Initialize the initialization vector. */
+       switch (cipher) {
+       case SSH_CIPHER_NONE:
+               break;
+
+       case SSH_CIPHER_3DES:
+       case SSH_CIPHER_BLOWFISH:
+               fatal("cipher_set_key_iv: illegal cipher: %s", cipher_name(cipher));
+               break;
+
+       case SSH_CIPHER_3DES_CBC:
+               if (keylen < 24)
+                       error("Key length %d is insufficient for 3des-cbc.", keylen);
+               des_set_key((void *) key, context->u.des3.key1);
+               des_set_key((void *) (key+8), context->u.des3.key2);
+               des_set_key((void *) (key+16), context->u.des3.key3);
+               if (ivlen < 8)
+                       error("IV length %d is insufficient for 3des-cbc.", ivlen);
+               memcpy(context->u.des3.iv3, (char *)iv, 8);
+               break;
+
+       case SSH_CIPHER_BLOWFISH_CBC:
+               if (keylen < 16)
+                       error("Key length %d is insufficient for blowfish.", keylen);
+               if (ivlen < 8)
+                       error("IV length %d is insufficient for blowfish.", ivlen);
+               BF_set_key(&context->u.bf.key, keylen, (unsigned char *)key);
+               memcpy(context->u.bf.iv, (char *)iv, 8);
+               break;
+
+       case SSH_CIPHER_ARCFOUR:
+               if (keylen < 16)
+                       error("Key length %d is insufficient for arcfour.", keylen);
+               RC4_set_key(&context->u.rc4, keylen, (unsigned char *)key);
+               break;
+
+       case SSH_CIPHER_CAST128_CBC:
+               if (keylen < 16)
+                       error("Key length %d is insufficient for cast128.", keylen);
+               if (ivlen < 8)
+                       error("IV length %d is insufficient for cast128.", ivlen);
+               CAST_set_key(&context->u.cast.key, keylen, (unsigned char *) key);
+               memcpy(context->u.cast.iv, (char *)iv, 8);
+               break;
+
+       default:
+               fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher));
+       }
+}
+
 /* Encrypts data using the cipher. */
 
 void 
@@ -272,6 +349,27 @@ cipher_encrypt(CipherContext *context, unsigned char *dest,
                swap_bytes(dest, dest, len);
                break;
 
+       case SSH_CIPHER_BLOWFISH_CBC:
+               BF_cbc_encrypt((void *)src, dest, len,
+                              &context->u.bf.key, context->u.bf.iv,
+                              BF_ENCRYPT);
+               break;
+
+       case SSH_CIPHER_3DES_CBC:
+               des_ede3_cbc_encrypt(src, dest, len,
+                   context->u.des3.key1, context->u.des3.key2,
+                   context->u.des3.key3, &context->u.des3.iv3, DES_ENCRYPT);
+               break;
+
+       case SSH_CIPHER_ARCFOUR:
+               RC4(&context->u.rc4, len, (unsigned char *)src, dest);
+               break;
+
+       case SSH_CIPHER_CAST128_CBC:
+               CAST_cbc_encrypt(src, dest, len,
+                   &context->u.cast.key, context->u.cast.iv, CAST_ENCRYPT);
+               break;
+
        default:
                fatal("cipher_encrypt: unknown cipher: %s", cipher_name(context->type));
        }
@@ -306,6 +404,27 @@ cipher_decrypt(CipherContext *context, unsigned char *dest,
                swap_bytes(dest, dest, len);
                break;
 
+       case SSH_CIPHER_BLOWFISH_CBC:
+               BF_cbc_encrypt((void *) src, dest, len,
+                              &context->u.bf.key, context->u.bf.iv,
+                              BF_DECRYPT);
+               break;
+
+       case SSH_CIPHER_3DES_CBC:
+               des_ede3_cbc_encrypt(src, dest, len,
+                   context->u.des3.key1, context->u.des3.key2,
+                   context->u.des3.key3, &context->u.des3.iv3, DES_DECRYPT);
+               break;
+
+       case SSH_CIPHER_ARCFOUR:
+               RC4(&context->u.rc4, len, (unsigned char *)src, dest);
+               break;
+
+       case SSH_CIPHER_CAST128_CBC:
+               CAST_cbc_encrypt(src, dest, len,
+                   &context->u.cast.key, context->u.cast.iv, CAST_DECRYPT);
+               break;
+
        default:
                fatal("cipher_decrypt: unknown cipher: %s", cipher_name(context->type));
        }
index 7f550a542d178c02694f07173cbd78b7c6d85883..8d61be52a847e4be1d9c9ef64b507bc715212918 100644 (file)
--- a/cipher.h
+++ b/cipher.h
 #ifdef HAVE_OPENSSL
 #include <openssl/des.h>
 #include <openssl/blowfish.h>
+#include <openssl/rc4.h>
+#include <openssl/cast.h>
 #endif
 #ifdef HAVE_SSL
 #include <ssl/des.h>
 #include <ssl/blowfish.h>
+#include <ssl/rc4.h>
+#include <ssl/cast.h>
 #endif
 
 /* Cipher types.  New types can be added, but old types should not be removed
 #define SSH_CIPHER_BROKEN_TSS  4       /* TRI's Simple Stream encryption CBC */
 #define SSH_CIPHER_BROKEN_RC4  5       /* Alleged RC4 */
 #define SSH_CIPHER_BLOWFISH    6
+#define SSH_CIPHER_RESERVED    7
+
+/* these ciphers are used in SSH2: */
+#define SSH_CIPHER_BLOWFISH_CBC        8
+#define SSH_CIPHER_3DES_CBC    9
+#define SSH_CIPHER_ARCFOUR     10      /* Alleged RC4 */
+#define SSH_CIPHER_CAST128_CBC 11
 
 typedef struct {
        unsigned int type;
@@ -52,6 +63,11 @@ typedef struct {
                        struct bf_key_st key;
                        unsigned char iv[8];
                }       bf;
+               struct {
+                       CAST_KEY key;
+                       unsigned char iv[8];
+               } cast;
+               RC4_KEY rc4;
        }       u;
 }       CipherContext;
 /*
@@ -77,6 +93,10 @@ int     cipher_number(const char *name);
 void 
 cipher_set_key(CipherContext * context, int cipher,
     const unsigned char *key, int keylen, int for_encryption);
+void 
+cipher_set_key_iv(CipherContext * context, int cipher,
+    const unsigned char *key, int keylen, 
+    const unsigned char *iv, int ivlen);
 
 /*
  * Sets key for the cipher by computing the MD5 checksum of the passphrase,
index 6ae3c673624f8d3e627827ac1ac6e2fcbbdac1d0..5329ad901924a8c7ba53ac4d141d75a3de7cc814 100644 (file)
@@ -24,6 +24,11 @@ RCSID("$Id$");
 #include "authfd.h"
 #include "readconf.h"
 
+#include "compat.h"
+#include "channels.h"
+#include "dispatch.h"
+
+
 /* Flag indicating that stdin should be redirected from /dev/null. */
 extern int stdin_null_flag;
 
@@ -228,108 +233,6 @@ client_check_initial_eof_on_stdin()
        }
 }
 
-/*
- * Get packets from the connection input buffer, and process them as long as
- * there are packets available.
- */
-
-void 
-client_process_buffered_input_packets()
-{
-       int type;
-       char *data;
-       unsigned int data_len;
-       int payload_len;
-
-       /* Process any buffered packets from the server. */
-       while (!quit_pending &&
-              (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) {
-               switch (type) {
-
-               case SSH_SMSG_STDOUT_DATA:
-                       data = packet_get_string(&data_len);
-                       packet_integrity_check(payload_len, 4 + data_len, type);
-                       buffer_append(&stdout_buffer, data, data_len);
-                       stdout_bytes += data_len;
-                       memset(data, 0, data_len);
-                       xfree(data);
-                       break;
-
-               case SSH_SMSG_STDERR_DATA:
-                       data = packet_get_string(&data_len);
-                       packet_integrity_check(payload_len, 4 + data_len, type);
-                       buffer_append(&stderr_buffer, data, data_len);
-                       stdout_bytes += data_len;
-                       memset(data, 0, data_len);
-                       xfree(data);
-                       break;
-
-               case SSH_SMSG_EXITSTATUS:
-                       packet_integrity_check(payload_len, 4, type);
-                       exit_status = packet_get_int();
-                       /* Acknowledge the exit. */
-                       packet_start(SSH_CMSG_EXIT_CONFIRMATION);
-                       packet_send();
-                       /*
-                        * Must wait for packet to be sent since we are
-                        * exiting the loop.
-                        */
-                       packet_write_wait();
-                       /* Flag that we want to exit. */
-                       quit_pending = 1;
-                       break;
-
-               case SSH_SMSG_X11_OPEN:
-                       x11_input_open(payload_len);
-                       break;
-
-               case SSH_MSG_PORT_OPEN:
-                       channel_input_port_open(payload_len);
-                       break;
-
-               case SSH_SMSG_AGENT_OPEN:
-                       packet_integrity_check(payload_len, 4, type);
-                       auth_input_open_request();
-                       break;
-
-               case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
-                       packet_integrity_check(payload_len, 4 + 4, type);
-                       channel_input_open_confirmation();
-                       break;
-
-               case SSH_MSG_CHANNEL_OPEN_FAILURE:
-                       packet_integrity_check(payload_len, 4, type);
-                       channel_input_open_failure();
-                       break;
-
-               case SSH_MSG_CHANNEL_DATA:
-                       channel_input_data(payload_len);
-                       break;
-
-               case SSH_MSG_CHANNEL_CLOSE:
-                       packet_integrity_check(payload_len, 4, type);
-                       channel_input_close();
-                       break;
-
-               case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
-                       packet_integrity_check(payload_len, 4, type);
-                       channel_input_close_confirmation();
-                       break;
-
-               default:
-                       /*
-                        * Any unknown packets received during the actual
-                        * session cause the session to terminate.  This is
-                        * intended to make debugging easier since no
-                        * confirmations are sent.  Any compatible protocol
-                        * extensions must be negotiated during the
-                        * preparatory phase.
-                        */
-                       packet_disconnect("Protocol error during session: type %d",
-                                         type);
-               }
-       }
-}
 
 /*
  * Make packets from buffered stdin data, and buffer them for sending to the
@@ -775,6 +678,24 @@ client_process_output(fd_set * writeset)
        }
 }
 
+/*
+ * Get packets from the connection input buffer, and process them as long as
+ * there are packets available.
+ *
+ * Any unknown packets received during the actual
+ * session cause the session to terminate.  This is
+ * intended to make debugging easier since no
+ * confirmations are sent.  Any compatible protocol
+ * extensions must be negotiated during the
+ * preparatory phase.
+ */
+
+void 
+client_process_buffered_input_packets()
+{
+       dispatch_run(DISPATCH_NONBLOCK, &quit_pending);
+}
+
 /*
  * Implements the interactive session with the server.  This is called after
  * the user has been authenticated, and a command has been started on the
@@ -782,6 +703,8 @@ client_process_output(fd_set * writeset)
  * character for terminating or suspending the session.
  */
 
+void client_init_dispatch(void);
+
 int 
 client_loop(int have_pty, int escape_char_arg)
 {
@@ -816,6 +739,8 @@ client_loop(int have_pty, int escape_char_arg)
        buffer_init(&stdout_buffer);
        buffer_init(&stderr_buffer);
 
+       client_init_dispatch();
+
        /* Set signal handlers to restore non-blocking mode.  */
        signal(SIGINT, signal_handler);
        signal(SIGQUIT, signal_handler);
@@ -950,3 +875,77 @@ client_loop(int have_pty, int escape_char_arg)
        debug("Exit status %d", exit_status);
        return exit_status;
 }
+
+/*********/
+
+void
+client_input_stdout_data(int type, int plen)
+{
+       unsigned int data_len;
+       char *data = packet_get_string(&data_len);
+       packet_integrity_check(plen, 4 + data_len, type);
+       buffer_append(&stdout_buffer, data, data_len);
+       stdout_bytes += data_len;
+       memset(data, 0, data_len);
+       xfree(data);
+}
+void
+client_input_stderr_data(int type, int plen)
+{
+       unsigned int data_len;
+       char *data = packet_get_string(&data_len);
+       packet_integrity_check(plen, 4 + data_len, type);
+       buffer_append(&stderr_buffer, data, data_len);
+       stdout_bytes += data_len;
+       memset(data, 0, data_len);
+       xfree(data);
+}
+void
+client_input_exit_status(int type, int plen)
+{
+       packet_integrity_check(plen, 4, type);
+       exit_status = packet_get_int();
+       /* Acknowledge the exit. */
+       packet_start(SSH_CMSG_EXIT_CONFIRMATION);
+       packet_send();
+       /*
+        * Must wait for packet to be sent since we are
+        * exiting the loop.
+        */
+       packet_write_wait();
+       /* Flag that we want to exit. */
+       quit_pending = 1;
+}
+
+void 
+client_init_dispatch_13()
+{
+       dispatch_init(NULL);
+       dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
+       dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
+       dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
+       dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
+       dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
+       dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
+       dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
+       dispatch_set(SSH_SMSG_AGENT_OPEN, &auth_input_open_request);
+       dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
+       dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
+       dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
+       dispatch_set(SSH_SMSG_X11_OPEN, &x11_input_open);
+}
+void 
+client_init_dispatch_15()
+{
+       client_init_dispatch_13();
+       dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
+       dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
+}
+void 
+client_init_dispatch()
+{
+       if (compat13)
+               client_init_dispatch_13();
+       else
+               client_init_dispatch_15();
+}
index fbe8a0060219e6e2728e0b15c504972c9fef8e42..e8ba760f1ba840f82cd61d0e0fc0d80c84e4b0ad 100644 (file)
@@ -90,23 +90,13 @@ buffer_compress(Buffer * input_buffer, Buffer * output_buffer)
                case Z_OK:
                        /* Append compressed data to output_buffer. */
                        buffer_append(output_buffer, buf,
-                                     sizeof(buf) - outgoing_stream.avail_out);
+                           sizeof(buf) - outgoing_stream.avail_out);
                        break;
-               case Z_STREAM_END:
-                       fatal("buffer_compress: deflate returned Z_STREAM_END");
-                       /* NOTREACHED */
-               case Z_STREAM_ERROR:
-                       fatal("buffer_compress: deflate returned Z_STREAM_ERROR");
-                       /* NOTREACHED */
-               case Z_BUF_ERROR:
-                       fatal("buffer_compress: deflate returned Z_BUF_ERROR");
-                       /* NOTREACHED */
                default:
                        fatal("buffer_compress: deflate returned %d", status);
                        /* NOTREACHED */
                }
-       }
-       while (outgoing_stream.avail_out == 0);
+       } while (outgoing_stream.avail_out == 0);
 }
 
 /*
@@ -127,27 +117,17 @@ buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer)
        incoming_stream.next_in = (unsigned char *) buffer_ptr(input_buffer);
        incoming_stream.avail_in = buffer_len(input_buffer);
 
-       incoming_stream.next_out = (unsigned char *) buf;
-       incoming_stream.avail_out = sizeof(buf);
-
        for (;;) {
+               /* Set up fixed-size output buffer. */
+               incoming_stream.next_out = (unsigned char *) buf;
+               incoming_stream.avail_out = sizeof(buf);
+
                status = inflate(&incoming_stream, Z_PARTIAL_FLUSH);
                switch (status) {
                case Z_OK:
                        buffer_append(output_buffer, buf,
-                                     sizeof(buf) - incoming_stream.avail_out);
-                       incoming_stream.next_out = (unsigned char *) buf;
-                       incoming_stream.avail_out = sizeof(buf);
+                           sizeof(buf) - incoming_stream.avail_out);
                        break;
-               case Z_STREAM_END:
-                       fatal("buffer_uncompress: inflate returned Z_STREAM_END");
-                       /* NOTREACHED */
-               case Z_DATA_ERROR:
-                       fatal("buffer_uncompress: inflate returned Z_DATA_ERROR");
-                       /* NOTREACHED */
-               case Z_STREAM_ERROR:
-                       fatal("buffer_uncompress: inflate returned Z_STREAM_ERROR");
-                       /* NOTREACHED */
                case Z_BUF_ERROR:
                        /*
                         * Comments in zlib.h say that we should keep calling
@@ -155,11 +135,9 @@ buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer)
                         * be the error that we get.
                         */
                        return;
-               case Z_MEM_ERROR:
-                       fatal("buffer_uncompress: inflate returned Z_MEM_ERROR");
-                       /* NOTREACHED */
                default:
                        fatal("buffer_uncompress: inflate returned %d", status);
+                       /* NOTREACHED */
                }
        }
 }
diff --git a/dispatch.c b/dispatch.c
new file mode 100644 (file)
index 0000000..aa431f7
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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("$Id$");
+#include "ssh.h"
+#include "dispatch.h"
+#include "packet.h"
+
+#define DISPATCH_MIN   0
+#define DISPATCH_MAX   255
+
+dispatch_fn *dispatch[DISPATCH_MAX];
+
+void
+dispatch_protocol_error(int type, int plen)
+{
+       error("Hm, dispatch protocol error: type %d plen %d", type, plen);
+}
+void 
+dispatch_init(dispatch_fn *dflt)
+{
+       int i;
+       for (i = 0; i < DISPATCH_MAX; i++)
+               dispatch[i] = dflt;
+}
+void
+dispatch_set(int type, dispatch_fn *fn)
+{
+       dispatch[type] = fn;
+}
+void
+dispatch_run(int mode, int *done)
+{
+       for (;;) {
+               int plen;
+               int type;
+
+               if (mode == DISPATCH_BLOCK) {
+                       type = packet_read(&plen);
+               } else {
+                       type = packet_read_poll(&plen);
+                       if (type == SSH_MSG_NONE)
+                               return;
+               }
+               if (type > 0 && type < DISPATCH_MAX && dispatch[type] != NULL)
+                       (*dispatch[type])(type, plen);
+               else
+                       packet_disconnect("protocol error: rcvd type %d", type);        
+               if (done != NULL && *done)
+                       return;
+       }
+}
diff --git a/dispatch.h b/dispatch.h
new file mode 100644 (file)
index 0000000..12084aa
--- /dev/null
@@ -0,0 +1,11 @@
+enum {
+       DISPATCH_BLOCK,
+       DISPATCH_NONBLOCK
+};
+
+typedef void dispatch_fn(int type, int plen);
+
+void   dispatch_init(dispatch_fn *dflt);
+void   dispatch_set(int type, dispatch_fn *fn);
+void   dispatch_run(int mode, int *done);
+void   dispatch_protocol_error(int type, int plen);
index 38c534427ff18e76420717381365f771a8c08e0e..5478e3189d8e0371b6fcbfb0251872a183b8af25 100644 (file)
@@ -137,9 +137,11 @@ do_log(LogLevel level, const char *fmt, va_list args)
        } else {
                vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
        }
-       if (log_on_stderr)
+       if (log_on_stderr) {
                fprintf(stderr, "%s\n", msgbuf);
-       openlog(__progname, LOG_PID, log_facility);
-       syslog(pri, "%.500s", msgbuf);
-       closelog();
+       } else {
+               openlog(__progname, LOG_PID, log_facility);
+               syslog(pri, "%.500s", msgbuf);
+               closelog();
+       }
 }
diff --git a/mpaux.c b/mpaux.c
index 3a235023248d056b693fbba79892f47e6a207ac1..d37de0e9e3ab546aadb04eea79d43c514ad9a528 100644 (file)
--- a/mpaux.c
+++ b/mpaux.c
@@ -31,9 +31,9 @@ RCSID("$Id$");
 
 void
 compute_session_id(unsigned char session_id[16],
-                  unsigned char cookie[8],
-                  BIGNUM* host_key_n,
-                  BIGNUM* session_key_n)
+    unsigned char cookie[8],
+    BIGNUM* host_key_n,
+    BIGNUM* session_key_n)
 {
        unsigned int host_key_bytes = BN_num_bytes(host_key_n);
        unsigned int session_key_bytes = BN_num_bytes(session_key_n);
diff --git a/nchan.c b/nchan.c
index 8021dd6cb1cb4f295037d8143fba83d736c74049..265ac9e1ef3c85d7ebd6009e27b28da7ca859621 100644 (file)
--- a/nchan.c
+++ b/nchan.c
@@ -41,7 +41,6 @@ static void chan_send_ieof(Channel *c);
 static void chan_send_oclose(Channel *c);
 static void chan_shutdown_write(Channel *c);
 static void chan_shutdown_read(Channel *c);
-static void chan_delete_if_full_closed(Channel *c);
 
 /*
  * EVENTS update channel input/output states execute ACTIONS
@@ -73,7 +72,6 @@ chan_rcvd_oclose(Channel *c)
                error("protocol error: chan_rcvd_oclose %d for istate %d", c->self, c->istate);
                return;
        }
-       chan_delete_if_full_closed(c);
 }
 void
 chan_read_failed(Channel *c)
@@ -121,7 +119,6 @@ chan_rcvd_ieof(Channel *c)
        case CHAN_OUTPUT_WAIT_IEOF:
                debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self);
                c->ostate = CHAN_OUTPUT_CLOSED;
-               chan_delete_if_full_closed(c);
                break;
        default:
                error("protocol error: chan_rcvd_ieof %d for ostate %d", c->self, c->ostate);
@@ -141,7 +138,6 @@ chan_write_failed(Channel *c)
                debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self);
                chan_send_oclose(c);
                c->ostate = CHAN_OUTPUT_CLOSED;
-               chan_delete_if_full_closed(c);
                break;
        default:
                error("internal error: chan_write_failed %d for ostate %d", c->self, c->ostate);
@@ -160,7 +156,6 @@ chan_obuf_empty(Channel *c)
                debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self);
                chan_send_oclose(c);
                c->ostate = CHAN_OUTPUT_CLOSED;
-               chan_delete_if_full_closed(c);
                break;
        default:
                error("internal error: chan_obuf_empty %d for ostate %d", c->self, c->ostate);
@@ -222,7 +217,7 @@ chan_shutdown_read(Channel *c)
                error("chan_shutdown_read failed for #%d/fd%d [i%d o%d]: %.100s",
                      c->self, c->sock, c->istate, c->ostate, strerror(errno));
 }
-static void
+void
 chan_delete_if_full_closed(Channel *c)
 {
        if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) {
diff --git a/nchan.h b/nchan.h
index 45e8f87df3c145fc725e7aee1075d0a800401261..064b99f103c42b29382ee2d2fb148e9a1df02e33 100644 (file)
--- a/nchan.h
+++ b/nchan.h
@@ -83,4 +83,6 @@ void    chan_write_failed(Channel * c);
 void    chan_obuf_empty(Channel * c);
 
 void    chan_init_iostates(Channel * c);
+
+void   chan_delete_if_full_closed(Channel *c);
 #endif
index ec4e5f9795d76c1ca943afff7afcaf36b1cb2f8e..69efac3c9c635388b35ba3a200b1963928c9fbeb 100644 (file)
--- a/packet.c
+++ b/packet.c
@@ -28,6 +28,7 @@ RCSID("$Id$");
 
 #include "compress.h"
 #include "deattack.h"
+#include "channels.h"
 
 /*
  * This variable contains the file descriptors used for communicating with
diff --git a/pty.h b/pty.h
index 82da8620fd417ec3d04fb606389c2890e7ac0db3..c65f8c9302667fd71d5ae166a4cd75c4c237e0b5 100644 (file)
--- a/pty.h
+++ b/pty.h
@@ -45,6 +45,4 @@ pty_change_window_size(int ptyfd, int row, int col,
 
 void   pty_setowner(struct passwd *pw, const char *ttyname);
 
-void   pty_setowner(struct passwd *pw, const char *ttyname);
-
 #endif                         /* PTY_H */
index 4dfeece7e06dd527c53d758e88ba3dd56f33a5e5..c1aa3ceb8684a14dc658a805159d019fbc18926c 100644 (file)
@@ -19,6 +19,7 @@ RCSID("$Id$");
 #include "ssh.h"
 #include "cipher.h"
 #include "readconf.h"
+#include "match.h"
 #include "xmalloc.h"
 
 /* Format of the configuration file:
index 2afca7637039345b82d766314378004442be0575..8bf448cebe9c50b395d46482bcd10428bbe4ed07 100644 (file)
 #include "buffer.h"
 #include "servconf.h"
 #include "pty.h"
+#include "channels.h"
+
+#include "compat.h"
+#include "dispatch.h"
 
 static Buffer stdin_buffer;    /* Buffer for stdin data. */
 static Buffer stdout_buffer;   /* Buffer for stdout data. */
@@ -47,6 +51,8 @@ static volatile int child_terminated; /* The child has terminated. */
 static volatile int child_has_selected; /* Child has had chance to drain. */
 static volatile int child_wait_status; /* Status from wait(). */
 
+void   server_init_dispatch(void);
+
 void 
 sigchld_handler(int sig)
 {
@@ -67,104 +73,6 @@ sigchld_handler(int sig)
        errno = save_errno;
 }
 
-/*
- * Process any buffered packets that have been received from the client.
- */
-void 
-process_buffered_input_packets()
-{
-       int type;
-       char *data;
-       unsigned int data_len;
-       int row, col, xpixel, ypixel;
-       int payload_len;
-
-       /* Process buffered packets from the client. */
-       while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) {
-               switch (type) {
-               case SSH_CMSG_STDIN_DATA:
-                       /* Stdin data from the client.  Append it to the buffer. */
-                       /* Ignore any data if the client has closed stdin. */
-                       if (fdin == -1)
-                               break;
-                       data = packet_get_string(&data_len);
-                       packet_integrity_check(payload_len, (4 + data_len), type);
-                       buffer_append(&stdin_buffer, data, data_len);
-                       memset(data, 0, data_len);
-                       xfree(data);
-                       break;
-
-               case SSH_CMSG_EOF:
-                       /*
-                        * Eof from the client.  The stdin descriptor to the
-                        * program will be closed when all buffered data has
-                        * drained.
-                        */
-                       debug("EOF received for stdin.");
-                       packet_integrity_check(payload_len, 0, type);
-                       stdin_eof = 1;
-                       break;
-
-               case SSH_CMSG_WINDOW_SIZE:
-                       debug("Window change received.");
-                       packet_integrity_check(payload_len, 4 * 4, type);
-                       row = packet_get_int();
-                       col = packet_get_int();
-                       xpixel = packet_get_int();
-                       ypixel = packet_get_int();
-                       if (fdin != -1)
-                               pty_change_window_size(fdin, row, col, xpixel, ypixel);
-                       break;
-
-               case SSH_MSG_PORT_OPEN:
-                       debug("Received port open request.");
-                       channel_input_port_open(payload_len);
-                       break;
-
-               case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
-                       debug("Received channel open confirmation.");
-                       packet_integrity_check(payload_len, 4 + 4, type);
-                       channel_input_open_confirmation();
-                       break;
-
-               case SSH_MSG_CHANNEL_OPEN_FAILURE:
-                       debug("Received channel open failure.");
-                       packet_integrity_check(payload_len, 4, type);
-                       channel_input_open_failure();
-                       break;
-
-               case SSH_MSG_CHANNEL_DATA:
-                       channel_input_data(payload_len);
-                       break;
-
-               case SSH_MSG_CHANNEL_CLOSE:
-                       debug("Received channel close.");
-                       packet_integrity_check(payload_len, 4, type);
-                       channel_input_close();
-                       break;
-
-               case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
-                       debug("Received channel close confirmation.");
-                       packet_integrity_check(payload_len, 4, type);
-                       channel_input_close_confirmation();
-                       break;
-
-               default:
-                       /*
-                        * In this phase, any unexpected messages cause a
-                        * protocol error.  This is to ease debugging; also,
-                        * since no confirmations are sent messages,
-                        * unprocessed unknown messages could cause strange
-                        * problems.  Any compatible protocol extensions must
-                        * be negotiated before entering the interactive
-                        * session.
-                        */
-                       packet_disconnect("Protocol error during session: type %d",
-                                         type);
-               }
-       }
-}
-
 /*
  * Make packets from buffered stderr data, and buffer it for sending
  * to the client.
@@ -378,7 +286,7 @@ process_output(fd_set * writeset)
 #ifdef USE_PIPES
                        close(fdin);
 #else
-                       if (fdout == -1)
+                       if (fdin != fdout)
                                close(fdin);
                        else
                                shutdown(fdin, SHUT_WR); /* We will no longer send. */
@@ -425,6 +333,12 @@ drain_output()
        packet_write_wait();
 }
 
+void 
+process_buffered_input_packets()
+{
+       dispatch_run(DISPATCH_NONBLOCK, NULL);
+}
+
 /*
  * Performs the interactive session.  This handles data transmission between
  * the client and the program.  Note that the notion of stdin, stdout, and
@@ -490,6 +404,8 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg)
        if (fderr == -1)
                fderr_eof = 1;
 
+       server_init_dispatch();
+
        /* Main loop of the server for the interactive session mode. */
        for (;;) {
                fd_set readset, writeset;
@@ -505,7 +421,7 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg)
 #ifdef USE_PIPES
                        close(fdin);
 #else
-                       if (fdout == -1)
+                       if (fdin != fdout)
                                close(fdin);
                        else
                                shutdown(fdin, SHUT_WR); /* We will no longer send. */
@@ -549,7 +465,7 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg)
                    (buffer_len(&stdout_buffer) == 0) && 
                         (buffer_len(&stderr_buffer) == 0)) {
                        if (!channel_still_open())
-                               goto quit;
+                               break;
                        if (!waiting_termination) {
                                const char *s = "Waiting for forwarded connections to terminate...\r\n";
                                char *cp;
@@ -576,7 +492,6 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg)
                process_output(&writeset);
        }
 
-quit:
        /* Cleanup and termination code. */
 
        /* Wait until all output has been sent to the client. */
@@ -662,3 +577,79 @@ quit:
        packet_disconnect("wait returned status %04x.", wait_status);
        /* NOTREACHED */
 }
+
+void
+server_input_stdin_data(int type, int plen)
+{
+       char *data;
+       unsigned int data_len;
+
+       /* Stdin data from the client.  Append it to the buffer. */
+       /* Ignore any data if the client has closed stdin. */
+       if (fdin == -1)
+               return;
+       data = packet_get_string(&data_len);
+       packet_integrity_check(plen, (4 + data_len), type);
+       buffer_append(&stdin_buffer, data, data_len);
+       memset(data, 0, data_len);
+       xfree(data);
+}
+
+void
+server_input_eof(int type, int plen)
+{
+       /*
+        * Eof from the client.  The stdin descriptor to the
+        * program will be closed when all buffered data has
+        * drained.
+        */
+       debug("EOF received for stdin.");
+       packet_integrity_check(plen, 0, type);
+       stdin_eof = 1;
+}
+
+void
+server_input_window_size(int type, int plen)
+{
+       int row = packet_get_int();
+       int col = packet_get_int();
+       int xpixel = packet_get_int();
+       int ypixel = packet_get_int();
+
+       debug("Window change received.");
+       packet_integrity_check(plen, 4 * 4, type);
+       if (fdin != -1)
+               pty_change_window_size(fdin, row, col, xpixel, ypixel);
+}
+
+void 
+server_init_dispatch_13()
+{
+       debug("server_init_dispatch_13");
+       dispatch_init(NULL);
+       dispatch_set(SSH_CMSG_EOF, &server_input_eof);
+       dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data);
+       dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size);
+       dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
+       dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
+       dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
+       dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
+       dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
+       dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
+}
+void 
+server_init_dispatch_15()
+{
+       server_init_dispatch_13();
+       debug("server_init_dispatch_15");
+       dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
+       dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose);
+}
+void 
+server_init_dispatch()
+{
+       if (compat13)
+               server_init_dispatch_13();
+       else
+               server_init_dispatch_15();
+}
diff --git a/session.c b/session.c
new file mode 100644 (file)
index 0000000..2128fe3
--- /dev/null
+++ b/session.c
@@ -0,0 +1,1153 @@
+/*
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ */
+
+#include "includes.h"
+RCSID("$OpenBSD: session.c,v 1.1 2000/03/28 21:15:45 markus Exp $");
+
+#include "xmalloc.h"
+#include "ssh.h"
+#include "pty.h"
+#include "packet.h"
+#include "buffer.h"
+#include "cipher.h"
+#include "mpaux.h"
+#include "servconf.h"
+#include "uidswap.h"
+#include "compat.h"
+#include "channels.h"
+#include "nchan.h"
+
+/* types */
+
+#define TTYSZ 64
+typedef struct Session Session;
+struct Session {
+       int     used;
+       int     self;
+       struct  passwd *pw;
+       pid_t   pid;
+       /* tty */
+       char    *term;
+       int     ptyfd, ttyfd, ptymaster;
+       int     row, col, xpixel, ypixel;
+       char    tty[TTYSZ];
+       /* X11 */
+       char    *display;
+       int     screen;
+       char    *auth_proto;
+       char    *auth_data;
+       /* proto 2 */
+       int     chanid;
+};
+
+/* func */
+
+Session *session_new(void);
+void   session_set_fds(Session *s, int fdin, int fdout, int fderr);
+void   session_pty_cleanup(Session *s);
+void   do_exec_pty(Session *s, const char *command, struct passwd * pw);
+void   do_exec_no_pty(Session *s, const char *command, struct passwd * pw);
+
+void
+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);
+
+/* import */
+extern ServerOptions options;
+extern char *__progname;
+extern int log_stderr;
+extern int debug_flag;
+
+/* Local Xauthority file. */
+static char *xauthfile;
+
+/* data */
+#define MAX_SESSIONS 10
+Session        sessions[MAX_SESSIONS];
+
+/* Flags set in auth-rsa from authorized_keys flags.  These are set in auth-rsa.c. */
+int no_port_forwarding_flag = 0;
+int no_agent_forwarding_flag = 0;
+int no_x11_forwarding_flag = 0;
+int no_pty_flag = 0;
+
+/* RSA authentication "command=" option. */
+char *forced_command = NULL;
+
+/* RSA authentication "environment=" options. */
+struct envstring *custom_environment = NULL;
+
+/*
+ * Remove local Xauthority file.
+ */
+void
+xauthfile_cleanup_proc(void *ignore)
+{
+       debug("xauthfile_cleanup_proc called");
+
+       if (xauthfile != NULL) {
+               char *p;
+               unlink(xauthfile);
+               p = strrchr(xauthfile, '/');
+               if (p != NULL) {
+                       *p = '\0';
+                       rmdir(xauthfile);
+               }
+               xfree(xauthfile);
+               xauthfile = NULL;
+       }
+}
+
+/*
+ * Function to perform cleanup if we get aborted abnormally (e.g., due to a
+ * dropped connection).
+ */
+void 
+pty_cleanup_proc(void *session)
+{
+       Session *s=session;
+       if (s == NULL)
+               fatal("pty_cleanup_proc: no session");
+       debug("pty_cleanup_proc: %s", s->tty);
+
+       if (s->pid != 0) {
+               /* Record that the user has logged out. */
+               record_logout(s->pid, s->tty);
+       }
+
+       /* Release the pseudo-tty. */
+       pty_release(s->tty);
+}
+
+/*
+ * Prepares for an interactive session.  This is called after the user has
+ * been successfully authenticated.  During this message exchange, pseudo
+ * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
+ * are requested, etc.
+ */
+void 
+do_authenticated(struct passwd * pw)
+{
+       Session *s;
+       int type;
+       int compression_level = 0, enable_compression_after_reply = 0;
+       int have_pty = 0;
+       char *command;
+       int n_bytes;
+       int plen;
+       unsigned int proto_len, data_len, dlen;
+
+       /*
+        * Cancel the alarm we set to limit the time taken for
+        * authentication.
+        */
+       alarm(0);
+
+       /*
+        * Inform the channel mechanism that we are the server side and that
+        * the client may request to connect to any port at all. (The user
+        * could do it anyway, and we wouldn\'t know what is permitted except
+        * by the client telling us, so we can equally well trust the client
+        * not to request anything bogus.)
+        */
+       if (!no_port_forwarding_flag)
+               channel_permit_all_opens();
+
+       s = session_new();
+
+       /*
+        * We stay in this loop until the client requests to execute a shell
+        * or a command.
+        */
+       for (;;) {
+               int success = 0;
+
+               /* Get a packet from the client. */
+               type = packet_read(&plen);
+
+               /* Process the packet. */
+               switch (type) {
+               case SSH_CMSG_REQUEST_COMPRESSION:
+                       packet_integrity_check(plen, 4, type);
+                       compression_level = packet_get_int();
+                       if (compression_level < 1 || compression_level > 9) {
+                               packet_send_debug("Received illegal compression level %d.",
+                                    compression_level);
+                               break;
+                       }
+                       /* Enable compression after we have responded with SUCCESS. */
+                       enable_compression_after_reply = 1;
+                       success = 1;
+                       break;
+
+               case SSH_CMSG_REQUEST_PTY:
+                       if (no_pty_flag) {
+                               debug("Allocating a pty not permitted for this authentication.");
+                               break;
+                       }
+                       if (have_pty)
+                               packet_disconnect("Protocol error: you already have a pty.");
+
+                       debug("Allocating pty.");
+
+                       /* Allocate a pty and open it. */
+                       if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
+                           sizeof(s->tty))) {
+                               error("Failed to allocate pty.");
+                               break;
+                       }
+                       fatal_add_cleanup(pty_cleanup_proc, (void *)s);
+                       pty_setowner(pw, s->tty);
+
+                       /* Get TERM from the packet.  Note that the value may be of arbitrary length. */
+                       s->term = packet_get_string(&dlen);
+                       packet_integrity_check(dlen, strlen(s->term), type);
+                       /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */
+                       /* Remaining bytes */
+                       n_bytes = plen - (4 + dlen + 4 * 4);
+
+                       if (strcmp(s->term, "") == 0) {
+                               xfree(s->term);
+                               s->term = NULL;
+                       }
+                       /* Get window size from the packet. */
+                       s->row = packet_get_int();
+                       s->col = 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);
+
+                       /* Get tty modes from the packet. */
+                       tty_parse_modes(s->ttyfd, &n_bytes);
+                       packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type);
+
+                       /* Indicate that we now have a pty. */
+                       success = 1;
+                       have_pty = 1;
+                       break;
+
+               case SSH_CMSG_X11_REQUEST_FORWARDING:
+                       if (!options.x11_forwarding) {
+                               packet_send_debug("X11 forwarding disabled in server configuration file.");
+                               break;
+                       }
+#ifdef XAUTH_PATH
+                       if (no_x11_forwarding_flag) {
+                               packet_send_debug("X11 forwarding not permitted for this authentication.");
+                               break;
+                       }
+                       debug("Received request for X11 forwarding with auth spoofing.");
+                       if (s->display != NULL)
+                               packet_disconnect("Protocol error: X11 display already set.");
+
+                       s->auth_proto = packet_get_string(&proto_len);
+                       s->auth_data = packet_get_string(&data_len);
+                       packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type);
+
+                       if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER)
+                               s->screen = packet_get_int();
+                       else
+                               s->screen = 0;
+                       s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
+
+                       if (s->display == NULL)
+                               break;
+
+                       /* Setup to always have a local .Xauthority. */
+                       xauthfile = xmalloc(MAXPATHLEN);
+                       strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
+                       temporarily_use_uid(pw->pw_uid);
+                       if (mkdtemp(xauthfile) == NULL) {
+                               restore_uid();
+                               error("private X11 dir: mkdtemp %s failed: %s",
+                                   xauthfile, strerror(errno));
+                               xfree(xauthfile);
+                               xauthfile = NULL;
+                               break;
+                       }
+                       strlcat(xauthfile, "/cookies", MAXPATHLEN);
+                       open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
+                       restore_uid();
+                       fatal_add_cleanup(xauthfile_cleanup_proc, NULL);
+                       success = 1;
+                       break;
+#else /* XAUTH_PATH */
+                       packet_send_debug("No xauth program; cannot forward with spoofing.");
+                       break;
+#endif /* XAUTH_PATH */
+
+               case SSH_CMSG_AGENT_REQUEST_FORWARDING:
+                       if (no_agent_forwarding_flag || compat13) {
+                               debug("Authentication agent forwarding not permitted for this authentication.");
+                               break;
+                       }
+                       debug("Received authentication agent forwarding request.");
+                       auth_input_request_forwarding(pw);
+                       success = 1;
+                       break;
+
+               case SSH_CMSG_PORT_FORWARD_REQUEST:
+                       if (no_port_forwarding_flag) {
+                               debug("Port forwarding not permitted for this authentication.");
+                               break;
+                       }
+                       debug("Received TCP/IP port forwarding request.");
+                       channel_input_port_forward_request(pw->pw_uid == 0);
+                       success = 1;
+                       break;
+
+               case SSH_CMSG_MAX_PACKET_SIZE:
+                       if (packet_set_maxsize(packet_get_int()) > 0)
+                               success = 1;
+                       break;
+
+               case SSH_CMSG_EXEC_SHELL:
+               case SSH_CMSG_EXEC_CMD:
+                       /* Set interactive/non-interactive mode. */
+                       packet_set_interactive(have_pty || s->display != NULL,
+                           options.keepalives);
+
+                       if (type == SSH_CMSG_EXEC_CMD) {
+                               command = packet_get_string(&dlen);
+                               debug("Exec command '%.500s'", command);
+                               packet_integrity_check(plen, 4 + dlen, type);
+                       } else {
+                               command = NULL;
+                               packet_integrity_check(plen, 0, type);
+                       }
+                       if (forced_command != NULL) {
+                               command = forced_command;
+                               debug("Forced command '%.500s'", forced_command);
+                       }
+                       if (have_pty)
+                               do_exec_pty(s, command, pw);
+                       else
+                               do_exec_no_pty(s, command, pw);
+
+                       if (command != NULL)
+                               xfree(command);
+                       /* Cleanup user's local Xauthority file. */
+                       if (xauthfile)
+                               xauthfile_cleanup_proc(NULL);
+                       return;
+
+               default:
+                       /*
+                        * Any unknown messages in this phase are ignored,
+                        * and a failure message is returned.
+                        */
+                       log("Unknown packet type received after authentication: %d", type);
+               }
+               packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE);
+               packet_send();
+               packet_write_wait();
+
+               /* Enable compression now that we have replied if appropriate. */
+               if (enable_compression_after_reply) {
+                       enable_compression_after_reply = 0;
+                       packet_start_compression(compression_level);
+               }
+       }
+}
+
+/*
+ * This is called to fork and execute a command when we have no tty.  This
+ * will call do_child from the child, and server_loop from the parent after
+ * setting up file descriptors and such.
+ */
+void 
+do_exec_no_pty(Session *s, const char *command, struct passwd * pw)
+{
+       int pid;
+
+#ifdef USE_PIPES
+       int pin[2], pout[2], perr[2];
+       /* Allocate pipes for communicating with the program. */
+       if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
+               packet_disconnect("Could not create pipes: %.100s",
+                                 strerror(errno));
+#else /* USE_PIPES */
+       int inout[2], err[2];
+       /* Uses socket pairs to communicate with the program. */
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
+           socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
+               packet_disconnect("Could not create socket pairs: %.100s",
+                                 strerror(errno));
+#endif /* USE_PIPES */
+       if (s == NULL)
+               fatal("do_exec_no_pty: no session");
+
+       setproctitle("%s@notty", pw->pw_name);
+
+#ifdef USE_PAM
+                       do_pam_setcred();
+#endif /* USE_PAM */
+
+       /* Fork the child. */
+       if ((pid = fork()) == 0) {
+               /* Child.  Reinitialize the log since the pid has changed. */
+               log_init(__progname, options.log_level, options.log_facility, log_stderr);
+
+               /*
+                * Create a new session and process group since the 4.4BSD
+                * setlogin() affects the entire process group.
+                */
+               if (setsid() < 0)
+                       error("setsid failed: %.100s", strerror(errno));
+
+#ifdef USE_PIPES
+               /*
+                * Redirect stdin.  We close the parent side of the socket
+                * pair, and make the child side the standard input.
+                */
+               close(pin[1]);
+               if (dup2(pin[0], 0) < 0)
+                       perror("dup2 stdin");
+               close(pin[0]);
+
+               /* Redirect stdout. */
+               close(pout[0]);
+               if (dup2(pout[1], 1) < 0)
+                       perror("dup2 stdout");
+               close(pout[1]);
+
+               /* Redirect stderr. */
+               close(perr[0]);
+               if (dup2(perr[1], 2) < 0)
+                       perror("dup2 stderr");
+               close(perr[1]);
+#else /* USE_PIPES */
+               /*
+                * Redirect stdin, stdout, and stderr.  Stdin and stdout will
+                * use the same socket, as some programs (particularly rdist)
+                * seem to depend on it.
+                */
+               close(inout[1]);
+               close(err[1]);
+               if (dup2(inout[0], 0) < 0)      /* stdin */
+                       perror("dup2 stdin");
+               if (dup2(inout[0], 1) < 0)      /* stdout.  Note: same socket as stdin. */
+                       perror("dup2 stdout");
+               if (dup2(err[0], 2) < 0)        /* stderr */
+                       perror("dup2 stderr");
+#endif /* USE_PIPES */
+
+               /* Do processing for the child (exec command etc). */
+               do_child(command, pw, NULL, s->display, s->auth_proto, s->auth_data, NULL);
+               /* NOTREACHED */
+       }
+       if (pid < 0)
+               packet_disconnect("fork failed: %.100s", strerror(errno));
+       s->pid = pid;
+#ifdef USE_PIPES
+       /* We are the parent.  Close the child sides of the pipes. */
+       close(pin[0]);
+       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]. */
+#else /* USE_PIPES */
+       /* We are the parent.  Close the child sides of the socket pairs. */
+       close(inout[0]);
+       close(err[0]);
+
+       /*
+        * 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]. */
+#endif /* USE_PIPES */
+}
+
+/*
+ * This is called to fork and execute a command when we have a tty.  This
+ * will call do_child from the child, and server_loop from the parent after
+ * setting up file descriptors, controlling tty, updating wtmp, utmp,
+ * lastlog, and other such operations.
+ */
+void 
+do_exec_pty(Session *s, const char *command, struct passwd * pw)
+{
+       FILE *f;
+       char buf[100], *time_string;
+       char line[256];
+       const char *hostname;
+       int fdout, ptyfd, ttyfd, ptymaster;
+       int quiet_login;
+       pid_t pid;
+       socklen_t fromlen;
+       struct sockaddr_storage from;
+       struct stat st;
+       time_t last_login_time;
+
+       if (s == NULL)
+               fatal("do_exec_pty: no session");
+       ptyfd = s->ptyfd;
+       ttyfd = s->ttyfd;
+
+       /* Get remote host name. */
+       hostname = get_canonical_hostname();
+
+       /*
+        * Get the time when the user last logged in.  Buf will be set to
+        * contain the hostname the last login was from.
+        */
+       if (!options.use_login) {
+               last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
+                                                     buf, sizeof(buf));
+       }
+       setproctitle("%s@%s", pw->pw_name, strrchr(s->tty, '/') + 1);
+
+#ifdef USE_PAM
+                       do_pam_session(pw->pw_name, s->tty);
+                       do_pam_setcred();
+#endif /* USE_PAM */
+
+       /* Fork the child. */
+       if ((pid = fork()) == 0) {
+               pid = getpid();
+
+               /* Child.  Reinitialize the log because the pid has
+                  changed. */
+               log_init(__progname, options.log_level, options.log_facility, log_stderr);
+
+               /* Close the master side of the pseudo tty. */
+               close(ptyfd);
+
+               /* Make the pseudo tty our controlling tty. */
+               pty_make_controlling_tty(&ttyfd, s->tty);
+
+               /* Redirect stdin from the pseudo tty. */
+               if (dup2(ttyfd, fileno(stdin)) < 0)
+                       error("dup2 stdin failed: %.100s", strerror(errno));
+
+               /* Redirect stdout to the pseudo tty. */
+               if (dup2(ttyfd, fileno(stdout)) < 0)
+                       error("dup2 stdin failed: %.100s", strerror(errno));
+
+               /* Redirect stderr to the pseudo tty. */
+               if (dup2(ttyfd, fileno(stderr)) < 0)
+                       error("dup2 stdin failed: %.100s", strerror(errno));
+
+               /* Close the extra descriptor for the pseudo tty. */
+               close(ttyfd);
+
+///XXXX ? move to do_child() ??
+               /*
+                * Get IP address of client.  This is needed because we want
+                * to record where the user logged in from.  If the
+                * connection is not a socket, let the ip address be 0.0.0.0.
+                */
+               memset(&from, 0, sizeof(from));
+               if (packet_connection_is_on_socket()) {
+                       fromlen = sizeof(from);
+                       if (getpeername(packet_get_connection_in(),
+                            (struct sockaddr *) & from, &fromlen) < 0) {
+                               debug("getpeername: %.100s", strerror(errno));
+                               fatal_cleanup();
+                       }
+               }
+               /* Record that there was a login on that terminal. */
+               record_login(pid, s->tty, pw->pw_name, pw->pw_uid, hostname,
+                            (struct sockaddr *)&from);
+
+               /* Check if .hushlogin exists. */
+               snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir);
+               quiet_login = stat(line, &st) >= 0;
+
+#ifdef USE_PAM
+               if (!quiet_login)
+                       print_pam_messages();
+#endif /* USE_PAM */
+
+               /*
+                * If the user has logged in before, display the time of last
+                * login. However, don't display anything extra if a command
+                * has been specified (so that ssh can be used to execute
+                * commands on a remote machine without users knowing they
+                * are going to another machine). Login(1) will do this for
+                * us as well, so check if login(1) is used
+                */
+               if (command == NULL && last_login_time != 0 && !quiet_login &&
+                   !options.use_login) {
+                       /* Convert the date to a string. */
+                       time_string = ctime(&last_login_time);
+                       /* Remove the trailing newline. */
+                       if (strchr(time_string, '\n'))
+                               *strchr(time_string, '\n') = 0;
+                       /* Display the last login time.  Host if displayed
+                          if known. */
+                       if (strcmp(buf, "") == 0)
+                               printf("Last login: %s\r\n", time_string);
+                       else
+                               printf("Last login: %s from %s\r\n", time_string, buf);
+               }
+               /*
+                * Print /etc/motd unless a command was specified or printing
+                * it was disabled in server options or login(1) will be
+                * used.  Note that some machines appear to print it in
+                * /etc/profile or similar.
+                */
+               if (command == NULL && options.print_motd && !quiet_login &&
+                   !options.use_login) {
+                       /* Print /etc/motd if it exists. */
+                       f = fopen("/etc/motd", "r");
+                       if (f) {
+                               while (fgets(line, sizeof(line), f))
+                                       fputs(line, stdout);
+                               fclose(f);
+                       }
+               }
+               /* Do common processing for the child, such as execing the command. */
+               do_child(command, pw, s->term, s->display, s->auth_proto, s->auth_data, s->tty);
+               /* NOTREACHED */
+       }
+       if (pid < 0)
+               packet_disconnect("fork failed: %.100s", strerror(errno));
+       s->pid = pid;
+
+       /* Parent.  Close the slave side of the pseudo tty. */
+       close(ttyfd);
+
+       /*
+        * Create another descriptor of the pty master side for use as the
+        * standard input.  We could use the original descriptor, but this
+        * simplifies code in server_loop.  The descriptor is bidirectional.
+        */
+       fdout = dup(ptyfd);
+       if (fdout < 0)
+               packet_disconnect("dup #1 failed: %.100s", strerror(errno));
+
+       /* we keep a reference to the pty master */
+       ptymaster = dup(ptyfd);
+       if (ptymaster < 0)
+               packet_disconnect("dup #2 failed: %.100s", strerror(errno));
+       s->ptymaster = ptymaster;
+
+       /* Enter interactive session. */
+       server_loop(pid, ptyfd, fdout, -1);
+       /* server_loop _has_ closed ptyfd and fdout. */
+       session_pty_cleanup(s);
+}
+
+/*
+ * Sets the value of the given variable in the environment.  If the variable
+ * already exists, its value is overriden.
+ */
+void 
+child_set_env(char ***envp, unsigned int *envsizep, const char *name,
+             const char *value)
+{
+       unsigned int i, namelen;
+       char **env;
+
+       /*
+        * Find the slot where the value should be stored.  If the variable
+        * already exists, we reuse the slot; otherwise we append a new slot
+        * at the end of the array, expanding if necessary.
+        */
+       env = *envp;
+       namelen = strlen(name);
+       for (i = 0; env[i]; i++)
+               if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
+                       break;
+       if (env[i]) {
+               /* Reuse the slot. */
+               xfree(env[i]);
+       } else {
+               /* New variable.  Expand if necessary. */
+               if (i >= (*envsizep) - 1) {
+                       (*envsizep) += 50;
+                       env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
+               }
+               /* Need to set the NULL pointer at end of array beyond the new slot. */
+               env[i + 1] = NULL;
+       }
+
+       /* Allocate space and format the variable in the appropriate slot. */
+       env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
+       snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
+}
+
+/*
+ * Reads environment variables from the given file and adds/overrides them
+ * into the environment.  If the file does not exist, this does nothing.
+ * Otherwise, it must consist of empty lines, comments (line starts with '#')
+ * and assignments of the form name=value.  No other forms are allowed.
+ */
+void 
+read_environment_file(char ***env, unsigned int *envsize,
+                     const char *filename)
+{
+       FILE *f;
+       char buf[4096];
+       char *cp, *value;
+
+       f = fopen(filename, "r");
+       if (!f)
+               return;
+
+       while (fgets(buf, sizeof(buf), f)) {
+               for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
+                       ;
+               if (!*cp || *cp == '#' || *cp == '\n')
+                       continue;
+               if (strchr(cp, '\n'))
+                       *strchr(cp, '\n') = '\0';
+               value = strchr(cp, '=');
+               if (value == NULL) {
+                       fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf);
+                       continue;
+               }
+               /* Replace the equals sign by nul, and advance value to the value string. */
+               *value = '\0';
+               value++;
+               child_set_env(env, envsize, cp, value);
+       }
+       fclose(f);
+}
+
+#ifdef USE_PAM
+/*
+ * Sets any environment variables which have been specified by PAM
+ */
+void do_pam_environment(char ***env, int *envsize)
+{
+       char *equals, var_name[512], var_val[512];
+       char **pam_env;
+       int i;
+
+       if ((pam_env = fetch_pam_environment()) == NULL)
+               return;
+       
+       for(i = 0; pam_env[i] != NULL; i++) {
+               if ((equals = strstr(pam_env[i], "=")) == NULL)
+                       continue;
+                       
+               if (strlen(pam_env[i]) < (sizeof(var_name) - 1)) {
+                       memset(var_name, '\0', sizeof(var_name));
+                       memset(var_val, '\0', sizeof(var_val));
+
+                       strncpy(var_name, pam_env[i], equals - pam_env[i]);
+                       strcpy(var_val, equals + 1);
+
+                       debug("PAM environment: %s=%s", var_name, var_val);
+
+                       child_set_env(env, envsize, var_name, var_val);
+               }
+       }
+}
+#endif /* USE_PAM */
+
+/*
+ * Performs common processing for the child, such as setting up the
+ * environment, closing extra file descriptors, setting the user and group
+ * ids, and executing the command or shell.
+ */
+void 
+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, *cp = NULL;
+       char buf[256];
+       FILE *f;
+       unsigned int envsize, i;
+       char **env;
+       extern char **environ;
+       struct stat st;
+       char *argv[10];
+
+#ifndef USE_PAM /* pam_nologin handles this */
+       f = fopen("/etc/nologin", "r");
+       if (f) {
+               /* /etc/nologin exists.  Print its contents and exit. */
+               while (fgets(buf, sizeof(buf), f))
+                       fputs(buf, stderr);
+               fclose(f);
+               if (pw->pw_uid != 0)
+                       exit(254);
+       }
+#endif /* USE_PAM */
+
+       /* Set login name in the kernel. */
+       if (setlogin(pw->pw_name) < 0)
+               error("setlogin failed: %s", strerror(errno));
+
+       /* Set uid, gid, and groups. */
+       /* Login(1) does this as well, and it needs uid 0 for the "-h"
+          switch, so we let login(1) to this for us. */
+       if (!options.use_login) {
+               if (getuid() == 0 || geteuid() == 0) {
+                       if (setgid(pw->pw_gid) < 0) {
+                               perror("setgid");
+                               exit(1);
+                       }
+                       /* Initialize the group list. */
+                       if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
+                               perror("initgroups");
+                               exit(1);
+                       }
+                       endgrent();
+
+                       /* Permanently switch to the desired uid. */
+                       permanently_set_uid(pw->pw_uid);
+               }
+               if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
+                       fatal("Failed to set uids to %d.", (int) pw->pw_uid);
+       }
+       /*
+        * Get the shell from the password data.  An empty shell field is
+        * legal, and means /bin/sh.
+        */
+       shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
+
+#ifdef AFS
+       /* Try to get AFS tokens for the local cell. */
+       if (k_hasafs()) {
+               char cell[64];
+
+               if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
+                       krb_afslog(cell, 0);
+
+               krb_afslog(0, 0);
+       }
+#endif /* AFS */
+
+       /* Initialize the environment. */
+       envsize = 100;
+       env = xmalloc(envsize * sizeof(char *));
+       env[0] = NULL;
+
+       if (!options.use_login) {
+               /* Set basic environment. */
+               child_set_env(&env, &envsize, "USER", pw->pw_name);
+               child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
+               child_set_env(&env, &envsize, "HOME", pw->pw_dir);
+               child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
+
+               snprintf(buf, sizeof buf, "%.200s/%.50s",
+                        _PATH_MAILDIR, pw->pw_name);
+               child_set_env(&env, &envsize, "MAIL", buf);
+
+               /* Normal systems set SHELL by default. */
+               child_set_env(&env, &envsize, "SHELL", shell);
+       }
+       if (getenv("TZ"))
+               child_set_env(&env, &envsize, "TZ", getenv("TZ"));
+
+       /* Set custom environment options from RSA authentication. */
+       while (custom_environment) {
+               struct envstring *ce = custom_environment;
+               char *s = ce->s;
+               int i;
+               for (i = 0; s[i] != '=' && s[i]; i++);
+               if (s[i] == '=') {
+                       s[i] = 0;
+                       child_set_env(&env, &envsize, s, s + i + 1);
+               }
+               custom_environment = ce->next;
+               xfree(ce->s);
+               xfree(ce);
+       }
+
+       snprintf(buf, sizeof buf, "%.50s %d %d",
+                get_remote_ipaddr(), get_remote_port(), get_local_port());
+       child_set_env(&env, &envsize, "SSH_CLIENT", buf);
+
+       if (ttyname)
+               child_set_env(&env, &envsize, "SSH_TTY", ttyname);
+       if (term)
+               child_set_env(&env, &envsize, "TERM", term);
+       if (display)
+               child_set_env(&env, &envsize, "DISPLAY", display);
+
+#ifdef _AIX
+       {
+           char *authstate,*krb5cc;
+
+          if ((authstate = getenv("AUTHSTATE")) != NULL)
+                child_set_env(&env,&envsize,"AUTHSTATE",authstate);
+
+          if ((krb5cc = getenv("KRB5CCNAME")) != NULL)
+                child_set_env(&env,&envsize,"KRB5CCNAME",krb5cc);
+       }
+#endif
+
+#ifdef KRB4
+       {
+               extern char *ticket;
+
+               if (ticket)
+                       child_set_env(&env, &envsize, "KRBTKFILE", ticket);
+       }
+#endif /* KRB4 */
+
+#ifdef USE_PAM
+       /* Pull in any environment variables that may have been set by PAM. */
+       do_pam_environment(&env, &envsize);
+#endif /* USE_PAM */
+
+       read_environment_file(&env,&envsize,"/etc/environment");
+
+       if (xauthfile)
+               child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
+       if (auth_get_socket_name() != NULL)
+               child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
+                             auth_get_socket_name());
+
+       /* read $HOME/.ssh/environment. */
+       if (!options.use_login) {
+               snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir);
+               read_environment_file(&env, &envsize, buf);
+       }
+       if (debug_flag) {
+               /* dump the environment */
+               fprintf(stderr, "Environment:\n");
+               for (i = 0; env[i]; i++)
+                       fprintf(stderr, "  %.200s\n", env[i]);
+       }
+       /*
+        * Close the connection descriptors; note that this is the child, and
+        * the server will still have the socket open, and it is important
+        * that we do not shutdown it.  Note that the descriptors cannot be
+        * closed before building the environment, as we call
+        * get_remote_ipaddr there.
+        */
+       if (packet_get_connection_in() == packet_get_connection_out())
+               close(packet_get_connection_in());
+       else {
+               close(packet_get_connection_in());
+               close(packet_get_connection_out());
+       }
+       /*
+        * Close all descriptors related to channels.  They will still remain
+        * open in the parent.
+        */
+       /* XXX better use close-on-exec? -markus */
+       channel_close_all();
+
+       /*
+        * Close any extra file descriptors.  Note that there may still be
+        * descriptors left by system functions.  They will be closed later.
+        */
+       endpwent();
+
+       /*
+        * Close any extra open file descriptors so that we don\'t have them
+        * hanging around in clients.  Note that we want to do this after
+        * initgroups, because at least on Solaris 2.3 it leaves file
+        * descriptors open.
+        */
+       for (i = 3; i < 64; i++)
+               close(i);
+
+       /* Change current directory to the user\'s home directory. */
+       if (chdir(pw->pw_dir) < 0)
+               fprintf(stderr, "Could not chdir to home directory %s: %s\n",
+                       pw->pw_dir, strerror(errno));
+
+       /*
+        * Must take new environment into use so that .ssh/rc, /etc/sshrc and
+        * xauth are run in the proper environment.
+        */
+       environ = env;
+
+       /*
+        * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
+        * in this order).
+        */
+       if (!options.use_login) {
+               if (stat(SSH_USER_RC, &st) >= 0) {
+                       if (debug_flag)
+                               fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC);
+
+                       f = popen("/bin/sh " SSH_USER_RC, "w");
+                       if (f) {
+                               if (auth_proto != NULL && auth_data != NULL)
+                                       fprintf(f, "%s %s\n", auth_proto, auth_data);
+                               pclose(f);
+                       } else
+                               fprintf(stderr, "Could not run %s\n", SSH_USER_RC);
+               } else if (stat(SSH_SYSTEM_RC, &st) >= 0) {
+                       if (debug_flag)
+                               fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC);
+
+                       f = popen("/bin/sh " SSH_SYSTEM_RC, "w");
+                       if (f) {
+                               if (auth_proto != NULL && auth_data != NULL)
+                                       fprintf(f, "%s %s\n", auth_proto, auth_data);
+                               pclose(f);
+                       } else
+                               fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC);
+               }
+#ifdef XAUTH_PATH
+               else {
+                       /* Add authority data to .Xauthority if appropriate. */
+                       if (auth_proto != NULL && auth_data != NULL) {
+                               if (debug_flag)
+                                       fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n",
+                                               XAUTH_PATH, display, auth_proto, auth_data);
+
+                               f = popen(XAUTH_PATH " -q -", "w");
+                               if (f) {
+                                       fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data);
+                                       pclose(f);
+                               } else
+                                       fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH);
+                       }
+               }
+#endif /* XAUTH_PATH */
+
+               /* Get the last component of the shell name. */
+               cp = strrchr(shell, '/');
+               if (cp)
+                       cp++;
+               else
+                       cp = shell;
+       }
+       /*
+        * If we have no command, execute the shell.  In this case, the shell
+        * name to be passed in argv[0] is preceded by '-' to indicate that
+        * this is a login shell.
+        */
+       if (!command) {
+               if (!options.use_login) {
+                       char buf[256];
+
+                       /*
+                        * Check for mail if we have a tty and it was enabled
+                        * in server options.
+                        */
+                       if (ttyname && options.check_mail) {
+                               char *mailbox;
+                               struct stat mailstat;
+                               mailbox = getenv("MAIL");
+                               if (mailbox != NULL) {
+                                       if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0)
+                                               printf("No mail.\n");
+                                       else if (mailstat.st_mtime < mailstat.st_atime)
+                                               printf("You have mail.\n");
+                                       else
+                                               printf("You have new mail.\n");
+                               }
+                       }
+                       /* Start the shell.  Set initial character to '-'. */
+                       buf[0] = '-';
+                       strncpy(buf + 1, cp, sizeof(buf) - 1);
+                       buf[sizeof(buf) - 1] = 0;
+
+                       /* Execute the shell. */
+                       argv[0] = buf;
+                       argv[1] = NULL;
+                       execve(shell, argv, env);
+
+                       /* Executing the shell failed. */
+                       perror(shell);
+                       exit(1);
+
+               } else {
+                       /* Launch login(1). */
+
+                       execl("/usr/bin/login", "login", "-h", get_remote_ipaddr(),
+                             "-p", "-f", "--", pw->pw_name, NULL);
+
+                       /* Login couldn't be executed, die. */
+
+                       perror("login");
+                       exit(1);
+               }
+       }
+       /*
+        * Execute the command using the user's shell.  This uses the -c
+        * option to execute the command.
+        */
+       argv[0] = (char *) cp;
+       argv[1] = "-c";
+       argv[2] = (char *) command;
+       argv[3] = NULL;
+       execve(shell, argv, env);
+       perror(shell);
+       exit(1);
+}
+
+Session *
+session_new(void)
+{
+       int i;
+       static int did_init = 0;
+       if (!did_init) {
+               debug("session_new: init");
+               for(i = 0; i < MAX_SESSIONS; i++) {
+                       sessions[i].used = 0;
+                       sessions[i].self = i;
+               }
+               did_init = 1;
+       }
+       for(i = 0; i < MAX_SESSIONS; i++) {
+               Session *s = &sessions[i];
+               if (! s->used) {
+                       s->pid = 0;
+                       s->chanid = -1;
+                       s->ptyfd = -1;
+                       s->ttyfd = -1;
+                       s->term = NULL;
+                       s->pw = NULL;
+                       s->display = NULL;
+                       s->screen = 0;
+                       s->auth_data = NULL;
+                       s->auth_proto = NULL;
+                       s->used = 1;
+                       debug("session_new: session %d", i);
+                       return s;
+               }
+       }
+       return NULL;
+}
+
+void
+session_dump(void)
+{
+       int i;
+       for(i = 0; i < MAX_SESSIONS; i++) {
+               Session *s = &sessions[i];
+               debug("dump: used %d session %d %p channel %d pid %d",
+                   s->used,
+                   s->self,
+                   s,
+                   s->chanid,
+                   s->pid);
+       }
+}
+
+void
+session_pty_cleanup(Session *s)
+{
+       if (s == NULL || s->ttyfd == -1)
+               return;
+
+       debug("session_pty_cleanup: session %i release %s", s->self, s->tty);
+
+       /* Cancel the cleanup function. */
+       fatal_remove_cleanup(pty_cleanup_proc, (void *)s);
+
+       /* Record that the user has logged out. */
+       record_logout(s->pid, s->tty);
+
+       /* Release the pseudo-tty. */
+       pty_release(s->tty);
+
+       /*
+        * Close the server side of the socket pairs.  We must do this after
+        * the pty cleanup, so that another process doesn't get this pty
+        * while we're still cleaning up.
+        */
+       if (close(s->ptymaster) < 0)
+               error("close(s->ptymaster): %s", strerror(errno));
+}
diff --git a/session.h b/session.h
new file mode 100644 (file)
index 0000000..2051b73
--- /dev/null
+++ b/session.h
@@ -0,0 +1,7 @@
+#ifndef SESSION_H
+#define SESSION_H
+
+/* SSH1 */
+void   do_authenticated(struct passwd * pw);
+
+#endif
diff --git a/ssh.c b/ssh.c
index cfe4417789aa63f2e42460e7252b1fb786c1a973..5852f0355b918fac9fb73b9b91e84721bcfb74c5 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -20,6 +20,7 @@ RCSID("$Id$");
 #include "authfd.h"
 #include "readconf.h"
 #include "uidswap.h"
+#include "channels.h"
 
 #ifdef HAVE___PROGNAME
 extern char *__progname;
diff --git a/ssh.h b/ssh.h
index 7b75d17711eed679469c399725a3da037d119b61..407dc1c36d17d4c90ed495428b313095aa401602 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -486,175 +486,6 @@ void    fatal_add_cleanup(void (*proc) (void *context), void *context);
 /* Removes a cleanup function to be called at fatal(). */
 void    fatal_remove_cleanup(void (*proc) (void *context), void *context);
 
-/*---------------- definitions for channels ------------------*/
-
-/* Sets specific protocol options. */
-void    channel_set_options(int hostname_in_open);
-
-/*
- * Allocate a new channel object and set its type and socket.  Remote_name
- * must have been allocated with xmalloc; this will free it when the channel
- * is freed.
- */
-int     channel_allocate(int type, int sock, char *remote_name);
-
-/* Free the channel and close its socket. */
-void    channel_free(int channel);
-
-/* Add any bits relevant to channels in select bitmasks. */
-void    channel_prepare_select(fd_set * readset, fd_set * writeset);
-
-/*
- * After select, perform any appropriate operations for channels which have
- * events pending.
- */
-void    channel_after_select(fd_set * readset, fd_set * writeset);
-
-/* If there is data to send to the connection, send some of it now. */
-void    channel_output_poll(void);
-
-/*
- * This is called when a packet of type CHANNEL_DATA has just been received.
- * The message type has already been consumed, but channel number and data is
- * still there.
- */
-void    channel_input_data(int payload_len);
-
-/* Returns true if no channel has too much buffered data. */
-int     channel_not_very_much_buffered_data(void);
-
-/* This is called after receiving CHANNEL_CLOSE. */
-void    channel_input_close(void);
-
-/* This is called after receiving CHANNEL_CLOSE_CONFIRMATION. */
-void    channel_input_close_confirmation(void);
-
-/* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */
-void    channel_input_open_confirmation(void);
-
-/* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */
-void    channel_input_open_failure(void);
-
-/* This closes any sockets that are listening for connections; this removes
-   any unix domain sockets. */
-void    channel_stop_listening(void);
-
-/*
- * Closes the sockets of all channels.  This is used to close extra file
- * descriptors after a fork.
- */
-void    channel_close_all(void);
-
-/* Returns the maximum file descriptor number used by the channels. */
-int     channel_max_fd(void);
-
-/* Returns true if there is still an open channel over the connection. */
-int     channel_still_open(void);
-
-/*
- * Returns a string containing a list of all open channels.  The list is
- * suitable for displaying to the user.  It uses crlf instead of newlines.
- * The caller should free the string with xfree.
- */
-char   *channel_open_message(void);
-
-/*
- * Initiate forwarding of connections to local port "port" through the secure
- * channel to host:port from remote side.  This never returns if there was an
- * error.
- */
-void 
-channel_request_local_forwarding(u_short port, const char *host,
-    u_short remote_port, int gateway_ports);
-
-/*
- * Initiate forwarding of connections to port "port" on remote host through
- * the secure channel to host:port from local side.  This never returns if
- * there was an error.  This registers that open requests for that port are
- * permitted.
- */
-void 
-channel_request_remote_forwarding(u_short port, const char *host,
-    u_short remote_port);
-
-/*
- * Permits opening to any host/port in SSH_MSG_PORT_OPEN.  This is usually
- * called by the server, because the user could connect to any port anyway,
- * and the server has no way to know but to trust the client anyway.
- */
-void    channel_permit_all_opens(void);
-
-/*
- * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
- * listening for the port, and sends back a success reply (or disconnect
- * message if there was an error).  This never returns if there was an error.
- */
-void    channel_input_port_forward_request(int is_root);
-
-/*
- * This is called after receiving PORT_OPEN message.  This attempts to
- * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION
- * or CHANNEL_OPEN_FAILURE.
- */
-void    channel_input_port_open(int payload_len);
-
-/*
- * Creates a port for X11 connections, and starts listening for it. Returns
- * the display name, or NULL if an error was encountered.
- */
-char   *x11_create_display(int screen);
-
-/*
- * Creates an internet domain socket for listening for X11 connections.
- * Returns a suitable value for the DISPLAY variable, or NULL if an error
- * occurs.
- */
-char   *x11_create_display_inet(int screen, int x11_display_offset);
-
-/*
- * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
- * the remote channel number.  We should do whatever we want, and respond
- * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
- */
-void    x11_input_open(int payload_len);
-
-/*
- * Requests forwarding of X11 connections.  This should be called on the
- * client only.
- */
-void    x11_request_forwarding(void);
-
-/*
- * Requests forwarding for X11 connections, with authentication spoofing.
- * This should be called in the client only.
- */
-void    x11_request_forwarding_with_spoofing(const char *proto, const char *data);
-
-/* Sends a message to the server to request authentication fd forwarding. */
-void    auth_request_forwarding(void);
-
-/*
- * Returns the name of the forwarded authentication socket.  Returns NULL if
- * there is no forwarded authentication socket.  The returned value points to
- * a static buffer.
- */
-char   *auth_get_socket_name(void);
-
-/*
- * This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server.
- * This starts forwarding authentication requests.
- */
-void    auth_input_request_forwarding(struct passwd * pw);
-
-/* This is called to process an SSH_SMSG_AGENT_OPEN message. */
-void    auth_input_open_request(void);
-
-/*
- * Returns true if the given string matches the pattern (which may contain ?
- * and * as wildcards), and zero if it does not match.
- */
-int     match_pattern(const char *s, const char *pattern);
-
 /*
  * Expands tildes in the file name.  Returns data allocated by xmalloc.
  * Warning: this calls getpw*.
diff --git a/ssh2.h b/ssh2.h
new file mode 100644 (file)
index 0000000..cc659f8
--- /dev/null
+++ b/ssh2.h
@@ -0,0 +1,106 @@
+/*
+ * draft-ietf-secsh-architecture-04.txt 
+ *
+ *   Transport layer protocol:
+ * 
+ *     1-19     Transport layer generic (e.g. disconnect, ignore, debug,
+ *              etc)
+ *     20-29    Algorithm negotiation
+ *     30-49    Key exchange method specific (numbers can be reused for
+ *              different authentication methods)
+ * 
+ *   User authentication protocol:
+ * 
+ *     50-59    User authentication generic
+ *     60-79    User authentication method specific (numbers can be reused
+ *              for different authentication methods)
+ * 
+ *   Connection protocol:
+ * 
+ *     80-89    Connection protocol generic
+ *     90-127   Channel related messages
+ * 
+ *   Reserved for client protocols:
+ * 
+ *     128-191  Reserved
+ * 
+ *   Local extensions:
+ * 
+ *     192-255  Local extensions
+ */
+
+/* transport layer: generic */
+
+#define SSH2_MSG_DISCONNECT                            1
+#define SSH2_MSG_IGNORE                                        2
+#define SSH2_MSG_UNIMPLEMENTED                         3
+#define SSH2_MSG_DEBUG                                 4
+#define SSH2_MSG_SERVICE_REQUEST                       5
+#define SSH2_MSG_SERVICE_ACCEPT                                6
+
+/* transport layer: alg negotiation */
+
+#define SSH2_MSG_KEXINIT                               20
+#define SSH2_MSG_NEWKEYS                               21
+
+/* transport layer: kex specific messages, can be reused */
+
+#define SSH2_MSG_KEXDH_INIT                            30
+#define SSH2_MSG_KEXDH_REPLY                           31
+
+/* user authentication: generic */
+
+#define SSH2_MSG_USERAUTH_REQUEST                      50
+#define SSH2_MSG_USERAUTH_FAILURE                      51
+#define SSH2_MSG_USERAUTH_SUCCESS                      52
+#define SSH2_MSG_USERAUTH_BANNER                       53
+
+/* user authentication: method specific, can be reused */
+
+#define SSH2_MSG_USERAUTH_PK_OK                                60
+#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ             60
+#define SSH2_MSG_USERAUTH_INFO_REQUEST                 60
+#define SSH2_MSG_USERAUTH_INFO_RESPONSE                        61
+
+/* connection protocol: generic */
+
+#define SSH2_MSG_GLOBAL_REQUEST                                80
+#define SSH2_MSG_REQUEST_SUCCESS                       81
+#define SSH2_MSG_REQUEST_FAILURE                       82
+
+/* channel related messages */
+
+#define SSH2_MSG_CHANNEL_OPEN                          90
+#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION             91
+#define SSH2_MSG_CHANNEL_OPEN_FAILURE                  92
+#define SSH2_MSG_CHANNEL_WINDOW_ADJUST                 93
+#define SSH2_MSG_CHANNEL_DATA                          94
+#define SSH2_MSG_CHANNEL_EXTENDED_DATA                 95
+#define SSH2_MSG_CHANNEL_EOF                           96
+#define SSH2_MSG_CHANNEL_CLOSE                         97
+#define SSH2_MSG_CHANNEL_REQUEST                       98
+#define SSH2_MSG_CHANNEL_SUCCESS                       99
+#define SSH2_MSG_CHANNEL_FAILURE                       100
+
+/* disconnect reason code */
+
+#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT    1
+#define SSH2_DISCONNECT_PROTOCOL_ERROR                 2
+#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED            3
+#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED     4
+#define SSH2_DISCONNECT_MAC_ERROR                      5
+#define SSH2_DISCONNECT_COMPRESSION_ERROR              6
+#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE          7
+#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8
+#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE                9
+#define SSH2_DISCONNECT_CONNECTION_LOST                        10
+#define SSH2_DISCONNECT_BY_APPLICATION                 11
+
+/* misc */
+
+#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED          1
+#define SSH2_OPEN_CONNECT_FAILED                       2
+#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE                 3
+#define SSH2_OPEN_RESOURCE_SHORTAGE                    4
+
+#define SSH2_EXTENDED_DATA_STDERR                      1
diff --git a/sshd.8 b/sshd.8
index 8c64491083e12e32445bae7050785e605f76924a..d7863bb6e848e445c4bbefc3c82d06615cea10de 100644 (file)
--- a/sshd.8
+++ b/sshd.8
@@ -69,7 +69,7 @@ random number as a session key which is used to encrypt all further
 communications in the session.
 The rest of the session is encrypted
 using a conventional cipher, currently Blowfish and 3DES, with 3DES
-being is used by default.
+being used by default.
 The client selects the encryption algorithm
 to use from those offered by the server.
 .Pp
@@ -877,11 +877,11 @@ The libraries described in
 .Xr ssl 8
 are required for proper operation.
 .Sh SEE ALSO
-.Xr rlogin 1 ,
-.Xr rsh 1 ,
 .Xr scp 1 ,
 .Xr ssh 1 ,
 .Xr ssh-add 1 ,
 .Xr ssh-agent 1 ,
 .Xr ssh-keygen 1 ,
-.Xr ssl 8
+.Xr ssl 8 ,
+.Xr rlogin 1 ,
+.Xr rsh 1
diff --git a/sshd.c b/sshd.c
index bf951212c82c919af1688b85b1d32e32bbe01439..6ec413d6fab7f5c2ced47f712b00eb7adda60745 100644 (file)
--- a/sshd.c
+++ b/sshd.c
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshd.c,v 1.94 2000/03/23 22:15:34 markus Exp $");
+RCSID("$OpenBSD: sshd.c,v 1.96 2000/03/28 21:15:45 markus Exp $");
 
 #include "xmalloc.h"
 #include "rsa.h"
 #include "ssh.h"
 #include "pty.h"
 #include "packet.h"
-#include "buffer.h"
 #include "cipher.h"
 #include "mpaux.h"
 #include "servconf.h"
 #include "uidswap.h"
 #include "compat.h"
+#include "buffer.h"
+
+#ifdef HAVE_OPENSSL
+# include <openssl/dh.h>
+# include <openssl/bn.h>
+# include <openssl/hmac.h>
+# include <openssl/dsa.h>
+# include <openssl/rsa.h>
+#endif
+#ifdef HAVE_SSL
+# include <ssl/dh.h>
+# include <ssl/bn.h>
+# include <ssl/hmac.h>
+# include <ssl/dsa.h>
+# include <ssl/rsa.h>
+#endif
+#include "key.h"
+
+#include "auth.h"
 
 #ifdef LIBWRAP
 #include <tcpd.h>
@@ -36,9 +54,6 @@ int deny_severity = LOG_WARNING;
 #define O_NOCTTY       0
 #endif
 
-/* Local Xauthority file. */
-static char *xauthfile = NULL;
-
 /* Server configuration options. */
 ServerOptions options;
 
@@ -88,21 +103,7 @@ int num_listen_socks = 0;
  * sshd will skip the version-number exchange
  */
 char *client_version_string = NULL;
-
-/* Flags set in auth-rsa from authorized_keys flags.  These are set in auth-rsa.c. */
-int no_port_forwarding_flag = 0;
-int no_agent_forwarding_flag = 0;
-int no_x11_forwarding_flag = 0;
-int no_pty_flag = 0;
-
-/* RSA authentication "command=" option. */
-char *forced_command = NULL;
-
-/* RSA authentication "environment=" options. */
-struct envstring *custom_environment = NULL;
-
-/* Session id for the current session. */
-unsigned char session_id[16];
+char *server_version_string = NULL;
 
 /*
  * Any really sensitive data in the application is contained in this
@@ -130,43 +131,11 @@ int received_sighup = 0;
    the private key. */
 RSA *public_key;
 
-/* Prototypes for various functions defined later in this file. */
-void do_ssh_kex();
-void do_authentication();
-void do_authloop(struct passwd * pw);
-void do_fake_authloop(char *user);
-void do_authenticated(struct passwd * pw);
-void do_exec_pty(const char *command, int ptyfd, int ttyfd,
-                const char *ttyname, struct passwd * pw, const char *term,
-                const char *display, const char *auth_proto,
-                const char *auth_data);
-void do_exec_no_pty(const char *command, struct passwd * pw,
-                   const char *display, const char *auth_proto,
-                   const char *auth_data);
-void 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);
+/* session identifier, used by RSA-auth */
+unsigned char session_id[16];
 
-/*
- * Remove local Xauthority file.
- */
-void
-xauthfile_cleanup_proc(void *ignore)
-{
-       debug("xauthfile_cleanup_proc called");
-
-       if (xauthfile != NULL) {
-               char *p;
-               unlink(xauthfile);
-               p = strrchr(xauthfile, '/');
-               if (p != NULL) {
-                       *p = '\0';
-                       rmdir(xauthfile);
-               }
-               xfree(xauthfile);
-               xauthfile = NULL;
-       }
-}
+/* Prototypes for various functions defined later in this file. */
+void do_ssh1_kex();
 
 /*
  * Close all listening sockets
@@ -249,35 +218,6 @@ grace_alarm_handler(int sig)
        fatal("Timeout before authentication for %s.", get_remote_ipaddr());
 }
 
-/*
- * convert ssh auth msg type into description
- */
-char *
-get_authname(int type)
-{
-       static char buf[1024];
-       switch (type) {
-       case SSH_CMSG_AUTH_PASSWORD:
-               return "password";
-       case SSH_CMSG_AUTH_RSA:
-               return "rsa";
-       case SSH_CMSG_AUTH_RHOSTS_RSA:
-               return "rhosts-rsa";
-       case SSH_CMSG_AUTH_RHOSTS:
-               return "rhosts";
-#ifdef KRB4
-       case SSH_CMSG_AUTH_KERBEROS:
-               return "kerberos";
-#endif
-#ifdef SKEY
-       case SSH_CMSG_AUTH_TIS_RESPONSE:
-               return "s/key";
-#endif
-       }
-       snprintf(buf, sizeof buf, "bad-auth-msg-%d", type);
-       return buf;
-}
-
 /*
  * Signal handler for the key regeneration alarm.  Note that this
  * alarm only occurs in the daemon waiting for connections, and it does not
@@ -315,6 +255,88 @@ key_regeneration_alarm(int sig)
        errno = save_errno;
 }
 
+void
+sshd_exchange_identification(int sock_in, int sock_out)
+{
+       int i;
+       int remote_major, remote_minor;
+       char *s;
+       char buf[256];                  /* Must not be larger than remote_version. */
+       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);
+       server_version_string = xstrdup(buf);
+
+       if (client_version_string == NULL) {
+               /* Send our protocol version identification. */
+               if (atomicio(write, sock_out, server_version_string, strlen(server_version_string))
+                   != strlen(server_version_string)) {
+                       log("Could not write ident string to %s.", get_remote_ipaddr());
+                       fatal_cleanup();
+               }
+
+               /* Read other side\'s version identification. */
+               for (i = 0; i < sizeof(buf) - 1; i++) {
+                       if (read(sock_in, &buf[i], 1) != 1) {
+                               log("Did not receive ident string from %s.", get_remote_ipaddr());
+                               fatal_cleanup();
+                       }
+                       if (buf[i] == '\r') {
+                               buf[i] = '\n';
+                               buf[i + 1] = 0;
+                               continue;
+                               /*break; XXX eat \r */
+                       }
+                       if (buf[i] == '\n') {
+                               /* buf[i] == '\n' */
+                               buf[i + 1] = 0;
+                               break;
+                       }
+               }
+               buf[sizeof(buf) - 1] = 0;
+               client_version_string = xstrdup(buf);
+       }
+
+       /*
+        * Check that the versions match.  In future this might accept
+        * several versions and set appropriate flags to handle them.
+        */
+       if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n",
+           &remote_major, &remote_minor, remote_version) != 3) {
+               s = "Protocol mismatch.\n";
+               (void) atomicio(write, sock_out, s, strlen(s));
+               close(sock_in);
+               close(sock_out);
+               log("Bad protocol version identification '%.100s' from %s",
+                   client_version_string, get_remote_ipaddr());
+               fatal_cleanup();
+       }
+       debug("Client protocol version %d.%d; client software version %.100s",
+             remote_major, remote_minor, remote_version);
+
+       switch(remote_major) {
+       case 1:
+               if (remote_minor < 3) {
+                       packet_disconnect("Your ssh version is too old and"
+                           "is no longer supported.  Please install a newer version.");
+               } else if (remote_minor == 3) {
+                       /* note that this disables agent-forwarding */
+                       enable_compat13();
+               }
+               break;
+       default: 
+               s = "Protocol major versions differ.\n";
+               (void) atomicio(write, sock_out, s, strlen(s));
+               close(sock_in);
+               close(sock_out);
+               log("Protocol major versions differ for %s: %d vs. %d",
+                   get_remote_ipaddr(), PROTOCOL_MAJOR, remote_major);
+               fatal_cleanup();
+               break;
+       }
+}
+
 /*
  * Main program for the daemon.
  */
@@ -325,12 +347,9 @@ main(int ac, char **av)
        extern int optind;
        int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, pid, on = 1;
        socklen_t fromlen;
-       int remote_major, remote_minor;
        int silentrsa = 0;
        fd_set *fdset;
        struct sockaddr_storage from;
-       char buf[100];                  /* Must not be larger than remote_version. */
-       char remote_version[100];       /* Must be at least as big as buf. */
        const char *remote_ip;
        int remote_port;
        char *comment;
@@ -794,73 +813,7 @@ main(int ac, char **av)
        if (!debug_flag)
                alarm(options.login_grace_time);
 
-       if (client_version_string != NULL) {
-               /* we are exec'ed by sshd2, so skip exchange of protocol version */
-               strlcpy(buf, client_version_string, sizeof(buf));
-       } else {
-               /* Send our protocol version identification. */
-               snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
-                        PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
-               if (atomicio(write, sock_out, buf, strlen(buf)) != strlen(buf)) {
-                       log("Could not write ident string to %s.", remote_ip);
-                       fatal_cleanup();
-               }
-
-               /* Read other side\'s version identification. */
-               for (i = 0; i < sizeof(buf) - 1; i++) {
-                       if (read(sock_in, &buf[i], 1) != 1) {
-                               log("Did not receive ident string from %s.", remote_ip);
-                               fatal_cleanup();
-                       }
-                       if (buf[i] == '\r') {
-                               buf[i] = '\n';
-                               buf[i + 1] = 0;
-                               break;
-                       }
-                       if (buf[i] == '\n') {
-                               /* buf[i] == '\n' */
-                               buf[i + 1] = 0;
-                               break;
-                       }
-               }
-               buf[sizeof(buf) - 1] = 0;
-       }
-
-       /*
-        * Check that the versions match.  In future this might accept
-        * several versions and set appropriate flags to handle them.
-        */
-       if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor,
-           remote_version) != 3) {
-               char *s = "Protocol mismatch.\n";
-
-               (void) atomicio(write, sock_out, s, strlen(s));
-               close(sock_in);
-               close(sock_out);
-               log("Bad protocol version identification '%.100s' from %s",
-                   buf, remote_ip);
-               fatal_cleanup();
-       }
-       debug("Client protocol version %d.%d; client software version %.100s",
-             remote_major, remote_minor, remote_version);
-       if (remote_major != PROTOCOL_MAJOR) {
-               char *s = "Protocol major versions differ.\n";
-
-               (void) atomicio(write, sock_out, s, strlen(s));
-               close(sock_in);
-               close(sock_out);
-               log("Protocol major versions differ for %s: %d vs. %d",
-                   remote_ip, PROTOCOL_MAJOR, remote_major);
-               fatal_cleanup();
-       }
-       /* Check that the client has sufficiently high software version. */
-       if (remote_major == 1 && remote_minor < 3)
-               packet_disconnect("Your ssh version is too old and is no longer supported.  Please install a newer version.");
-
-       if (remote_major == 1 && remote_minor == 3) {
-               /* note that this disables agent-forwarding */
-               enable_compat13();
-       }
+       sshd_exchange_identification(sock_in, sock_out);
        /*
         * Check that the connection comes from a privileged port.  Rhosts-
         * and Rhosts-RSA-Authentication only make sense from priviledged
@@ -884,8 +837,7 @@ main(int ac, char **av)
        packet_set_nonblocking();
 
        /* perform the key exchange */
-       do_ssh_kex();
-
+       do_ssh1_kex();
        /* authenticate user and start session */
        do_authentication();
 
@@ -895,10 +847,6 @@ main(int ac, char **av)
                (void) dest_tkt();
 #endif /* KRB4 */
 
-       /* Cleanup user's local Xauthority file. */
-       if (xauthfile)
-               xauthfile_cleanup_proc(NULL);
-
        /* The connection has been terminated. */
        verbose("Closing connection to %.100s", remote_ip);
 
@@ -914,7 +862,7 @@ main(int ac, char **av)
  * SSH1 key exchange
  */
 void
-do_ssh_kex()
+do_ssh1_kex()
 {
        int i, len;
        int plen, slen;
@@ -1101,1586 +1049,3 @@ do_ssh_kex()
        packet_send();
        packet_write_wait();
 }
-
-
-/*
- * Check if the user is allowed to log in via ssh. If user is listed in
- * DenyUsers or user's primary group is listed in DenyGroups, false will
- * be returned. If AllowUsers isn't empty and user isn't listed there, or
- * if AllowGroups isn't empty and user isn't listed there, false will be
- * returned. 
- * If the user's shell is not executable, false will be returned.
- * Otherwise true is returned. 
- */
-static int
-allowed_user(struct passwd * pw)
-{
-       struct stat st;
-       struct group *grp;
-       int i;
-#ifdef WITH_AIXAUTHENTICATE
-       char *loginmsg;
-#endif /* WITH_AIXAUTHENTICATE */
-
-       /* Shouldn't be called if pw is NULL, but better safe than sorry... */
-       if (!pw)
-               return 0;
-
-       /* deny if shell does not exists or is not executable */
-       if (stat(pw->pw_shell, &st) != 0)
-               return 0;
-       if (!((st.st_mode & S_IFREG) && (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP))))
-               return 0;
-
-       /* Return false if user is listed in DenyUsers */
-       if (options.num_deny_users > 0) {
-               if (!pw->pw_name)
-                       return 0;
-               for (i = 0; i < options.num_deny_users; i++)
-                       if (match_pattern(pw->pw_name, options.deny_users[i]))
-                               return 0;
-       }
-       /* Return false if AllowUsers isn't empty and user isn't listed there */
-       if (options.num_allow_users > 0) {
-               if (!pw->pw_name)
-                       return 0;
-               for (i = 0; i < options.num_allow_users; i++)
-                       if (match_pattern(pw->pw_name, options.allow_users[i]))
-                               break;
-               /* i < options.num_allow_users iff we break for loop */
-               if (i >= options.num_allow_users)
-                       return 0;
-       }
-       /* Get the primary group name if we need it. Return false if it fails */
-       if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
-               grp = getgrgid(pw->pw_gid);
-               if (!grp)
-                       return 0;
-
-               /* Return false if user's group is listed in DenyGroups */
-               if (options.num_deny_groups > 0) {
-                       if (!grp->gr_name)
-                               return 0;
-                       for (i = 0; i < options.num_deny_groups; i++)
-                               if (match_pattern(grp->gr_name, options.deny_groups[i]))
-                                       return 0;
-               }
-               /*
-                * Return false if AllowGroups isn't empty and user's group
-                * isn't listed there
-                */
-               if (options.num_allow_groups > 0) {
-                       if (!grp->gr_name)
-                               return 0;
-                       for (i = 0; i < options.num_allow_groups; i++)
-                               if (match_pattern(grp->gr_name, options.allow_groups[i]))
-                                       break;
-                       /* i < options.num_allow_groups iff we break for
-                          loop */
-                       if (i >= options.num_allow_groups)
-                               return 0;
-               }
-       }
-
-#ifdef WITH_AIXAUTHENTICATE
-       if (loginrestrictions(pw->pw_name,S_LOGIN,NULL,&loginmsg) != 0)
-               return 0;
-#endif /* WITH_AIXAUTHENTICATE */
-
-       /* We found no reason not to let this user try to log on... */
-       return 1;
-}
-
-/*
- * Performs authentication of an incoming connection.  Session key has already
- * been exchanged and encryption is enabled.
- */
-void
-do_authentication()
-{
-       struct passwd *pw, pwcopy;
-       int plen;
-       unsigned int ulen;
-       char *user;
-
-       /* Get the name of the user that we wish to log in as. */
-       packet_read_expect(&plen, SSH_CMSG_USER);
-
-       /* Get the user name. */
-       user = packet_get_string(&ulen);
-       packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER);
-
-       setproctitle("%s", user);
-
-#ifdef WITH_AIXAUTHENTICATE
-       char *loginmsg;
-#endif /* WITH_AIXAUTHENTICATE */
-
-#ifdef AFS
-       /* If machine has AFS, set process authentication group. */
-       if (k_hasafs()) {
-               k_setpag();
-               k_unlog();
-       }
-#endif /* AFS */
-
-       /* Verify that the user is a valid user. */
-       pw = getpwnam(user);
-       if (!pw || !allowed_user(pw))
-               do_fake_authloop(user);
-       xfree(user);
-
-       /* Take a copy of the returned structure. */
-       memset(&pwcopy, 0, sizeof(pwcopy));
-       pwcopy.pw_name = xstrdup(pw->pw_name);
-       pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
-       pwcopy.pw_uid = pw->pw_uid;
-       pwcopy.pw_gid = pw->pw_gid;
-       pwcopy.pw_dir = xstrdup(pw->pw_dir);
-       pwcopy.pw_shell = xstrdup(pw->pw_shell);
-       pw = &pwcopy;
-
-#ifdef USE_PAM
-       start_pam(pw);
-#endif
-
-       /*
-        * If we are not running as root, the user must have the same uid as
-        * the server.
-        */
-       if (getuid() != 0 && pw->pw_uid != getuid())
-               packet_disconnect("Cannot change user when server not running as root.");
-
-       debug("Attempting authentication for %.100s.", pw->pw_name);
-
-       /* If the user has no password, accept authentication immediately. */
-       if (options.password_authentication &&
-#ifdef KRB4
-           (!options.kerberos_authentication || options.kerberos_or_local_passwd) &&
-#endif /* KRB4 */
-#ifdef USE_PAM
-           auth_pam_password(pw, "")) {
-#else /* USE_PAM */
-           auth_password(pw, "")) {
-#endif /* USE_PAM */
-               /* Authentication with empty password succeeded. */
-               log("Login for user %s from %.100s, accepted without authentication.",
-                   pw->pw_name, get_remote_ipaddr());
-       } else {
-               /* Loop until the user has been authenticated or the
-                  connection is closed, do_authloop() returns only if
-                  authentication is successfull */
-               do_authloop(pw);
-       }
-
-       /* The user has been authenticated and accepted. */
-#ifdef WITH_AIXAUTHENTICATE
-       loginsuccess(user,get_canonical_hostname(),"ssh",&loginmsg);
-#endif /* WITH_AIXAUTHENTICATE */
-       packet_start(SSH_SMSG_SUCCESS);
-       packet_send();
-       packet_write_wait();
-
-       /* Perform session preparation. */
-       do_authenticated(pw);
-}
-
-#define AUTH_FAIL_MAX 6
-#define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2)
-#define AUTH_FAIL_MSG "Too many authentication failures for %.100s"
-
-/*
- * read packets and try to authenticate local user *pw.
- * return if authentication is successfull
- */
-void
-do_authloop(struct passwd * pw)
-{
-       int attempt = 0;
-       unsigned int bits;
-       RSA *client_host_key;
-       BIGNUM *n;
-       char *client_user = NULL, *password = NULL;
-       char user[1024];
-       unsigned int dlen;
-       int plen, nlen, elen;
-       unsigned int ulen;
-       int type = 0;
-       void (*authlog) (const char *fmt,...) = verbose;
-
-       /* Indicate that authentication is needed. */
-       packet_start(SSH_SMSG_FAILURE);
-       packet_send();
-       packet_write_wait();
-
-       for (attempt = 1;; attempt++) {
-               int authenticated = 0;
-               strlcpy(user, "", sizeof user);
-
-               /* Get a packet from the client. */
-               type = packet_read(&plen);
-
-               /* Process the packet. */
-               switch (type) {
-#ifdef AFS
-               case SSH_CMSG_HAVE_KERBEROS_TGT:
-                       if (!options.kerberos_tgt_passing) {
-                               /* packet_get_all(); */
-                               verbose("Kerberos tgt passing disabled.");
-                               break;
-                       } else {
-                               /* Accept Kerberos tgt. */
-                               char *tgt = packet_get_string(&dlen);
-                               packet_integrity_check(plen, 4 + dlen, type);
-                               if (!auth_kerberos_tgt(pw, tgt))
-                                       verbose("Kerberos tgt REFUSED for %s", pw->pw_name);
-                               xfree(tgt);
-                       }
-                       continue;
-
-               case SSH_CMSG_HAVE_AFS_TOKEN:
-                       if (!options.afs_token_passing || !k_hasafs()) {
-                               /* packet_get_all(); */
-                               verbose("AFS token passing disabled.");
-                               break;
-                       } else {
-                               /* Accept AFS token. */
-                               char *token_string = packet_get_string(&dlen);
-                               packet_integrity_check(plen, 4 + dlen, type);
-                               if (!auth_afs_token(pw, token_string))
-                                       verbose("AFS token REFUSED for %s", pw->pw_name);
-                               xfree(token_string);
-                       }
-                       continue;
-#endif /* AFS */
-#ifdef KRB4
-               case SSH_CMSG_AUTH_KERBEROS:
-                       if (!options.kerberos_authentication) {
-                               /* packet_get_all(); */
-                               verbose("Kerberos authentication disabled.");
-                               break;
-                       } else {
-                               /* Try Kerberos v4 authentication. */
-                               KTEXT_ST auth;
-                               char *tkt_user = NULL;
-                               char *kdata = packet_get_string((unsigned int *) &auth.length);
-                               packet_integrity_check(plen, 4 + auth.length, type);
-
-                               if (auth.length < MAX_KTXT_LEN)
-                                       memcpy(auth.dat, kdata, auth.length);
-                               xfree(kdata);
-
-                               authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user);
-
-                               if (authenticated) {
-                                       snprintf(user, sizeof user, " tktuser %s", tkt_user);
-                                       xfree(tkt_user);
-                               }
-                       }
-                       break;
-#endif /* KRB4 */
-
-               case SSH_CMSG_AUTH_RHOSTS:
-                       if (!options.rhosts_authentication) {
-                               verbose("Rhosts authentication disabled.");
-                               break;
-                       }
-                       /*
-                        * Get client user name.  Note that we just have to
-                        * trust the client; this is one reason why rhosts
-                        * authentication is insecure. (Another is
-                        * IP-spoofing on a local network.)
-                        */
-                       client_user = packet_get_string(&ulen);
-                       packet_integrity_check(plen, 4 + ulen, type);
-
-                       /* Try to authenticate using /etc/hosts.equiv and
-                          .rhosts. */
-                       authenticated = auth_rhosts(pw, client_user);
-
-                       snprintf(user, sizeof user, " ruser %s", client_user);
-                       break;
-
-               case SSH_CMSG_AUTH_RHOSTS_RSA:
-                       if (!options.rhosts_rsa_authentication) {
-                               verbose("Rhosts with RSA authentication disabled.");
-                               break;
-                       }
-                       /*
-                        * Get client user name.  Note that we just have to
-                        * trust the client; root on the client machine can
-                        * claim to be any user.
-                        */
-                       client_user = packet_get_string(&ulen);
-
-                       /* Get the client host key. */
-                       client_host_key = RSA_new();
-                       if (client_host_key == NULL)
-                               fatal("RSA_new failed");
-                       client_host_key->e = BN_new();
-                       client_host_key->n = BN_new();
-                       if (client_host_key->e == NULL || client_host_key->n == NULL)
-                               fatal("BN_new failed");
-                       bits = packet_get_int();
-                       packet_get_bignum(client_host_key->e, &elen);
-                       packet_get_bignum(client_host_key->n, &nlen);
-
-                       if (bits != BN_num_bits(client_host_key->n))
-                               error("Warning: keysize mismatch for client_host_key: "
-                                     "actual %d, announced %d", BN_num_bits(client_host_key->n), bits);
-                       packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type);
-
-                       authenticated = auth_rhosts_rsa(pw, client_user, client_host_key);
-                       RSA_free(client_host_key);
-
-                       snprintf(user, sizeof user, " ruser %s", client_user);
-                       break;
-
-               case SSH_CMSG_AUTH_RSA:
-                       if (!options.rsa_authentication) {
-                               verbose("RSA authentication disabled.");
-                               break;
-                       }
-                       /* RSA authentication requested. */
-                       n = BN_new();
-                       packet_get_bignum(n, &nlen);
-                       packet_integrity_check(plen, nlen, type);
-                       authenticated = auth_rsa(pw, n);
-                       BN_clear_free(n);
-                       break;
-
-               case SSH_CMSG_AUTH_PASSWORD:
-                       if (!options.password_authentication) {
-                               verbose("Password authentication disabled.");
-                               break;
-                       }
-                       /*
-                        * Read user password.  It is in plain text, but was
-                        * transmitted over the encrypted channel so it is
-                        * not visible to an outside observer.
-                        */
-                       password = packet_get_string(&dlen);
-                       packet_integrity_check(plen, 4 + dlen, type);
-
-#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, strlen(password));
-                       xfree(password);
-                       break;
-
-#ifdef SKEY
-               case SSH_CMSG_AUTH_TIS:
-                       debug("rcvd SSH_CMSG_AUTH_TIS");
-                       if (options.skey_authentication == 1) {
-                               char *skeyinfo = skey_keyinfo(pw->pw_name);
-                               if (skeyinfo == NULL) {
-                                       debug("generating fake skeyinfo for %.100s.", pw->pw_name);
-                                       skeyinfo = skey_fake_keyinfo(pw->pw_name);
-                               }
-                               if (skeyinfo != NULL) {
-                                       /* we send our s/key- in tis-challenge messages */
-                                       debug("sending challenge '%s'", skeyinfo);
-                                       packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
-                                       packet_put_string(skeyinfo, strlen(skeyinfo));
-                                       packet_send();
-                                       packet_write_wait();
-                                       continue;
-                               }
-                       }
-                       break;
-               case SSH_CMSG_AUTH_TIS_RESPONSE:
-                       debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE");
-                       if (options.skey_authentication == 1) {
-                               char *response = packet_get_string(&dlen);
-                               debug("skey response == '%s'", response);
-                               packet_integrity_check(plen, 4 + dlen, type);
-                               authenticated = (skey_haskey(pw->pw_name) == 0 &&
-                                                skey_passcheck(pw->pw_name, response) != -1);
-                               xfree(response);
-                       }
-                       break;
-#else
-               case SSH_CMSG_AUTH_TIS:
-                       /* TIS Authentication is unsupported */
-                       log("TIS authentication unsupported.");
-                       break;
-#endif
-
-               default:
-                       /*
-                        * Any unknown messages will be ignored (and failure
-                        * returned) during authentication.
-                        */
-                       log("Unknown message during authentication: type %d", type);
-                       break;
-               }
-
-               /*
-                * Check if the user is logging in as root and root logins
-                * are disallowed.
-                * Note that root login is allowed for forced commands.
-                */
-               if (authenticated && pw->pw_uid == 0 && !options.permit_root_login) {
-                       if (forced_command) {
-                               log("Root login accepted for forced command.");
-                       } else {
-                               authenticated = 0;
-                               log("ROOT LOGIN REFUSED FROM %.200s",
-                                   get_canonical_hostname());
-                       }
-               }
-
-               /* Raise logging level */
-               if (authenticated ||
-                   attempt == AUTH_FAIL_LOG ||
-                   type == SSH_CMSG_AUTH_PASSWORD)
-                       authlog = log;
-
-               authlog("%s %s for %.200s from %.200s port %d%s",
-                       authenticated ? "Accepted" : "Failed",
-                       get_authname(type),
-                       pw->pw_uid == 0 ? "ROOT" : pw->pw_name,
-                       get_remote_ipaddr(),
-                       get_remote_port(),
-                       user);
-
-#ifdef USE_PAM
-               if (authenticated) {
-                       if (!do_pam_account(pw->pw_name, client_user)) {
-                               if (client_user != NULL) {
-                                       xfree(client_user);
-                                       client_user = NULL;
-                               }
-                               do_fake_authloop(pw->pw_name);
-                       }
-                       return;
-               }
-#else /* USE_PAM */
-               if (authenticated) {
-                       return;
-               }
-#endif /* USE_PAM */
-
-               if (client_user != NULL) {
-                       xfree(client_user);
-                       client_user = NULL;
-               }
-
-               if (attempt > AUTH_FAIL_MAX)
-                       packet_disconnect(AUTH_FAIL_MSG, pw->pw_name);
-
-               /* Send a message indicating that the authentication attempt failed. */
-               packet_start(SSH_SMSG_FAILURE);
-               packet_send();
-               packet_write_wait();
-       }
-}
-
-/*
- * The user does not exist or access is denied,
- * but fake indication that authentication is needed.
- */
-void
-do_fake_authloop(char *user)
-{
-       int attempt = 0;
-
-       log("Faking authloop for illegal user %.200s from %.200s port %d",
-           user,
-           get_remote_ipaddr(),
-           get_remote_port());
-
-       /* Indicate that authentication is needed. */
-       packet_start(SSH_SMSG_FAILURE);
-       packet_send();
-       packet_write_wait();
-
-       /*
-        * Keep reading packets, and always respond with a failure.  This is
-        * to avoid disclosing whether such a user really exists.
-        */
-       for (attempt = 1;; attempt++) {
-               /* Read a packet.  This will not return if the client disconnects. */
-               int plen;
-#ifndef SKEY
-               (void)packet_read(&plen);
-#else /* SKEY */
-               int type = packet_read(&plen);
-               unsigned int dlen;
-               char *password, *skeyinfo;
-               /* Try to send a fake s/key challenge. */
-               if (options.skey_authentication == 1 &&
-                   (skeyinfo = skey_fake_keyinfo(user)) != NULL) {
-                       password = NULL;
-                       if (type == SSH_CMSG_AUTH_TIS) {
-                               packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
-                               packet_put_string(skeyinfo, strlen(skeyinfo));
-                               packet_send();
-                               packet_write_wait();
-                               continue;
-                       } else if (type == SSH_CMSG_AUTH_PASSWORD &&
-                                  options.password_authentication &&
-                                  (password = packet_get_string(&dlen)) != NULL &&
-                                  dlen == 5 &&
-                                  strncasecmp(password, "s/key", 5) == 0 ) {
-                               packet_send_debug(skeyinfo);
-                       }
-                       if (password != NULL)
-                               xfree(password);
-               }
-#endif
-               if (attempt > AUTH_FAIL_MAX)
-                       packet_disconnect(AUTH_FAIL_MSG, user);
-
-               /*
-                * Send failure.  This should be indistinguishable from a
-                * failed authentication.
-                */
-               packet_start(SSH_SMSG_FAILURE);
-               packet_send();
-               packet_write_wait();
-#ifdef WITH_AIXAUTHENTICATE 
-               if (strncmp(get_authname(type),"password",
-                   strlen(get_authname(type))) == 0)
-                       loginfailed(pw->pw_name,get_canonical_hostname(),"ssh");
-#endif /* WITH_AIXAUTHENTICATE */
-       }
-       /* NOTREACHED */
-       abort();
-}
-
-struct pty_cleanup_context {
-       const char *ttyname;
-       int pid;
-};
-
-/*
- * Function to perform cleanup if we get aborted abnormally (e.g., due to a
- * dropped connection).
- */
-void 
-pty_cleanup_proc(void *context)
-{
-       struct pty_cleanup_context *cu = context;
-
-       debug("pty_cleanup_proc called");
-
-       /* Record that the user has logged out. */
-       record_logout(cu->pid, cu->ttyname);
-
-       /* Release the pseudo-tty. */
-       pty_release(cu->ttyname);
-}
-
-/* simple cleanup: chown tty slave back to root */
-static void
-pty_release_proc(void *tty)
-{
-       char *ttyname = tty;
-       pty_release(ttyname);
-}
-
-/*
- * Prepares for an interactive session.  This is called after the user has
- * been successfully authenticated.  During this message exchange, pseudo
- * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
- * are requested, etc.
- */
-void 
-do_authenticated(struct passwd * pw)
-{
-       int type;
-       int compression_level = 0, enable_compression_after_reply = 0;
-       int have_pty = 0, ptyfd = -1, ttyfd = -1;
-       int row, col, xpixel, ypixel, screen;
-       char ttyname[64];
-       char *command, *term = NULL, *display = NULL, *proto = NULL, *data = NULL;
-       int plen;
-       unsigned int dlen;
-       int n_bytes;
-
-       /*
-        * Cancel the alarm we set to limit the time taken for
-        * authentication.
-        */
-       alarm(0);
-
-       /*
-        * Inform the channel mechanism that we are the server side and that
-        * the client may request to connect to any port at all. (The user
-        * could do it anyway, and we wouldn\'t know what is permitted except
-        * by the client telling us, so we can equally well trust the client
-        * not to request anything bogus.)
-        */
-       if (!no_port_forwarding_flag)
-               channel_permit_all_opens();
-
-       /*
-        * We stay in this loop until the client requests to execute a shell
-        * or a command.
-        */
-       while (1) {
-
-               /* Get a packet from the client. */
-               type = packet_read(&plen);
-
-               /* Process the packet. */
-               switch (type) {
-               case SSH_CMSG_REQUEST_COMPRESSION:
-                       packet_integrity_check(plen, 4, type);
-                       compression_level = packet_get_int();
-                       if (compression_level < 1 || compression_level > 9) {
-                               packet_send_debug("Received illegal compression level %d.",
-                                                 compression_level);
-                               goto fail;
-                       }
-                       /* Enable compression after we have responded with SUCCESS. */
-                       enable_compression_after_reply = 1;
-                       break;
-
-               case SSH_CMSG_REQUEST_PTY:
-                       if (no_pty_flag) {
-                               debug("Allocating a pty not permitted for this authentication.");
-                               goto fail;
-                       }
-                       if (have_pty)
-                               packet_disconnect("Protocol error: you already have a pty.");
-
-                       debug("Allocating pty.");
-
-                       /* Allocate a pty and open it. */
-                       if (!pty_allocate(&ptyfd, &ttyfd, ttyname,
-                           sizeof(ttyname))) {
-                               error("Failed to allocate pty.");
-                               goto fail;
-                       }
-                       fatal_add_cleanup(pty_release_proc, (void *)ttyname);
-                       pty_setowner(pw, ttyname);
-
-                       /* Get TERM from the packet.  Note that the value may be of arbitrary length. */
-                       term = packet_get_string(&dlen);
-                       packet_integrity_check(dlen, strlen(term), type);
-
-                       /* Remaining bytes */
-                       n_bytes = plen - (4 + dlen + 4 * 4);
-
-                       if (strcmp(term, "") == 0) {
-                               xfree(term);
-                               term = NULL;
-                       }
-
-                       /* Get window size from the packet. */
-                       row = packet_get_int();
-                       col = packet_get_int();
-                       xpixel = packet_get_int();
-                       ypixel = packet_get_int();
-                       pty_change_window_size(ptyfd, row, col, xpixel, ypixel);
-
-                       /* Get tty modes from the packet. */
-                       tty_parse_modes(ttyfd, &n_bytes);
-                       packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type);
-
-                       /* Indicate that we now have a pty. */
-                       have_pty = 1;
-                       break;
-
-               case SSH_CMSG_X11_REQUEST_FORWARDING:
-                       if (!options.x11_forwarding) {
-                               packet_send_debug("X11 forwarding disabled in server configuration file.");
-                               goto fail;
-                       }
-#ifdef XAUTH_PATH
-                       if (no_x11_forwarding_flag) {
-                               packet_send_debug("X11 forwarding not permitted for this authentication.");
-                               goto fail;
-                       }
-                       debug("Received request for X11 forwarding with auth spoofing.");
-                       if (display)
-                               packet_disconnect("Protocol error: X11 display already set.");
-                       {
-                               unsigned int proto_len, data_len;
-                               proto = packet_get_string(&proto_len);
-                               data = packet_get_string(&data_len);
-                               packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type);
-                       }
-                       if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER)
-                               screen = packet_get_int();
-                       else
-                               screen = 0;
-                       display = x11_create_display_inet(screen, options.x11_display_offset);
-                       if (!display)
-                               goto fail;
-
-                       /* Setup to always have a local .Xauthority. */
-                       xauthfile = xmalloc(MAXPATHLEN);
-                       strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
-                       temporarily_use_uid(pw->pw_uid);
-                       if (mkdtemp(xauthfile) == NULL) {
-                               restore_uid();
-                               error("private X11 dir: mkdtemp %s failed: %s",
-                                   xauthfile, strerror(errno));
-                               xfree(xauthfile);
-                               xauthfile = NULL;
-                               goto fail;
-                       }
-                       strlcat(xauthfile, "/cookies", MAXPATHLEN);
-                       open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
-                       restore_uid();
-                       fatal_add_cleanup(xauthfile_cleanup_proc, NULL);
-                       break;
-#else /* XAUTH_PATH */
-                       packet_send_debug("No xauth program; cannot forward with spoofing.");
-                       goto fail;
-#endif /* XAUTH_PATH */
-
-               case SSH_CMSG_AGENT_REQUEST_FORWARDING:
-                       if (no_agent_forwarding_flag || compat13) {
-                               debug("Authentication agent forwarding not permitted for this authentication.");
-                               goto fail;
-                       }
-                       debug("Received authentication agent forwarding request.");
-                       auth_input_request_forwarding(pw);
-                       break;
-
-               case SSH_CMSG_PORT_FORWARD_REQUEST:
-                       if (no_port_forwarding_flag) {
-                               debug("Port forwarding not permitted for this authentication.");
-                               goto fail;
-                       }
-                       debug("Received TCP/IP port forwarding request.");
-                       channel_input_port_forward_request(pw->pw_uid == 0);
-                       break;
-
-               case SSH_CMSG_MAX_PACKET_SIZE:
-                       if (packet_set_maxsize(packet_get_int()) < 0)
-                               goto fail;
-                       break;
-
-               case SSH_CMSG_EXEC_SHELL:
-                       /* Set interactive/non-interactive mode. */
-                       packet_set_interactive(have_pty || display != NULL,
-                                              options.keepalives);
-
-                       if (forced_command != NULL)
-                               goto do_forced_command;
-                       debug("Forking shell.");
-                       packet_integrity_check(plen, 0, type);
-                       if (have_pty)
-                               do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto, data);
-                       else
-                               do_exec_no_pty(NULL, pw, display, proto, data);
-                       return;
-
-               case SSH_CMSG_EXEC_CMD:
-                       /* Set interactive/non-interactive mode. */
-                       packet_set_interactive(have_pty || display != NULL,
-                                              options.keepalives);
-
-                       if (forced_command != NULL)
-                               goto do_forced_command;
-                       /* Get command from the packet. */
-                       {
-                               unsigned int dlen;
-                               command = packet_get_string(&dlen);
-                               debug("Executing command '%.500s'", command);
-                               packet_integrity_check(plen, 4 + dlen, type);
-                       }
-                       if (have_pty)
-                               do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display, proto, data);
-                       else
-                               do_exec_no_pty(command, pw, display, proto, data);
-                       xfree(command);
-                       return;
-
-               default:
-                       /*
-                        * Any unknown messages in this phase are ignored,
-                        * and a failure message is returned.
-                        */
-                       log("Unknown packet type received after authentication: %d", type);
-                       goto fail;
-               }
-
-               /* The request was successfully processed. */
-               packet_start(SSH_SMSG_SUCCESS);
-               packet_send();
-               packet_write_wait();
-
-               /* Enable compression now that we have replied if appropriate. */
-               if (enable_compression_after_reply) {
-                       enable_compression_after_reply = 0;
-                       packet_start_compression(compression_level);
-               }
-               continue;
-
-fail:
-               /* The request failed. */
-               packet_start(SSH_SMSG_FAILURE);
-               packet_send();
-               packet_write_wait();
-               continue;
-
-do_forced_command:
-               /*
-                * There is a forced command specified for this login.
-                * Execute it.
-                */
-               debug("Executing forced command: %.900s", forced_command);
-               if (have_pty)
-                       do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display, proto, data);
-               else
-                       do_exec_no_pty(forced_command, pw, display, proto, data);
-               return;
-       }
-}
-
-/*
- * This is called to fork and execute a command when we have no tty.  This
- * will call do_child from the child, and server_loop from the parent after
- * setting up file descriptors and such.
- */
-void 
-do_exec_no_pty(const char *command, struct passwd * pw,
-              const char *display, const char *auth_proto,
-              const char *auth_data)
-{
-       int pid;
-
-#ifdef USE_PIPES
-       int pin[2], pout[2], perr[2];
-       /* Allocate pipes for communicating with the program. */
-       if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
-               packet_disconnect("Could not create pipes: %.100s",
-                                 strerror(errno));
-#else /* USE_PIPES */
-       int inout[2], err[2];
-       /* Uses socket pairs to communicate with the program. */
-       if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
-           socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
-               packet_disconnect("Could not create socket pairs: %.100s",
-                                 strerror(errno));
-#endif /* USE_PIPES */
-
-       setproctitle("%s@notty", pw->pw_name);
-
-#ifdef USE_PAM
-                       do_pam_setcred();
-#endif /* USE_PAM */
-
-       /* Fork the child. */
-       if ((pid = fork()) == 0) {
-               /* Child.  Reinitialize the log since the pid has changed. */
-               log_init(av0, options.log_level, options.log_facility, log_stderr);
-
-               /*
-                * Create a new session and process group since the 4.4BSD
-                * setlogin() affects the entire process group.
-                */
-               if (setsid() < 0)
-                       error("setsid failed: %.100s", strerror(errno));
-
-#ifdef USE_PIPES
-               /*
-                * Redirect stdin.  We close the parent side of the socket
-                * pair, and make the child side the standard input.
-                */
-               close(pin[1]);
-               if (dup2(pin[0], 0) < 0)
-                       perror("dup2 stdin");
-               close(pin[0]);
-
-               /* Redirect stdout. */
-               close(pout[0]);
-               if (dup2(pout[1], 1) < 0)
-                       perror("dup2 stdout");
-               close(pout[1]);
-
-               /* Redirect stderr. */
-               close(perr[0]);
-               if (dup2(perr[1], 2) < 0)
-                       perror("dup2 stderr");
-               close(perr[1]);
-#else /* USE_PIPES */
-               /*
-                * Redirect stdin, stdout, and stderr.  Stdin and stdout will
-                * use the same socket, as some programs (particularly rdist)
-                * seem to depend on it.
-                */
-               close(inout[1]);
-               close(err[1]);
-               if (dup2(inout[0], 0) < 0)      /* stdin */
-                       perror("dup2 stdin");
-               if (dup2(inout[0], 1) < 0)      /* stdout.  Note: same socket as stdin. */
-                       perror("dup2 stdout");
-               if (dup2(err[0], 2) < 0)        /* stderr */
-                       perror("dup2 stderr");
-#endif /* USE_PIPES */
-
-               /* Do processing for the child (exec command etc). */
-               do_child(command, pw, NULL, display, auth_proto, auth_data, NULL);
-               /* NOTREACHED */
-       }
-       if (pid < 0)
-               packet_disconnect("fork failed: %.100s", strerror(errno));
-#ifdef USE_PIPES
-       /* We are the parent.  Close the child sides of the pipes. */
-       close(pin[0]);
-       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]. */
-#else /* USE_PIPES */
-       /* We are the parent.  Close the child sides of the socket pairs. */
-       close(inout[0]);
-       close(err[0]);
-
-       /*
-        * 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]. */
-#endif /* USE_PIPES */
-}
-
-/*
- * This is called to fork and execute a command when we have a tty.  This
- * will call do_child from the child, and server_loop from the parent after
- * setting up file descriptors, controlling tty, updating wtmp, utmp,
- * lastlog, and other such operations.
- */
-void 
-do_exec_pty(const char *command, int ptyfd, int ttyfd,
-           const char *ttyname, struct passwd * pw, const char *term,
-           const char *display, const char *auth_proto,
-           const char *auth_data)
-{
-       int pid, fdout;
-       int ptymaster;
-       const char *hostname;
-       time_t last_login_time;
-       char buf[100], *time_string;
-       FILE *f;
-       char line[256];
-       struct stat st;
-       int quiet_login;
-       struct sockaddr_storage from;
-       socklen_t fromlen;
-       struct pty_cleanup_context cleanup_context;
-
-       /* Get remote host name. */
-       hostname = get_canonical_hostname();
-
-       /*
-        * Get the time when the user last logged in.  Buf will be set to
-        * contain the hostname the last login was from.
-        */
-       if (!options.use_login) {
-               last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
-                                                     buf, sizeof(buf));
-       }
-       setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1);
-
-#ifdef USE_PAM
-                       do_pam_session(pw->pw_name, ttyname);
-                       do_pam_setcred();
-#endif /* USE_PAM */
-
-       /* Fork the child. */
-       if ((pid = fork()) == 0) {
-               pid = getpid();
-
-               /* Child.  Reinitialize the log because the pid has
-                  changed. */
-               log_init(av0, options.log_level, options.log_facility, log_stderr);
-
-               /* Close the master side of the pseudo tty. */
-               close(ptyfd);
-
-               /* Make the pseudo tty our controlling tty. */
-               pty_make_controlling_tty(&ttyfd, ttyname);
-
-               /* Redirect stdin from the pseudo tty. */
-               if (dup2(ttyfd, fileno(stdin)) < 0)
-                       error("dup2 stdin failed: %.100s", strerror(errno));
-
-               /* Redirect stdout to the pseudo tty. */
-               if (dup2(ttyfd, fileno(stdout)) < 0)
-                       error("dup2 stdin failed: %.100s", strerror(errno));
-
-               /* Redirect stderr to the pseudo tty. */
-               if (dup2(ttyfd, fileno(stderr)) < 0)
-                       error("dup2 stdin failed: %.100s", strerror(errno));
-
-               /* Close the extra descriptor for the pseudo tty. */
-               close(ttyfd);
-
-               /*
-                * Get IP address of client.  This is needed because we want
-                * to record where the user logged in from.  If the
-                * connection is not a socket, let the ip address be 0.0.0.0.
-                */
-               memset(&from, 0, sizeof(from));
-               if (packet_get_connection_in() == packet_get_connection_out()) {
-                       fromlen = sizeof(from);
-                       if (getpeername(packet_get_connection_in(),
-                            (struct sockaddr *) & from, &fromlen) < 0) {
-                               debug("getpeername: %.100s", strerror(errno));
-                               fatal_cleanup();
-                       }
-               }
-               /* Record that there was a login on that terminal. */
-               record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname,
-                            (struct sockaddr *)&from);
-
-               /* Check if .hushlogin exists. */
-               snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir);
-               quiet_login = stat(line, &st) >= 0;
-
-#ifdef USE_PAM
-               if (!quiet_login)
-                       print_pam_messages();
-#endif /* USE_PAM */
-
-               /*
-                * If the user has logged in before, display the time of last
-                * login. However, don't display anything extra if a command
-                * has been specified (so that ssh can be used to execute
-                * commands on a remote machine without users knowing they
-                * are going to another machine). Login(1) will do this for
-                * us as well, so check if login(1) is used
-                */
-               if (command == NULL && last_login_time != 0 && !quiet_login &&
-                   !options.use_login) {
-                       /* Convert the date to a string. */
-                       time_string = ctime(&last_login_time);
-                       /* Remove the trailing newline. */
-                       if (strchr(time_string, '\n'))
-                               *strchr(time_string, '\n') = 0;
-                       /* Display the last login time.  Host if displayed
-                          if known. */
-                       if (strcmp(buf, "") == 0)
-                               printf("Last login: %s\r\n", time_string);
-                       else
-                               printf("Last login: %s from %s\r\n", time_string, buf);
-               }
-               /*
-                * Print /etc/motd unless a command was specified or printing
-                * it was disabled in server options or login(1) will be
-                * used.  Note that some machines appear to print it in
-                * /etc/profile or similar.
-                */
-               if (command == NULL && options.print_motd && !quiet_login &&
-                   !options.use_login) {
-                       /* Print /etc/motd if it exists. */
-                       f = fopen("/etc/motd", "r");
-                       if (f) {
-                               while (fgets(line, sizeof(line), f))
-                                       fputs(line, stdout);
-                               fclose(f);
-                       }
-               }
-               /* Do common processing for the child, such as execing the command. */
-               do_child(command, pw, term, display, auth_proto, auth_data, ttyname);
-               /* NOTREACHED */
-       }
-       if (pid < 0)
-               packet_disconnect("fork failed: %.100s", strerror(errno));
-       /* Parent.  Close the slave side of the pseudo tty. */
-       close(ttyfd);
-
-       /*
-        * Add a cleanup function to clear the utmp entry and record logout
-        * time in case we call fatal() (e.g., the connection gets closed).
-        */
-       cleanup_context.pid = pid;
-       cleanup_context.ttyname = ttyname;
-       fatal_add_cleanup(pty_cleanup_proc, (void *) &cleanup_context);
-       fatal_remove_cleanup(pty_release_proc, (void *) ttyname);
-
-       /*
-        * Create another descriptor of the pty master side for use as the
-        * standard input.  We could use the original descriptor, but this
-        * simplifies code in server_loop.  The descriptor is bidirectional.
-        */
-       fdout = dup(ptyfd);
-       if (fdout < 0)
-               packet_disconnect("dup #1 failed: %.100s", strerror(errno));
-
-       /* we keep a reference to the pty master */
-       ptymaster = dup(ptyfd);
-       if (ptymaster < 0)
-               packet_disconnect("dup #2 failed: %.100s", strerror(errno));
-
-       /* Enter interactive session. */
-       server_loop(pid, ptyfd, fdout, -1);
-       /* server_loop _has_ closed ptyfd and fdout. */
-
-       /* Cancel the cleanup function. */
-       fatal_remove_cleanup(pty_cleanup_proc, (void *) &cleanup_context);
-
-       /* Record that the user has logged out. */
-       record_logout(pid, ttyname);
-
-       /* Release the pseudo-tty. */
-       pty_release(ttyname);
-
-       /*
-        * Close the server side of the socket pairs.  We must do this after
-        * the pty cleanup, so that another process doesn't get this pty
-        * while we're still cleaning up.
-        */
-       if (close(ptymaster) < 0)
-               error("close(ptymaster): %s", strerror(errno));
-}
-
-/*
- * Sets the value of the given variable in the environment.  If the variable
- * already exists, its value is overriden.
- */
-void 
-child_set_env(char ***envp, unsigned int *envsizep, const char *name,
-             const char *value)
-{
-       unsigned int i, namelen;
-       char **env;
-
-       /*
-        * Find the slot where the value should be stored.  If the variable
-        * already exists, we reuse the slot; otherwise we append a new slot
-        * at the end of the array, expanding if necessary.
-        */
-       env = *envp;
-       namelen = strlen(name);
-       for (i = 0; env[i]; i++)
-               if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
-                       break;
-       if (env[i]) {
-               /* Reuse the slot. */
-               xfree(env[i]);
-       } else {
-               /* New variable.  Expand if necessary. */
-               if (i >= (*envsizep) - 1) {
-                       (*envsizep) += 50;
-                       env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
-               }
-               /* Need to set the NULL pointer at end of array beyond the new slot. */
-               env[i + 1] = NULL;
-       }
-
-       /* Allocate space and format the variable in the appropriate slot. */
-       env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
-       snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
-}
-
-/*
- * Reads environment variables from the given file and adds/overrides them
- * into the environment.  If the file does not exist, this does nothing.
- * Otherwise, it must consist of empty lines, comments (line starts with '#')
- * and assignments of the form name=value.  No other forms are allowed.
- */
-void 
-read_environment_file(char ***env, unsigned int *envsize,
-                     const char *filename)
-{
-       FILE *f;
-       char buf[4096];
-       char *cp, *value;
-
-       f = fopen(filename, "r");
-       if (!f)
-               return;
-
-       while (fgets(buf, sizeof(buf), f)) {
-               for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
-                       ;
-               if (!*cp || *cp == '#' || *cp == '\n')
-                       continue;
-               if (strchr(cp, '\n'))
-                       *strchr(cp, '\n') = '\0';
-               value = strchr(cp, '=');
-               if (value == NULL) {
-                       fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf);
-                       continue;
-               }
-               /* Replace the equals sign by nul, and advance value to the value string. */
-               *value = '\0';
-               value++;
-               child_set_env(env, envsize, cp, value);
-       }
-       fclose(f);
-}
-
-#ifdef USE_PAM
-/*
- * Sets any environment variables which have been specified by PAM
- */
-void do_pam_environment(char ***env, int *envsize)
-{
-       char *equals, var_name[512], var_val[512];
-       char **pam_env;
-       int i;
-
-       if ((pam_env = fetch_pam_environment()) == NULL)
-               return;
-       
-       for(i = 0; pam_env[i] != NULL; i++) {
-               if ((equals = strstr(pam_env[i], "=")) == NULL)
-                       continue;
-                       
-               if (strlen(pam_env[i]) < (sizeof(var_name) - 1))
-               {
-                       memset(var_name, '\0', sizeof(var_name));
-                       memset(var_val, '\0', sizeof(var_val));
-
-                       strncpy(var_name, pam_env[i], equals - pam_env[i]);
-                       strcpy(var_val, equals + 1);
-
-                       debug("PAM environment: %s=%s", var_name, var_val);
-
-                       child_set_env(env, envsize, var_name, var_val);
-               }
-       }
-}
-#endif /* USE_PAM */
-
-/*
- * Performs common processing for the child, such as setting up the
- * environment, closing extra file descriptors, setting the user and group
- * ids, and executing the command or shell.
- */
-void 
-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, *cp = NULL;
-       char buf[256];
-       FILE *f;
-       unsigned int envsize, i;
-       char **env;
-       extern char **environ;
-       struct stat st;
-       char *argv[10];
-
-#ifndef USE_PAM /* pam_nologin handles this */
-       /* Check /etc/nologin. */
-       f = fopen("/etc/nologin", "r");
-       if (f) {
-               /* /etc/nologin exists.  Print its contents and exit. */
-               while (fgets(buf, sizeof(buf), f))
-                       fputs(buf, stderr);
-               fclose(f);
-               if (pw->pw_uid != 0)
-                       exit(254);
-       }
-#endif /* USE_PAM */
-
-       /* Set login name in the kernel. */
-       if (setlogin(pw->pw_name) < 0)
-               error("setlogin failed: %s", strerror(errno));
-
-       /* Set uid, gid, and groups. */
-       /* Login(1) does this as well, and it needs uid 0 for the "-h"
-          switch, so we let login(1) to this for us. */
-       if (!options.use_login) {
-               if (getuid() == 0 || geteuid() == 0) {
-                       if (setgid(pw->pw_gid) < 0) {
-                               perror("setgid");
-                               exit(1);
-                       }
-                       /* Initialize the group list. */
-                       if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
-                               perror("initgroups");
-                               exit(1);
-                       }
-                       endgrent();
-
-                       /* Permanently switch to the desired uid. */
-                       permanently_set_uid(pw->pw_uid);
-               }
-               if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
-                       fatal("Failed to set uids to %d.", (int) pw->pw_uid);
-       }
-       /*
-        * Get the shell from the password data.  An empty shell field is
-        * legal, and means /bin/sh.
-        */
-       shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
-
-#ifdef AFS
-       /* Try to get AFS tokens for the local cell. */
-       if (k_hasafs()) {
-               char cell[64];
-
-               if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
-                       krb_afslog(cell, 0);
-
-               krb_afslog(0, 0);
-       }
-#endif /* AFS */
-
-       /* Initialize the environment. */
-       envsize = 100;
-       env = xmalloc(envsize * sizeof(char *));
-       env[0] = NULL;
-
-       if (!options.use_login) {
-               /* Set basic environment. */
-               child_set_env(&env, &envsize, "USER", pw->pw_name);
-               child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
-               child_set_env(&env, &envsize, "HOME", pw->pw_dir);
-               child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
-
-               snprintf(buf, sizeof buf, "%.200s/%.50s",
-                        _PATH_MAILDIR, pw->pw_name);
-               child_set_env(&env, &envsize, "MAIL", buf);
-
-               /* Normal systems set SHELL by default. */
-               child_set_env(&env, &envsize, "SHELL", shell);
-       }
-       if (getenv("TZ"))
-               child_set_env(&env, &envsize, "TZ", getenv("TZ"));
-
-       /* Set custom environment options from RSA authentication. */
-       while (custom_environment) {
-               struct envstring *ce = custom_environment;
-               char *s = ce->s;
-               int i;
-               for (i = 0; s[i] != '=' && s[i]; i++);
-               if (s[i] == '=') {
-                       s[i] = 0;
-                       child_set_env(&env, &envsize, s, s + i + 1);
-               }
-               custom_environment = ce->next;
-               xfree(ce->s);
-               xfree(ce);
-       }
-
-       snprintf(buf, sizeof buf, "%.50s %d %d",
-                get_remote_ipaddr(), get_remote_port(), get_local_port());
-       child_set_env(&env, &envsize, "SSH_CLIENT", buf);
-
-       if (ttyname)
-               child_set_env(&env, &envsize, "SSH_TTY", ttyname);
-       if (term)
-               child_set_env(&env, &envsize, "TERM", term);
-       if (display)
-               child_set_env(&env, &envsize, "DISPLAY", display);
-
-#ifdef _AIX
-       {
-           char *authstate,*krb5cc;
-
-          if ((authstate = getenv("AUTHSTATE")) != NULL)
-                child_set_env(&env,&envsize,"AUTHSTATE",authstate);
-
-          if ((krb5cc = getenv("KRB5CCNAME")) != NULL)
-                child_set_env(&env,&envsize,"KRB5CCNAME",krb5cc);
-       }
-#endif
-
-#ifdef KRB4
-       {
-               extern char *ticket;
-
-               if (ticket)
-                       child_set_env(&env, &envsize, "KRBTKFILE", ticket);
-       }
-#endif /* KRB4 */
-
-#ifdef USE_PAM
-       /* Pull in any environment variables that may have been set by PAM. */
-       do_pam_environment(&env, &envsize);
-#endif /* USE_PAM */
-
-       if (xauthfile)
-               child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
-
-       if (auth_get_socket_name() != NULL)
-               child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
-                             auth_get_socket_name());
-
-       read_environment_file(&env,&envsize,"/etc/environment");
-
-       /* read $HOME/.ssh/environment. */
-       if (!options.use_login) {
-               snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir);
-               read_environment_file(&env, &envsize, buf);
-       }
-       if (debug_flag) {
-               /* dump the environment */
-               fprintf(stderr, "Environment:\n");
-               for (i = 0; env[i]; i++)
-                       fprintf(stderr, "  %.200s\n", env[i]);
-       }
-       /*
-        * Close the connection descriptors; note that this is the child, and
-        * the server will still have the socket open, and it is important
-        * that we do not shutdown it.  Note that the descriptors cannot be
-        * closed before building the environment, as we call
-        * get_remote_ipaddr there.
-        */
-       if (packet_get_connection_in() == packet_get_connection_out())
-               close(packet_get_connection_in());
-       else {
-               close(packet_get_connection_in());
-               close(packet_get_connection_out());
-       }
-       /*
-        * Close all descriptors related to channels.  They will still remain
-        * open in the parent.
-        */
-       /* XXX better use close-on-exec? -markus */
-       channel_close_all();
-
-       /*
-        * Close any extra file descriptors.  Note that there may still be
-        * descriptors left by system functions.  They will be closed later.
-        */
-       endpwent();
-
-       /*
-        * Close any extra open file descriptors so that we don\'t have them
-        * hanging around in clients.  Note that we want to do this after
-        * initgroups, because at least on Solaris 2.3 it leaves file
-        * descriptors open.
-        */
-       for (i = 3; i < 64; i++)
-               close(i);
-
-       /* Change current directory to the user\'s home directory. */
-       if (chdir(pw->pw_dir) < 0)
-               fprintf(stderr, "Could not chdir to home directory %s: %s\n",
-                       pw->pw_dir, strerror(errno));
-
-       /*
-        * Must take new environment into use so that .ssh/rc, /etc/sshrc and
-        * xauth are run in the proper environment.
-        */
-       environ = env;
-
-       /*
-        * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
-        * in this order).
-        */
-       if (!options.use_login) {
-               if (stat(SSH_USER_RC, &st) >= 0) {
-                       if (debug_flag)
-                               fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC);
-
-                       f = popen("/bin/sh " SSH_USER_RC, "w");
-                       if (f) {
-                               if (auth_proto != NULL && auth_data != NULL)
-                                       fprintf(f, "%s %s\n", auth_proto, auth_data);
-                               pclose(f);
-                       } else
-                               fprintf(stderr, "Could not run %s\n", SSH_USER_RC);
-               } else if (stat(SSH_SYSTEM_RC, &st) >= 0) {
-                       if (debug_flag)
-                               fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC);
-
-                       f = popen("/bin/sh " SSH_SYSTEM_RC, "w");
-                       if (f) {
-                               if (auth_proto != NULL && auth_data != NULL)
-                                       fprintf(f, "%s %s\n", auth_proto, auth_data);
-                               pclose(f);
-                       } else
-                               fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC);
-               }
-#ifdef XAUTH_PATH
-               else {
-                       /* Add authority data to .Xauthority if appropriate. */
-                       if (auth_proto != NULL && auth_data != NULL) {
-                               if (debug_flag)
-                                       fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n",
-                                               XAUTH_PATH, display, auth_proto, auth_data);
-
-                               f = popen(XAUTH_PATH " -q -", "w");
-                               if (f) {
-                                       fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data);
-                                       pclose(f);
-                               } else
-                                       fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH);
-                       }
-               }
-#endif /* XAUTH_PATH */
-
-               /* Get the last component of the shell name. */
-               cp = strrchr(shell, '/');
-               if (cp)
-                       cp++;
-               else
-                       cp = shell;
-       }
-       /*
-        * If we have no command, execute the shell.  In this case, the shell
-        * name to be passed in argv[0] is preceded by '-' to indicate that
-        * this is a login shell.
-        */
-       if (!command) {
-               if (!options.use_login) {
-                       char buf[256];
-
-                       /*
-                        * Check for mail if we have a tty and it was enabled
-                        * in server options.
-                        */
-                       if (ttyname && options.check_mail) {
-                               char *mailbox;
-                               struct stat mailstat;
-                               mailbox = getenv("MAIL");
-                               if (mailbox != NULL) {
-                                       if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0)
-                                               printf("No mail.\n");
-                                       else if (mailstat.st_mtime < mailstat.st_atime)
-                                               printf("You have mail.\n");
-                                       else
-                                               printf("You have new mail.\n");
-                               }
-                       }
-                       /* Start the shell.  Set initial character to '-'. */
-                       buf[0] = '-';
-                       strncpy(buf + 1, cp, sizeof(buf) - 1);
-                       buf[sizeof(buf) - 1] = 0;
-
-                       /* Execute the shell. */
-                       argv[0] = buf;
-                       argv[1] = NULL;
-                       execve(shell, argv, env);
-
-                       /* Executing the shell failed. */
-                       perror(shell);
-                       exit(1);
-
-               } else {
-                       /* Launch login(1). */
-
-                       execl(LOGIN_PROGRAM, "login", "-h", get_remote_ipaddr(),
-                             "-p", "-f", "--", pw->pw_name, NULL);
-
-                       /* Login couldn't be executed, die. */
-
-                       perror("login");
-                       exit(1);
-               }
-       }
-       /*
-        * Execute the command using the user's shell.  This uses the -c
-        * option to execute the command.
-        */
-       argv[0] = (char *) cp;
-       argv[1] = "-c";
-       argv[2] = (char *) command;
-       argv[3] = NULL;
-       execve(shell, argv, env);
-       perror(shell);
-       exit(1);
-}
This page took 0.321262 seconds and 5 git commands to generate.