]> 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 
 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)
 
 
 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
 
 
 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
 
 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.
  *
  * Auxiliary functions for storing and retrieving various data types to/from
  * Buffers.
  *
+ * SSH2 packet format added by Markus Friedl
+ *
  */
 
 #include "includes.h"
  */
 
 #include "includes.h"
@@ -82,6 +84,50 @@ buffer_get_bignum(Buffer *buffer, BIGNUM *value)
        return 2 + bytes;
 }
 
        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).
  */
 /*
  * 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);
 }
        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).
 
 /*
  * 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);
  * 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);
 
 /* 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);
 
 /* 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);
 
 /* 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 */
 
 #endif                         /* BUFAUX_H */
index 16e3df3980ef3a64f6f18e7da858636e5bf0d3a5..0becd4ca0555e75315e929811c30475a326240ed 100644 (file)
@@ -37,6 +37,10 @@ RCSID("$Id$");
 /* Max len of agent socket */
 #define MAX_SOCKET_NAME 100
 
 /* 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.
 /*
  * 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 {
  * 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. */
 } ForwardPermission;
 
 /* List of all permitted host/port pairs to connect. */
@@ -119,20 +124,43 @@ channel_permit_all_opens()
        all_opens_permitted = 1;
 }
 
        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 
 /*
  * 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. */
 {
        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. */
        /* 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);
        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;
        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_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;
 }
        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 
 
 /* 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;
 {
        struct sockaddr addr;
-       int newsock, i, newch, len;
+       int newsock, newch;
        socklen_t addrlen;
        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;
                        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;
 /* 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++) {
 
        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) {
 
                /* 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 {
                                continue;
                } else {
-                       if (ch->type != SSH_CHANNEL_OPEN)
+                       if (c->type != SSH_CHANNEL_OPEN)
                                continue;
                                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. */
                                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()) {
                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. */
                                        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;
                        }
                                        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
                         */
                        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 
  */
 
 void 
-channel_input_data(int payload_len)
+channel_input_data(int type, int plen)
 {
        int id;
        char *data;
        unsigned int data_len;
 {
        int id;
        char *data;
        unsigned int data_len;
-       Channel *ch;
+       Channel *c;
 
        /* Get the channel number and verify it. */
        id = packet_get_int();
 
        /* 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);
                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) */
 
        /* 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 */
                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);
                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);
 }
 
        xfree(data);
 }
 
@@ -624,45 +780,60 @@ int
 channel_not_very_much_buffered_data()
 {
        unsigned int i;
 channel_not_very_much_buffered_data()
 {
        unsigned int i;
-       Channel *ch;
+       Channel *c;
 
        for (i = 0; i < channels_alloc; i++) {
 
        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;
                                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 0;
+                       }
                }
        }
        return 1;
 }
 
                }
        }
        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 
 
 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);
 
        /*
         * 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();
 
        /*
        packet_send();
 
        /*
@@ -672,81 +843,80 @@ channel_input_close()
         * no-one to receive the confirmation.  The channel gets freed when
         * the confirmation arrives.
         */
         * 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.
                 */
                /*
                 * 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 
 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 
 
 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. */
        /* 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 
 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. */
 
        /* 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:
                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->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:
                        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 */
                }
        }
                        /* NOTREACHED */
                }
        }
@@ -950,8 +1121,11 @@ channel_request_local_forwarding(u_short port, const char *host,
                        continue;
                }
                /* Allocate a channel number for the socket. */
                        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;
                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 
  */
 
 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");
 
 {
        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);
        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();
        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.
        /*
         * 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);
 }
 
        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;
        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;
 
        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));
        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) {
        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. */
                        continue;
                }
                /* Create the socket. */
@@ -1121,37 +1245,82 @@ channel_input_port_open(int payload_len)
 
        }
        freeaddrinfo(aitop);
 
        }
        freeaddrinfo(aitop);
-
        if (!ai) {
                error("connect %.100s port %d: failed.", host, host_port);      
        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 
  */
 
 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;
 {
        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.");
        }
 
        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");
 
        /* 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));
                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, 
        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 
 /* 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;
 
 {
        int remch, sock, newch;
        char *dummyname;
 
+       packet_integrity_check(plen, 4, type);
+
        /* Read the remote channel number from the message. */
        remch = packet_get_int();
 
        /* 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 */
 #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.
  */
 
 /*
  * 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 */
 
 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) */
        /* 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  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 */
        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;
 }       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
 #endif
index ea1a473dc78200339e833938d752b389d3d38b84..21d543d3626736d3a87cf3f679df2dc909989c9e 100644 (file)
--- a/cipher.c
+++ b/cipher.c
@@ -122,7 +122,12 @@ static char *cipher_names[] =
        "3des",
        "tss",
        "rc4",
        "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;
        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;
 }
 
        return mask;
 }
 
@@ -233,16 +242,84 @@ cipher_set_key(CipherContext *context, int cipher,
                break;
 
        case SSH_CIPHER_BLOWFISH:
                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;
 
                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));
 }
 
        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 
 /* Encrypts data using the cipher. */
 
 void 
@@ -272,6 +349,27 @@ cipher_encrypt(CipherContext *context, unsigned char *dest,
                swap_bytes(dest, dest, len);
                break;
 
                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));
        }
        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;
 
                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));
        }
        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>
 #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>
 #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
 #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_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;
 
 typedef struct {
        unsigned int type;
@@ -52,6 +63,11 @@ typedef struct {
                        struct bf_key_st key;
                        unsigned char iv[8];
                }       bf;
                        struct bf_key_st key;
                        unsigned char iv[8];
                }       bf;
