summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2002-03-13 12:47:54 +1100
committerDamien Miller <djm@mindrot.org>2002-03-13 12:47:54 +1100
commit646e7cf3d7e7d4231c2d97d27c09fe5fe1d749e2 (patch)
treea693368c47d2d044514878fbb1516f87b487f78b
parent29bdd2c9bca2737e7a246ed50fd827a6ccba0c61 (diff)
Import of Niels Provos' 20020312 ssh-complete.diff
PAM, Cygwin and OSF SIA will not work for sure
-rw-r--r--Makefile.in6
-rw-r--r--auth.h4
-rw-r--r--auth1.c30
-rw-r--r--auth2.c119
-rw-r--r--bufaux.c2
-rw-r--r--cipher.c40
-rw-r--r--cipher.h2
-rw-r--r--compress.c4
-rw-r--r--kex.c4
-rw-r--r--kex.h1
-rw-r--r--kexdh.c13
-rw-r--r--kexgex.c19
-rw-r--r--key.c43
-rw-r--r--key.h1
-rw-r--r--monitor.c656
-rw-r--r--monitor.h57
-rw-r--r--monitor_fdpass.c89
-rw-r--r--monitor_fdpass.h32
-rw-r--r--monitor_mm.c329
-rw-r--r--monitor_mm.h64
-rw-r--r--monitor_wrap.c538
-rw-r--r--monitor_wrap.h99
-rw-r--r--packet.c106
-rw-r--r--packet.h7
-rw-r--r--servconf.c15
-rw-r--r--session.c53
-rw-r--r--session.h28
-rw-r--r--sshd.c173
28 files changed, 2425 insertions, 109 deletions
diff --git a/Makefile.in b/Makefile.in
index b58250aa..38c1d381 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,4 +1,4 @@
-# $Id: Makefile.in,v 1.197 2002/02/26 19:24:22 mouring Exp $
+# $Id: Makefile.in,v 1.198 2002/03/13 01:47:54 djm Exp $
prefix=@prefix@
exec_prefix=@exec_prefix@
@@ -50,11 +50,11 @@ INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@
TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} $(SFTP_PROGS)
-LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dh.o dispatch.o fatal.o mac.o hostfile.o key.o kex.o kexdh.o kexgex.o log.o match.o misc.o mpaux.o nchan.o packet.o radix.o rijndael.o entropy.o readpass.o rsa.o scard.o ssh-dss.o ssh-rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.o
+LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dh.o dispatch.o fatal.o mac.o hostfile.o key.o kex.o kexdh.o kexgex.o log.o match.o misc.o monitor_fdpass.c monitor_wrap.c mpaux.o nchan.o packet.o radix.o rijndael.o entropy.o readpass.o rsa.o scard.o ssh-dss.o ssh-rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.o
SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o sshtty.o readconf.o clientloop.o
-SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-chall.o auth2-chall.o auth-rhosts.o auth-options.o auth-krb4.o auth-pam.o auth2-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o auth-sia.o sshpty.o sshlogin.o loginrec.o servconf.o serverloop.o md5crypt.o session.o groupaccess.o auth-skey.o auth-bsdauth.o
+SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-chall.o auth2-chall.o auth-rhosts.o auth-options.o auth-krb4.o auth-pam.o auth2-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o auth-sia.o monitor.c monitor_mm.c sshpty.o sshlogin.o loginrec.o servconf.o serverloop.o md5crypt.o session.o groupaccess.o auth-skey.o auth-bsdauth.o
MANPAGES = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out
MANPAGES_IN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1
diff --git a/auth.h b/auth.h
index c7175405..9b5b19f6 100644
--- a/auth.h
+++ b/auth.h
@@ -121,8 +121,8 @@ void krb5_cleanup_proc(void *authctxt);
#include "auth-pam.h"
#include "auth2-pam.h"
-void do_authentication(void);
-void do_authentication2(void);
+Authctxt *do_authentication(void);
+Authctxt *do_authentication2(void);
Authctxt *authctxt_new(void);
void auth_log(Authctxt *, int, char *, char *);
diff --git a/auth1.c b/auth1.c
index c2d99895..c52f6389 100644
--- a/auth1.c
+++ b/auth1.c
@@ -26,8 +26,13 @@ RCSID("$OpenBSD: auth1.c,v 1.35 2002/02/03 17:53:25 markus Exp $");
#include "session.h"
#include "misc.h"
#include "uidswap.h"
+#include "monitor.h"
+#include "monitor_wrap.h"
/* import */
+extern int use_privsep;
+extern int mm_recvfd;
+
extern ServerOptions options;
/*
@@ -355,12 +360,13 @@ do_authloop(Authctxt *authctxt)
* Performs authentication of an incoming connection. Session key has already
* been exchanged and encryption is enabled.
*/
-void
+Authctxt *
do_authentication(void)
{
Authctxt *authctxt;
- struct passwd *pw;
+ struct passwd *pw = NULL, *pwent;
u_int ulen;
+ int allowed;
char *p, *user, *style = NULL;
/* Get the name of the user that we wish to log in as. */
@@ -382,17 +388,26 @@ do_authentication(void)
authctxt->style = style;
/* Verify that the user is a valid user. */
- pw = getpwnam(user);
- if (pw && allowed_user(pw)) {
+ if (!use_privsep) {
+ pwent = getpwnam(user);
+ allowed = pwent ? allowed_user(pwent) : 0;
+ } else
+ pwent = mm_getpwnamallow(mm_recvfd, user, &allowed);
+ if (pwent && allowed) {
authctxt->valid = 1;
- pw = pwcopy(pw);
+ pw = pwcopy(pwent);
} else {
debug("do_authentication: illegal user %s", user);
pw = NULL;
}
+ /* Free memory */
+ if (use_privsep)
+ pwfree(pwent);
+
authctxt->pw = pw;
- setproctitle("%s", pw ? user : "unknown");
+ setproctitle("%s%s", use_privsep ? " [net]" : "",
+ pw ? user : "unknown");
#ifdef USE_PAM
start_pam(pw == NULL ? "NOUSER" : user);
@@ -418,6 +433,5 @@ do_authentication(void)
packet_send();
packet_write_wait();
- /* Perform session preparation. */
- do_authenticated(authctxt);
+ return (authctxt);
}
diff --git a/auth2.c b/auth2.c
index f2a801ec..f661f8d7 100644
--- a/auth2.c
+++ b/auth2.c
@@ -51,8 +51,13 @@ RCSID("$OpenBSD: auth2.c,v 1.85 2002/02/24 19:14:59 markus Exp $");
#include "hostfile.h"
#include "canohost.h"
#include "match.h"
+#include "monitor.h"
+#include "monitor_wrap.h"
/* import */
+extern int use_privsep;
+extern int mm_recvfd;
+
extern ServerOptions options;
extern u_char *session_id2;
extern int session_id2_len;
@@ -75,8 +80,8 @@ static void input_userauth_request(int, u_int32_t, void *);
/* helper */
static Authmethod *authmethod_lookup(const char *);
static char *authmethods_get(void);
-static int user_key_allowed(struct passwd *, Key *);
-static int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
+int user_key_allowed(struct passwd *, Key *);
+int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
/* auth */
static void userauth_banner(void);
@@ -109,7 +114,7 @@ Authmethod authmethods[] = {
* loop until authctxt->success == TRUE
*/
-void
+Authctxt *
do_authentication2(void)
{
Authctxt *authctxt = authctxt_new();
@@ -125,7 +130,8 @@ do_authentication2(void)
dispatch_init(&dispatch_protocol_error);
dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
- do_authenticated(authctxt);
+
+ return(authctxt);
}
static void
@@ -182,10 +188,15 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
*style++ = 0;
if (authctxt->attempt++ == 0) {
- /* setup auth context */
+ /* setup auth context */
+ int allowed;
struct passwd *pw = NULL;
- pw = getpwnam(user);
- if (pw && allowed_user(pw) && strcmp(service, "ssh-connection")==0) {
+ if (!use_privsep) {
+ pw = getpwnam(user);
+ allowed = pw ? allowed_user(pw) : 0;
+ } else
+ pw = mm_getpwnamallow(mm_recvfd, user, &allowed);
+ if (pw && allowed && strcmp(service, "ssh-connection")==0) {
authctxt->pw = pwcopy(pw);
authctxt->valid = 1;
debug2("input_userauth_request: setting up authctxt for %s", user);
@@ -198,10 +209,18 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
start_pam("NOUSER");
#endif
}
- setproctitle("%s", pw ? user : "unknown");
+ /* Free memory */
+ if (use_privsep)
+ pwfree(pw);
+
+ setproctitle("%s%s", use_privsep ? " [net]" : "",
+ pw ? user : "unknown");
authctxt->user = xstrdup(user);
authctxt->service = xstrdup(service);
authctxt->style = style ? xstrdup(style) : NULL;
+
+ if (use_privsep)
+ mm_inform_authserv(mm_recvfd, service, style);
} else if (strcmp(user, authctxt->user) != 0 ||
strcmp(service, authctxt->service) != 0) {
packet_disconnect("Change of username or service not allowed: "
@@ -313,6 +332,8 @@ done:
static int
userauth_none(Authctxt *authctxt)
{
+ int res = 0;
+
/* disable method "none", only allowed one time */
Authmethod *m = authmethod_lookup("none");
if (m != NULL)
@@ -322,18 +343,16 @@ userauth_none(Authctxt *authctxt)
if (authctxt->valid == 0)
return(0);
-
-#ifdef HAVE_CYGWIN
- if (check_nt_auth(1, authctxt->pw) == 0)
- return(0);
+ if (!authctxt->valid)
+ return (0);
+ if (use_privsep)
+#if defined(USE_PAM) || defined(HAVE_OSF_SIA)
+#error NOT IMPLEMENTED FOR PRIVSEP
#endif
-#ifdef USE_PAM
- return auth_pam_password(authctxt->pw, "");
-#elif defined(HAVE_OSF_SIA)
- return 0;
-#else /* !HAVE_OSF_SIA && !USE_PAM */
- return auth_password(authctxt, "");
-#endif /* USE_PAM */
+ res = mm_auth_password(mm_recvfd, "");
+ else
+ res = auth_password(authctxt, "");
+ return (res);
}
static int
@@ -348,18 +367,16 @@ userauth_passwd(Authctxt *authctxt)
log("password change not supported");
password = packet_get_string(&len);
packet_check_eom();
- if (authctxt->valid &&
-#ifdef HAVE_CYGWIN
- check_nt_auth(1, authctxt->pw) &&
+
+#if defined(HAVE_CYGWIN) || defined(USE_PAM) || defined(HAVE_OSF_SIA)
+#error NOT IMPLEMENTED FOR PRIVSEP
#endif
-#ifdef USE_PAM
- auth_pam_password(authctxt->pw, password) == 1)
-#elif defined(HAVE_OSF_SIA)
- auth_sia_password(authctxt->user, password) == 1)
-#else /* !USE_PAM && !HAVE_OSF_SIA */
- auth_password(authctxt, password) == 1)
-#endif /* USE_PAM */
- authenticated = 1;
+ if (authctxt->valid) {
+ if (use_privsep)
+ authenticated = mm_auth_password(mm_recvfd, password);
+ else
+ authenticated = auth_password(authctxt, password);
+ }
memset(password, 0, len);
xfree(password);
return authenticated;
@@ -467,12 +484,23 @@ userauth_pubkey(Authctxt *authctxt)
buffer_dump(&b);
#endif
/* test for correct signature */
- if (user_key_allowed(authctxt->pw, key) &&
- key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
- authenticated = 1;
+ authenticated = 0;
+ if (use_privsep) {
+ if (mm_user_key_allowed(mm_recvfd, key) &&
+ mm_key_verify(mm_recvfd,
+ MM_USERKEY, NULL, NULL, key, sig, slen,
+ buffer_ptr(&b), buffer_len(&b)) == 1)
+ authenticated = 1;
+ } else {
+ if (user_key_allowed(authctxt->pw, key) &&
+ key_verify(key, sig, slen, buffer_ptr(&b),
+ buffer_len(&b)) == 1)
+ authenticated = 1;
+ }
buffer_clear(&b);
xfree(sig);
} else {
+ int res = 0;
debug("test whether pkalg/pkblob are acceptable");
packet_check_eom();
@@ -484,7 +512,11 @@ userauth_pubkey(Authctxt *authctxt)
* if a user is not allowed to login. is this an
* issue? -markus
*/
- if (user_key_allowed(authctxt->pw, key)) {
+ if (use_privsep)
+ res = mm_user_key_allowed(mm_recvfd, key);
+ else
+ res = user_key_allowed(authctxt->pw, key);
+ if (res) {
packet_start(SSH2_MSG_USERAUTH_PK_OK);
packet_put_string(pkalg, alen);
packet_put_string(pkblob, blen);
@@ -572,9 +604,18 @@ userauth_hostbased(Authctxt *authctxt)
buffer_dump(&b);
#endif
/* test for allowed key and correct signature */
- if (hostbased_key_allowed(authctxt->pw, cuser, chost, key) &&
- key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
- authenticated = 1;
+ authenticated = 0;
+ if (use_privsep) {
+ if (mm_hostbased_key_allowed(mm_recvfd, cuser, chost, key) &&
+ mm_key_verify(mm_recvfd, MM_HOSTKEY, cuser, chost, key,
+ sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
+ authenticated = 1;
+ } else {
+ if (hostbased_key_allowed(authctxt->pw, cuser, chost, key) &&
+ key_verify(key, sig, slen, buffer_ptr(&b),
+ buffer_len(&b)) == 1)
+ authenticated = 1;
+ }
buffer_clear(&b);
done:
@@ -730,7 +771,7 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file)
}
/* check whether given key is in .ssh/authorized_keys* */
-static int
+int
user_key_allowed(struct passwd *pw, Key *key)
{
int success;
@@ -750,7 +791,7 @@ user_key_allowed(struct passwd *pw, Key *key)
}
/* return 1 if given hostkey is allowed */
-static int
+int
hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost,
Key *key)
{
diff --git a/bufaux.c b/bufaux.c
index 23bc0c81..64b9a26e 100644
--- a/bufaux.c
+++ b/bufaux.c
@@ -221,6 +221,8 @@ buffer_put_string(Buffer *buffer, const void *buf, u_int len)
void
buffer_put_cstring(Buffer *buffer, const char *s)
{
+ if (s == NULL)
+ fatal("buffer_put_cstring: s == NULL");
buffer_put_string(buffer, s, strlen(s));
}
diff --git a/cipher.c b/cipher.c
index 9e8f42f5..5ddf819c 100644
--- a/cipher.c
+++ b/cipher.c
@@ -541,3 +541,43 @@ evp_rijndael(void)
#endif
return (&rijndal_cbc);
}
+
+/*
+ * Exports an IV from the CipherContext required to export the key
+ * state back from the unprivileged child to the privileged parent
+ * process.
+ */
+
+void
+cipher_get_keyiv(CipherContext *cc, u_char *iv, u_int len)
+{
+ Cipher *c = cc->cipher;
+ u_char *civ = NULL;
+ int evplen;
+
+ switch (c->number) {
+ case SSH_CIPHER_SSH2:
+ evplen = EVP_CIPHER_CTX_iv_length(&cc->evp);
+ if (evplen == 0)
+ return;
+ if (evplen != len)
+ fatal("%s: wrong iv length %d != %d", __FUNCTION__,
+ evplen, len);
+
+ if (strncmp(c->name, "aes", 3) == 0) {
+ struct ssh_rijndael_ctx *aesc;
+
+ aesc = EVP_CIPHER_CTX_get_app_data(&cc->evp);
+ if (aesc == NULL)
+ fatal("ssh_rijndael_cbc: no context");
+ civ = aesc->r_iv;
+ } else {
+ civ = cc->evp.iv;
+ }
+ break;
+ default:
+ fatal("%s: bad cipher %d", __FUNCTION__, c->number);
+ }
+ memcpy(iv, civ, len);
+}
+
diff --git a/cipher.h b/cipher.h
index b3b0303c..c7724469 100644
--- a/cipher.h
+++ b/cipher.h
@@ -81,4 +81,6 @@ void cipher_cleanup(CipherContext *);
void cipher_set_key_string(CipherContext *, Cipher *, const char *, int);
u_int cipher_blocksize(Cipher *);
u_int cipher_keylen(Cipher *);
+
+void cipher_get_keyiv(CipherContext *, u_char *, u_int);
#endif /* CIPHER_H */
diff --git a/compress.c b/compress.c
index 3badbf45..dec96ba5 100644
--- a/compress.c
+++ b/compress.c
@@ -19,8 +19,8 @@ RCSID("$OpenBSD: compress.c,v 1.17 2001/12/29 21:56:01 stevesk Exp $");
#include "zlib.h"
#include "compress.h"
-static z_stream incoming_stream;
-static z_stream outgoing_stream;
+z_stream incoming_stream;
+z_stream outgoing_stream;
static int compress_init_send_called = 0;
static int compress_init_recv_called = 0;
diff --git a/kex.c b/kex.c
index bf8fd95b..e5c0b0d0 100644
--- a/kex.c
+++ b/kex.c
@@ -43,6 +43,10 @@ RCSID("$OpenBSD: kex.c,v 1.47 2002/02/28 15:46:33 markus Exp $");
#define KEX_COOKIE_LEN 16
+/* Use privilege separation for sshd */
+int use_privsep;
+int mm_recvfd;
+
/* prototype */
static void kex_kexinit_finish(Kex *);
static void kex_choose_conf(Kex *);
diff --git a/kex.h b/kex.h
index 755bf332..c99afaec 100644
--- a/kex.h
+++ b/kex.h
@@ -111,6 +111,7 @@ struct Kex {
char *server_version_string;
int (*verify_host_key)(Key *);
Key *(*load_host_key)(int);
+ int (*host_key_index)(Key *);
};
Kex *kex_setup(char *[PROPOSAL_MAX]);
diff --git a/kexdh.c b/kexdh.c
index eaf497ca..6256722f 100644
--- a/kexdh.c
+++ b/kexdh.c
@@ -37,6 +37,12 @@ RCSID("$OpenBSD: kexdh.c,v 1.17 2002/02/28 15:46:33 markus Exp $");
#include "packet.h"
#include "dh.h"
#include "ssh2.h"
+#include "monitor.h"
+#include "monitor_wrap.h"
+
+/* Imports */
+extern int use_privsep;
+extern int mm_recvfd;
static u_char *
kex_dh_hash(
@@ -275,7 +281,12 @@ kexdh_server(Kex *kex)
/* sign H */
/* XXX hashlen depends on KEX */
- key_sign(server_host_key, &signature, &slen, hash, 20);
+ if (use_privsep)
+ mm_key_sign(mm_recvfd,
+ kex->host_key_index(server_host_key),
+ &signature, &slen, hash, 20);
+ else
+ key_sign(server_host_key, &signature, &slen, hash, 20);
/* destroy_sensitive_data(); */
diff --git a/kexgex.c b/kexgex.c
index 61896e6e..3c811f33 100644
--- a/kexgex.c
+++ b/kexgex.c
@@ -38,6 +38,12 @@ RCSID("$OpenBSD: kexgex.c,v 1.20 2002/02/28 15:46:33 markus Exp $");
#include "dh.h"
#include "ssh2.h"
#include "compat.h"
+#include "monitor.h"
+#include "monitor_wrap.h"
+
+/* Imports */
+extern int use_privsep;
+extern int mm_recvfd;
static u_char *
kexgex_hash(
@@ -296,7 +302,11 @@ kexgex_server(Kex *kex)
fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d",
min, nbits, max);
- dh = choose_dh(min, nbits, max);
+ /* Contact privileged parent */
+ if (use_privsep)
+ dh = mm_choose_dh(mm_recvfd, min, nbits, max);
+ else
+ dh = choose_dh(min, nbits, max);
if (dh == NULL)
packet_disconnect("Protocol error: no matching DH grp found");
@@ -379,7 +389,11 @@ kexgex_server(Kex *kex)
/* sign H */
/* XXX hashlen depends on KEX */
- key_sign(server_host_key, &signature, &slen, hash, 20);
+ if (use_privsep)
+ mm_key_sign(mm_recvfd, kex->host_key_index(server_host_key),
+ &signature, &slen, hash, 20);
+ else
+ key_sign(server_host_key, &signature, &slen, hash, 20);
/* destroy_sensitive_data(); */
@@ -390,6 +404,7 @@ kexgex_server(Kex *kex)
packet_put_bignum2(dh->pub_key); /* f */
packet_put_string(signature, slen);
packet_send();
+
xfree(signature);
xfree(server_host_key_blob);
/* have keys, free DH */
diff --git a/key.c b/key.c
index cda91571..fb6bff95 100644
--- a/key.c
+++ b/key.c
@@ -801,3 +801,46 @@ key_verify(
break;
}
}
+
+/* Converts a private to a public key */
+
+Key *
+key_demote(Key *k)
+{
+ Key *pk;
+
+ pk = xmalloc(sizeof(*pk));
+ pk->type = k->type;
+ pk->flags = k->flags;
+ pk->dsa = NULL;
+ pk->rsa = NULL;
+
+ switch (k->type) {
+ case KEY_RSA1:
+ case KEY_RSA:
+ if ((pk->rsa = RSA_new()) == NULL)
+ fatal("key_demote: RSA_new failed");
+ if ((pk->rsa->e = BN_dup(k->rsa->e)) == NULL)
+ fatal("key_demote: BN_dup failed");
+ if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL)
+ fatal("key_demote: BN_dup failed");
+ break;
+ case KEY_DSA:
+ if ((pk->dsa = DSA_new()) == NULL)
+ fatal("key_demote: DSA_new failed");
+ if ((pk->dsa->p = BN_dup(k->dsa->p)) == NULL)
+ fatal("key_demote: BN_dup failed");
+ if ((pk->dsa->q = BN_dup(k->dsa->q)) == NULL)
+ fatal("key_demote: BN_dup failed");
+ if ((pk->dsa->g = BN_dup(k->dsa->g)) == NULL)
+ fatal("key_demote: BN_dup failed");
+ if ((pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL)
+ fatal("key_demote: BN_dup failed");
+ break;
+ default:
+ fatal("key_free: bad key type %d", k->type);
+ break;
+ }
+
+ return (pk);
+}
diff --git a/key.h b/key.h
index a2257731..bc8b3d06 100644
--- a/key.h
+++ b/key.h
@@ -58,6 +58,7 @@ struct Key {
Key *key_new(int);
Key *key_new_private(int);
void key_free(Key *);
+Key *key_demote(Key *);
int key_equal(Key *, Key *);
char *key_fingerprint(Key *, enum fp_type, enum fp_rep);
char *key_type(Key *);
diff --git a/monitor.c b/monitor.c
new file mode 100644
index 00000000..b8579027
--- /dev/null
+++ b/monitor.c
@@ -0,0 +1,656 @@
+/*
+ * Copyright 2001 Niels Provos <provos@citi.umich.edu>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+RCSID("$OpenBSD$");
+
+#include <openssl/dh.h>
+
+#include "ssh.h"
+#include "auth.h"
+#include "kex.h"
+#include "dh.h"
+#include "zlib.h"
+#include "packet.h"
+#include "auth-options.h"
+#include "sshpty.h"
+#include "channels.h"
+#include "session.h"
+#include "log.h"
+#include "monitor.h"
+#include "monitor_mm.h"
+#include "monitor_wrap.h"
+#include "monitor_fdpass.h"
+#include "xmalloc.h"
+#include "misc.h"
+#include "buffer.h"
+#include "bufaux.h"
+
+/* Imports */
+extern Newkeys *current_keys[];
+extern z_stream incoming_stream;
+extern z_stream outgoing_stream;
+extern int compat20;
+extern int mm_sendfd;
+
+/* State exported from the child */
+
+struct {
+ z_stream incoming;
+ z_stream outgoing;
+ u_char *keyin;
+ u_int keyinlen;
+ u_char *keyout;
+ u_int keyoutlen;
+} child_state;
+
+/* Prototype for authentication functions */
+
+int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
+int user_key_allowed(struct passwd *, Key *);
+Key *get_hostkey_by_index(int);
+
+void session_pty_cleanup(void *);
+
+static Authctxt *authctxt;
+
+struct mon_table {
+ enum monitor_reqtype type;
+ int flags;
+ int (*f)(int, Buffer *);
+};
+
+#define MON_PROTOONE 0x0001 /* Used in protocol 1 */
+#define MON_PROTOTWO 0x0002 /* Used in protocol 2 */
+#define MON_AUTH 0x0004 /* Authentication Request */
+
+#define MON_BOTH (MON_PROTOONE|MON_PROTOTWO)
+
+#define MON_PERMIT 0x1000 /* Request is permitted */
+
+struct mon_table mon_dispatch_proto20[] = {
+ {MONITOR_REQ_MODULI, MON_PROTOTWO, mm_answer_moduli},
+ {MONITOR_REQ_SIGN, MON_PROTOTWO, mm_answer_sign},
+ {MONITOR_REQ_PWNAM, MON_BOTH, mm_answer_pwnamallow},
+ {MONITOR_REQ_AUTHSERV, MON_BOTH, mm_answer_authserv},
+ {MONITOR_REQ_AUTHPASSWORD, MON_BOTH | MON_AUTH, mm_answer_authpassword},
+ {MONITOR_REQ_KEYALLOWED, MON_BOTH | MON_AUTH, mm_answer_keyallowed},
+ {MONITOR_REQ_KEYVERIFY, MON_BOTH | MON_AUTH, mm_answer_keyverify},
+ {0, 0, NULL}
+};
+
+struct mon_table mon_dispatch_postauth20[] = {
+ {MONITOR_REQ_MODULI, MON_PROTOTWO, mm_answer_moduli},
+ {MONITOR_REQ_SIGN, MON_PROTOTWO, mm_answer_sign},
+ {MONITOR_REQ_PTY, MON_BOTH, mm_answer_pty},
+ {MONITOR_REQ_TERM, MON_BOTH, mm_answer_term},
+ {0, 0, NULL}
+};
+
+struct mon_table mon_dispatch_proto15[] = {
+ {0, 0, NULL}
+};
+
+struct mon_table *mon_dispatch;
+
+/* Specifies if a certain message is allowed at the moment */
+
+void
+monitor_permit(struct mon_table *ent, enum monitor_reqtype type, int permit)
+{
+ while (ent->f != NULL) {
+ if (ent->type == type) {
+ ent->flags &= ~MON_PERMIT;
+ ent->flags |= permit ? MON_PERMIT : 0;
+ return;
+ }
+ ent++;
+ }
+}
+
+void
+monitor_permit_authentications(int permit)
+{
+ struct mon_table *ent = mon_dispatch;
+
+ while (ent->f != NULL) {
+ if (ent->flags & MON_AUTH) {
+ ent->flags &= ~MON_PERMIT;
+ ent->flags |= permit ? MON_PERMIT : 0;
+ }
+ ent++;
+ }
+}
+
+#define FD_CLOSEONEXEC(x) do { \
+ if (fcntl(x, F_SETFD, 1) == -1) \
+ fatal("fcntl(%d, F_SETFD)", x); \
+} while (0)
+
+void
+monitor_socketpair(int *pair)
+{
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pair) == -1)
+ fatal("%s: socketpair", __FUNCTION__);
+ FD_CLOSEONEXEC(pair[0]);
+ FD_CLOSEONEXEC(pair[1]);
+}
+
+Authctxt *
+monitor_child_preauth(int socket)
+{
+ debug3("preauth child monitor started");
+
+ if (compat20) {
+ mon_dispatch = mon_dispatch_proto20;
+
+ /* Permit requests for moduli and signatures */
+ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
+ } else
+ mon_dispatch = mon_dispatch_proto15;
+
+ authctxt = authctxt_new();
+
+ /* The first few requests do not require asynchronous access */
+ for (;;) {
+ if (monitor_read(socket, mon_dispatch))
+ break;
+ }
+
+ debug("%s: %s has been authenticated by privileged process",
+ __FUNCTION__, authctxt->user);
+
+ if (compat20) {
+ mm_get_keystate(socket);
+ } else {
+ fatal("Use loose");
+ }
+
+ return (authctxt);
+}
+
+void
+monitor_child_postauth(int socket)
+{
+ if (compat20) {
+ mon_dispatch = mon_dispatch_postauth20;
+
+ /* Permit requests for moduli and signatures */
+ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
+ monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
+
+ if (!no_pty_flag)
+ monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
+ } else
+ mon_dispatch = mon_dispatch_proto15;
+
+ for (;;) {
+ if (monitor_read(socket, mon_dispatch))
+ break;
+ }
+}
+
+int
+monitor_read(int socket, struct mon_table *ent)
+{
+ Buffer m;
+ int ret;
+ u_char type;
+
+ buffer_init(&m);
+
+ mm_request_receive(socket, &m);
+ type = buffer_get_char(&m);
+
+ debug3("%s: checking request %d", __FUNCTION__, type);
+
+ while (ent->f != NULL) {
+ if (ent->type == type)
+ break;
+ ent++;
+ }
+
+ if (ent->f != NULL) {
+ if (!(ent->flags & MON_PERMIT))
+ fatal("%s: unpermitted request %d", __FUNCTION__,
+ type);
+ ret = (*ent->f)(socket, &m);
+ buffer_free(&m);
+ return ret;
+ }
+
+ fatal("%s: unsupported request: %d\n", __FUNCTION__, type);
+
+ /* NOTREACHED */
+ return (-1);
+}
+
+int
+mm_answer_moduli(int socket, Buffer *m)
+{
+ DH *dh;
+ int min, want, max;