+               struct {
+                       CAST_KEY key;
+                       unsigned char iv[8];
+               } cast;
+               RC4_KEY rc4;
        }       u;
 }       CipherContext;
 /*
        }       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(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,
 
 /*
  * 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 "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;
 
 /* 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
 
 /*
  * 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
 /*
  * 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.
  */
 
  * character for terminating or suspending the session.
  */
 
+void client_init_dispatch(void);
+
 int 
 client_loop(int have_pty, int escape_char_arg)
 {
 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);
 
        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);
        /* 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;
 }
        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,
                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;
                        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 */
                }
                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_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 (;;) {
        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,
                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;
                        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
                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;
                         * 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);
                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);
        }
        } else {
                vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
        }
-       if (log_on_stderr)
+       if (log_on_stderr) {
                fprintf(stderr, "%s\n", msgbuf);
                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],
 
 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);
 {
        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_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
 
 /*
  * 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;
        }
                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)
 }
 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;
        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);
                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;
                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);
                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;
                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);
                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));
 }
                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) {
 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_obuf_empty(Channel * c);
 
 void    chan_init_iostates(Channel * c);
+
+void   chan_delete_if_full_closed(Channel *c);
 #endif
 #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 "compress.h"
 #include "deattack.h"
+#include "channels.h"
 
 /*
  * This variable contains the file descriptors used for communicating with
 
 /*
  * 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);
 
-void   pty_setowner(struct passwd *pw, const char *ttyname);
-
 #endif                         /* PTY_H */
 #endif                         /* PTY_H */
index 4dfeece7e06dd527c53d758e88ba3dd56f33a5e5..c1aa3ceb8684a14dc658a805159d019fbc18926c 100644 (file)
@@ -19,6 +19,7 @@ RCSID("$Id$");
 #include "ssh.h"
 #include "cipher.h"
 #include "readconf.h"
 #include "ssh.h"
 #include "cipher.h"
 #include "readconf.h"
+#include "match.h"
 #include "xmalloc.h"
 
 /* Format of the configuration file:
 #include "xmalloc.h"
 
 /* Format of the configuration file:
index 2afca7637039345b82d766314378004442be0575..8bf448cebe9c50b395d46482bcd10428bbe4ed07 100644 (file)
 #include "buffer.h"
 #include "servconf.h"
 #include "pty.h"
 #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. */
 
 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(). */
 
 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)
 {
 void 
 sigchld_handler(int sig)
 {
@@ -67,104 +73,6 @@ sigchld_handler(int sig)
        errno = save_errno;
 }
 
        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.
 /*
  * 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
 #ifdef USE_PIPES
                        close(fdin);
 #else
-                       if (fdout == -1)
+                       if (fdin != fdout)
                                close(fdin);
                        else
                                shutdown(fdin, SHUT_WR); /* We will no longer send. */
                                close(fdin);
                        else
                                shutdown(fdin, SHUT_WR); /* We will no longer send. */
@@ -425,6 +333,12 @@ drain_output()
        packet_write_wait();
 }
 
        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
 /*
  * 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;
 
        if (fderr == -1)
                fderr_eof = 1;
 
+       server_init_dispatch();
+
        /* Main loop of the server for the interactive session mode. */
        for (;;) {
                fd_set readset, writeset;
        /* 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
 #ifdef USE_PIPES
                        close(fdin);
 #else
-                       if (fdout == -1)
+                       if (fdin != fdout)
                                close(fdin);
                        else
                                shutdown(fdin, SHUT_WR); /* We will no longer send. */
                                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())
                    (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;
                        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);
        }
 
                process_output(&writeset);
        }
 
-quit:
        /* Cleanup and termination code. */
 
        /* Wait until all output has been sent to the client. */
        /* 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 */
 }
        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 "authfd.h"
 #include "readconf.h"
 #include "uidswap.h"
+#include "channels.h"
 
 #ifdef HAVE___PROGNAME
 extern char *__progname;
 
 #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);
 
 /* 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*.
 /*
  * 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
 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
 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 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 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"
  */
 
 #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 "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 "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>
 
 #ifdef LIBWRAP
 #include <tcpd.h>
@@ -36,9 +54,6 @@ int deny_severity = LOG_WARNING;
 #define O_NOCTTY       0
 #endif
 
 #define O_NOCTTY       0
 #endif
 
-/* Local Xauthority file. */
-static char *xauthfile = NULL;
-
 /* Server configuration options. */
 ServerOptions options;
 
 /* 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;
  * 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
 
 /*
  * 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;
 
    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
 
 /*
  * Close all listening sockets
@@ -249,35 +218,6 @@ grace_alarm_handler(int sig)
        fatal("Timeout before authentication for %s.", get_remote_ipaddr());
 }
 
        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
 /*
  * 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;
 }
 
        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.
  */
 /*
  * 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;
        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;
        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;
        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 (!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
        /*
         * 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 */
        packet_set_nonblocking();
 
        /* perform the key exchange */
-       do_ssh_kex();
-
+       do_ssh1_kex();
        /* authenticate user and start session */
        do_authentication();
 
        /* authenticate user and start session */
        do_authentication();
 
@@ -895,10 +847,6 @@ main(int ac, char **av)
                (void) dest_tkt();
 #endif /* KRB4 */
 
                (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);
 
        /* 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
  * SSH1 key exchange
  */
 void
-do_ssh_kex()
+do_ssh1_kex()
 {
        int i, len;
        int plen, slen;
 {
        int i, len;
        int plen, slen;
@@ -1101,1586 +1049,3 @@ do_ssh_kex()
        packet_send();
        packet_write_wait();
 }
        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.600091 seconds and 5 git commands to generate.