Initial empty username on top of selinux and gss keyex
authorSam Hartman <hartmans@debian.org>
Mon, 19 Sep 2011 20:37:16 +0000 (16:37 -0400)
committerSam Hartman <hartmans@painless-security.com>
Wed, 21 Sep 2011 23:15:26 +0000 (19:15 -0400)
51 files changed:
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/.timestamp [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth-pam.c [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth-pam.h [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth2-gss.c [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth2.c [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/gss-serv.c [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/misc.c [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/misc.h [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor.c [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor.h [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor_wrap.c [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor_wrap.h [new file with mode: 0644]
.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/ssh-gss.h [new file with mode: 0644]
.pc/applied-patches
.pc/auth-log-verbosity.patch/.timestamp [new file with mode: 0644]
.pc/authorized-keys-man-symlink.patch/.timestamp [new file with mode: 0644]
.pc/debian-banner.patch/.timestamp [new file with mode: 0644]
.pc/debian-config.patch/.timestamp [new file with mode: 0644]
.pc/dnssec-sshfp.patch/.timestamp [new file with mode: 0644]
.pc/doc-hash-tab-completion.patch/.timestamp [new file with mode: 0644]
.pc/gnome-ssh-askpass2-icon.patch/.timestamp [new file with mode: 0644]
.pc/gssapi.patch/.timestamp [new file with mode: 0644]
.pc/gssapi_generic/gss-serv.c [new file with mode: 0644]
.pc/helpful-wait-terminate.patch/.timestamp [new file with mode: 0644]
.pc/keepalive-extensions.patch/.timestamp [new file with mode: 0644]
.pc/lintian-symlink-pickiness.patch/.timestamp [new file with mode: 0644]
.pc/openbsd-docs.patch/.timestamp [new file with mode: 0644]
.pc/package-versioning.patch/.timestamp [new file with mode: 0644]
.pc/quieter-signals.patch/.timestamp [new file with mode: 0644]
.pc/scp-quoting.patch/.timestamp [new file with mode: 0644]
.pc/selinux-role.patch/.timestamp [new file with mode: 0644]
.pc/shell-path.patch/.timestamp [new file with mode: 0644]
.pc/ssh-argv0.patch/.timestamp [new file with mode: 0644]
.pc/ssh-vulnkey.patch/.timestamp [new file with mode: 0644]
.pc/ssh1-keepalive.patch/.timestamp [new file with mode: 0644]
.pc/syslog-level-silent.patch/.timestamp [new file with mode: 0644]
.pc/user-group-modes.patch/.timestamp [new file with mode: 0644]
auth-pam.c
auth-pam.h
auth2-gss.c
auth2.c
debian/patches/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch [new file with mode: 0644]
debian/patches/series
gss-serv.c
misc.c
misc.h
monitor.c
monitor.h
monitor_wrap.c
monitor_wrap.h
ssh-gss.h

diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/.timestamp b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth-pam.c b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth-pam.c
new file mode 100644 (file)
index 0000000..675006e
--- /dev/null
@@ -0,0 +1,1221 @@
+/*-
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by ThinkSec AS and
+ * NAI Labs, the Security Research Division of Network Associates, Inc.
+ * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
+ * DARPA CHATS research program.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
+ * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef USE_PAM
+#if defined(HAVE_SECURITY_PAM_APPL_H)
+#include <security/pam_appl.h>
+#elif defined (HAVE_PAM_PAM_APPL_H)
+#include <pam/pam_appl.h>
+#endif
+
+/* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */
+#ifdef PAM_SUN_CODEBASE
+# define sshpam_const          /* Solaris, HP-UX, AIX */
+#else
+# define sshpam_const  const   /* LinuxPAM, OpenPAM */
+#endif
+
+/* Ambiguity in spec: is it an array of pointers or a pointer to an array? */
+#ifdef PAM_SUN_CODEBASE
+# define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member)
+#else
+# define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member)
+#endif
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "key.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "auth-pam.h"
+#include "canohost.h"
+#include "log.h"
+#include "msg.h"
+#include "packet.h"
+#include "misc.h"
+#include "servconf.h"
+#include "ssh2.h"
+#include "auth-options.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+#include "monitor_wrap.h"
+
+extern ServerOptions options;
+extern Buffer loginmsg;
+extern int compat20;
+extern u_int utmp_len;
+
+/* so we don't silently change behaviour */
+#ifdef USE_POSIX_THREADS
+# error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK"
+#endif
+
+/*
+ * Formerly known as USE_POSIX_THREADS, using this is completely unsupported
+ * and generally a bad idea.  Use at own risk and do not expect support if
+ * this breaks.
+ */
+#ifdef UNSUPPORTED_POSIX_THREADS_HACK
+#include <pthread.h>
+/*
+ * Avoid namespace clash when *not* using pthreads for systems *with*
+ * pthreads, which unconditionally define pthread_t via sys/types.h
+ * (e.g. Linux)
+ */
+typedef pthread_t sp_pthread_t;
+#else
+typedef pid_t sp_pthread_t;
+#endif
+
+struct pam_ctxt {
+       sp_pthread_t     pam_thread;
+       int              pam_psock;
+       int              pam_csock;
+       int              pam_done;
+};
+
+static void sshpam_free_ctx(void *);
+static struct pam_ctxt *cleanup_ctxt;
+
+#ifndef UNSUPPORTED_POSIX_THREADS_HACK
+/*
+ * Simulate threads with processes.
+ */
+
+static int sshpam_thread_status = -1;
+static mysig_t sshpam_oldsig;
+
+static void
+sshpam_sigchld_handler(int sig)
+{
+       signal(SIGCHLD, SIG_DFL);
+       if (cleanup_ctxt == NULL)
+               return; /* handler called after PAM cleanup, shouldn't happen */
+       if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)
+           <= 0) {
+               /* PAM thread has not exitted, privsep slave must have */
+               kill(cleanup_ctxt->pam_thread, SIGTERM);
+               if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0)
+                   <= 0)
+                       return; /* could not wait */
+       }
+       if (WIFSIGNALED(sshpam_thread_status) &&
+           WTERMSIG(sshpam_thread_status) == SIGTERM)
+               return; /* terminated by pthread_cancel */
+       if (!WIFEXITED(sshpam_thread_status))
+               sigdie("PAM: authentication thread exited unexpectedly");
+       if (WEXITSTATUS(sshpam_thread_status) != 0)
+               sigdie("PAM: authentication thread exited uncleanly");
+}
+
+/* ARGSUSED */
+static void
+pthread_exit(void *value)
+{
+       _exit(0);
+}
+
+/* ARGSUSED */
+static int
+pthread_create(sp_pthread_t *thread, const void *attr,
+    void *(*thread_start)(void *), void *arg)
+{
+       pid_t pid;
+       struct pam_ctxt *ctx = arg;
+
+       sshpam_thread_status = -1;
+       switch ((pid = fork())) {
+       case -1:
+               error("fork(): %s", strerror(errno));
+               return (-1);
+       case 0:
+               close(ctx->pam_psock);
+               ctx->pam_psock = -1;
+               thread_start(arg);
+               _exit(1);
+       default:
+               *thread = pid;
+               close(ctx->pam_csock);
+               ctx->pam_csock = -1;
+               sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler);
+               return (0);
+       }
+}
+
+static int
+pthread_cancel(sp_pthread_t thread)
+{
+       signal(SIGCHLD, sshpam_oldsig);
+       return (kill(thread, SIGTERM));
+}
+
+/* ARGSUSED */
+static int
+pthread_join(sp_pthread_t thread, void **value)
+{
+       int status;
+
+       if (sshpam_thread_status != -1)
+               return (sshpam_thread_status);
+       signal(SIGCHLD, sshpam_oldsig);
+       waitpid(thread, &status, 0);
+       return (status);
+}
+#endif
+
+
+static pam_handle_t *sshpam_handle = NULL;
+static int sshpam_err = 0;
+static int sshpam_authenticated = 0;
+static int sshpam_session_open = 0;
+static int sshpam_cred_established = 0;
+static int sshpam_account_status = -1;
+static char **sshpam_env = NULL;
+static Authctxt *sshpam_authctxt = NULL;
+static const char *sshpam_password = NULL;
+static char badpw[] = "\b\n\r\177INCORRECT";
+
+/* Some PAM implementations don't implement this */
+#ifndef HAVE_PAM_GETENVLIST
+static char **
+pam_getenvlist(pam_handle_t *pamh)
+{
+       /*
+        * XXX - If necessary, we can still support envrionment passing
+        * for platforms without pam_getenvlist by searching for known
+        * env vars (e.g. KRB5CCNAME) from the PAM environment.
+        */
+        return NULL;
+}
+#endif
+
+/*
+ * Some platforms, notably Solaris, do not enforce password complexity
+ * rules during pam_chauthtok() if the real uid of the calling process
+ * is 0, on the assumption that it's being called by "passwd" run by root.
+ * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
+ * the right thing.
+ */
+#ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
+static int
+sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
+{
+       int result;
+
+       if (sshpam_authctxt == NULL)
+               fatal("PAM: sshpam_authctxt not initialized");
+       if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
+               fatal("%s: setreuid failed: %s", __func__, strerror(errno));
+       result = pam_chauthtok(pamh, flags);
+       if (setreuid(0, -1) == -1)
+               fatal("%s: setreuid failed: %s", __func__, strerror(errno));
+       return result;
+}
+# define pam_chauthtok(a,b)    (sshpam_chauthtok_ruid((a), (b)))
+#endif
+
+void
+sshpam_password_change_required(int reqd)
+{
+       debug3("%s %d", __func__, reqd);
+       if (sshpam_authctxt == NULL)
+               fatal("%s: PAM authctxt not initialized", __func__);
+       sshpam_authctxt->force_pwchange = reqd;
+       if (reqd) {
+               no_port_forwarding_flag |= 2;
+               no_agent_forwarding_flag |= 2;
+               no_x11_forwarding_flag |= 2;
+       } else {
+               no_port_forwarding_flag &= ~2;
+               no_agent_forwarding_flag &= ~2;
+               no_x11_forwarding_flag &= ~2;
+       }
+}
+
+/* Import regular and PAM environment from subprocess */
+static void
+import_environments(Buffer *b)
+{
+       char *env;
+       u_int i, num_env;
+       int err;
+
+       debug3("PAM: %s entering", __func__);
+
+#ifndef UNSUPPORTED_POSIX_THREADS_HACK
+       /* Import variables set by do_pam_account */
+       sshpam_account_status = buffer_get_int(b);
+       sshpam_password_change_required(buffer_get_int(b));
+
+       /* Import environment from subprocess */
+       num_env = buffer_get_int(b);
+       if (num_env > 1024)
+               fatal("%s: received %u environment variables, expected <= 1024",
+                   __func__, num_env);
+       sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env));
+       debug3("PAM: num env strings %d", num_env);
+       for(i = 0; i < num_env; i++)
+               sshpam_env[i] = buffer_get_string(b, NULL);
+
+       sshpam_env[num_env] = NULL;
+
+       /* Import PAM environment from subprocess */
+       num_env = buffer_get_int(b);
+       debug("PAM: num PAM env strings %d", num_env);
+       for(i = 0; i < num_env; i++) {
+               env = buffer_get_string(b, NULL);
+
+#ifdef HAVE_PAM_PUTENV
+               /* Errors are not fatal here */
+               if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
+                       error("PAM: pam_putenv: %s",
+                           pam_strerror(sshpam_handle, sshpam_err));
+               }
+#endif
+       }
+#endif
+}
+
+/*
+ * Conversation function for authentication thread.
+ */
+static int
+sshpam_thread_conv(int n, sshpam_const struct pam_message **msg,
+    struct pam_response **resp, void *data)
+{
+       Buffer buffer;
+       struct pam_ctxt *ctxt;
+       struct pam_response *reply;
+       int i;
+
+       debug3("PAM: %s entering, %d messages", __func__, n);
+       *resp = NULL;
+
+       if (data == NULL) {
+               error("PAM: conversation function passed a null context");
+               return (PAM_CONV_ERR);
+       }
+       ctxt = data;
+       if (n <= 0 || n > PAM_MAX_NUM_MSG)
+               return (PAM_CONV_ERR);
+
+       if ((reply = calloc(n, sizeof(*reply))) == NULL)
+               return (PAM_CONV_ERR);
+
+       buffer_init(&buffer);
+       for (i = 0; i < n; ++i) {
+               switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
+               case PAM_PROMPT_ECHO_OFF:
+                       buffer_put_cstring(&buffer,
+                           PAM_MSG_MEMBER(msg, i, msg));
+                       if (ssh_msg_send(ctxt->pam_csock,
+                           PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
+                               goto fail;
+                       if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
+                               goto fail;
+                       if (buffer_get_char(&buffer) != PAM_AUTHTOK)
+                               goto fail;
+                       reply[i].resp = buffer_get_string(&buffer, NULL);
+                       break;
+               case PAM_PROMPT_ECHO_ON:
+                       buffer_put_cstring(&buffer,
+                           PAM_MSG_MEMBER(msg, i, msg));
+                       if (ssh_msg_send(ctxt->pam_csock,
+                           PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
+                               goto fail;
+                       if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
+                               goto fail;
+                       if (buffer_get_char(&buffer) != PAM_AUTHTOK)
+                               goto fail;
+                       reply[i].resp = buffer_get_string(&buffer, NULL);
+                       break;
+               case PAM_ERROR_MSG:
+                       buffer_put_cstring(&buffer,
+                           PAM_MSG_MEMBER(msg, i, msg));
+                       if (ssh_msg_send(ctxt->pam_csock,
+                           PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
+                               goto fail;
+                       break;
+               case PAM_TEXT_INFO:
+                       buffer_put_cstring(&buffer,
+                           PAM_MSG_MEMBER(msg, i, msg));
+                       if (ssh_msg_send(ctxt->pam_csock,
+                           PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
+                               goto fail;
+                       break;
+               default:
+                       goto fail;
+               }
+               buffer_clear(&buffer);
+       }
+       buffer_free(&buffer);
+       *resp = reply;
+       return (PAM_SUCCESS);
+
+ fail:
+       for(i = 0; i < n; i++) {
+               if (reply[i].resp != NULL)
+                       xfree(reply[i].resp);
+       }
+       xfree(reply);
+       buffer_free(&buffer);
+       return (PAM_CONV_ERR);
+}
+
+/*
+ * Authentication thread.
+ */
+static void *
+sshpam_thread(void *ctxtp)
+{
+       struct pam_ctxt *ctxt = ctxtp;
+       Buffer buffer;
+       struct pam_conv sshpam_conv;
+       int flags = (options.permit_empty_passwd == 0 ?
+           PAM_DISALLOW_NULL_AUTHTOK : 0);
+#ifndef UNSUPPORTED_POSIX_THREADS_HACK
+       extern char **environ;
+       char **env_from_pam;
+       u_int i;
+       const char *pam_user;
+       const char **ptr_pam_user = &pam_user;
+       char *tz = getenv("TZ");
+
+       pam_get_item(sshpam_handle, PAM_USER,
+           (sshpam_const void **)ptr_pam_user);
+
+       environ[0] = NULL;
+       if (tz != NULL)
+               if (setenv("TZ", tz, 1) == -1)
+                       error("PAM: could not set TZ environment: %s",
+                           strerror(errno));
+
+       if (sshpam_authctxt != NULL) {
+               setproctitle("%s [pam]",
+                   sshpam_authctxt->valid ? pam_user : "unknown");
+       }
+#endif
+
+       sshpam_conv.conv = sshpam_thread_conv;
+       sshpam_conv.appdata_ptr = ctxt;
+
+       if (sshpam_authctxt == NULL)
+               fatal("%s: PAM authctxt not initialized", __func__);
+
+       buffer_init(&buffer);
+       sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
+           (const void *)&sshpam_conv);
+       if (sshpam_err != PAM_SUCCESS)
+               goto auth_fail;
+       sshpam_err = pam_authenticate(sshpam_handle, flags);
+       if (sshpam_err != PAM_SUCCESS)
+               goto auth_fail;
+
+       if (compat20) {
+               if (!do_pam_account()) {
+                       sshpam_err = PAM_ACCT_EXPIRED;
+                       goto auth_fail;
+               }
+               if (sshpam_authctxt->force_pwchange) {
+                       sshpam_err = pam_chauthtok(sshpam_handle,
+                           PAM_CHANGE_EXPIRED_AUTHTOK);
+                       if (sshpam_err != PAM_SUCCESS)
+                               goto auth_fail;
+                       sshpam_password_change_required(0);
+               }
+       }
+
+       buffer_put_cstring(&buffer, "OK");
+
+#ifndef UNSUPPORTED_POSIX_THREADS_HACK
+       /* Export variables set by do_pam_account */
+       buffer_put_int(&buffer, sshpam_account_status);
+       buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);
+
+       /* Export any environment strings set in child */
+       for(i = 0; environ[i] != NULL; i++)
+               ; /* Count */
+       buffer_put_int(&buffer, i);
+       for(i = 0; environ[i] != NULL; i++)
+               buffer_put_cstring(&buffer, environ[i]);
+
+       /* Export any environment strings set by PAM in child */
+       env_from_pam = pam_getenvlist(sshpam_handle);
+       for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
+               ; /* Count */
+       buffer_put_int(&buffer, i);
+       for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
+               buffer_put_cstring(&buffer, env_from_pam[i]);
+#endif /* UNSUPPORTED_POSIX_THREADS_HACK */
+
+       /* XXX - can't do much about an error here */
+       ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
+       buffer_free(&buffer);
+       pthread_exit(NULL);
+
+ auth_fail:
+       buffer_put_cstring(&buffer,
+           pam_strerror(sshpam_handle, sshpam_err));
+       /* XXX - can't do much about an error here */
+       if (sshpam_err == PAM_ACCT_EXPIRED)
+               ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer);
+       else
+               ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
+       buffer_free(&buffer);
+       pthread_exit(NULL);
+
+       return (NULL); /* Avoid warning for non-pthread case */
+}
+
+void
+sshpam_thread_cleanup(void)
+{
+       struct pam_ctxt *ctxt = cleanup_ctxt;
+
+       debug3("PAM: %s entering", __func__);
+       if (ctxt != NULL && ctxt->pam_thread != 0) {
+               pthread_cancel(ctxt->pam_thread);
+               pthread_join(ctxt->pam_thread, NULL);
+               close(ctxt->pam_psock);
+               close(ctxt->pam_csock);
+               memset(ctxt, 0, sizeof(*ctxt));
+               cleanup_ctxt = NULL;
+       }
+}
+
+static int
+sshpam_null_conv(int n, sshpam_const struct pam_message **msg,
+    struct pam_response **resp, void *data)
+{
+       debug3("PAM: %s entering, %d messages", __func__, n);
+       return (PAM_CONV_ERR);
+}
+
+static struct pam_conv null_conv = { sshpam_null_conv, NULL };
+
+static int
+sshpam_store_conv(int n, sshpam_const struct pam_message **msg,
+    struct pam_response **resp, void *data)
+{
+       struct pam_response *reply;
+       int i;
+       size_t len;
+
+       debug3("PAM: %s called with %d messages", __func__, n);
+       *resp = NULL;
+
+       if (n <= 0 || n > PAM_MAX_NUM_MSG)
+               return (PAM_CONV_ERR);
+
+       if ((reply = calloc(n, sizeof(*reply))) == NULL)
+               return (PAM_CONV_ERR);
+
+       for (i = 0; i < n; ++i) {
+               switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
+               case PAM_ERROR_MSG:
+               case PAM_TEXT_INFO:
+                       len = strlen(PAM_MSG_MEMBER(msg, i, msg));
+                       buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
+                       buffer_append(&loginmsg, "\n", 1 );
+                       reply[i].resp_retcode = PAM_SUCCESS;
+                       break;
+               default:
+                       goto fail;
+               }
+       }
+       *resp = reply;
+       return (PAM_SUCCESS);
+
+ fail:
+       for(i = 0; i < n; i++) {
+               if (reply[i].resp != NULL)
+                       xfree(reply[i].resp);
+       }
+       xfree(reply);
+       return (PAM_CONV_ERR);
+}
+
+static struct pam_conv store_conv = { sshpam_store_conv, NULL };
+
+void
+sshpam_cleanup(void)
+{
+       if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor()))
+               return;
+       debug("PAM: cleanup");
+       pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
+       if (sshpam_session_open) {
+               debug("PAM: closing session");
+               pam_close_session(sshpam_handle, PAM_SILENT);
+               sshpam_session_open = 0;
+       }
+       if (sshpam_cred_established) {
+               debug("PAM: deleting credentials");
+               pam_setcred(sshpam_handle, PAM_DELETE_CRED);
+               sshpam_cred_established = 0;
+       }
+       sshpam_authenticated = 0;
+       pam_end(sshpam_handle, sshpam_err);
+       sshpam_handle = NULL;
+}
+
+static int
+sshpam_init(Authctxt *authctxt)
+{
+       extern char *__progname;
+       const char *pam_rhost, *pam_user, *user = authctxt->user;
+       const char **ptr_pam_user = &pam_user;
+
+       if (sshpam_handle != NULL) {
+               /* We already have a PAM context; check if the user matches */
+               sshpam_err = pam_get_item(sshpam_handle,
+                   PAM_USER, (sshpam_const void **)ptr_pam_user);
+               if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
+                       return (0);
+               pam_end(sshpam_handle, sshpam_err);
+               sshpam_handle = NULL;
+       }
+       debug("PAM: initializing for \"%s\"", user);
+       sshpam_err =
+           pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle);
+       sshpam_authctxt = authctxt;
+
+       if (sshpam_err != PAM_SUCCESS) {
+               pam_end(sshpam_handle, sshpam_err);
+               sshpam_handle = NULL;
+               return (-1);
+       }
+       pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
+       debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
+       sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
+       if (sshpam_err != PAM_SUCCESS) {
+               pam_end(sshpam_handle, sshpam_err);
+               sshpam_handle = NULL;
+               return (-1);
+       }
+#ifdef PAM_TTY_KLUDGE
+       /*
+        * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
+        * sshd doesn't set the tty until too late in the auth process and
+        * may not even set one (for tty-less connections)
+        */
+       debug("PAM: setting PAM_TTY to \"ssh\"");
+       sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
+       if (sshpam_err != PAM_SUCCESS) {
+               pam_end(sshpam_handle, sshpam_err);
+               sshpam_handle = NULL;
+               return (-1);
+       }
+#endif
+       return (0);
+}
+
+static void *
+sshpam_init_ctx(Authctxt *authctxt)
+{
+       struct pam_ctxt *ctxt;
+       int socks[2];
+
+       debug3("PAM: %s entering", __func__);
+       /*
+        * Refuse to start if we don't have PAM enabled or do_pam_account
+        * has previously failed.
+        */
+       if (!options.use_pam || sshpam_account_status == 0)
+               return NULL;
+
+       /* Initialize PAM */
+       if (sshpam_init(authctxt) == -1) {
+               error("PAM: initialization failed");
+               return (NULL);
+       }
+
+       ctxt = xcalloc(1, sizeof *ctxt);
+
+       /* Start the authentication thread */
+       if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
+               error("PAM: failed create sockets: %s", strerror(errno));
+               xfree(ctxt);
+               return (NULL);
+       }
+       ctxt->pam_psock = socks[0];
+       ctxt->pam_csock = socks[1];
+       if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
+               error("PAM: failed to start authentication thread: %s",
+                   strerror(errno));
+               close(socks[0]);
+               close(socks[1]);
+               xfree(ctxt);
+               return (NULL);
+       }
+       cleanup_ctxt = ctxt;
+       return (ctxt);
+}
+
+static int
+sshpam_query(void *ctx, char **name, char **info,
+    u_int *num, char ***prompts, u_int **echo_on)
+{
+       Buffer buffer;
+       struct pam_ctxt *ctxt = ctx;
+       size_t plen;
+       u_char type;
+       char *msg;
+       size_t len, mlen;
+
+       debug3("PAM: %s entering", __func__);
+       buffer_init(&buffer);
+       *name = xstrdup("");
+       *info = xstrdup("");
+       *prompts = xmalloc(sizeof(char *));
+       **prompts = NULL;
+       plen = 0;
+       *echo_on = xmalloc(sizeof(u_int));
+       while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
+               type = buffer_get_char(&buffer);
+               msg = buffer_get_string(&buffer, NULL);
+               mlen = strlen(msg);
+               switch (type) {
+               case PAM_PROMPT_ECHO_ON:
+               case PAM_PROMPT_ECHO_OFF:
+                       *num = 1;
+                       len = plen + mlen + 1;
+                       **prompts = xrealloc(**prompts, 1, len);
+                       strlcpy(**prompts + plen, msg, len - plen);
+                       plen += mlen;
+                       **echo_on = (type == PAM_PROMPT_ECHO_ON);
+                       xfree(msg);
+                       return (0);
+               case PAM_ERROR_MSG:
+               case PAM_TEXT_INFO:
+                       /* accumulate messages */
+                       len = plen + mlen + 2;
+                       **prompts = xrealloc(**prompts, 1, len);
+                       strlcpy(**prompts + plen, msg, len - plen);
+                       plen += mlen;
+                       strlcat(**prompts + plen, "\n", len - plen);
+                       plen++;
+                       xfree(msg);
+                       break;
+               case PAM_ACCT_EXPIRED:
+                       sshpam_account_status = 0;
+                       /* FALLTHROUGH */
+               case PAM_AUTH_ERR:
+                       debug3("PAM: %s", pam_strerror(sshpam_handle, type));
+                       if (**prompts != NULL && strlen(**prompts) != 0) {
+                               *info = **prompts;
+                               **prompts = NULL;
+                               *num = 0;
+                               **echo_on = 0;
+                               ctxt->pam_done = -1;
+                               xfree(msg);
+                               return 0;
+                       }
+                       /* FALLTHROUGH */
+               case PAM_SUCCESS:
+                       if (**prompts != NULL) {
+                               /* drain any accumulated messages */
+                               debug("PAM: %s", **prompts);
+                               buffer_append(&loginmsg, **prompts,
+                                   strlen(**prompts));
+                               xfree(**prompts);
+                               **prompts = NULL;
+                       }
+                       if (type == PAM_SUCCESS) {
+                               if (!sshpam_authctxt->valid ||
+                                   (sshpam_authctxt->pw->pw_uid == 0 &&
+                                   options.permit_root_login != PERMIT_YES))
+                                       fatal("Internal error: PAM auth "
+                                           "succeeded when it should have "
+                                           "failed");
+                               import_environments(&buffer);
+                               *num = 0;
+                               **echo_on = 0;
+                               ctxt->pam_done = 1;
+                               xfree(msg);
+                               return (0);
+                       }
+                       error("PAM: %s for %s%.100s from %.100s", msg,
+                           sshpam_authctxt->valid ? "" : "illegal user ",
+                           sshpam_authctxt->user,
+                           get_remote_name_or_ip(utmp_len, options.use_dns));
+                       /* FALLTHROUGH */
+               default:
+                       *num = 0;
+                       **echo_on = 0;
+                       xfree(msg);
+                       ctxt->pam_done = -1;
+                       return (-1);
+               }
+       }
+       return (-1);
+}
+
+/* XXX - see also comment in auth-chall.c:verify_response */
+static int
+sshpam_respond(void *ctx, u_int num, char **resp)
+{
+       Buffer buffer;
+       struct pam_ctxt *ctxt = ctx;
+
+       debug2("PAM: %s entering, %u responses", __func__, num);
+       switch (ctxt->pam_done) {
+       case 1:
+               sshpam_authenticated = 1;
+               return (0);
+       case 0:
+               break;
+       default:
+               return (-1);
+       }
+       if (num != 1) {
+               error("PAM: expected one response, got %u", num);
+               return (-1);
+       }
+       buffer_init(&buffer);
+       if (sshpam_authctxt->valid &&
+           (sshpam_authctxt->pw->pw_uid != 0 ||
+           options.permit_root_login == PERMIT_YES))
+               buffer_put_cstring(&buffer, *resp);
+       else
+               buffer_put_cstring(&buffer, badpw);
+       if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
+               buffer_free(&buffer);
+               return (-1);
+       }
+       buffer_free(&buffer);
+       return (1);
+}
+
+static void
+sshpam_free_ctx(void *ctxtp)
+{
+       struct pam_ctxt *ctxt = ctxtp;
+
+       debug3("PAM: %s entering", __func__);
+       sshpam_thread_cleanup();
+       xfree(ctxt);
+       /*
+        * We don't call sshpam_cleanup() here because we may need the PAM
+        * handle at a later stage, e.g. when setting up a session.  It's
+        * still on the cleanup list, so pam_end() *will* be called before
+        * the server process terminates.
+        */
+}
+
+KbdintDevice sshpam_device = {
+       "pam",
+       sshpam_init_ctx,
+       sshpam_query,
+       sshpam_respond,
+       sshpam_free_ctx
+};
+
+KbdintDevice mm_sshpam_device = {
+       "pam",
+       mm_sshpam_init_ctx,
+       mm_sshpam_query,
+       mm_sshpam_respond,
+       mm_sshpam_free_ctx
+};
+
+/*
+ * This replaces auth-pam.c
+ */
+void
+start_pam(Authctxt *authctxt)
+{
+       if (!options.use_pam)
+               fatal("PAM: initialisation requested when UsePAM=no");
+
+       if (sshpam_init(authctxt) == -1)
+               fatal("PAM: initialisation failed");
+}
+
+void
+finish_pam(void)
+{
+       sshpam_cleanup();
+}
+
+u_int
+do_pam_account(void)
+{
+       debug("%s: called", __func__);
+       if (sshpam_account_status != -1)
+               return (sshpam_account_status);
+
+       sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
+       debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
+           pam_strerror(sshpam_handle, sshpam_err));
+
+       if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
+               sshpam_account_status = 0;
+               return (sshpam_account_status);
+       }
+
+       if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
+               sshpam_password_change_required(1);
+
+       sshpam_account_status = 1;
+       return (sshpam_account_status);
+}
+
+void
+do_pam_set_tty(const char *tty)
+{
+       if (tty != NULL) {
+               debug("PAM: setting PAM_TTY to \"%s\"", tty);
+               sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
+               if (sshpam_err != PAM_SUCCESS)
+                       fatal("PAM: failed to set PAM_TTY: %s",
+                           pam_strerror(sshpam_handle, sshpam_err));
+       }
+}
+
+void
+do_pam_setcred(int init)
+{
+       sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
+           (const void *)&store_conv);
+       if (sshpam_err != PAM_SUCCESS)
+               fatal("PAM: failed to set PAM_CONV: %s",
+                   pam_strerror(sshpam_handle, sshpam_err));
+       if (init) {
+               debug("PAM: establishing credentials");
+               sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
+       } else {
+               debug("PAM: reinitializing credentials");
+               sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
+       }
+       if (sshpam_err == PAM_SUCCESS) {
+               sshpam_cred_established = 1;
+               return;
+       }
+       if (sshpam_authenticated)
+               fatal("PAM: pam_setcred(): %s",
+                   pam_strerror(sshpam_handle, sshpam_err));
+       else
+               debug("PAM: pam_setcred(): %s",
+                   pam_strerror(sshpam_handle, sshpam_err));
+}
+
+static int
+sshpam_tty_conv(int n, sshpam_const struct pam_message **msg,
+    struct pam_response **resp, void *data)
+{
+       char input[PAM_MAX_MSG_SIZE];
+       struct pam_response *reply;
+       int i;
+
+       debug3("PAM: %s called with %d messages", __func__, n);
+
+       *resp = NULL;
+
+       if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
+               return (PAM_CONV_ERR);
+
+       if ((reply = calloc(n, sizeof(*reply))) == NULL)
+               return (PAM_CONV_ERR);
+
+       for (i = 0; i < n; ++i) {
+               switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
+               case PAM_PROMPT_ECHO_OFF:
+                       reply[i].resp =
+                           read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
+                           RP_ALLOW_STDIN);
+                       reply[i].resp_retcode = PAM_SUCCESS;
+                       break;
+               case PAM_PROMPT_ECHO_ON:
+                       fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
+                       if (fgets(input, sizeof input, stdin) == NULL)
+                               input[0] = '\0';
+                       if ((reply[i].resp = strdup(input)) == NULL)
+                               goto fail;
+                       reply[i].resp_retcode = PAM_SUCCESS;
+                       break;
+               case PAM_ERROR_MSG:
+               case PAM_TEXT_INFO:
+                       fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
+                       reply[i].resp_retcode = PAM_SUCCESS;
+                       break;
+               default:
+                       goto fail;
+               }
+       }
+       *resp = reply;
+       return (PAM_SUCCESS);
+
+ fail:
+       for(i = 0; i < n; i++) {
+               if (reply[i].resp != NULL)
+                       xfree(reply[i].resp);
+       }
+       xfree(reply);
+       return (PAM_CONV_ERR);
+}
+
+static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
+
+/*
+ * XXX this should be done in the authentication phase, but ssh1 doesn't
+ * support that
+ */
+void
+do_pam_chauthtok(void)
+{
+       if (use_privsep)
+               fatal("Password expired (unable to change with privsep)");
+       sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
+           (const void *)&tty_conv);
+       if (sshpam_err != PAM_SUCCESS)
+               fatal("PAM: failed to set PAM_CONV: %s",
+                   pam_strerror(sshpam_handle, sshpam_err));
+       debug("PAM: changing password");
+       sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
+       if (sshpam_err != PAM_SUCCESS)
+               fatal("PAM: pam_chauthtok(): %s",
+                   pam_strerror(sshpam_handle, sshpam_err));
+}
+
+void
+do_pam_session(void)
+{
+       debug3("PAM: opening session");
+       sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
+           (const void *)&store_conv);
+       if (sshpam_err != PAM_SUCCESS)
+               fatal("PAM: failed to set PAM_CONV: %s",
+                   pam_strerror(sshpam_handle, sshpam_err));
+       sshpam_err = pam_open_session(sshpam_handle, 0);
+       if (sshpam_err == PAM_SUCCESS)
+               sshpam_session_open = 1;
+       else {
+               sshpam_session_open = 0;
+               disable_forwarding();
+               error("PAM: pam_open_session(): %s",
+                   pam_strerror(sshpam_handle, sshpam_err));
+       }
+
+}
+
+int
+is_pam_session_open(void)
+{
+       return sshpam_session_open;
+}
+
+/*
+ * Set a PAM environment string. We need to do this so that the session
+ * modules can handle things like Kerberos/GSI credentials that appear
+ * during the ssh authentication process.
+ */
+int
+do_pam_putenv(char *name, char *value)
+{
+       int ret = 1;
+#ifdef HAVE_PAM_PUTENV
+       char *compound;
+       size_t len;
+
+       len = strlen(name) + strlen(value) + 2;
+       compound = xmalloc(len);
+
+       snprintf(compound, len, "%s=%s", name, value);
+       ret = pam_putenv(sshpam_handle, compound);
+       xfree(compound);
+#endif
+
+       return (ret);
+}
+
+char **
+fetch_pam_child_environment(void)
+{
+       return sshpam_env;
+}
+
+char **
+fetch_pam_environment(void)
+{
+       return (pam_getenvlist(sshpam_handle));
+}
+
+void
+free_pam_environment(char **env)
+{
+       char **envp;
+
+       if (env == NULL)
+               return;
+
+       for (envp = env; *envp; envp++)
+               xfree(*envp);
+       xfree(env);
+}
+
+/*
+ * "Blind" conversation function for password authentication.  Assumes that
+ * echo-off prompts are for the password and stores messages for later
+ * display.
+ */
+static int
+sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg,
+    struct pam_response **resp, void *data)
+{
+       struct pam_response *reply;
+       int i;
+       size_t len;
+
+       debug3("PAM: %s called with %d messages", __func__, n);
+
+       *resp = NULL;
+
+       if (n <= 0 || n > PAM_MAX_NUM_MSG)
+               return (PAM_CONV_ERR);
+
+       if ((reply = calloc(n, sizeof(*reply))) == NULL)
+               return (PAM_CONV_ERR);
+
+       for (i = 0; i < n; ++i) {
+               switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
+               case PAM_PROMPT_ECHO_OFF:
+                       if (sshpam_password == NULL)
+                               goto fail;
+                       if ((reply[i].resp = strdup(sshpam_password)) == NULL)
+                               goto fail;
+                       reply[i].resp_retcode = PAM_SUCCESS;
+                       break;
+               case PAM_ERROR_MSG:
+               case PAM_TEXT_INFO:
+                       len = strlen(PAM_MSG_MEMBER(msg, i, msg));
+                       if (len > 0) {
+                               buffer_append(&loginmsg,
+                                   PAM_MSG_MEMBER(msg, i, msg), len);
+                               buffer_append(&loginmsg, "\n", 1);
+                       }
+                       if ((reply[i].resp = strdup("")) == NULL)
+                               goto fail;
+                       reply[i].resp_retcode = PAM_SUCCESS;
+                       break;
+               default:
+                       goto fail;
+               }
+       }
+       *resp = reply;
+       return (PAM_SUCCESS);
+
+ fail:
+       for(i = 0; i < n; i++) {
+               if (reply[i].resp != NULL)
+                       xfree(reply[i].resp);
+       }
+       xfree(reply);
+       return (PAM_CONV_ERR);
+}
+
+static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
+
+/*
+ * Attempt password authentication via PAM
+ */
+int
+sshpam_auth_passwd(Authctxt *authctxt, const char *password)
+{
+       int flags = (options.permit_empty_passwd == 0 ?
+           PAM_DISALLOW_NULL_AUTHTOK : 0);
+
+       if (!options.use_pam || sshpam_handle == NULL)
+               fatal("PAM: %s called when PAM disabled or failed to "
+                   "initialise.", __func__);
+
+       sshpam_password = password;
+       sshpam_authctxt = authctxt;
+
+       /*
+        * If the user logging in is invalid, or is root but is not permitted
+        * by PermitRootLogin, use an invalid password to prevent leaking
+        * information via timing (eg if the PAM config has a delay on fail).
+        */
+       if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
+           options.permit_root_login != PERMIT_YES))
+               sshpam_password = badpw;
+
+       sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
+           (const void *)&passwd_conv);
+       if (sshpam_err != PAM_SUCCESS)
+               fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
+                   pam_strerror(sshpam_handle, sshpam_err));
+
+       sshpam_err = pam_authenticate(sshpam_handle, flags);
+       sshpam_password = NULL;
+       if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
+               debug("PAM: password authentication accepted for %.100s",
+                   authctxt->user);
+               return 1;
+       } else {
+               debug("PAM: password authentication failed for %.100s: %s",
+                   authctxt->valid ? authctxt->user : "an illegal user",
+                   pam_strerror(sshpam_handle, sshpam_err));
+               return 0;
+       }
+}
+#endif /* USE_PAM */
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth-pam.h b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth-pam.h
new file mode 100644 (file)
index 0000000..a1a2b52
--- /dev/null
@@ -0,0 +1,50 @@
+/* $Id: auth-pam.h,v 1.27 2004/09/11 12:17:26 dtucker Exp $ */
+
+/*
+ * Copyright (c) 2000 Damien Miller.  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"
+#ifdef USE_PAM
+
+#if !defined(SSHD_PAM_SERVICE)
+# define SSHD_PAM_SERVICE              __progname
+#endif
+
+void start_pam(Authctxt *);
+void finish_pam(void);
+u_int do_pam_account(void);
+void do_pam_session(void);
+void do_pam_set_tty(const char *);
+void do_pam_setcred(int );
+void do_pam_chauthtok(void);
+int do_pam_putenv(char *, char *);
+char ** fetch_pam_environment(void);
+char ** fetch_pam_child_environment(void);
+void free_pam_environment(char **);
+void sshpam_thread_cleanup(void);
+void sshpam_cleanup(void);
+int sshpam_auth_passwd(Authctxt *, const char *);
+int is_pam_session_open(void);
+
+#endif /* USE_PAM */
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth2-gss.c b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth2-gss.c
new file mode 100644 (file)
index 0000000..7dc87db
--- /dev/null
@@ -0,0 +1,345 @@
+/* $OpenBSD: auth2-gss.c,v 1.17 2011/03/10 02:52:57 djm Exp $ */
+
+/*
+ * Copyright (c) 2001-2007 Simon Wilkinson. 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"
+
+#ifdef GSSAPI
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+
+#include "xmalloc.h"
+#include "key.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "ssh2.h"
+#include "log.h"
+#include "dispatch.h"
+#include "buffer.h"
+#include "servconf.h"
+#include "packet.h"
+#include "ssh-gss.h"
+#include "monitor_wrap.h"
+
+extern ServerOptions options;
+
+static void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
+static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
+static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
+static void input_gssapi_errtok(int, u_int32_t, void *);
+
+/* 
+ * The 'gssapi_keyex' userauth mechanism.
+ */
+static int
+userauth_gsskeyex(Authctxt *authctxt)
+{
+       int authenticated = 0;
+       Buffer b;
+       gss_buffer_desc mic, gssbuf;
+       u_int len;
+
+       mic.value = packet_get_string(&len);
+       mic.length = len;
+
+       packet_check_eom();
+
+       ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
+           "gssapi-keyex");
+
+       gssbuf.value = buffer_ptr(&b);
+       gssbuf.length = buffer_len(&b);
+
+       /* gss_kex_context is NULL with privsep, so we can't check it here */
+       if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, 
+           &gssbuf, &mic))))
+               authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+                   authctxt->pw));
+       
+       buffer_free(&b);
+       xfree(mic.value);
+
+       return (authenticated);
+}
+
+/*
+ * We only support those mechanisms that we know about (ie ones that we know
+ * how to check local user kuserok and the like)
+ */
+static int
+userauth_gssapi(Authctxt *authctxt)
+{
+       gss_OID_desc goid = {0, NULL};
+       Gssctxt *ctxt = NULL;
+       int mechs;
+       gss_OID_set supported;
+       int present;
+       OM_uint32 ms;
+       u_int len;
+       u_char *doid = NULL;
+
+       if (!authctxt->valid || authctxt->user == NULL)
+               return (0);
+
+       mechs = packet_get_int();
+       if (mechs == 0) {
+               debug("Mechanism negotiation is not supported");
+               return (0);
+       }
+
+       ssh_gssapi_supported_oids(&supported);
+       do {
+               mechs--;
+
+               if (doid)
+                       xfree(doid);
+
+               present = 0;
+               doid = packet_get_string(&len);
+
+               if (len > 2 && doid[0] == SSH_GSS_OIDTYPE &&
+                   doid[1] == len - 2) {
+                       goid.elements = doid + 2;
+                       goid.length   = len - 2;
+                       gss_test_oid_set_member(&ms, &goid, supported,
+                           &present);
+               } else {
+                       logit("Badly formed OID received");
+               }
+       } while (mechs > 0 && !present);
+
+       gss_release_oid_set(&ms, &supported);
+
+       if (!present) {
+               xfree(doid);
+               authctxt->server_caused_failure = 1;
+               return (0);
+       }
+
+       if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) {
+               if (ctxt != NULL)
+                       ssh_gssapi_delete_ctx(&ctxt);
+               xfree(doid);
+               authctxt->server_caused_failure = 1;
+               return (0);
+       }
+
+       authctxt->methoddata = (void *)ctxt;
+
+       packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
+
+       /* Return the OID that we received */
+       packet_put_string(doid, len);
+
+       packet_send();
+       xfree(doid);
+
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
+       authctxt->postponed = 1;
+
+       return (0);
+}
+
+static void
+input_gssapi_token(int type, u_int32_t plen, void *ctxt)
+{
+       Authctxt *authctxt = ctxt;
+       Gssctxt *gssctxt;
+       gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc recv_tok;
+       OM_uint32 maj_status, min_status, flags;
+       u_int len;
+
+       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+               fatal("No authentication or GSSAPI context");
+
+       gssctxt = authctxt->methoddata;
+       recv_tok.value = packet_get_string(&len);
+       recv_tok.length = len; /* u_int vs. size_t */
+
+       packet_check_eom();
+
+       maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
+           &send_tok, &flags));
+
+       xfree(recv_tok.value);
+
+       if (GSS_ERROR(maj_status)) {
+               if (send_tok.length != 0) {
+                       packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+                       packet_put_string(send_tok.value, send_tok.length);
+                       packet_send();
+               }
+               authctxt->postponed = 0;
+               dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+               userauth_finish(authctxt, 0, "gssapi-with-mic");
+       } else {
+               if (send_tok.length != 0) {
+                       packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
+                       packet_put_string(send_tok.value, send_tok.length);
+                       packet_send();
+               }
+               if (maj_status == GSS_S_COMPLETE) {
+                       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+                       if (flags & GSS_C_INTEG_FLAG)
+                               dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC,
+                                   &input_gssapi_mic);
+                       else
+                               dispatch_set(
+                                   SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
+                                   &input_gssapi_exchange_complete);
+               }
+       }
+
+       gss_release_buffer(&min_status, &send_tok);
+}
+
+static void
+input_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
+{
+       Authctxt *authctxt = ctxt;
+       Gssctxt *gssctxt;
+       gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc recv_tok;
+       OM_uint32 maj_status;
+       u_int len;
+
+       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+               fatal("No authentication or GSSAPI context");
+
+       gssctxt = authctxt->methoddata;
+       recv_tok.value = packet_get_string(&len);
+       recv_tok.length = len;
+
+       packet_check_eom();
+
+       /* Push the error token into GSSAPI to see what it says */
+       maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
+           &send_tok, NULL));
+
+       xfree(recv_tok.value);
+
+       /* We can't return anything to the client, even if we wanted to */
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
+
+       /* The client will have already moved on to the next auth */
+
+       gss_release_buffer(&maj_status, &send_tok);
+}
+
+/*
+ * This is called when the client thinks we've completed authentication.
+ * It should only be enabled in the dispatch handler by the function above,
+ * which only enables it once the GSSAPI exchange is complete.
+ */
+
+static void
+input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
+{
+       Authctxt *authctxt = ctxt;
+       Gssctxt *gssctxt;
+       int authenticated;
+
+       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+               fatal("No authentication or GSSAPI context");
+
+       gssctxt = authctxt->methoddata;
+
+       /*
+        * We don't need to check the status, because we're only enabled in
+        * the dispatcher once the exchange is complete
+        */
+
+       packet_check_eom();
+
+       authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+           authctxt->pw));
+
+       authctxt->postponed = 0;
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
+       userauth_finish(authctxt, authenticated, "gssapi-with-mic");
+}
+
+static void
+input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
+{
+       Authctxt *authctxt = ctxt;
+       Gssctxt *gssctxt;
+       int authenticated = 0;
+       Buffer b;
+       gss_buffer_desc mic, gssbuf;
+       u_int len;
+
+       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+               fatal("No authentication or GSSAPI context");
+
+       gssctxt = authctxt->methoddata;
+
+       mic.value = packet_get_string(&len);
+       mic.length = len;
+
+       ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service,
+           "gssapi-with-mic");
+
+       gssbuf.value = buffer_ptr(&b);
+       gssbuf.length = buffer_len(&b);
+
+       if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
+               authenticated = 
+                   PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
+       else
+               logit("GSSAPI MIC check failed");
+
+       buffer_free(&b);
+       xfree(mic.value);
+
+       authctxt->postponed = 0;
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
+       userauth_finish(authctxt, authenticated, "gssapi-with-mic");
+}
+
+Authmethod method_gsskeyex = {
+       "gssapi-keyex",
+       userauth_gsskeyex,
+       &options.gss_authentication
+};
+
+Authmethod method_gssapi = {
+       "gssapi-with-mic",
+       userauth_gssapi,
+       &options.gss_authentication
+};
+
+#endif /* GSSAPI */
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth2.c b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/auth2.c
new file mode 100644 (file)
index 0000000..52acf46
--- /dev/null
@@ -0,0 +1,417 @@
+/* $OpenBSD: auth2.c,v 1.123 2011/03/10 02:52:57 djm Exp $ */
+/*
+ * 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.
+ *
+ * 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"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atomicio.h"
+#include "xmalloc.h"
+#include "ssh2.h"
+#include "packet.h"
+#include "log.h"
+#include "buffer.h"
+#include "servconf.h"
+#include "compat.h"
+#include "key.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "dispatch.h"
+#include "pathnames.h"
+#include "buffer.h"
+
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+#include "monitor_wrap.h"
+
+/* import */
+extern ServerOptions options;
+extern u_char *session_id2;
+extern u_int session_id2_len;
+extern Buffer loginmsg;
+
+/* methods */
+
+extern Authmethod method_none;
+extern Authmethod method_pubkey;
+extern Authmethod method_passwd;
+extern Authmethod method_kbdint;
+extern Authmethod method_hostbased;
+#ifdef GSSAPI
+extern Authmethod method_gsskeyex;
+extern Authmethod method_gssapi;
+#endif
+#ifdef JPAKE
+extern Authmethod method_jpake;
+#endif
+
+Authmethod *authmethods[] = {
+       &method_none,
+       &method_pubkey,
+#ifdef GSSAPI
+       &method_gsskeyex,
+       &method_gssapi,
+#endif
+#ifdef JPAKE
+       &method_jpake,
+#endif
+       &method_passwd,
+       &method_kbdint,
+       &method_hostbased,
+       NULL
+};
+
+/* protocol */
+
+static void input_service_request(int, u_int32_t, void *);
+static void input_userauth_request(int, u_int32_t, void *);
+
+/* helper */
+static Authmethod *authmethod_lookup(const char *);
+static char *authmethods_get(void);
+
+char *
+auth2_read_banner(void)
+{
+       struct stat st;
+       char *banner = NULL;
+       size_t len, n;
+       int fd;
+
+       if ((fd = open(options.banner, O_RDONLY)) == -1)
+               return (NULL);
+       if (fstat(fd, &st) == -1) {
+               close(fd);
+               return (NULL);
+       }
+       if (st.st_size > 1*1024*1024) {
+               close(fd);
+               return (NULL);
+       }
+
+       len = (size_t)st.st_size;               /* truncate */
+       banner = xmalloc(len + 1);
+       n = atomicio(read, fd, banner, len);
+       close(fd);
+
+       if (n != len) {
+               xfree(banner);
+               return (NULL);
+       }
+       banner[n] = '\0';
+
+       return (banner);
+}
+
+void
+userauth_send_banner(const char *msg)
+{
+       if (datafellows & SSH_BUG_BANNER)
+               return;
+
+       packet_start(SSH2_MSG_USERAUTH_BANNER);
+       packet_put_cstring(msg);
+       packet_put_cstring("");         /* language, unused */
+       packet_send();
+       debug("%s: sent", __func__);
+}
+
+static void
+userauth_banner(void)
+{
+       char *banner = NULL;
+
+       if (options.banner == NULL ||
+           strcasecmp(options.banner, "none") == 0 ||
+           (datafellows & SSH_BUG_BANNER) != 0)
+               return;
+
+       if ((banner = PRIVSEP(auth2_read_banner())) == NULL)
+               goto done;
+       userauth_send_banner(banner);
+
+done:
+       if (banner)
+               xfree(banner);
+}
+
+/*
+ * loop until authctxt->success == TRUE
+ */
+void
+do_authentication2(Authctxt *authctxt)
+{
+       dispatch_init(&dispatch_protocol_error);
+       dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
+       dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
+}
+
+/*ARGSUSED*/
+static void
+input_service_request(int type, u_int32_t seq, void *ctxt)
+{
+       Authctxt *authctxt = ctxt;
+       u_int len;
+       int acceptit = 0;
+       char *service = packet_get_cstring(&len);
+       packet_check_eom();
+
+       if (authctxt == NULL)
+               fatal("input_service_request: no authctxt");
+
+       if (strcmp(service, "ssh-userauth") == 0) {
+               if (!authctxt->success) {
+                       acceptit = 1;
+                       /* now we can handle user-auth requests */
+                       dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
+               }
+       }
+       /* XXX all other service requests are denied */
+
+       if (acceptit) {
+               packet_start(SSH2_MSG_SERVICE_ACCEPT);
+               packet_put_cstring(service);
+               packet_send();
+               packet_write_wait();
+       } else {
+               debug("bad service request %s", service);
+               packet_disconnect("bad service request %s", service);
+       }
+       xfree(service);
+}
+
+/*ARGSUSED*/
+static void
+input_userauth_request(int type, u_int32_t seq, void *ctxt)
+{
+       Authctxt *authctxt = ctxt;
+       Authmethod *m = NULL;
+       char *user, *service, *method, *style = NULL, *role = NULL;
+       int authenticated = 0;
+
+       if (authctxt == NULL)
+               fatal("input_userauth_request: no authctxt");
+
+       user = packet_get_cstring(NULL);
+       service = packet_get_cstring(NULL);
+       method = packet_get_cstring(NULL);
+       debug("userauth-request for user %s service %s method %s", user, service, method);
+       debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
+
+       if ((role = strchr(user, '/')) != NULL)
+               *role++ = 0;
+
+       if ((style = strchr(user, ':')) != NULL)
+               *style++ = 0;
+       else if (role && (style = strchr(role, ':')) != NULL)
+               *style++ = '\0';
+
+       if (authctxt->attempt++ == 0) {
+               /* setup auth context */
+               authctxt->pw = PRIVSEP(getpwnamallow(user));
+               authctxt->user = xstrdup(user);
+               if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
+                       authctxt->valid = 1;
+                       debug2("input_userauth_request: setting up authctxt for %s", user);
+               } else {
+                       logit("input_userauth_request: invalid user %s", user);
+                       authctxt->pw = fakepw();
+#ifdef SSH_AUDIT_EVENTS
+                       PRIVSEP(audit_event(SSH_INVALID_USER));
+#endif
+               }
+#ifdef USE_PAM
+               if (options.use_pam)
+                       PRIVSEP(start_pam(authctxt));
+#endif
+               setproctitle("%s%s", authctxt->valid ? user : "unknown",
+                   use_privsep ? " [net]" : "");
+               authctxt->service = xstrdup(service);
+               authctxt->style = style ? xstrdup(style) : NULL;
+               authctxt->role = role ? xstrdup(role) : NULL;
+               if (use_privsep)
+                       mm_inform_authserv(service, style, role);
+               userauth_banner();
+       } else if (strcmp(user, authctxt->user) != 0 ||
+           strcmp(service, authctxt->service) != 0) {
+               packet_disconnect("Change of username or service not allowed: "
+                   "(%s,%s) -> (%s,%s)",
+                   authctxt->user, authctxt->service, user, service);
+       }
+       /* reset state */
+       auth2_challenge_stop(authctxt);
+#ifdef JPAKE
+       auth2_jpake_stop(authctxt);
+#endif
+
+#ifdef GSSAPI
+       /* XXX move to auth2_gssapi_stop() */
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
+#endif
+
+       authctxt->postponed = 0;
+       authctxt->server_caused_failure = 0;
+
+       /* try to authenticate user */
+       m = authmethod_lookup(method);
+       if (m != NULL && authctxt->failures < options.max_authtries) {
+               debug2("input_userauth_request: try method %s", method);
+               authenticated = m->userauth(authctxt);
+       }
+       userauth_finish(authctxt, authenticated, method);
+
+       xfree(service);
+       xfree(user);
+       xfree(method);
+}
+
+void
+userauth_finish(Authctxt *authctxt, int authenticated, char *method)
+{
+       char *methods;
+
+       if (!authctxt->valid && authenticated)
+               fatal("INTERNAL ERROR: authenticated invalid user %s",
+                   authctxt->user);
+
+       /* Special handling for root */
+       if (authenticated && authctxt->pw->pw_uid == 0 &&
+           !auth_root_allowed(method)) {
+               authenticated = 0;
+#ifdef SSH_AUDIT_EVENTS
+               PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED));
+#endif
+       }
+
+#ifdef USE_PAM
+       if (options.use_pam && authenticated) {
+               if (!PRIVSEP(do_pam_account())) {
+                       /* if PAM returned a message, send it to the user */
+                       if (buffer_len(&loginmsg) > 0) {
+                               buffer_append(&loginmsg, "\0", 1);
+                               userauth_send_banner(buffer_ptr(&loginmsg));
+                               packet_write_wait();
+                       }
+                       fatal("Access denied for user %s by PAM account "
+                           "configuration", authctxt->user);
+               }
+       }
+#endif
+
+#ifdef _UNICOS
+       if (authenticated && cray_access_denied(authctxt->user)) {
+               authenticated = 0;
+               fatal("Access denied for user %s.",authctxt->user);
+       }
+#endif /* _UNICOS */
+
+       /* Log before sending the reply */
+       auth_log(authctxt, authenticated, method, " ssh2");
+
+       if (authctxt->postponed)
+               return;
+
+       /* XXX todo: check if multiple auth methods are needed */
+       if (authenticated == 1) {
+               /* turn off userauth */
+               dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
+               packet_start(SSH2_MSG_USERAUTH_SUCCESS);
+               packet_send();
+               packet_write_wait();
+               /* now we can break out */
+               authctxt->success = 1;
+       } else {
+
+               /* Allow initial try of "none" auth without failure penalty */
+               if (!authctxt->server_caused_failure &&
+                   (authctxt->attempt > 1 || strcmp(method, "none") != 0))
+                       authctxt->failures++;
+               if (authctxt->failures >= options.max_authtries) {
+#ifdef SSH_AUDIT_EVENTS
+                       PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES));
+#endif
+                       packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
+               }
+               methods = authmethods_get();
+               packet_start(SSH2_MSG_USERAUTH_FAILURE);
+               packet_put_cstring(methods);
+               packet_put_char(0);     /* XXX partial success, unused */
+               packet_send();
+               packet_write_wait();
+               xfree(methods);
+       }
+}
+
+static char *
+authmethods_get(void)
+{
+       Buffer b;
+       char *list;
+       int i;
+
+       buffer_init(&b);
+       for (i = 0; authmethods[i] != NULL; i++) {
+               if (strcmp(authmethods[i]->name, "none") == 0)
+                       continue;
+               if (authmethods[i]->enabled != NULL &&
+                   *(authmethods[i]->enabled) != 0) {
+                       if (buffer_len(&b) > 0)
+                               buffer_append(&b, ",", 1);
+                       buffer_append(&b, authmethods[i]->name,
+                           strlen(authmethods[i]->name));
+               }
+       }
+       buffer_append(&b, "\0", 1);
+       list = xstrdup(buffer_ptr(&b));
+       buffer_free(&b);
+       return list;
+}
+
+static Authmethod *
+authmethod_lookup(const char *name)
+{
+       int i;
+
+       if (name != NULL)
+               for (i = 0; authmethods[i] != NULL; i++)
+                       if (authmethods[i]->enabled != NULL &&
+                           *(authmethods[i]->enabled) != 0 &&
+                           strcmp(name, authmethods[i]->name) == 0)
+                               return authmethods[i];
+       debug2("Unrecognized authentication method name: %s",
+           name ? name : "NULL");
+       return NULL;
+}
+
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/gss-serv.c b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/gss-serv.c
new file mode 100644 (file)
index 0000000..380895e
--- /dev/null
@@ -0,0 +1,531 @@
+/* $OpenBSD: gss-serv.c,v 1.23 2011/08/01 19:18:15 markus Exp $ */
+
+/*
+ * Copyright (c) 2001-2009 Simon Wilkinson. 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"
+
+#ifdef GSSAPI
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "openbsd-compat/sys-queue.h"
+#include "xmalloc.h"
+#include "buffer.h"
+#include "key.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "log.h"
+#include "channels.h"
+#include "session.h"
+#include "misc.h"
+#include "servconf.h"
+#include "uidswap.h"
+
+#include "ssh-gss.h"
+#include "monitor_wrap.h"
+
+extern ServerOptions options;
+
+static ssh_gssapi_client gssapi_client =
+    { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
+    GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME,  NULL, {NULL, NULL, NULL}, 0, 0};
+
+ssh_gssapi_mech gssapi_null_mech =
+    { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
+
+#ifdef KRB5
+extern ssh_gssapi_mech gssapi_kerberos_mech;
+#endif
+
+ssh_gssapi_mech* supported_mechs[]= {
+#ifdef KRB5
+       &gssapi_kerberos_mech,
+#endif
+       &gssapi_null_mech,
+};
+
+
+/*
+ * Acquire credentials for a server running on the current host.
+ * Requires that the context structure contains a valid OID
+ */
+
+/* Returns a GSSAPI error code */
+/* Privileged (called from ssh_gssapi_server_ctx) */
+static OM_uint32
+ssh_gssapi_acquire_cred(Gssctxt *ctx)
+{
+       OM_uint32 status;
+       char lname[MAXHOSTNAMELEN];
+       gss_OID_set oidset;
+
+       if (options.gss_strict_acceptor) {
+               gss_create_empty_oid_set(&status, &oidset);
+               gss_add_oid_set_member(&status, ctx->oid, &oidset);
+
+               if (gethostname(lname, MAXHOSTNAMELEN)) {
+                       gss_release_oid_set(&status, &oidset);
+                       return (-1);
+               }
+
+               if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
+                       gss_release_oid_set(&status, &oidset);
+                       return (ctx->major);
+               }
+
+               if ((ctx->major = gss_acquire_cred(&ctx->minor,
+                   ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, 
+                   NULL, NULL)))
+                       ssh_gssapi_error(ctx);
+
+               gss_release_oid_set(&status, &oidset);
+               return (ctx->major);
+       } else {
+               ctx->name = GSS_C_NO_NAME;
+               ctx->creds = GSS_C_NO_CREDENTIAL;
+       }
+       return GSS_S_COMPLETE;
+}
+
+/* Privileged */
+OM_uint32
+ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
+{
+       if (*ctx)
+               ssh_gssapi_delete_ctx(ctx);
+       ssh_gssapi_build_ctx(ctx);
+       ssh_gssapi_set_oid(*ctx, oid);
+       return (ssh_gssapi_acquire_cred(*ctx));
+}
+
+/* Unprivileged */
+char *
+ssh_gssapi_server_mechanisms() {
+       gss_OID_set     supported;
+
+       ssh_gssapi_supported_oids(&supported);
+       return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
+           NULL, NULL));
+}
+
+/* Unprivileged */
+int
+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
+    const char *dummy) {
+       Gssctxt *ctx = NULL;
+       int res;
+       res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
+       ssh_gssapi_delete_ctx(&ctx);
+
+       return (res);
+}
+
+/* Unprivileged */
+void
+ssh_gssapi_supported_oids(gss_OID_set *oidset)
+{
+       int i = 0;
+       OM_uint32 min_status;
+       int present;
+       gss_OID_set supported;
+
+       gss_create_empty_oid_set(&min_status, oidset);
+
+       if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
+               return;
+
+       while (supported_mechs[i]->name != NULL) {
+               if (GSS_ERROR(gss_test_oid_set_member(&min_status,
+                   &supported_mechs[i]->oid, supported, &present)))
+                       present = 0;
+               if (present)
+                       gss_add_oid_set_member(&min_status,
+                           &supported_mechs[i]->oid, oidset);
+               i++;
+       }
+
+       gss_release_oid_set(&min_status, &supported);
+}
+
+
+/* Wrapper around accept_sec_context
+ * Requires that the context contains:
+ *    oid
+ *    credentials      (from ssh_gssapi_acquire_cred)
+ */
+/* Privileged */
+OM_uint32
+ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok,
+    gss_buffer_desc *send_tok, OM_uint32 *flags)
+{
+       OM_uint32 status;
+       gss_OID mech;
+
+       ctx->major = gss_accept_sec_context(&ctx->minor,
+           &ctx->context, ctx->creds, recv_tok,
+           GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech,
+           send_tok, flags, NULL, &ctx->client_creds);
+
+       if (GSS_ERROR(ctx->major))
+               ssh_gssapi_error(ctx);
+
+       if (ctx->client_creds)
+               debug("Received some client credentials");
+       else
+               debug("Got no client credentials");
+
+       status = ctx->major;
+
+       /* Now, if we're complete and we have the right flags, then
+        * we flag the user as also having been authenticated
+        */
+
+       if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) &&
+           (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) {
+               if (ssh_gssapi_getclient(ctx, &gssapi_client))
+                       fatal("Couldn't convert client name");
+       }
+
+       return (status);
+}
+
+/*
+ * This parses an exported name, extracting the mechanism specific portion
+ * to use for ACL checking. It verifies that the name belongs the mechanism
+ * originally selected.
+ */
+static OM_uint32
+ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name)
+{
+       u_char *tok;
+       OM_uint32 offset;
+       OM_uint32 oidl;
+
+       tok = ename->value;
+
+       /*
+        * Check that ename is long enough for all of the fixed length
+        * header, and that the initial ID bytes are correct
+        */
+
+       if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0)
+               return GSS_S_FAILURE;
+
+       /*
+        * Extract the OID, and check it. Here GSSAPI breaks with tradition
+        * and does use the OID type and length bytes. To confuse things
+        * there are two lengths - the first including these, and the
+        * second without.
+        */
+
+       oidl = get_u16(tok+2); /* length including next two bytes */
+       oidl = oidl-2; /* turn it into the _real_ length of the variable OID */
+
+       /*
+        * Check the BER encoding for correct type and length, that the
+        * string is long enough and that the OID matches that in our context
+        */
+       if (tok[4] != 0x06 || tok[5] != oidl ||
+           ename->length < oidl+6 ||
+           !ssh_gssapi_check_oid(ctx, tok+6, oidl))
+               return GSS_S_FAILURE;
+
+       offset = oidl+6;
+
+       if (ename->length < offset+4)
+               return GSS_S_FAILURE;
+
+       name->length = get_u32(tok+offset);
+       offset += 4;
+
+       if (UINT_MAX - offset < name->length)
+               return GSS_S_FAILURE;
+       if (ename->length < offset+name->length)
+               return GSS_S_FAILURE;
+
+       name->value = xmalloc(name->length+1);
+       memcpy(name->value, tok+offset, name->length);
+       ((char *)name->value)[name->length] = 0;
+
+       return GSS_S_COMPLETE;
+}
+
+/* Extract the client details from a given context. This can only reliably
+ * be called once for a context */
+
+/* Privileged (called from accept_secure_ctx) */
+OM_uint32
+ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
+{
+       int i = 0;
+       int equal = 0;
+       gss_name_t new_name = GSS_C_NO_NAME;
+       gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
+
+       if (options.gss_store_rekey && client->used && ctx->client_creds) {
+               if (client->mech->oid.length != ctx->oid->length ||
+                   (memcmp(client->mech->oid.elements,
+                    ctx->oid->elements, ctx->oid->length) !=0)) {
+                       debug("Rekeyed credentials have different mechanism");
+                       return GSS_S_COMPLETE;
+               }
+
+               if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, 
+                   ctx->client_creds, ctx->oid, &new_name, 
+                   NULL, NULL, NULL))) {
+                       ssh_gssapi_error(ctx);
+                       return (ctx->major);
+               }
+
+               ctx->major = gss_compare_name(&ctx->minor, client->name, 
+                   new_name, &equal);
+
+               if (GSS_ERROR(ctx->major)) {
+                       ssh_gssapi_error(ctx);
+                       return (ctx->major);
+               }
+               if (!equal) {
+                       debug("Rekeyed credentials have different name");
+                       return GSS_S_COMPLETE;
+               }
+
+               debug("Marking rekeyed credentials for export");
+
+               gss_release_name(&ctx->minor, &client->name);
+               gss_release_cred(&ctx->minor, &client->creds);
+               client->name = new_name;
+               client->creds = ctx->client_creds;
+               ctx->client_creds = GSS_C_NO_CREDENTIAL;
+               client->updated = 1;
+               return GSS_S_COMPLETE;
+       }
+
+       client->mech = NULL;
+
+       while (supported_mechs[i]->name != NULL) {
+               if (supported_mechs[i]->oid.length == ctx->oid->length &&
+                   (memcmp(supported_mechs[i]->oid.elements,
+                   ctx->oid->elements, ctx->oid->length) == 0))
+                       client->mech = supported_mechs[i];
+               i++;
+       }
+
+       if (client->mech == NULL)
+               return GSS_S_FAILURE;
+
+       if (ctx->client_creds &&
+           (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
+            ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
+               ssh_gssapi_error(ctx);
+               return (ctx->major);
+       }
+
+       if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
+           &client->displayname, NULL))) {
+               ssh_gssapi_error(ctx);
+               return (ctx->major);
+       }
+
+       if ((ctx->major = gss_export_name(&ctx->minor, ctx->client,
+           &ename))) {
+               ssh_gssapi_error(ctx);
+               return (ctx->major);
+       }
+
+       if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
+           &client->exportedname))) {
+               return (ctx->major);
+       }
+
+       gss_release_buffer(&ctx->minor, &ename);
+
+       /* We can't copy this structure, so we just move the pointer to it */
+       client->creds = ctx->client_creds;
+       ctx->client_creds = GSS_C_NO_CREDENTIAL;
+       return (ctx->major);
+}
+
+/* As user - called on fatal/exit */
+void
+ssh_gssapi_cleanup_creds(void)
+{
+       if (gssapi_client.store.filename != NULL) {
+               /* Unlink probably isn't sufficient */
+               debug("removing gssapi cred file\"%s\"",
+                   gssapi_client.store.filename);
+               unlink(gssapi_client.store.filename);
+       }
+}
+
+/* As user */
+void
+ssh_gssapi_storecreds(void)
+{
+       if (gssapi_client.mech && gssapi_client.mech->storecreds) {
+               (*gssapi_client.mech->storecreds)(&gssapi_client);
+       } else
+               debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
+}
+
+/* This allows GSSAPI methods to do things to the childs environment based
+ * on the passed authentication process and credentials.
+ */
+/* As user */
+void
+ssh_gssapi_do_child(char ***envp, u_int *envsizep)
+{
+
+       if (gssapi_client.store.envvar != NULL &&
+           gssapi_client.store.envval != NULL) {
+               debug("Setting %s to %s", gssapi_client.store.envvar,
+                   gssapi_client.store.envval);
+               child_set_env(envp, envsizep, gssapi_client.store.envvar,
+                   gssapi_client.store.envval);
+       }
+}
+
+/* Privileged */
+int
+ssh_gssapi_userok(char *user, struct passwd *pw)
+{
+       OM_uint32 lmin;
+
+       if (gssapi_client.exportedname.length == 0 ||
+           gssapi_client.exportedname.value == NULL) {
+               debug("No suitable client data");
+               return 0;
+       }
+       if (gssapi_client.mech && gssapi_client.mech->userok)
+               if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
+                       gssapi_client.used = 1;
+                       gssapi_client.store.owner = pw;
+                       return 1;
+               } else {
+                       /* Destroy delegated credentials if userok fails */
+                       gss_release_buffer(&lmin, &gssapi_client.displayname);
+                       gss_release_buffer(&lmin, &gssapi_client.exportedname);
+                       gss_release_cred(&lmin, &gssapi_client.creds);
+                       memset(&gssapi_client, 0, sizeof(ssh_gssapi_client));
+                       return 0;
+               }
+       else
+               debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
+       return (0);
+}
+
+/* These bits are only used for rekeying. The unpriviledged child is running 
+ * as the user, the monitor is root.
+ *
+ * In the child, we want to :
+ *    *) Ask the monitor to store our credentials into the store we specify
+ *    *) If it succeeds, maybe do a PAM update
+ */
+
+/* Stuff for PAM */
+
+#ifdef USE_PAM
+static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, 
+    struct pam_response **resp, void *data)
+{
+       return (PAM_CONV_ERR);
+}
+#endif
+
+void
+ssh_gssapi_rekey_creds() {
+       int ok;
+       int ret;
+#ifdef USE_PAM
+       pam_handle_t *pamh = NULL;
+       struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
+       char *envstr;
+#endif
+
+       if (gssapi_client.store.filename == NULL && 
+           gssapi_client.store.envval == NULL &&
+           gssapi_client.store.envvar == NULL)
+               return;
+       ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
+
+       if (!ok)
+               return;
+
+       debug("Rekeyed credentials stored successfully");
+
+       /* Actually managing to play with the ssh pam stack from here will
+        * be next to impossible. In any case, we may want different options
+        * for rekeying. So, use our own :)
+        */
+#ifdef USE_PAM 
+       if (!use_privsep) {
+               debug("Not even going to try and do PAM with privsep disabled");
+               return;
+       }
+
+       ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
+           &pamconv, &pamh);
+       if (ret)
+               return;
+
+       xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, 
+           gssapi_client.store.envval);
+
+       ret = pam_putenv(pamh, envstr);
+       if (!ret)
+               pam_setcred(pamh, PAM_REINITIALIZE_CRED);
+       pam_end(pamh, PAM_SUCCESS);
+#endif
+}
+
+int 
+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
+       int ok = 0;
+
+       /* Check we've got credentials to store */
+       if (!gssapi_client.updated)
+               return 0;
+
+       gssapi_client.updated = 0;
+
+       temporarily_use_uid(gssapi_client.store.owner);
+       if (gssapi_client.mech && gssapi_client.mech->updatecreds)
+               ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
+       else
+               debug("No update function for this mechanism");
+
+       restore_uid();
+
+       return ok;
+}
+
+#endif
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/misc.c b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/misc.c
new file mode 100644 (file)
index 0000000..1814ae3
--- /dev/null
@@ -0,0 +1,1061 @@
+/* $OpenBSD: misc.c,v 1.85 2011/03/29 18:54:17 stevesk Exp $ */
+/*
+ * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2005,2006 Damien Miller.  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"
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+#include <pwd.h>
+#include <grp.h>
+#ifdef SSH_TUN_OPENBSD
+#include <net/if.h>
+#endif
+
+#include "xmalloc.h"
+#include "misc.h"
+#include "log.h"
+#include "ssh.h"
+
+/* remove newline at end of string */
+char *
+chop(char *s)
+{
+       char *t = s;
+       while (*t) {
+               if (*t == '\n' || *t == '\r') {
+                       *t = '\0';
+                       return s;
+               }
+               t++;
+       }
+       return s;
+
+}
+
+/* set/unset filedescriptor to non-blocking */
+int
+set_nonblock(int fd)
+{
+       int val;
+
+       val = fcntl(fd, F_GETFL, 0);
+       if (val < 0) {
+               error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
+               return (-1);
+       }
+       if (val & O_NONBLOCK) {
+               debug3("fd %d is O_NONBLOCK", fd);
+               return (0);
+       }
+       debug2("fd %d setting O_NONBLOCK", fd);
+       val |= O_NONBLOCK;
+       if (fcntl(fd, F_SETFL, val) == -1) {
+               debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd,
+                   strerror(errno));
+               return (-1);
+       }
+       return (0);
+}
+
+int
+unset_nonblock(int fd)
+{
+       int val;
+
+       val = fcntl(fd, F_GETFL, 0);
+       if (val < 0) {
+               error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
+               return (-1);
+       }
+       if (!(val & O_NONBLOCK)) {
+               debug3("fd %d is not O_NONBLOCK", fd);
+               return (0);
+       }
+       debug("fd %d clearing O_NONBLOCK", fd);
+       val &= ~O_NONBLOCK;
+       if (fcntl(fd, F_SETFL, val) == -1) {
+               debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s",
+                   fd, strerror(errno));
+               return (-1);
+       }
+       return (0);
+}
+
+const char *
+ssh_gai_strerror(int gaierr)
+{
+       if (gaierr == EAI_SYSTEM)
+               return strerror(errno);
+       return gai_strerror(gaierr);
+}
+
+/* disable nagle on socket */
+void
+set_nodelay(int fd)
+{
+       int opt;
+       socklen_t optlen;
+
+       optlen = sizeof opt;
+       if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) {
+               debug("getsockopt TCP_NODELAY: %.100s", strerror(errno));
+               return;
+       }
+       if (opt == 1) {
+               debug2("fd %d is TCP_NODELAY", fd);
+               return;
+       }
+       opt = 1;
+       debug2("fd %d setting TCP_NODELAY", fd);
+       if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1)
+               error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
+}
+
+/* Characters considered whitespace in strsep calls. */
+#define WHITESPACE " \t\r\n"
+#define QUOTE  "\""
+
+/* return next token in configuration line */
+char *
+strdelim(char **s)
+{
+       char *old;
+       int wspace = 0;
+
+       if (*s == NULL)
+               return NULL;
+
+       old = *s;
+
+       *s = strpbrk(*s, WHITESPACE QUOTE "=");
+       if (*s == NULL)
+               return (old);
+
+       if (*s[0] == '\"') {
+               memmove(*s, *s + 1, strlen(*s)); /* move nul too */
+               /* Find matching quote */
+               if ((*s = strpbrk(*s, QUOTE)) == NULL) {
+                       return (NULL);          /* no matching quote */
+               } else {
+                       *s[0] = '\0';
+                       *s += strspn(*s + 1, WHITESPACE) + 1;
+                       return (old);
+               }
+       }
+
+       /* Allow only one '=' to be skipped */
+       if (*s[0] == '=')
+               wspace = 1;
+       *s[0] = '\0';
+
+       /* Skip any extra whitespace after first token */
+       *s += strspn(*s + 1, WHITESPACE) + 1;
+       if (*s[0] == '=' && !wspace)
+               *s += strspn(*s + 1, WHITESPACE) + 1;
+
+       return (old);
+}
+
+struct passwd *
+pwcopy(struct passwd *pw)
+{
+       struct passwd *copy = xcalloc(1, sizeof(*copy));
+
+       copy->pw_name = xstrdup(pw->pw_name);
+       copy->pw_passwd = xstrdup(pw->pw_passwd);
+       copy->pw_gecos = xstrdup(pw->pw_gecos);
+       copy->pw_uid = pw->pw_uid;
+       copy->pw_gid = pw->pw_gid;
+#ifdef HAVE_PW_EXPIRE_IN_PASSWD
+       copy->pw_expire = pw->pw_expire;
+#endif
+#ifdef HAVE_PW_CHANGE_IN_PASSWD
+       copy->pw_change = pw->pw_change;
+#endif
+#ifdef HAVE_PW_CLASS_IN_PASSWD
+       copy->pw_class = xstrdup(pw->pw_class);
+#endif
+       copy->pw_dir = xstrdup(pw->pw_dir);
+       copy->pw_shell = xstrdup(pw->pw_shell);
+       return copy;
+}
+
+/*
+ * Convert ASCII string to TCP/IP port number.
+ * Port must be >=0 and <=65535.
+ * Return -1 if invalid.
+ */
+int
+a2port(const char *s)
+{
+       long long port;
+       const char *errstr;
+
+       port = strtonum(s, 0, 65535, &errstr);
+       if (errstr != NULL)
+               return -1;
+       return (int)port;
+}
+
+int
+a2tun(const char *s, int *remote)
+{
+       const char *errstr = NULL;
+       char *sp, *ep;
+       int tun;
+
+       if (remote != NULL) {
+               *remote = SSH_TUNID_ANY;
+               sp = xstrdup(s);
+               if ((ep = strchr(sp, ':')) == NULL) {
+                       xfree(sp);
+                       return (a2tun(s, NULL));
+               }
+               ep[0] = '\0'; ep++;
+               *remote = a2tun(ep, NULL);
+               tun = a2tun(sp, NULL);
+               xfree(sp);
+               return (*remote == SSH_TUNID_ERR ? *remote : tun);
+       }
+
+       if (strcasecmp(s, "any") == 0)
+               return (SSH_TUNID_ANY);
+
+       tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr);
+       if (errstr != NULL)
+               return (SSH_TUNID_ERR);
+
+       return (tun);
+}
+
+#define SECONDS                1
+#define MINUTES                (SECONDS * 60)
+#define HOURS          (MINUTES * 60)
+#define DAYS           (HOURS * 24)
+#define WEEKS          (DAYS * 7)
+
+/*
+ * Convert a time string into seconds; format is
+ * a sequence of:
+ *      time[qualifier]
+ *
+ * Valid time qualifiers are:
+ *      <none>  seconds
+ *      s|S     seconds
+ *      m|M     minutes
+ *      h|H     hours
+ *      d|D     days
+ *      w|W     weeks
+ *
+ * Examples:
+ *      90m     90 minutes
+ *      1h30m   90 minutes
+ *      2d      2 days
+ *      1w      1 week
+ *
+ * Return -1 if time string is invalid.
+ */
+long
+convtime(const char *s)
+{
+       long total, secs;
+       const char *p;
+       char *endp;
+
+       errno = 0;
+       total = 0;
+       p = s;
+
+       if (p == NULL || *p == '\0')
+               return -1;
+
+       while (*p) {
+               secs = strtol(p, &endp, 10);
+               if (p == endp ||
+                   (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) ||
+                   secs < 0)
+                       return -1;
+
+               switch (*endp++) {
+               case '\0':
+                       endp--;
+                       break;
+               case 's':
+               case 'S':
+                       break;
+               case 'm':
+               case 'M':
+                       secs *= MINUTES;
+                       break;
+               case 'h':
+               case 'H':
+                       secs *= HOURS;
+                       break;
+               case 'd':
+               case 'D':
+                       secs *= DAYS;
+                       break;
+               case 'w':
+               case 'W':
+                       secs *= WEEKS;
+                       break;
+               default:
+                       return -1;
+               }
+               total += secs;
+               if (total < 0)
+                       return -1;
+               p = endp;
+       }
+
+       return total;
+}
+
+/*
+ * Returns a standardized host+port identifier string.
+ * Caller must free returned string.
+ */
+char *
+put_host_port(const char *host, u_short port)
+{
+       char *hoststr;
+
+       if (port == 0 || port == SSH_DEFAULT_PORT)
+               return(xstrdup(host));
+       if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0)
+               fatal("put_host_port: asprintf: %s", strerror(errno));
+       debug3("put_host_port: %s", hoststr);
+       return hoststr;
+}
+
+/*
+ * Search for next delimiter between hostnames/addresses and ports.
+ * Argument may be modified (for termination).
+ * Returns *cp if parsing succeeds.
+ * *cp is set to the start of the next delimiter, if one was found.
+ * If this is the last field, *cp is set to NULL.
+ */
+char *
+hpdelim(char **cp)
+{
+       char *s, *old;
+
+       if (cp == NULL || *cp == NULL)
+               return NULL;
+
+       old = s = *cp;
+       if (*s == '[') {
+               if ((s = strchr(s, ']')) == NULL)
+                       return NULL;
+               else
+                       s++;
+       } else if ((s = strpbrk(s, ":/")) == NULL)
+               s = *cp + strlen(*cp); /* skip to end (see first case below) */
+
+       switch (*s) {
+       case '\0':
+               *cp = NULL;     /* no more fields*/
+               break;
+
+       case ':':
+       case '/':
+               *s = '\0';      /* terminate */
+               *cp = s + 1;
+               break;
+
+       default:
+               return NULL;
+       }
+
+       return old;
+}
+
+char *
+cleanhostname(char *host)
+{
+       if (*host == '[' && host[strlen(host) - 1] == ']') {
+               host[strlen(host) - 1] = '\0';
+               return (host + 1);
+       } else
+               return host;
+}
+
+char *
+colon(char *cp)
+{
+       int flag = 0;
+
+       if (*cp == ':')         /* Leading colon is part of file name. */
+               return NULL;
+       if (*cp == '[')
+               flag = 1;
+
+       for (; *cp; ++cp) {
+               if (*cp == '@' && *(cp+1) == '[')
+                       flag = 1;
+               if (*cp == ']' && *(cp+1) == ':' && flag)
+                       return (cp+1);
+               if (*cp == ':' && !flag)
+                       return (cp);
+               if (*cp == '/')
+                       return NULL;
+       }
+       return NULL;
+}
+
+/* function to assist building execv() arguments */
+void
+addargs(arglist *args, char *fmt, ...)
+{
+       va_list ap;
+       char *cp;
+       u_int nalloc;
+       int r;
+
+       va_start(ap, fmt);
+       r = vasprintf(&cp, fmt, ap);
+       va_end(ap);
+       if (r == -1)
+               fatal("addargs: argument too long");
+
+       nalloc = args->nalloc;
+       if (args->list == NULL) {
+               nalloc = 32;
+               args->num = 0;
+       } else if (args->num+2 >= nalloc)
+               nalloc *= 2;
+
+       args->list = xrealloc(args->list, nalloc, sizeof(char *));
+       args->nalloc = nalloc;
+       args->list[args->num++] = cp;
+       args->list[args->num] = NULL;
+}
+
+void
+replacearg(arglist *args, u_int which, char *fmt, ...)
+{
+       va_list ap;
+       char *cp;
+       int r;
+
+       va_start(ap, fmt);
+       r = vasprintf(&cp, fmt, ap);
+       va_end(ap);
+       if (r == -1)
+               fatal("replacearg: argument too long");
+
+       if (which >= args->num)
+               fatal("replacearg: tried to replace invalid arg %d >= %d",
+                   which, args->num);
+       xfree(args->list[which]);
+       args->list[which] = cp;
+}
+
+void
+freeargs(arglist *args)
+{
+       u_int i;
+
+       if (args->list != NULL) {
+               for (i = 0; i < args->num; i++)
+                       xfree(args->list[i]);
+               xfree(args->list);
+               args->nalloc = args->num = 0;
+               args->list = NULL;
+       }
+}
+
+/*
+ * Expands tildes in the file name.  Returns data allocated by xmalloc.
+ * Warning: this calls getpw*.
+ */
+char *
+tilde_expand_filename(const char *filename, uid_t uid)
+{
+       const char *path;
+       char user[128], ret[MAXPATHLEN];
+       struct passwd *pw;
+       u_int len, slash;
+
+       if (*filename != '~')
+               return (xstrdup(filename));
+       filename++;
+
+       path = strchr(filename, '/');
+       if (path != NULL && path > filename) {          /* ~user/path */
+               slash = path - filename;
+               if (slash > sizeof(user) - 1)
+                       fatal("tilde_expand_filename: ~username too long");
+               memcpy(user, filename, slash);
+               user[slash] = '\0';
+               if ((pw = getpwnam(user)) == NULL)
+                       fatal("tilde_expand_filename: No such user %s", user);
+       } else if ((pw = getpwuid(uid)) == NULL)        /* ~/path */
+               fatal("tilde_expand_filename: No such uid %ld", (long)uid);
+
+       if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret))
+               fatal("tilde_expand_filename: Path too long");
+
+       /* Make sure directory has a trailing '/' */
+       len = strlen(pw->pw_dir);
+       if ((len == 0 || pw->pw_dir[len - 1] != '/') &&
+           strlcat(ret, "/", sizeof(ret)) >= sizeof(ret))
+               fatal("tilde_expand_filename: Path too long");
+
+       /* Skip leading '/' from specified path */
+       if (path != NULL)
+               filename = path + 1;
+       if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret))
+               fatal("tilde_expand_filename: Path too long");
+
+       return (xstrdup(ret));
+}
+
+/*
+ * Expand a string with a set of %[char] escapes. A number of escapes may be
+ * specified as (char *escape_chars, char *replacement) pairs. The list must
+ * be terminated by a NULL escape_char. Returns replaced string in memory
+ * allocated by xmalloc.
+ */
+char *
+percent_expand(const char *string, ...)
+{
+#define EXPAND_MAX_KEYS        16
+       u_int num_keys, i, j;
+       struct {
+               const char *key;
+               const char *repl;
+       } keys[EXPAND_MAX_KEYS];
+       char buf[4096];
+       va_list ap;
+
+       /* Gather keys */
+       va_start(ap, string);
+       for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) {
+               keys[num_keys].key = va_arg(ap, char *);
+               if (keys[num_keys].key == NULL)
+                       break;
+               keys[num_keys].repl = va_arg(ap, char *);
+               if (keys[num_keys].repl == NULL)
+                       fatal("%s: NULL replacement", __func__);
+       }
+       if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL)
+               fatal("%s: too many keys", __func__);
+       va_end(ap);
+
+       /* Expand string */
+       *buf = '\0';
+       for (i = 0; *string != '\0'; string++) {
+               if (*string != '%') {
+ append:
+                       buf[i++] = *string;
+                       if (i >= sizeof(buf))
+                               fatal("%s: string too long", __func__);
+                       buf[i] = '\0';
+                       continue;
+               }
+               string++;
+               /* %% case */
+               if (*string == '%')
+                       goto append;
+               for (j = 0; j < num_keys; j++) {
+                       if (strchr(keys[j].key, *string) != NULL) {
+                               i = strlcat(buf, keys[j].repl, sizeof(buf));
+                               if (i >= sizeof(buf))
+                                       fatal("%s: string too long", __func__);
+                               break;
+                       }
+               }
+               if (j >= num_keys)
+                       fatal("%s: unknown key %%%c", __func__, *string);
+       }
+       return (xstrdup(buf));
+#undef EXPAND_MAX_KEYS
+}
+
+/*
+ * Read an entire line from a public key file into a static buffer, discarding
+ * lines that exceed the buffer size.  Returns 0 on success, -1 on failure.
+ */
+int
+read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
+   u_long *lineno)
+{
+       while (fgets(buf, bufsz, f) != NULL) {
+               if (buf[0] == '\0')
+                       continue;
+               (*lineno)++;
+               if (buf[strlen(buf) - 1] == '\n' || feof(f)) {
+                       return 0;
+               } else {
+                       debug("%s: %s line %lu exceeds size limit", __func__,
+                           filename, *lineno);
+                       /* discard remainder of line */
+                       while (fgetc(f) != '\n' && !feof(f))
+                               ;       /* nothing */
+               }
+       }
+       return -1;
+}
+
+int
+secure_permissions(struct stat *st, uid_t uid)
+{
+       if (st->st_uid != 0 && st->st_uid != uid)
+               return 0;
+       if ((st->st_mode & 002) != 0)
+               return 0;
+       if ((st->st_mode & 020) != 0) {
+               /* If the file is group-writable, the group in question must
+                * have exactly one member, namely the file's owner.
+                * (Zero-member groups are typically used by setgid
+                * binaries, and are unlikely to be suitable.)
+                */
+               struct passwd *pw;
+               struct group *gr;
+               int members = 0;
+
+               gr = getgrgid(st->st_gid);
+               if (!gr)
+                       return 0;
+
+               /* Check primary group memberships. */
+               while ((pw = getpwent()) != NULL) {
+                       if (pw->pw_gid == gr->gr_gid) {
+                               ++members;
+                               if (pw->pw_uid != uid)
+                                       return 0;
+                       }
+               }
+               endpwent();
+
+               pw = getpwuid(st->st_uid);
+               if (!pw)
+                       return 0;
+
+               /* Check supplementary group memberships. */
+               if (gr->gr_mem[0]) {
+                       ++members;
+                       if (strcmp(pw->pw_name, gr->gr_mem[0]) ||
+                           gr->gr_mem[1])
+                               return 0;
+               }
+
+               if (!members)
+                       return 0;
+       }
+       return 1;
+}
+
+int
+tun_open(int tun, int mode)
+{
+#if defined(CUSTOM_SYS_TUN_OPEN)
+       return (sys_tun_open(tun, mode));
+#elif defined(SSH_TUN_OPENBSD)
+       struct ifreq ifr;
+       char name[100];
+       int fd = -1, sock;
+
+       /* Open the tunnel device */
+       if (tun <= SSH_TUNID_MAX) {
+               snprintf(name, sizeof(name), "/dev/tun%d", tun);
+               fd = open(name, O_RDWR);
+       } else if (tun == SSH_TUNID_ANY) {
+               for (tun = 100; tun >= 0; tun--) {
+                       snprintf(name, sizeof(name), "/dev/tun%d", tun);
+                       if ((fd = open(name, O_RDWR)) >= 0)
+                               break;
+               }
+       } else {
+               debug("%s: invalid tunnel %u", __func__, tun);
+               return (-1);
+       }
+
+       if (fd < 0) {
+               debug("%s: %s open failed: %s", __func__, name, strerror(errno));
+               return (-1);
+       }
+
+       debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
+
+       /* Set the tunnel device operation mode */
+       snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun);
+       if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
+               goto failed;
+
+       if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
+               goto failed;
+
+       /* Set interface mode */
+       ifr.ifr_flags &= ~IFF_UP;
+       if (mode == SSH_TUNMODE_ETHERNET)
+               ifr.ifr_flags |= IFF_LINK0;
+       else
+               ifr.ifr_flags &= ~IFF_LINK0;
+       if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
+               goto failed;
+
+       /* Bring interface up */
+       ifr.ifr_flags |= IFF_UP;
+       if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
+               goto failed;
+
+       close(sock);
+       return (fd);
+
+ failed:
+       if (fd >= 0)
+               close(fd);
+       if (sock >= 0)
+               close(sock);
+       debug("%s: failed to set %s mode %d: %s", __func__, name,
+           mode, strerror(errno));
+       return (-1);
+#else
+       error("Tunnel interfaces are not supported on this platform");
+       return (-1);
+#endif
+}
+
+void
+sanitise_stdfd(void)
+{
+       int nullfd, dupfd;
+
+       if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
+               fprintf(stderr, "Couldn't open /dev/null: %s\n",
+                   strerror(errno));
+               exit(1);
+       }
+       while (++dupfd <= 2) {
+               /* Only clobber closed fds */
+               if (fcntl(dupfd, F_GETFL, 0) >= 0)
+                       continue;
+               if (dup2(nullfd, dupfd) == -1) {
+                       fprintf(stderr, "dup2: %s\n", strerror(errno));
+                       exit(1);
+               }
+       }
+       if (nullfd > 2)
+               close(nullfd);
+}
+
+char *
+tohex(const void *vp, size_t l)
+{
+       const u_char *p = (const u_char *)vp;
+       char b[3], *r;
+       size_t i, hl;
+
+       if (l > 65536)
+               return xstrdup("tohex: length > 65536");
+
+       hl = l * 2 + 1;
+       r = xcalloc(1, hl);
+       for (i = 0; i < l; i++) {
+               snprintf(b, sizeof(b), "%02x", p[i]);
+               strlcat(r, b, hl);
+       }
+       return (r);
+}
+
+u_int64_t
+get_u64(const void *vp)
+{
+       const u_char *p = (const u_char *)vp;
+       u_int64_t v;
+
+       v  = (u_int64_t)p[0] << 56;
+       v |= (u_int64_t)p[1] << 48;
+       v |= (u_int64_t)p[2] << 40;
+       v |= (u_int64_t)p[3] << 32;
+       v |= (u_int64_t)p[4] << 24;
+       v |= (u_int64_t)p[5] << 16;
+       v |= (u_int64_t)p[6] << 8;
+       v |= (u_int64_t)p[7];
+
+       return (v);
+}
+
+u_int32_t
+get_u32(const void *vp)
+{
+       const u_char *p = (const u_char *)vp;
+       u_int32_t v;
+
+       v  = (u_int32_t)p[0] << 24;
+       v |= (u_int32_t)p[1] << 16;
+       v |= (u_int32_t)p[2] << 8;
+       v |= (u_int32_t)p[3];
+
+       return (v);
+}
+
+u_int16_t
+get_u16(const void *vp)
+{
+       const u_char *p = (const u_char *)vp;
+       u_int16_t v;
+
+       v  = (u_int16_t)p[0] << 8;
+       v |= (u_int16_t)p[1];
+
+       return (v);
+}
+
+void
+put_u64(void *vp, u_int64_t v)
+{
+       u_char *p = (u_char *)vp;
+
+       p[0] = (u_char)(v >> 56) & 0xff;
+       p[1] = (u_char)(v >> 48) & 0xff;
+       p[2] = (u_char)(v >> 40) & 0xff;
+       p[3] = (u_char)(v >> 32) & 0xff;
+       p[4] = (u_char)(v >> 24) & 0xff;
+       p[5] = (u_char)(v >> 16) & 0xff;
+       p[6] = (u_char)(v >> 8) & 0xff;
+       p[7] = (u_char)v & 0xff;
+}
+
+void
+put_u32(void *vp, u_int32_t v)
+{
+       u_char *p = (u_char *)vp;
+
+       p[0] = (u_char)(v >> 24) & 0xff;
+       p[1] = (u_char)(v >> 16) & 0xff;
+       p[2] = (u_char)(v >> 8) & 0xff;
+       p[3] = (u_char)v & 0xff;
+}
+
+
+void
+put_u16(void *vp, u_int16_t v)
+{
+       u_char *p = (u_char *)vp;
+
+       p[0] = (u_char)(v >> 8) & 0xff;
+       p[1] = (u_char)v & 0xff;
+}
+
+void
+ms_subtract_diff(struct timeval *start, int *ms)
+{
+       struct timeval diff, finish;
+
+       gettimeofday(&finish, NULL);
+       timersub(&finish, start, &diff);        
+       *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000);
+}
+
+void
+ms_to_timeval(struct timeval *tv, int ms)
+{
+       if (ms < 0)
+               ms = 0;
+       tv->tv_sec = ms / 1000;
+       tv->tv_usec = (ms % 1000) * 1000;
+}
+
+void
+bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen)
+{
+       bw->buflen = buflen;
+       bw->rate = kbps;
+       bw->thresh = bw->rate;
+       bw->lamt = 0;
+       timerclear(&bw->bwstart);
+       timerclear(&bw->bwend);
+}      
+
+/* Callback from read/write loop to insert bandwidth-limiting delays */
+void
+bandwidth_limit(struct bwlimit *bw, size_t read_len)
+{
+       u_int64_t waitlen;
+       struct timespec ts, rm;
+
+       if (!timerisset(&bw->bwstart)) {
+               gettimeofday(&bw->bwstart, NULL);
+               return;
+       }
+
+       bw->lamt += read_len;
+       if (bw->lamt < bw->thresh)
+               return;
+
+       gettimeofday(&bw->bwend, NULL);
+       timersub(&bw->bwend, &bw->bwstart, &bw->bwend);
+       if (!timerisset(&bw->bwend))
+               return;
+
+       bw->lamt *= 8;
+       waitlen = (double)1000000L * bw->lamt / bw->rate;
+
+       bw->bwstart.tv_sec = waitlen / 1000000L;
+       bw->bwstart.tv_usec = waitlen % 1000000L;
+
+       if (timercmp(&bw->bwstart, &bw->bwend, >)) {
+               timersub(&bw->bwstart, &bw->bwend, &bw->bwend);
+
+               /* Adjust the wait time */
+               if (bw->bwend.tv_sec) {
+                       bw->thresh /= 2;
+                       if (bw->thresh < bw->buflen / 4)
+                               bw->thresh = bw->buflen / 4;
+               } else if (bw->bwend.tv_usec < 10000) {
+                       bw->thresh *= 2;
+                       if (bw->thresh > bw->buflen * 8)
+                               bw->thresh = bw->buflen * 8;
+               }
+
+               TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts);
+               while (nanosleep(&ts, &rm) == -1) {
+                       if (errno != EINTR)
+                               break;
+                       ts = rm;
+               }
+       }
+
+       bw->lamt = 0;
+       gettimeofday(&bw->bwstart, NULL);
+}
+
+/* Make a template filename for mk[sd]temp() */
+void
+mktemp_proto(char *s, size_t len)
+{
+       const char *tmpdir;
+       int r;
+
+       if ((tmpdir = getenv("TMPDIR")) != NULL) {
+               r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir);
+               if (r > 0 && (size_t)r < len)
+                       return;
+       }
+       r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX");
+       if (r < 0 || (size_t)r >= len)
+               fatal("%s: template string too short", __func__);
+}
+
+static const struct {
+       const char *name;
+       int value;
+} ipqos[] = {
+       { "af11", IPTOS_DSCP_AF11 },
+       { "af12", IPTOS_DSCP_AF12 },
+       { "af13", IPTOS_DSCP_AF13 },
+       { "af14", IPTOS_DSCP_AF21 },
+       { "af22", IPTOS_DSCP_AF22 },
+       { "af23", IPTOS_DSCP_AF23 },
+       { "af31", IPTOS_DSCP_AF31 },
+       { "af32", IPTOS_DSCP_AF32 },
+       { "af33", IPTOS_DSCP_AF33 },
+       { "af41", IPTOS_DSCP_AF41 },
+       { "af42", IPTOS_DSCP_AF42 },
+       { "af43", IPTOS_DSCP_AF43 },
+       { "cs0", IPTOS_DSCP_CS0 },
+       { "cs1", IPTOS_DSCP_CS1 },
+       { "cs2", IPTOS_DSCP_CS2 },
+       { "cs3", IPTOS_DSCP_CS3 },
+       { "cs4", IPTOS_DSCP_CS4 },
+       { "cs5", IPTOS_DSCP_CS5 },
+       { "cs6", IPTOS_DSCP_CS6 },
+       { "cs7", IPTOS_DSCP_CS7 },
+       { "ef", IPTOS_DSCP_EF },
+       { "lowdelay", IPTOS_LOWDELAY },
+       { "throughput", IPTOS_THROUGHPUT },
+       { "reliability", IPTOS_RELIABILITY },
+       { NULL, -1 }
+};
+
+int
+parse_ipqos(const char *cp)
+{
+       u_int i;
+       char *ep;
+       long val;
+
+       if (cp == NULL)
+               return -1;
+       for (i = 0; ipqos[i].name != NULL; i++) {
+               if (strcasecmp(cp, ipqos[i].name) == 0)
+                       return ipqos[i].value;
+       }
+       /* Try parsing as an integer */
+       val = strtol(cp, &ep, 0);
+       if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255)
+               return -1;
+       return val;
+}
+
+const char *
+iptos2str(int iptos)
+{
+       int i;
+       static char iptos_str[sizeof "0xff"];
+
+       for (i = 0; ipqos[i].name != NULL; i++) {
+               if (ipqos[i].value == iptos)
+                       return ipqos[i].name;
+       }
+       snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos);
+       return iptos_str;
+}
+void
+sock_set_v6only(int s)
+{
+#ifdef IPV6_V6ONLY
+       int on = 1;
+
+       debug3("%s: set socket %d IPV6_V6ONLY", __func__, s);
+       if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1)
+               error("setsockopt IPV6_V6ONLY: %s", strerror(errno));
+#endif
+}
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/misc.h b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/misc.h
new file mode 100644 (file)
index 0000000..904edc7
--- /dev/null
@@ -0,0 +1,108 @@
+/* $OpenBSD: misc.h,v 1.48 2011/03/29 18:54:17 stevesk Exp $ */
+
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose.  Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+#ifndef _MISC_H
+#define _MISC_H
+
+/* misc.c */
+
+char   *chop(char *);
+char   *strdelim(char **);
+int     set_nonblock(int);
+int     unset_nonblock(int);
+void    set_nodelay(int);
+int     a2port(const char *);
+int     a2tun(const char *, int *);
+char   *put_host_port(const char *, u_short);
+char   *hpdelim(char **);
+char   *cleanhostname(char *);
+char   *colon(char *);
+long    convtime(const char *);
+char   *tilde_expand_filename(const char *, uid_t);
+char   *percent_expand(const char *, ...) __attribute__((__sentinel__));
+char   *tohex(const void *, size_t);
+void    sanitise_stdfd(void);
+void    ms_subtract_diff(struct timeval *, int *);
+void    ms_to_timeval(struct timeval *, int);
+void    sock_set_v6only(int);
+
+struct passwd *pwcopy(struct passwd *);
+const char *ssh_gai_strerror(int);
+
+typedef struct arglist arglist;
+struct arglist {
+       char    **list;
+       u_int   num;
+       u_int   nalloc;
+};
+void    addargs(arglist *, char *, ...)
+            __attribute__((format(printf, 2, 3)));
+void    replacearg(arglist *, u_int, char *, ...)
+            __attribute__((format(printf, 3, 4)));
+void    freeargs(arglist *);
+
+int     tun_open(int, int);
+
+/* Common definitions for ssh tunnel device forwarding */
+#define SSH_TUNMODE_NO         0x00
+#define SSH_TUNMODE_POINTOPOINT        0x01
+#define SSH_TUNMODE_ETHERNET   0x02
+#define SSH_TUNMODE_DEFAULT    SSH_TUNMODE_POINTOPOINT
+#define SSH_TUNMODE_YES                (SSH_TUNMODE_POINTOPOINT|SSH_TUNMODE_ETHERNET)
+
+#define SSH_TUNID_ANY          0x7fffffff
+#define SSH_TUNID_ERR          (SSH_TUNID_ANY - 1)
+#define SSH_TUNID_MAX          (SSH_TUNID_ANY - 2)
+
+/* Functions to extract or store big-endian words of various sizes */
+u_int64_t      get_u64(const void *)
+    __attribute__((__bounded__( __minbytes__, 1, 8)));
+u_int32_t      get_u32(const void *)
+    __attribute__((__bounded__( __minbytes__, 1, 4)));
+u_int16_t      get_u16(const void *)
+    __attribute__((__bounded__( __minbytes__, 1, 2)));
+void           put_u64(void *, u_int64_t)
+    __attribute__((__bounded__( __minbytes__, 1, 8)));
+void           put_u32(void *, u_int32_t)
+    __attribute__((__bounded__( __minbytes__, 1, 4)));
+void           put_u16(void *, u_int16_t)
+    __attribute__((__bounded__( __minbytes__, 1, 2)));
+
+struct bwlimit {
+       size_t buflen;
+       u_int64_t rate, thresh, lamt;
+       struct timeval bwstart, bwend;
+};
+
+void bandwidth_limit_init(struct bwlimit *, u_int64_t, size_t);
+void bandwidth_limit(struct bwlimit *, size_t);
+
+int parse_ipqos(const char *);
+const char *iptos2str(int);
+void mktemp_proto(char *, size_t);
+
+/* readpass.c */
+
+#define RP_ECHO                        0x0001
+#define RP_ALLOW_STDIN         0x0002
+#define RP_ALLOW_EOF           0x0004
+#define RP_USE_ASKPASS         0x0008
+
+char   *read_passphrase(const char *, int);
+int     ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
+int     read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
+
+int     secure_permissions(struct stat *st, uid_t uid);
+
+#endif /* _MISC_H */
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor.c b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor.c
new file mode 100644 (file)
index 0000000..985916b
--- /dev/null
@@ -0,0 +1,2440 @@
+/* $OpenBSD: monitor.c,v 1.115 2011/06/23 23:35:42 djm Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * Copyright 2002 Markus Friedl <markus@openbsd.org>
+ * 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"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include "openbsd-compat/sys-tree.h"
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else
+# ifdef HAVE_SYS_POLL_H
+#  include <sys/poll.h>
+# endif
+#endif
+
+#ifdef SKEY
+#include <skey.h>
+#endif
+
+#include <openssl/dh.h>
+
+#include "openbsd-compat/sys-queue.h"
+#include "atomicio.h"
+#include "xmalloc.h"
+#include "ssh.h"
+#include "key.h"
+#include "buffer.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "cipher.h"
+#include "kex.h"
+#include "dh.h"
+#ifdef TARGET_OS_MAC   /* XXX Broken krb5 headers on Mac */
+#undef TARGET_OS_MAC
+#include "zlib.h"
+#define TARGET_OS_MAC 1
+#else
+#include "zlib.h"
+#endif
+#include "packet.h"
+#include "auth-options.h"
+#include "sshpty.h"
+#include "channels.h"
+#include "session.h"
+#include "sshlogin.h"
+#include "canohost.h"
+#include "log.h"
+#include "servconf.h"
+#include "monitor.h"
+#include "monitor_mm.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+#include "monitor_wrap.h"
+#include "monitor_fdpass.h"
+#include "misc.h"
+#include "compat.h"
+#include "ssh2.h"
+#include "jpake.h"
+#include "roaming.h"
+
+#ifdef GSSAPI
+static Gssctxt *gsscontext = NULL;
+#endif
+
+/* Imports */
+extern ServerOptions options;
+extern u_int utmp_len;
+extern Newkeys *current_keys[];
+extern z_stream incoming_stream;
+extern z_stream outgoing_stream;
+extern u_char session_id[];
+extern Buffer auth_debug;
+extern int auth_debug_init;
+extern Buffer loginmsg;
+
+/* State exported from the child */
+
+struct {
+       z_stream incoming;
+       z_stream outgoing;
+       u_char *keyin;
+       u_int keyinlen;
+       u_char *keyout;
+       u_int keyoutlen;
+       u_char *ivin;
+       u_int ivinlen;
+       u_char *ivout;
+       u_int ivoutlen;
+       u_char *ssh1key;
+       u_int ssh1keylen;
+       int ssh1cipher;
+       int ssh1protoflags;
+       u_char *input;
+       u_int ilen;
+       u_char *output;
+       u_int olen;
+       u_int64_t sent_bytes;
+       u_int64_t recv_bytes;
+} child_state;
+
+/* Functions on the monitor that answer unprivileged requests */
+
+int mm_answer_moduli(int, Buffer *);
+int mm_answer_sign(int, Buffer *);
+int mm_answer_pwnamallow(int, Buffer *);
+int mm_answer_auth2_read_banner(int, Buffer *);
+int mm_answer_authserv(int, Buffer *);
+int mm_answer_authrole(int, Buffer *);
+int mm_answer_authpassword(int, Buffer *);
+int mm_answer_bsdauthquery(int, Buffer *);
+int mm_answer_bsdauthrespond(int, Buffer *);
+int mm_answer_skeyquery(int, Buffer *);
+int mm_answer_skeyrespond(int, Buffer *);
+int mm_answer_keyallowed(int, Buffer *);
+int mm_answer_keyverify(int, Buffer *);
+int mm_answer_pty(int, Buffer *);
+int mm_answer_pty_cleanup(int, Buffer *);
+int mm_answer_term(int, Buffer *);
+int mm_answer_rsa_keyallowed(int, Buffer *);
+int mm_answer_rsa_challenge(int, Buffer *);
+int mm_answer_rsa_response(int, Buffer *);
+int mm_answer_sesskey(int, Buffer *);
+int mm_answer_sessid(int, Buffer *);
+int mm_answer_jpake_get_pwdata(int, Buffer *);
+int mm_answer_jpake_step1(int, Buffer *);
+int mm_answer_jpake_step2(int, Buffer *);
+int mm_answer_jpake_key_confirm(int, Buffer *);
+int mm_answer_jpake_check_confirm(int, Buffer *);
+
+#ifdef USE_PAM
+int mm_answer_pam_start(int, Buffer *);
+int mm_answer_pam_account(int, Buffer *);
+int mm_answer_pam_init_ctx(int, Buffer *);
+int mm_answer_pam_query(int, Buffer *);
+int mm_answer_pam_respond(int, Buffer *);
+int mm_answer_pam_free_ctx(int, Buffer *);
+#endif
+
+#ifdef GSSAPI
+int mm_answer_gss_setup_ctx(int, Buffer *);
+int mm_answer_gss_accept_ctx(int, Buffer *);
+int mm_answer_gss_userok(int, Buffer *);
+int mm_answer_gss_checkmic(int, Buffer *);
+int mm_answer_gss_sign(int, Buffer *);
+int mm_answer_gss_updatecreds(int, Buffer *);
+#endif
+
+#ifdef SSH_AUDIT_EVENTS
+int mm_answer_audit_event(int, Buffer *);
+int mm_answer_audit_command(int, Buffer *);
+#endif
+
+static int monitor_read_log(struct monitor *);
+
+static Authctxt *authctxt;
+static BIGNUM *ssh1_challenge = NULL;  /* used for ssh1 rsa auth */
+
+/* local state for key verify */
+static u_char *key_blob = NULL;
+static u_int key_bloblen = 0;
+static int key_blobtype = MM_NOKEY;
+static char *hostbased_cuser = NULL;
+static char *hostbased_chost = NULL;
+static char *auth_method = "unknown";
+static u_int session_id2_len = 0;
+static u_char *session_id2 = NULL;
+static pid_t monitor_child_pid;
+
+struct mon_table {
+       enum monitor_reqtype type;
+       int flags;
+       int (*f)(int, Buffer *);
+};
+
+#define MON_ISAUTH     0x0004  /* Required for Authentication */
+#define MON_AUTHDECIDE 0x0008  /* Decides Authentication */
+#define MON_ONCE       0x0010  /* Disable after calling */
+#define MON_ALOG       0x0020  /* Log auth attempt without authenticating */
+
+#define MON_AUTH       (MON_ISAUTH|MON_AUTHDECIDE)
+
+#define MON_PERMIT     0x1000  /* Request is permitted */
+
+struct mon_table mon_dispatch_proto20[] = {
+    {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli},
+    {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign},
+    {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
+    {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv},
+    {MONITOR_REQ_AUTHROLE, MON_ONCE, mm_answer_authrole},
+    {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner},
+    {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword},
+#ifdef USE_PAM
+    {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start},
+    {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account},
+    {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx},
+    {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query},
+    {MONITOR_REQ_PAM_RESPOND, MON_ISAUTH, mm_answer_pam_respond},
+    {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx},
+#endif
+#ifdef SSH_AUDIT_EVENTS
+    {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event},
+#endif
+#ifdef BSD_AUTH
+    {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery},
+    {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH, mm_answer_bsdauthrespond},
+#endif
+#ifdef SKEY
+    {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery},
+    {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond},
+#endif
+    {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed},
+    {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify},
+#ifdef GSSAPI
+    {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx},
+    {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
+    {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
+    {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
+    {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
+#endif
+#ifdef JPAKE
+    {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata},
+    {MONITOR_REQ_JPAKE_STEP1, MON_ISAUTH, mm_answer_jpake_step1},
+    {MONITOR_REQ_JPAKE_STEP2, MON_ONCE, mm_answer_jpake_step2},
+    {MONITOR_REQ_JPAKE_KEY_CONFIRM, MON_ONCE, mm_answer_jpake_key_confirm},
+    {MONITOR_REQ_JPAKE_CHECK_CONFIRM, MON_AUTH, mm_answer_jpake_check_confirm},
+#endif
+    {0, 0, NULL}
+};
+
+struct mon_table mon_dispatch_postauth20[] = {
+#ifdef GSSAPI
+    {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
+    {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
+    {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
+    {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
+#endif
+    {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
+    {MONITOR_REQ_SIGN, 0, mm_answer_sign},
+    {MONITOR_REQ_PTY, 0, mm_answer_pty},
+    {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup},
+    {MONITOR_REQ_TERM, 0, mm_answer_term},
+#ifdef SSH_AUDIT_EVENTS
+    {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event},
+    {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command},
+#endif
+    {0, 0, NULL}
+};
+
+struct mon_table mon_dispatch_proto15[] = {
+    {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
+    {MONITOR_REQ_SESSKEY, MON_ONCE, mm_answer_sesskey},
+    {MONITOR_REQ_SESSID, MON_ONCE, mm_answer_sessid},
+    {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword},
+    {MONITOR_REQ_RSAKEYALLOWED, MON_ISAUTH|MON_ALOG, mm_answer_rsa_keyallowed},
+    {MONITOR_REQ_KEYALLOWED, MON_ISAUTH|MON_ALOG, mm_answer_keyallowed},
+    {MONITOR_REQ_RSACHALLENGE, MON_ONCE, mm_answer_rsa_challenge},
+    {MONITOR_REQ_RSARESPONSE, MON_ONCE|MON_AUTHDECIDE, mm_answer_rsa_response},
+#ifdef BSD_AUTH
+    {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery},
+    {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH, mm_answer_bsdauthrespond},
+#endif
+#ifdef SKEY
+    {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery},
+    {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond},
+#endif
+#ifdef USE_PAM
+    {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start},
+    {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account},
+    {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx},
+    {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query},
+    {MONITOR_REQ_PAM_RESPOND, MON_ISAUTH, mm_answer_pam_respond},
+    {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx},
+#endif
+#ifdef SSH_AUDIT_EVENTS
+    {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event},
+#endif
+    {0, 0, NULL}
+};
+
+struct mon_table mon_dispatch_postauth15[] = {
+    {MONITOR_REQ_PTY, MON_ONCE, mm_answer_pty},
+    {MONITOR_REQ_PTYCLEANUP, MON_ONCE, mm_answer_pty_cleanup},
+    {MONITOR_REQ_TERM, 0, mm_answer_term},
+#ifdef SSH_AUDIT_EVENTS
+    {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event},
+    {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT|MON_ONCE, mm_answer_audit_command},
+#endif
+    {0, 0, NULL}
+};
+
+struct mon_table *mon_dispatch;
+
+/* Specifies if a certain message is allowed at the moment */
+
+static 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++;
+       }
+}
+
+static 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++;
+       }
+}
+
+void
+monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
+{
+       struct mon_table *ent;
+       int authenticated = 0;
+
+       debug3("preauth child monitor started");
+
+       close(pmonitor->m_recvfd);
+       close(pmonitor->m_log_sendfd);
+       pmonitor->m_log_sendfd = pmonitor->m_recvfd = -1;
+
+       authctxt = _authctxt;
+       memset(authctxt, 0, sizeof(*authctxt));
+
+       authctxt->loginmsg = &loginmsg;
+
+       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);
+#ifdef GSSAPI
+               /* and for the GSSAPI key exchange */
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
+#endif
+       } else {
+               mon_dispatch = mon_dispatch_proto15;
+
+               monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 1);
+       }
+
+       /* The first few requests do not require asynchronous access */
+       while (!authenticated) {
+               auth_method = "unknown";
+               authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1);
+               if (authenticated) {
+                       if (!(ent->flags & MON_AUTHDECIDE))
+                               fatal("%s: unexpected authentication from %d",
+                                   __func__, ent->type);
+                       if (authctxt->pw->pw_uid == 0 &&
+                           !auth_root_allowed(auth_method))
+                               authenticated = 0;
+#ifdef USE_PAM
+                       /* PAM needs to perform account checks after auth */
+                       if (options.use_pam && authenticated) {
+                               Buffer m;
+
+                               buffer_init(&m);
+                               mm_request_receive_expect(pmonitor->m_sendfd,
+                                   MONITOR_REQ_PAM_ACCOUNT, &m);
+                               authenticated = mm_answer_pam_account(pmonitor->m_sendfd, &m);
+                               buffer_free(&m);
+                       }
+#endif
+               }
+
+               if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) {
+                       auth_log(authctxt, authenticated, auth_method,
+                           compat20 ? " ssh2" : "");
+                       if (!authenticated)
+                               authctxt->failures++;
+               }
+#ifdef JPAKE
+               /* Cleanup JPAKE context after authentication */
+               if (ent->flags & MON_AUTHDECIDE) {
+                       if (authctxt->jpake_ctx != NULL) {
+                               jpake_free(authctxt->jpake_ctx);
+                               authctxt->jpake_ctx = NULL;
+                       }
+               }
+#endif
+       }
+
+       /* Drain any buffered messages from the child */
+       while (pmonitor->m_log_recvfd != -1 && monitor_read_log(pmonitor) == 0)
+               ;
+
+       if (!authctxt->valid)
+               fatal("%s: authenticated invalid user", __func__);
+       if (strcmp(auth_method, "unknown") == 0)
+               fatal("%s: authentication method name unknown", __func__);
+
+       debug("%s: %s has been authenticated by privileged process",
+           __func__, authctxt->user);
+
+       mm_get_keystate(pmonitor);
+
+       close(pmonitor->m_sendfd);
+       close(pmonitor->m_log_recvfd);
+       pmonitor->m_sendfd = pmonitor->m_log_recvfd = -1;
+}
+
+static void
+monitor_set_child_handler(pid_t pid)
+{
+       monitor_child_pid = pid;
+}
+
+static void
+monitor_child_handler(int sig)
+{
+       kill(monitor_child_pid, sig);
+}
+
+void
+monitor_child_postauth(struct monitor *pmonitor)
+{
+       close(pmonitor->m_recvfd);
+       pmonitor->m_recvfd = -1;
+
+       monitor_set_child_handler(pmonitor->m_pid);
+       signal(SIGHUP, &monitor_child_handler);
+       signal(SIGTERM, &monitor_child_handler);
+       signal(SIGINT, &monitor_child_handler);
+
+       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);
+#ifdef GSSAPI
+               /* and for the GSSAPI key exchange */
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
+#endif         
+       } else {
+               mon_dispatch = mon_dispatch_postauth15;
+               monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
+       }
+       if (!no_pty_flag) {
+               monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1);
+       }
+
+       for (;;)
+               monitor_read(pmonitor, mon_dispatch, NULL);
+
+       close(pmonitor->m_sendfd);
+       pmonitor->m_sendfd = -1;
+}
+
+void
+monitor_sync(struct monitor *pmonitor)
+{
+       if (options.compression) {
+               /* The member allocation is not visible, so sync it */
+               mm_share_sync(&pmonitor->m_zlib, &pmonitor->m_zback);
+       }
+}
+
+static int
+monitor_read_log(struct monitor *pmonitor)
+{
+       Buffer logmsg;
+       u_int len, level;
+       char *msg;
+
+       buffer_init(&logmsg);
+
+       /* Read length */
+       buffer_append_space(&logmsg, 4);
+       if (atomicio(read, pmonitor->m_log_recvfd,
+           buffer_ptr(&logmsg), buffer_len(&logmsg)) != buffer_len(&logmsg)) {
+               if (errno == EPIPE) {
+                       debug("%s: child log fd closed", __func__);
+                       close(pmonitor->m_log_recvfd);
+                       pmonitor->m_log_recvfd = -1;
+                       return -1;
+               }
+               fatal("%s: log fd read: %s", __func__, strerror(errno));
+       }
+       len = buffer_get_int(&logmsg);
+       if (len <= 4 || len > 8192)
+               fatal("%s: invalid log message length %u", __func__, len);
+
+       /* Read severity, message */
+       buffer_clear(&logmsg);
+       buffer_append_space(&logmsg, len);
+       if (atomicio(read, pmonitor->m_log_recvfd,
+           buffer_ptr(&logmsg), buffer_len(&logmsg)) != buffer_len(&logmsg))
+               fatal("%s: log fd read: %s", __func__, strerror(errno));
+
+       /* Log it */
+       level = buffer_get_int(&logmsg);
+       msg = buffer_get_string(&logmsg, NULL);
+       if (log_level_name(level) == NULL)
+               fatal("%s: invalid log level %u (corrupted message?)",
+                   __func__, level);
+       do_log2(level, "%s [preauth]", msg);
+
+       buffer_free(&logmsg);
+       xfree(msg);
+
+       return 0;
+}
+
+int
+monitor_read(struct monitor *pmonitor, struct mon_table *ent,
+    struct mon_table **pent)
+{
+       Buffer m;
+       int ret;
+       u_char type;
+       struct pollfd pfd[2];
+
+       for (;;) {
+               bzero(&pfd, sizeof(pfd));
+               pfd[0].fd = pmonitor->m_sendfd;
+               pfd[0].events = POLLIN;
+               pfd[1].fd = pmonitor->m_log_recvfd;
+               pfd[1].events = pfd[1].fd == -1 ? 0 : POLLIN;
+               if (poll(pfd, pfd[1].fd == -1 ? 1 : 2, -1) == -1) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       fatal("%s: poll: %s", __func__, strerror(errno));
+               }
+               if (pfd[1].revents) {
+                       /*
+                        * Drain all log messages before processing next
+                        * monitor request.
+                        */
+                       monitor_read_log(pmonitor);
+                       continue;
+               }
+               if (pfd[0].revents)
+                       break;  /* Continues below */
+       }
+
+       buffer_init(&m);
+
+       mm_request_receive(pmonitor->m_sendfd, &m);
+       type = buffer_get_char(&m);
+
+       debug3("%s: checking request %d", __func__, 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", __func__,
+                           type);
+               ret = (*ent->f)(pmonitor->m_sendfd, &m);
+               buffer_free(&m);
+
+               /* The child may use this request only once, disable it */
+               if (ent->flags & MON_ONCE) {
+                       debug2("%s: %d used once, disabling now", __func__,
+                           type);
+                       ent->flags &= ~MON_PERMIT;
+               }
+
+               if (pent != NULL)
+                       *pent = ent;
+
+               return ret;
+       }
+
+       fatal("%s: unsupported request: %d", __func__, type);
+
+       /* NOTREACHED */
+       return (-1);
+}
+
+/* allowed key state */
+static int
+monitor_allowed_key(u_char *blob, u_int bloblen)
+{
+       /* make sure key is allowed */
+       if (key_blob == NULL || key_bloblen != bloblen ||
+           timingsafe_bcmp(key_blob, blob, key_bloblen))
+               return (0);
+       return (1);
+}
+
+static void
+monitor_reset_key_state(void)
+{
+       /* reset state */
+       if (key_blob != NULL)
+               xfree(key_blob);
+       if (hostbased_cuser != NULL)
+               xfree(hostbased_cuser);
+       if (hostbased_chost != NULL)
+               xfree(hostbased_chost);
+       key_blob = NULL;
+       key_bloblen = 0;
+       key_blobtype = MM_NOKEY;
+       hostbased_cuser = NULL;
+       hostbased_chost = NULL;
+}
+
+int
+mm_answer_moduli(int sock, Buffer *m)
+{
+       DH *dh;
+       int min, want, max;
+
+       min = buffer_get_int(m);
+       want = buffer_get_int(m);
+       max = buffer_get_int(m);
+
+       debug3("%s: got parameters: %d %d %d",
+           __func__, min, want, max);
+       /* We need to check here, too, in case the child got corrupted */
+       if (max < min || want < min || max < want)
+               fatal("%s: bad parameters: %d %d %d",
+                   __func__, min, want, max);
+
+       buffer_clear(m);
+
+       dh = choose_dh(min, want, max);
+       if (dh == NULL) {
+               buffer_put_char(m, 0);
+               return (0);
+       } else {
+               /* Send first bignum */
+               buffer_put_char(m, 1);
+               buffer_put_bignum2(m, dh->p);
+               buffer_put_bignum2(m, dh->g);
+
+               DH_free(dh);
+       }
+       mm_request_send(sock, MONITOR_ANS_MODULI, m);
+       return (0);
+}
+
+int
+mm_answer_sign(int sock, Buffer *m)
+{
+       Key *key;
+       u_char *p;
+       u_char *signature;
+       u_int siglen, datlen;
+       int keyid;
+
+       debug3("%s", __func__);
+
+       keyid = buffer_get_int(m);
+       p = buffer_get_string(m, &datlen);
+
+       /*
+        * Supported KEX types use SHA1 (20 bytes), SHA256 (32 bytes),
+        * SHA384 (48 bytes) and SHA512 (64 bytes).
+        */
+       if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64)
+               fatal("%s: data length incorrect: %u", __func__, datlen);
+
+       /* save session id, it will be passed on the first call */
+       if (session_id2_len == 0) {
+               session_id2_len = datlen;
+               session_id2 = xmalloc(session_id2_len);
+               memcpy(session_id2, p, session_id2_len);
+       }
+
+       if ((key = get_hostkey_by_index(keyid)) == NULL)
+               fatal("%s: no hostkey from index %d", __func__, keyid);
+       if (key_sign(key, &signature, &siglen, p, datlen) < 0)
+               fatal("%s: key_sign failed", __func__);
+
+       debug3("%s: signature %p(%u)", __func__, signature, siglen);
+
+       buffer_clear(m);
+       buffer_put_string(m, signature, siglen);
+
+       xfree(p);
+       xfree(signature);
+
+       mm_request_send(sock, MONITOR_ANS_SIGN, m);
+
+       /* Turn on permissions for getpwnam */
+       monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
+
+       return (0);
+}
+
+/* Retrieves the password entry and also checks if the user is permitted */
+
+int
+mm_answer_pwnamallow(int sock, Buffer *m)
+{
+       char *username;
+       struct passwd *pwent;
+       int allowed = 0;
+       u_int i;
+
+       debug3("%s", __func__);
+
+       if (authctxt->attempt++ != 0)
+               fatal("%s: multiple attempts for getpwnam", __func__);
+
+       username = buffer_get_string(m, NULL);
+
+       pwent = getpwnamallow(username);
+
+       authctxt->user = xstrdup(username);
+       setproctitle("%s [priv]", pwent ? username : "unknown");
+       xfree(username);
+
+       buffer_clear(m);
+
+       if (pwent == NULL) {
+               buffer_put_char(m, 0);
+               authctxt->pw = fakepw();
+               goto out;
+       }
+
+       allowed = 1;
+       authctxt->pw = pwent;
+       authctxt->valid = 1;
+
+       buffer_put_char(m, 1);
+       buffer_put_string(m, pwent, sizeof(struct passwd));
+       buffer_put_cstring(m, pwent->pw_name);
+       buffer_put_cstring(m, "*");
+       buffer_put_cstring(m, pwent->pw_gecos);
+#ifdef HAVE_PW_CLASS_IN_PASSWD
+       buffer_put_cstring(m, pwent->pw_class);
+#endif
+       buffer_put_cstring(m, pwent->pw_dir);
+       buffer_put_cstring(m, pwent->pw_shell);
+
+ out:
+       buffer_put_string(m, &options, sizeof(options));
+
+#define M_CP_STROPT(x) do { \
+               if (options.x != NULL) \
+                       buffer_put_cstring(m, options.x); \
+       } while (0)
+#define M_CP_STRARRAYOPT(x, nx) do { \
+               for (i = 0; i < options.nx; i++) \
+                       buffer_put_cstring(m, options.x[i]); \
+       } while (0)
+       /* See comment in servconf.h */
+       COPY_MATCH_STRING_OPTS();
+#undef M_CP_STROPT
+#undef M_CP_STRARRAYOPT
+       
+       debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed);
+       mm_request_send(sock, MONITOR_ANS_PWNAM, m);
+
+       /* For SSHv1 allow authentication now */
+       if (!compat20)
+               monitor_permit_authentications(1);
+       else {
+               /* Allow service/style information on the auth context */
+               monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_AUTHROLE, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1);
+       }
+#ifdef USE_PAM
+       if (options.use_pam)
+               monitor_permit(mon_dispatch, MONITOR_REQ_PAM_START, 1);
+#endif
+
+       return (0);
+}
+
+int mm_answer_auth2_read_banner(int sock, Buffer *m)
+{
+       char *banner;
+
+       buffer_clear(m);
+       banner = auth2_read_banner();
+       buffer_put_cstring(m, banner != NULL ? banner : "");
+       mm_request_send(sock, MONITOR_ANS_AUTH2_READ_BANNER, m);
+
+       if (banner != NULL)
+               xfree(banner);
+
+       return (0);
+}
+
+int
+mm_answer_authserv(int sock, Buffer *m)
+{
+       monitor_permit_authentications(1);
+
+       authctxt->service = buffer_get_string(m, NULL);
+       authctxt->style = buffer_get_string(m, NULL);
+       authctxt->role = buffer_get_string(m, NULL);
+       debug3("%s: service=%s, style=%s, role=%s",
+           __func__, authctxt->service, authctxt->style, authctxt->role);
+
+       if (strlen(authctxt->style) == 0) {
+               xfree(authctxt->style);
+               authctxt->style = NULL;
+       }
+
+       if (strlen(authctxt->role) == 0) {
+               xfree(authctxt->role);
+               authctxt->role = NULL;
+       }
+
+       return (0);
+}
+
+int
+mm_answer_authrole(int sock, Buffer *m)
+{
+       monitor_permit_authentications(1);
+
+       authctxt->role = buffer_get_string(m, NULL);
+       debug3("%s: role=%s",
+           __func__, authctxt->role);
+
+       if (strlen(authctxt->role) == 0) {
+               xfree(authctxt->role);
+               authctxt->role = NULL;
+       }
+
+       return (0);
+}
+
+int
+mm_answer_authpassword(int sock, Buffer *m)
+{
+       static int call_count;
+       char *passwd;
+       int authenticated;
+       u_int plen;
+
+       passwd = buffer_get_string(m, &plen);
+       /* Only authenticate if the context is valid */
+       authenticated = options.password_authentication &&
+           auth_password(authctxt, passwd);
+       memset(passwd, 0, strlen(passwd));
+       xfree(passwd);
+
+       buffer_clear(m);
+       buffer_put_int(m, authenticated);
+
+       debug3("%s: sending result %d", __func__, authenticated);
+       mm_request_send(sock, MONITOR_ANS_AUTHPASSWORD, m);
+
+       call_count++;
+       if (plen == 0 && call_count == 1)
+               auth_method = "none";
+       else
+               auth_method = "password";
+
+       /* Causes monitor loop to terminate if authenticated */
+       return (authenticated);
+}
+
+#ifdef BSD_AUTH
+int
+mm_answer_bsdauthquery(int sock, Buffer *m)
+{
+       char *name, *infotxt;
+       u_int numprompts;
+       u_int *echo_on;
+       char **prompts;
+       u_int success;
+
+       success = bsdauth_query(authctxt, &name, &infotxt, &numprompts,
+           &prompts, &echo_on) < 0 ? 0 : 1;
+
+       buffer_clear(m);
+       buffer_put_int(m, success);
+       if (success)
+               buffer_put_cstring(m, prompts[0]);
+
+       debug3("%s: sending challenge success: %u", __func__, success);
+       mm_request_send(sock, MONITOR_ANS_BSDAUTHQUERY, m);
+
+       if (success) {
+               xfree(name);
+               xfree(infotxt);
+               xfree(prompts);
+               xfree(echo_on);
+       }
+
+       return (0);
+}
+
+int
+mm_answer_bsdauthrespond(int sock, Buffer *m)
+{
+       char *response;
+       int authok;
+
+       if (authctxt->as == 0)
+               fatal("%s: no bsd auth session", __func__);
+
+       response = buffer_get_string(m, NULL);
+       authok = options.challenge_response_authentication &&
+           auth_userresponse(authctxt->as, response, 0);
+       authctxt->as = NULL;
+       debug3("%s: <%s> = <%d>", __func__, response, authok);
+       xfree(response);
+
+       buffer_clear(m);
+       buffer_put_int(m, authok);
+
+       debug3("%s: sending authenticated: %d", __func__, authok);
+       mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m);
+
+       auth_method = "bsdauth";
+
+       return (authok != 0);
+}
+#endif
+
+#ifdef SKEY
+int
+mm_answer_skeyquery(int sock, Buffer *m)
+{
+       struct skey skey;
+       char challenge[1024];
+       u_int success;
+
+       success = _compat_skeychallenge(&skey, authctxt->user, challenge,
+           sizeof(challenge)) < 0 ? 0 : 1;
+
+       buffer_clear(m);
+       buffer_put_int(m, success);
+       if (success)
+               buffer_put_cstring(m, challenge);
+
+       debug3("%s: sending challenge success: %u", __func__, success);
+       mm_request_send(sock, MONITOR_ANS_SKEYQUERY, m);
+
+       return (0);
+}
+
+int
+mm_answer_skeyrespond(int sock, Buffer *m)
+{
+       char *response;
+       int authok;
+
+       response = buffer_get_string(m, NULL);
+
+       authok = (options.challenge_response_authentication &&
+           authctxt->valid &&
+           skey_haskey(authctxt->pw->pw_name) == 0 &&
+           skey_passcheck(authctxt->pw->pw_name, response) != -1);
+
+       xfree(response);
+
+       buffer_clear(m);
+       buffer_put_int(m, authok);
+
+       debug3("%s: sending authenticated: %d", __func__, authok);
+       mm_request_send(sock, MONITOR_ANS_SKEYRESPOND, m);
+
+       auth_method = "skey";
+
+       return (authok != 0);
+}
+#endif
+
+#ifdef USE_PAM
+int
+mm_answer_pam_start(int sock, Buffer *m)
+{
+       if (!options.use_pam)
+               fatal("UsePAM not set, but ended up in %s anyway", __func__);
+
+       start_pam(authctxt);
+
+       monitor_permit(mon_dispatch, MONITOR_REQ_PAM_ACCOUNT, 1);
+
+       return (0);
+}
+
+int
+mm_answer_pam_account(int sock, Buffer *m)
+{
+       u_int ret;
+
+       if (!options.use_pam)
+               fatal("UsePAM not set, but ended up in %s anyway", __func__);
+
+       ret = do_pam_account();
+
+       buffer_put_int(m, ret);
+       buffer_put_string(m, buffer_ptr(&loginmsg), buffer_len(&loginmsg));
+
+       mm_request_send(sock, MONITOR_ANS_PAM_ACCOUNT, m);
+
+       return (ret);
+}
+
+static void *sshpam_ctxt, *sshpam_authok;
+extern KbdintDevice sshpam_device;
+
+int
+mm_answer_pam_init_ctx(int sock, Buffer *m)
+{
+
+       debug3("%s", __func__);
+       authctxt->user = buffer_get_string(m, NULL);
+       sshpam_ctxt = (sshpam_device.init_ctx)(authctxt);
+       sshpam_authok = NULL;
+       buffer_clear(m);
+       if (sshpam_ctxt != NULL) {
+               monitor_permit(mon_dispatch, MONITOR_REQ_PAM_FREE_CTX, 1);
+               buffer_put_int(m, 1);
+       } else {
+               buffer_put_int(m, 0);
+       }
+       mm_request_send(sock, MONITOR_ANS_PAM_INIT_CTX, m);
+       return (0);
+}
+
+int
+mm_answer_pam_query(int sock, Buffer *m)
+{
+       char *name = NULL, *info = NULL, **prompts = NULL;
+       u_int i, num = 0, *echo_on = 0;
+       int ret;
+
+       debug3("%s", __func__);
+       sshpam_authok = NULL;
+       ret = (sshpam_device.query)(sshpam_ctxt, &name, &info, &num, &prompts, &echo_on);
+       if (ret == 0 && num == 0)
+               sshpam_authok = sshpam_ctxt;
+       if (num > 1 || name == NULL || info == NULL)
+               ret = -1;
+       buffer_clear(m);
+       buffer_put_int(m, ret);
+       buffer_put_cstring(m, name);
+       xfree(name);
+       buffer_put_cstring(m, info);
+       xfree(info);
+       buffer_put_int(m, num);
+       for (i = 0; i < num; ++i) {
+               buffer_put_cstring(m, prompts[i]);
+               xfree(prompts[i]);
+               buffer_put_int(m, echo_on[i]);
+       }
+       if (prompts != NULL)
+               xfree(prompts);
+       if (echo_on != NULL)
+               xfree(echo_on);
+       auth_method = "keyboard-interactive/pam";
+       mm_request_send(sock, MONITOR_ANS_PAM_QUERY, m);
+       return (0);
+}
+
+int
+mm_answer_pam_respond(int sock, Buffer *m)
+{
+       char **resp;
+       u_int i, num;
+       int ret;
+
+       debug3("%s", __func__);
+       sshpam_authok = NULL;
+       num = buffer_get_int(m);
+       if (num > 0) {
+               resp = xcalloc(num, sizeof(char *));
+               for (i = 0; i < num; ++i)
+                       resp[i] = buffer_get_string(m, NULL);
+               ret = (sshpam_device.respond)(sshpam_ctxt, num, resp);
+               for (i = 0; i < num; ++i)
+                       xfree(resp[i]);
+               xfree(resp);
+       } else {
+               ret = (sshpam_device.respond)(sshpam_ctxt, num, NULL);
+       }
+       buffer_clear(m);
+       buffer_put_int(m, ret);
+       mm_request_send(sock, MONITOR_ANS_PAM_RESPOND, m);
+       auth_method = "keyboard-interactive/pam";
+       if (ret == 0)
+               sshpam_authok = sshpam_ctxt;
+       return (0);
+}
+
+int
+mm_answer_pam_free_ctx(int sock, Buffer *m)
+{
+
+       debug3("%s", __func__);
+       (sshpam_device.free_ctx)(sshpam_ctxt);
+       buffer_clear(m);
+       mm_request_send(sock, MONITOR_ANS_PAM_FREE_CTX, m);
+       auth_method = "keyboard-interactive/pam";
+       return (sshpam_authok == sshpam_ctxt);
+}
+#endif
+
+int
+mm_answer_keyallowed(int sock, Buffer *m)
+{
+       Key *key;
+       char *cuser, *chost;
+       u_char *blob;
+       u_int bloblen;
+       enum mm_keytype type = 0;
+       int allowed = 0;
+
+       debug3("%s entering", __func__);
+
+       type = buffer_get_int(m);
+       cuser = buffer_get_string(m, NULL);
+       chost = buffer_get_string(m, NULL);
+       blob = buffer_get_string(m, &bloblen);
+
+       key = key_from_blob(blob, bloblen);
+
+       if ((compat20 && type == MM_RSAHOSTKEY) ||
+           (!compat20 && type != MM_RSAHOSTKEY))
+               fatal("%s: key type and protocol mismatch", __func__);
+
+       debug3("%s: key_from_blob: %p", __func__, key);
+
+       if (key != NULL && authctxt->valid) {
+               switch (type) {
+               case MM_USERKEY:
+                       allowed = options.pubkey_authentication &&
+                           user_key_allowed(authctxt->pw, key);
+                       auth_method = "publickey";
+                       if (options.pubkey_authentication && allowed != 1)
+                               auth_clear_options();
+                       break;
+               case MM_HOSTKEY:
+                       allowed = options.hostbased_authentication &&
+                           hostbased_key_allowed(authctxt->pw,
+                           cuser, chost, key);
+                       auth_method = "hostbased";
+                       break;
+               case MM_RSAHOSTKEY:
+                       key->type = KEY_RSA1; /* XXX */
+                       allowed = options.rhosts_rsa_authentication &&
+                           auth_rhosts_rsa_key_allowed(authctxt->pw,
+                           cuser, chost, key);
+                       if (options.rhosts_rsa_authentication && allowed != 1)
+                               auth_clear_options();
+                       auth_method = "rsa";
+                       break;
+               default:
+                       fatal("%s: unknown key type %d", __func__, type);
+                       break;
+               }
+       }
+       if (key != NULL)
+               key_free(key);
+
+       /* clear temporarily storage (used by verify) */
+       monitor_reset_key_state();
+
+       if (allowed) {
+               /* Save temporarily for comparison in verify */
+               key_blob = blob;
+               key_bloblen = bloblen;
+               key_blobtype = type;
+               hostbased_cuser = cuser;
+               hostbased_chost = chost;
+       } else {
+               /* Log failed attempt */
+               auth_log(authctxt, 0, auth_method, compat20 ? " ssh2" : "");
+               xfree(blob);
+               xfree(cuser);
+               xfree(chost);
+       }
+
+       debug3("%s: key %p is %s",
+           __func__, key, allowed ? "allowed" : "not allowed");
+
+       buffer_clear(m);
+       buffer_put_int(m, allowed);
+       buffer_put_int(m, forced_command != NULL);
+
+       mm_request_send(sock, MONITOR_ANS_KEYALLOWED, m);
+
+       if (type == MM_RSAHOSTKEY)
+               monitor_permit(mon_dispatch, MONITOR_REQ_RSACHALLENGE, allowed);
+
+       return (0);
+}
+
+static int
+monitor_valid_userblob(u_char *data, u_int datalen)
+{
+       Buffer b;
+       char *p;
+       u_int len;
+       int fail = 0;
+
+       buffer_init(&b);
+       buffer_append(&b, data, datalen);
+
+       if (datafellows & SSH_OLD_SESSIONID) {
+               p = buffer_ptr(&b);
+               len = buffer_len(&b);
+               if ((session_id2 == NULL) ||
+                   (len < session_id2_len) ||
+                   (timingsafe_bcmp(p, session_id2, session_id2_len) != 0))
+                       fail++;
+               buffer_consume(&b, session_id2_len);
+       } else {
+               p = buffer_get_string(&b, &len);
+               if ((session_id2 == NULL) ||
+                   (len != session_id2_len) ||
+                   (timingsafe_bcmp(p, session_id2, session_id2_len) != 0))
+                       fail++;
+               xfree(p);
+       }
+       if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST)
+               fail++;
+       p = buffer_get_string(&b, NULL);
+       if (strcmp(authctxt->user, p) != 0) {
+               logit("wrong user name passed to monitor: expected %s != %.100s",
+                   authctxt->user, p);
+               fail++;
+       }
+       xfree(p);
+       buffer_skip_string(&b);
+       if (datafellows & SSH_BUG_PKAUTH) {
+               if (!buffer_get_char(&b))
+                       fail++;
+       } else {
+               p = buffer_get_string(&b, NULL);
+               if (strcmp("publickey", p) != 0)
+                       fail++;
+               xfree(p);
+               if (!buffer_get_char(&b))
+                       fail++;
+               buffer_skip_string(&b);
+       }
+       buffer_skip_string(&b);
+       if (buffer_len(&b) != 0)
+               fail++;
+       buffer_free(&b);
+       return (fail == 0);
+}
+
+static int
+monitor_valid_hostbasedblob(u_char *data, u_int datalen, char *cuser,
+    char *chost)
+{
+       Buffer b;
+       char *p;
+       u_int len;
+       int fail = 0;
+
+       buffer_init(&b);
+       buffer_append(&b, data, datalen);
+
+       p = buffer_get_string(&b, &len);
+       if ((session_id2 == NULL) ||
+           (len != session_id2_len) ||
+           (timingsafe_bcmp(p, session_id2, session_id2_len) != 0))
+               fail++;
+       xfree(p);
+
+       if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST)
+               fail++;
+       p = buffer_get_string(&b, NULL);
+       if (strcmp(authctxt->user, p) != 0) {
+               logit("wrong user name passed to monitor: expected %s != %.100s",
+                   authctxt->user, p);
+               fail++;
+       }
+       xfree(p);
+       buffer_skip_string(&b); /* service */
+       p = buffer_get_string(&b, NULL);
+       if (strcmp(p, "hostbased") != 0)
+               fail++;
+       xfree(p);
+       buffer_skip_string(&b); /* pkalg */
+       buffer_skip_string(&b); /* pkblob */
+
+       /* verify client host, strip trailing dot if necessary */
+       p = buffer_get_string(&b, NULL);
+       if (((len = strlen(p)) > 0) && p[len - 1] == '.')
+               p[len - 1] = '\0';
+       if (strcmp(p, chost) != 0)
+               fail++;
+       xfree(p);
+
+       /* verify client user */
+       p = buffer_get_string(&b, NULL);
+       if (strcmp(p, cuser) != 0)
+               fail++;
+       xfree(p);
+
+       if (buffer_len(&b) != 0)
+               fail++;
+       buffer_free(&b);
+       return (fail == 0);
+}
+
+int
+mm_answer_keyverify(int sock, Buffer *m)
+{
+       Key *key;
+       u_char *signature, *data, *blob;
+       u_int signaturelen, datalen, bloblen;
+       int verified = 0;
+       int valid_data = 0;
+
+       blob = buffer_get_string(m, &bloblen);
+       signature = buffer_get_string(m, &signaturelen);
+       data = buffer_get_string(m, &datalen);
+
+       if (hostbased_cuser == NULL || hostbased_chost == NULL ||
+         !monitor_allowed_key(blob, bloblen))
+               fatal("%s: bad key, not previously allowed", __func__);
+
+       key = key_from_blob(blob, bloblen);
+       if (key == NULL)
+               fatal("%s: bad public key blob", __func__);
+
+       switch (key_blobtype) {
+       case MM_USERKEY:
+               valid_data = monitor_valid_userblob(data, datalen);
+               break;
+       case MM_HOSTKEY:
+               valid_data = monitor_valid_hostbasedblob(data, datalen,
+                   hostbased_cuser, hostbased_chost);
+               break;
+       default:
+               valid_data = 0;
+               break;
+       }
+       if (!valid_data)
+               fatal("%s: bad signature data blob", __func__);
+
+       verified = key_verify(key, signature, signaturelen, data, datalen);
+       debug3("%s: key %p signature %s",
+           __func__, key, (verified == 1) ? "verified" : "unverified");
+
+       key_free(key);
+       xfree(blob);
+       xfree(signature);
+       xfree(data);
+
+       auth_method = key_blobtype == MM_USERKEY ? "publickey" : "hostbased";
+
+       monitor_reset_key_state();
+
+       buffer_clear(m);
+       buffer_put_int(m, verified);
+       mm_request_send(sock, MONITOR_ANS_KEYVERIFY, m);
+
+       return (verified == 1);
+}
+
+static void
+mm_record_login(Session *s, struct passwd *pw)
+{
+       socklen_t fromlen;
+       struct sockaddr_storage from;
+
+       /*
+        * Get IP address of client. If the connection is not a socket, let
+        * the address be 0.0.0.0.
+        */
+       memset(&from, 0, sizeof(from));
+       fromlen = sizeof(from);
+       if (packet_connection_is_on_socket()) {
+               if (getpeername(packet_get_connection_in(),
+                   (struct sockaddr *)&from, &fromlen) < 0) {
+                       debug("getpeername: %.100s", strerror(errno));
+                       cleanup_exit(255);
+               }
+       }
+       /* Record that there was a login on that tty from the remote host. */
+       record_login(s->pid, s->tty, pw->pw_name, pw->pw_uid,
+           get_remote_name_or_ip(utmp_len, options.use_dns),
+           (struct sockaddr *)&from, fromlen);
+}
+
+static void
+mm_session_close(Session *s)
+{
+       debug3("%s: session %d pid %ld", __func__, s->self, (long)s->pid);
+       if (s->ttyfd != -1) {
+               debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd);
+               session_pty_cleanup2(s);
+       }
+       session_unused(s->self);
+}
+
+int
+mm_answer_pty(int sock, Buffer *m)
+{
+       extern struct monitor *pmonitor;
+       Session *s;
+       int res, fd0;
+
+       debug3("%s entering", __func__);
+
+       buffer_clear(m);
+       s = session_new();
+       if (s == NULL)
+               goto error;
+       s->authctxt = authctxt;
+       s->pw = authctxt->pw;
+       s->pid = pmonitor->m_pid;
+       res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty));
+       if (res == 0)
+               goto error;
+       pty_setowner(authctxt->pw, s->tty, authctxt->role);
+
+       buffer_put_int(m, 1);
+       buffer_put_cstring(m, s->tty);
+
+       /* We need to trick ttyslot */
+       if (dup2(s->ttyfd, 0) == -1)
+               fatal("%s: dup2", __func__);
+
+       mm_record_login(s, authctxt->pw);
+
+       /* Now we can close the file descriptor again */
+       close(0);
+
+       /* send messages generated by record_login */
+       buffer_put_string(m, buffer_ptr(&loginmsg), buffer_len(&loginmsg));
+       buffer_clear(&loginmsg);
+
+       mm_request_send(sock, MONITOR_ANS_PTY, m);
+
+       if (mm_send_fd(sock, s->ptyfd) == -1 ||
+           mm_send_fd(sock, s->ttyfd) == -1)
+               fatal("%s: send fds failed", __func__);
+
+       /* make sure nothing uses fd 0 */
+       if ((fd0 = open(_PATH_DEVNULL, O_RDONLY)) < 0)
+               fatal("%s: open(/dev/null): %s", __func__, strerror(errno));
+       if (fd0 != 0)
+               error("%s: fd0 %d != 0", __func__, fd0);
+
+       /* slave is not needed */
+       close(s->ttyfd);
+       s->ttyfd = s->ptyfd;
+       /* no need to dup() because nobody closes ptyfd */
+       s->ptymaster = s->ptyfd;
+
+       debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ttyfd);
+
+       return (0);
+
+ error:
+       if (s != NULL)
+               mm_session_close(s);
+       buffer_put_int(m, 0);
+       mm_request_send(sock, MONITOR_ANS_PTY, m);
+       return (0);
+}
+
+int
+mm_answer_pty_cleanup(int sock, Buffer *m)
+{
+       Session *s;
+       char *tty;
+
+       debug3("%s entering", __func__);
+
+       tty = buffer_get_string(m, NULL);
+       if ((s = session_by_tty(tty)) != NULL)
+               mm_session_close(s);
+       buffer_clear(m);
+       xfree(tty);
+       return (0);
+}
+
+int
+mm_answer_sesskey(int sock, Buffer *m)
+{
+       BIGNUM *p;
+       int rsafail;
+
+       /* Turn off permissions */
+       monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 0);
+
+       if ((p = BN_new()) == NULL)
+               fatal("%s: BN_new", __func__);
+
+       buffer_get_bignum2(m, p);
+
+       rsafail = ssh1_session_key(p);
+
+       buffer_clear(m);
+       buffer_put_int(m, rsafail);
+       buffer_put_bignum2(m, p);
+
+       BN_clear_free(p);
+
+       mm_request_send(sock, MONITOR_ANS_SESSKEY, m);
+
+       /* Turn on permissions for sessid passing */
+       monitor_permit(mon_dispatch, MONITOR_REQ_SESSID, 1);
+
+       return (0);
+}
+
+int
+mm_answer_sessid(int sock, Buffer *m)
+{
+       int i;
+
+       debug3("%s entering", __func__);
+
+       if (buffer_len(m) != 16)
+               fatal("%s: bad ssh1 session id", __func__);
+       for (i = 0; i < 16; i++)
+               session_id[i] = buffer_get_char(m);
+
+       /* Turn on permissions for getpwnam */
+       monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
+
+       return (0);
+}
+
+int
+mm_answer_rsa_keyallowed(int sock, Buffer *m)
+{
+       BIGNUM *client_n;
+       Key *key = NULL;
+       u_char *blob = NULL;
+       u_int blen = 0;
+       int allowed = 0;
+
+       debug3("%s entering", __func__);
+
+       auth_method = "rsa";
+       if (options.rsa_authentication && authctxt->valid) {
+               if ((client_n = BN_new()) == NULL)
+                       fatal("%s: BN_new", __func__);
+               buffer_get_bignum2(m, client_n);
+               allowed = auth_rsa_key_allowed(authctxt->pw, client_n, &key);
+               BN_clear_free(client_n);
+       }
+       buffer_clear(m);
+       buffer_put_int(m, allowed);
+       buffer_put_int(m, forced_command != NULL);
+
+       /* clear temporarily storage (used by generate challenge) */
+       monitor_reset_key_state();
+
+       if (allowed && key != NULL) {
+               key->type = KEY_RSA;    /* cheat for key_to_blob */
+               if (key_to_blob(key, &blob, &blen) == 0)
+                       fatal("%s: key_to_blob failed", __func__);
+               buffer_put_string(m, blob, blen);
+
+               /* Save temporarily for comparison in verify */
+               key_blob = blob;
+               key_bloblen = blen;
+               key_blobtype = MM_RSAUSERKEY;
+       }
+       if (key != NULL)
+               key_free(key);
+
+       mm_request_send(sock, MONITOR_ANS_RSAKEYALLOWED, m);
+
+       monitor_permit(mon_dispatch, MONITOR_REQ_RSACHALLENGE, allowed);
+       monitor_permit(mon_dispatch, MONITOR_REQ_RSARESPONSE, 0);
+       return (0);
+}
+
+int
+mm_answer_rsa_challenge(int sock, Buffer *m)
+{
+       Key *key = NULL;
+       u_char *blob;
+       u_int blen;
+
+       debug3("%s entering", __func__);
+
+       if (!authctxt->valid)
+               fatal("%s: authctxt not valid", __func__);
+       blob = buffer_get_string(m, &blen);
+       if (!monitor_allowed_key(blob, blen))
+               fatal("%s: bad key, not previously allowed", __func__);
+       if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY)
+               fatal("%s: key type mismatch", __func__);
+       if ((key = key_from_blob(blob, blen)) == NULL)
+               fatal("%s: received bad key", __func__);
+       if (key->type != KEY_RSA)
+               fatal("%s: received bad key type %d", __func__, key->type);
+       key->type = KEY_RSA1;
+       if (ssh1_challenge)
+               BN_clear_free(ssh1_challenge);
+       ssh1_challenge = auth_rsa_generate_challenge(key);
+
+       buffer_clear(m);
+       buffer_put_bignum2(m, ssh1_challenge);
+
+       debug3("%s sending reply", __func__);
+       mm_request_send(sock, MONITOR_ANS_RSACHALLENGE, m);
+
+       monitor_permit(mon_dispatch, MONITOR_REQ_RSARESPONSE, 1);
+
+       xfree(blob);
+       key_free(key);
+       return (0);
+}
+
+int
+mm_answer_rsa_response(int sock, Buffer *m)
+{
+       Key *key = NULL;
+       u_char *blob, *response;
+       u_int blen, len;
+       int success;
+
+       debug3("%s entering", __func__);
+
+       if (!authctxt->valid)
+               fatal("%s: authctxt not valid", __func__);
+       if (ssh1_challenge == NULL)
+               fatal("%s: no ssh1_challenge", __func__);
+
+       blob = buffer_get_string(m, &blen);
+       if (!monitor_allowed_key(blob, blen))
+               fatal("%s: bad key, not previously allowed", __func__);
+       if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY)
+               fatal("%s: key type mismatch: %d", __func__, key_blobtype);
+       if ((key = key_from_blob(blob, blen)) == NULL)
+               fatal("%s: received bad key", __func__);
+       response = buffer_get_string(m, &len);
+       if (len != 16)
+               fatal("%s: received bad response to challenge", __func__);
+       success = auth_rsa_verify_response(key, ssh1_challenge, response);
+
+       xfree(blob);
+       key_free(key);
+       xfree(response);
+
+       auth_method = key_blobtype == MM_RSAUSERKEY ? "rsa" : "rhosts-rsa";
+
+       /* reset state */
+       BN_clear_free(ssh1_challenge);
+       ssh1_challenge = NULL;
+       monitor_reset_key_state();
+
+       buffer_clear(m);
+       buffer_put_int(m, success);
+       mm_request_send(sock, MONITOR_ANS_RSARESPONSE, m);
+
+       return (success);
+}
+
+int
+mm_answer_term(int sock, Buffer *req)
+{
+       extern struct monitor *pmonitor;
+       int res, status;
+
+       debug3("%s: tearing down sessions", __func__);
+
+       /* The child is terminating */
+       session_destroy_all(&mm_session_close);
+
+#ifdef USE_PAM
+       if (options.use_pam)
+               sshpam_cleanup();
+#endif
+
+       while (waitpid(pmonitor->m_pid, &status, 0) == -1)
+               if (errno != EINTR)
+                       exit(1);
+
+       res = WIFEXITED(status) ? WEXITSTATUS(status) : 1;
+
+       /* Terminate process */
+       exit(res);
+}
+
+#ifdef SSH_AUDIT_EVENTS
+/* Report that an audit event occurred */
+int
+mm_answer_audit_event(int socket, Buffer *m)
+{
+       ssh_audit_event_t event;
+
+       debug3("%s entering", __func__);
+
+       event = buffer_get_int(m);
+       switch(event) {
+       case SSH_AUTH_FAIL_PUBKEY:
+       case SSH_AUTH_FAIL_HOSTBASED:
+       case SSH_AUTH_FAIL_GSSAPI:
+       case SSH_LOGIN_EXCEED_MAXTRIES:
+       case SSH_LOGIN_ROOT_DENIED:
+       case SSH_CONNECTION_CLOSE:
+       case SSH_INVALID_USER:
+               audit_event(event);
+               break;
+       default:
+               fatal("Audit event type %d not permitted", event);
+       }
+
+       return (0);
+}
+
+int
+mm_answer_audit_command(int socket, Buffer *m)
+{
+       u_int len;
+       char *cmd;
+
+       debug3("%s entering", __func__);
+       cmd = buffer_get_string(m, &len);
+       /* sanity check command, if so how? */
+       audit_run_command(cmd);
+       xfree(cmd);
+       return (0);
+}
+#endif /* SSH_AUDIT_EVENTS */
+
+void
+monitor_apply_keystate(struct monitor *pmonitor)
+{
+       if (compat20) {
+               set_newkeys(MODE_IN);
+               set_newkeys(MODE_OUT);
+       } else {
+               packet_set_protocol_flags(child_state.ssh1protoflags);
+               packet_set_encryption_key(child_state.ssh1key,
+                   child_state.ssh1keylen, child_state.ssh1cipher);
+               xfree(child_state.ssh1key);
+       }
+
+       /* for rc4 and other stateful ciphers */
+       packet_set_keycontext(MODE_OUT, child_state.keyout);
+       xfree(child_state.keyout);
+       packet_set_keycontext(MODE_IN, child_state.keyin);
+       xfree(child_state.keyin);
+
+       if (!compat20) {
+               packet_set_iv(MODE_OUT, child_state.ivout);
+               xfree(child_state.ivout);
+               packet_set_iv(MODE_IN, child_state.ivin);
+               xfree(child_state.ivin);
+       }
+
+       memcpy(&incoming_stream, &child_state.incoming,
+           sizeof(incoming_stream));
+       memcpy(&outgoing_stream, &child_state.outgoing,
+           sizeof(outgoing_stream));
+
+       /* Update with new address */
+       if (options.compression)
+               mm_init_compression(pmonitor->m_zlib);
+
+       /* Network I/O buffers */
+       /* XXX inefficient for large buffers, need: buffer_init_from_string */
+       buffer_clear(packet_get_input());
+       buffer_append(packet_get_input(), child_state.input, child_state.ilen);
+       memset(child_state.input, 0, child_state.ilen);
+       xfree(child_state.input);
+
+       buffer_clear(packet_get_output());
+       buffer_append(packet_get_output(), child_state.output,
+                     child_state.olen);
+       memset(child_state.output, 0, child_state.olen);
+       xfree(child_state.output);
+
+       /* Roaming */
+       if (compat20)
+               roam_set_bytes(child_state.sent_bytes, child_state.recv_bytes);
+}
+
+static Kex *
+mm_get_kex(Buffer *m)
+{
+       Kex *kex;
+       void *blob;
+       u_int bloblen;
+
+       kex = xcalloc(1, sizeof(*kex));
+       kex->session_id = buffer_get_string(m, &kex->session_id_len);
+       if (session_id2 == NULL ||
+           kex->session_id_len != session_id2_len ||
+           timingsafe_bcmp(kex->session_id, session_id2, session_id2_len) != 0)
+               fatal("mm_get_get: internal error: bad session id");
+       kex->we_need = buffer_get_int(m);
+       kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
+       kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
+       kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
+       kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
+       kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
+#ifdef GSSAPI
+       if (options.gss_keyex) {
+               kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server;
+               kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server;
+               kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server;
+       }
+#endif
+       kex->server = 1;
+       kex->hostkey_type = buffer_get_int(m);
+       kex->kex_type = buffer_get_int(m);
+       blob = buffer_get_string(m, &bloblen);
+       buffer_init(&kex->my);
+       buffer_append(&kex->my, blob, bloblen);
+       xfree(blob);
+       blob = buffer_get_string(m, &bloblen);
+       buffer_init(&kex->peer);
+       buffer_append(&kex->peer, blob, bloblen);
+       xfree(blob);
+       kex->done = 1;
+       kex->flags = buffer_get_int(m);
+       kex->client_version_string = buffer_get_string(m, NULL);
+       kex->server_version_string = buffer_get_string(m, NULL);
+       kex->load_host_public_key=&get_hostkey_public_by_type;
+       kex->load_host_private_key=&get_hostkey_private_by_type;
+       kex->host_key_index=&get_hostkey_index;
+
+       return (kex);
+}
+
+/* This function requries careful sanity checking */
+
+void
+mm_get_keystate(struct monitor *pmonitor)
+{
+       Buffer m;
+       u_char *blob, *p;
+       u_int bloblen, plen;
+       u_int32_t seqnr, packets;
+       u_int64_t blocks, bytes;
+
+       debug3("%s: Waiting for new keys", __func__);
+
+       buffer_init(&m);
+       mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, &m);
+       if (!compat20) {
+               child_state.ssh1protoflags = buffer_get_int(&m);
+               child_state.ssh1cipher = buffer_get_int(&m);
+               child_state.ssh1key = buffer_get_string(&m,
+                   &child_state.ssh1keylen);
+               child_state.ivout = buffer_get_string(&m,
+                   &child_state.ivoutlen);
+               child_state.ivin = buffer_get_string(&m, &child_state.ivinlen);
+               goto skip;
+       } else {
+               /* Get the Kex for rekeying */
+               *pmonitor->m_pkex = mm_get_kex(&m);
+       }
+
+       blob = buffer_get_string(&m, &bloblen);
+       current_keys[MODE_OUT] = mm_newkeys_from_blob(blob, bloblen);
+       xfree(blob);
+
+       debug3("%s: Waiting for second key", __func__);
+       blob = buffer_get_string(&m, &bloblen);
+       current_keys[MODE_IN] = mm_newkeys_from_blob(blob, bloblen);
+       xfree(blob);
+
+       /* Now get sequence numbers for the packets */
+       seqnr = buffer_get_int(&m);
+       blocks = buffer_get_int64(&m);
+       packets = buffer_get_int(&m);
+       bytes = buffer_get_int64(&m);
+       packet_set_state(MODE_OUT, seqnr, blocks, packets, bytes);
+       seqnr = buffer_get_int(&m);
+       blocks = buffer_get_int64(&m);
+       packets = buffer_get_int(&m);
+       bytes = buffer_get_int64(&m);
+       packet_set_state(MODE_IN, seqnr, blocks, packets, bytes);
+
+ skip:
+       /* Get the key context */
+       child_state.keyout = buffer_get_string(&m, &child_state.keyoutlen);
+       child_state.keyin  = buffer_get_string(&m, &child_state.keyinlen);
+
+       debug3("%s: Getting compression state", __func__);
+       /* Get compression state */
+       p = buffer_get_string(&m, &plen);
+       if (plen != sizeof(child_state.outgoing))
+               fatal("%s: bad request size", __func__);
+       memcpy(&child_state.outgoing, p, sizeof(child_state.outgoing));
+       xfree(p);
+
+       p = buffer_get_string(&m, &plen);
+       if (plen != sizeof(child_state.incoming))
+               fatal("%s: bad request size", __func__);
+       memcpy(&child_state.incoming, p, sizeof(child_state.incoming));
+       xfree(p);
+
+       /* Network I/O buffers */
+       debug3("%s: Getting Network I/O buffers", __func__);
+       child_state.input = buffer_get_string(&m, &child_state.ilen);
+       child_state.output = buffer_get_string(&m, &child_state.olen);
+
+       /* Roaming */
+       if (compat20) {
+               child_state.sent_bytes = buffer_get_int64(&m);
+               child_state.recv_bytes = buffer_get_int64(&m);
+       }
+
+       buffer_free(&m);
+}
+
+
+/* Allocation functions for zlib */
+void *
+mm_zalloc(struct mm_master *mm, u_int ncount, u_int size)
+{
+       size_t len = (size_t) size * ncount;
+       void *address;
+
+       if (len == 0 || ncount > SIZE_T_MAX / size)
+               fatal("%s: mm_zalloc(%u, %u)", __func__, ncount, size);
+
+       address = mm_malloc(mm, len);
+
+       return (address);
+}
+
+void
+mm_zfree(struct mm_master *mm, void *address)
+{
+       mm_free(mm, address);
+}
+
+void
+mm_init_compression(struct mm_master *mm)
+{
+       outgoing_stream.zalloc = (alloc_func)mm_zalloc;
+       outgoing_stream.zfree = (free_func)mm_zfree;
+       outgoing_stream.opaque = mm;
+
+       incoming_stream.zalloc = (alloc_func)mm_zalloc;
+       incoming_stream.zfree = (free_func)mm_zfree;
+       incoming_stream.opaque = mm;
+}
+
+/* XXX */
+
+#define FD_CLOSEONEXEC(x) do { \
+       if (fcntl(x, F_SETFD, FD_CLOEXEC) == -1) \
+               fatal("fcntl(%d, F_SETFD)", x); \
+} while (0)
+
+static void
+monitor_openfds(struct monitor *mon, int do_logfds)
+{
+       int pair[2];
+
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
+               fatal("%s: socketpair: %s", __func__, strerror(errno));
+       FD_CLOSEONEXEC(pair[0]);
+       FD_CLOSEONEXEC(pair[1]);
+       mon->m_recvfd = pair[0];
+       mon->m_sendfd = pair[1];
+
+       if (do_logfds) {
+               if (pipe(pair) == -1)
+                       fatal("%s: pipe: %s", __func__, strerror(errno));
+               FD_CLOSEONEXEC(pair[0]);
+               FD_CLOSEONEXEC(pair[1]);
+               mon->m_log_recvfd = pair[0];
+               mon->m_log_sendfd = pair[1];
+       } else
+               mon->m_log_recvfd = mon->m_log_sendfd = -1;
+}
+
+#define MM_MEMSIZE     65536
+
+struct monitor *
+monitor_init(void)
+{
+       struct monitor *mon;
+
+       mon = xcalloc(1, sizeof(*mon));
+
+       monitor_openfds(mon, 1);
+
+       /* Used to share zlib space across processes */
+       if (options.compression) {
+               mon->m_zback = mm_create(NULL, MM_MEMSIZE);
+               mon->m_zlib = mm_create(mon->m_zback, 20 * MM_MEMSIZE);
+
+               /* Compression needs to share state across borders */
+               mm_init_compression(mon->m_zlib);
+       }
+
+       return mon;
+}
+
+void
+monitor_reinit(struct monitor *mon)
+{
+       monitor_openfds(mon, 0);
+}
+
+#ifdef GSSAPI
+int
+mm_answer_gss_setup_ctx(int sock, Buffer *m)
+{
+       gss_OID_desc goid;
+       OM_uint32 major;
+       u_int len;
+
+       if (!options.gss_authentication && !options.gss_keyex)
+               fatal("In GSSAPI monitor when GSSAPI is disabled");
+
+       goid.elements = buffer_get_string(m, &len);
+       goid.length = len;
+
+       major = ssh_gssapi_server_ctx(&gsscontext, &goid);
+
+       xfree(goid.elements);
+
+       buffer_clear(m);
+       buffer_put_int(m, major);
+
+       mm_request_send(sock, MONITOR_ANS_GSSSETUP, m);
+
+       /* Now we have a context, enable the step */
+       monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 1);
+
+       return (0);
+}
+
+int
+mm_answer_gss_accept_ctx(int sock, Buffer *m)
+{
+       gss_buffer_desc in;
+       gss_buffer_desc out = GSS_C_EMPTY_BUFFER;
+       OM_uint32 major, minor;
+       OM_uint32 flags = 0; /* GSI needs this */
+       u_int len;
+
+       if (!options.gss_authentication && !options.gss_keyex)
+               fatal("In GSSAPI monitor when GSSAPI is disabled");
+
+       in.value = buffer_get_string(m, &len);
+       in.length = len;
+       major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags);
+       xfree(in.value);
+
+       buffer_clear(m);
+       buffer_put_int(m, major);
+       buffer_put_string(m, out.value, out.length);
+       buffer_put_int(m, flags);
+       mm_request_send(sock, MONITOR_ANS_GSSSTEP, m);
+
+       gss_release_buffer(&minor, &out);
+
+       if (major == GSS_S_COMPLETE) {
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0);
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1);
+       }
+       return (0);
+}
+
+int
+mm_answer_gss_checkmic(int sock, Buffer *m)
+{
+       gss_buffer_desc gssbuf, mic;
+       OM_uint32 ret;
+       u_int len;
+
+       if (!options.gss_authentication && !options.gss_keyex)
+               fatal("In GSSAPI monitor when GSSAPI is disabled");
+
+       gssbuf.value = buffer_get_string(m, &len);
+       gssbuf.length = len;
+       mic.value = buffer_get_string(m, &len);
+       mic.length = len;
+
+       ret = ssh_gssapi_checkmic(gsscontext, &gssbuf, &mic);
+
+       xfree(gssbuf.value);
+       xfree(mic.value);
+
+       buffer_clear(m);
+       buffer_put_int(m, ret);
+
+       mm_request_send(sock, MONITOR_ANS_GSSCHECKMIC, m);
+
+       if (!GSS_ERROR(ret))
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1);
+
+       return (0);
+}
+
+int
+mm_answer_gss_userok(int sock, Buffer *m)
+{
+       int authenticated;
+
+       if (!options.gss_authentication && !options.gss_keyex)
+               fatal("In GSSAPI monitor when GSSAPI is disabled");
+
+       authenticated = authctxt->valid && 
+           ssh_gssapi_userok(authctxt->user, authctxt->pw);
+
+       buffer_clear(m);
+       buffer_put_int(m, authenticated);
+
+       debug3("%s: sending result %d", __func__, authenticated);
+       mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
+
+       auth_method = "gssapi-with-mic";
+
+       /* Monitor loop will terminate if authenticated */
+       return (authenticated);
+}
+
+int 
+mm_answer_gss_sign(int socket, Buffer *m)
+{
+       gss_buffer_desc data;
+       gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
+       OM_uint32 major, minor;
+       u_int len;
+
+       if (!options.gss_authentication && !options.gss_keyex)
+               fatal("In GSSAPI monitor when GSSAPI is disabled");
+
+       data.value = buffer_get_string(m, &len);
+       data.length = len;
+       if (data.length != 20) 
+               fatal("%s: data length incorrect: %d", __func__, 
+                   (int) data.length);
+
+       /* Save the session ID on the first time around */
+       if (session_id2_len == 0) {
+               session_id2_len = data.length;
+               session_id2 = xmalloc(session_id2_len);
+               memcpy(session_id2, data.value, session_id2_len);
+       }
+       major = ssh_gssapi_sign(gsscontext, &data, &hash);
+
+       xfree(data.value);
+
+       buffer_clear(m);
+       buffer_put_int(m, major);
+       buffer_put_string(m, hash.value, hash.length);
+
+       mm_request_send(socket, MONITOR_ANS_GSSSIGN, m);
+
+       gss_release_buffer(&minor, &hash);
+
+       /* Turn on getpwnam permissions */
+       monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
+       
+       /* And credential updating, for when rekeying */
+       monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1);
+
+       return (0);
+}
+
+int
+mm_answer_gss_updatecreds(int socket, Buffer *m) {
+       ssh_gssapi_ccache store;
+       int ok;
+
+       store.filename = buffer_get_string(m, NULL);
+       store.envvar   = buffer_get_string(m, NULL);
+       store.envval   = buffer_get_string(m, NULL);
+
+       ok = ssh_gssapi_update_creds(&store);
+
+       xfree(store.filename);
+       xfree(store.envvar);
+       xfree(store.envval);
+
+       buffer_clear(m);
+       buffer_put_int(m, ok);
+
+       mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
+
+       return(0);
+}
+
+#endif /* GSSAPI */
+
+#ifdef JPAKE
+int
+mm_answer_jpake_step1(int sock, Buffer *m)
+{
+       struct jpake_ctx *pctx;
+       u_char *x3_proof, *x4_proof;
+       u_int x3_proof_len, x4_proof_len;
+
+       if (!options.zero_knowledge_password_authentication)
+               fatal("zero_knowledge_password_authentication disabled");
+
+       if (authctxt->jpake_ctx != NULL)
+               fatal("%s: authctxt->jpake_ctx already set (%p)",
+                   __func__, authctxt->jpake_ctx);
+       authctxt->jpake_ctx = pctx = jpake_new();
+
+       jpake_step1(pctx->grp,
+           &pctx->server_id, &pctx->server_id_len,
+           &pctx->x3, &pctx->x4, &pctx->g_x3, &pctx->g_x4,
+           &x3_proof, &x3_proof_len,
+           &x4_proof, &x4_proof_len);
+
+       JPAKE_DEBUG_CTX((pctx, "step1 done in %s", __func__));
+
+       buffer_clear(m);
+
+       buffer_put_string(m, pctx->server_id, pctx->server_id_len);
+       buffer_put_bignum2(m, pctx->g_x3);
+       buffer_put_bignum2(m, pctx->g_x4);
+       buffer_put_string(m, x3_proof, x3_proof_len);
+       buffer_put_string(m, x4_proof, x4_proof_len);
+
+       debug3("%s: sending step1", __func__);
+       mm_request_send(sock, MONITOR_ANS_JPAKE_STEP1, m);
+
+       bzero(x3_proof, x3_proof_len);
+       bzero(x4_proof, x4_proof_len);
+       xfree(x3_proof);
+       xfree(x4_proof);
+
+       monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_GET_PWDATA, 1);
+       monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 0);
+
+       return 0;
+}
+
+int
+mm_answer_jpake_get_pwdata(int sock, Buffer *m)
+{
+       struct jpake_ctx *pctx = authctxt->jpake_ctx;
+       char *hash_scheme, *salt;
+
+       if (pctx == NULL)
+               fatal("%s: pctx == NULL", __func__);
+
+       auth2_jpake_get_pwdata(authctxt, &pctx->s, &hash_scheme, &salt);
+
+       buffer_clear(m);
+       /* pctx->s is sensitive, not returned to slave */
+       buffer_put_cstring(m, hash_scheme);
+       buffer_put_cstring(m, salt);
+
+       debug3("%s: sending pwdata", __func__);
+       mm_request_send(sock, MONITOR_ANS_JPAKE_GET_PWDATA, m);
+
+       bzero(hash_scheme, strlen(hash_scheme));
+       bzero(salt, strlen(salt));
+       xfree(hash_scheme);
+       xfree(salt);
+
+       monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP2, 1);
+
+       return 0;
+}
+
+int
+mm_answer_jpake_step2(int sock, Buffer *m)
+{
+       struct jpake_ctx *pctx = authctxt->jpake_ctx;
+       u_char *x1_proof, *x2_proof, *x4_s_proof;
+       u_int x1_proof_len, x2_proof_len, x4_s_proof_len;
+
+       if (pctx == NULL)
+               fatal("%s: pctx == NULL", __func__);
+
+       if ((pctx->g_x1 = BN_new()) == NULL ||
+           (pctx->g_x2 = BN_new()) == NULL)
+               fatal("%s: BN_new", __func__);
+       buffer_get_bignum2(m, pctx->g_x1);
+       buffer_get_bignum2(m, pctx->g_x2);
+       pctx->client_id = buffer_get_string(m, &pctx->client_id_len);
+       x1_proof = buffer_get_string(m, &x1_proof_len);
+       x2_proof = buffer_get_string(m, &x2_proof_len);
+
+       jpake_step2(pctx->grp, pctx->s, pctx->g_x3,
+           pctx->g_x1, pctx->g_x2, pctx->x4,
+           pctx->client_id, pctx->client_id_len,
+           pctx->server_id, pctx->server_id_len,
+           x1_proof, x1_proof_len,
+           x2_proof, x2_proof_len,
+           &pctx->b,
+           &x4_s_proof, &x4_s_proof_len);
+
+       JPAKE_DEBUG_CTX((pctx, "step2 done in %s", __func__));
+
+       bzero(x1_proof, x1_proof_len);
+       bzero(x2_proof, x2_proof_len);
+       xfree(x1_proof);
+       xfree(x2_proof);
+
+       buffer_clear(m);
+
+       buffer_put_bignum2(m, pctx->b);
+       buffer_put_string(m, x4_s_proof, x4_s_proof_len);
+
+       debug3("%s: sending step2", __func__);
+       mm_request_send(sock, MONITOR_ANS_JPAKE_STEP2, m);
+
+       bzero(x4_s_proof, x4_s_proof_len);
+       xfree(x4_s_proof);
+
+       monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_KEY_CONFIRM, 1);
+
+       return 0;
+}
+
+int
+mm_answer_jpake_key_confirm(int sock, Buffer *m)
+{
+       struct jpake_ctx *pctx = authctxt->jpake_ctx;
+       u_char *x2_s_proof;
+       u_int x2_s_proof_len;
+
+       if (pctx == NULL)
+               fatal("%s: pctx == NULL", __func__);
+
+       if ((pctx->a = BN_new()) == NULL)
+               fatal("%s: BN_new", __func__);
+       buffer_get_bignum2(m, pctx->a);
+       x2_s_proof = buffer_get_string(m, &x2_s_proof_len);
+
+       jpake_key_confirm(pctx->grp, pctx->s, pctx->a,
+           pctx->x4, pctx->g_x3, pctx->g_x4, pctx->g_x1, pctx->g_x2,
+           pctx->server_id, pctx->server_id_len,
+           pctx->client_id, pctx->client_id_len,
+           session_id2, session_id2_len,
+           x2_s_proof, x2_s_proof_len,
+           &pctx->k,
+           &pctx->h_k_sid_sessid, &pctx->h_k_sid_sessid_len);
+
+       JPAKE_DEBUG_CTX((pctx, "key_confirm done in %s", __func__));
+
+       bzero(x2_s_proof, x2_s_proof_len);
+       buffer_clear(m);
+
+       /* pctx->k is sensitive, not sent */
+       buffer_put_string(m, pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
+
+       debug3("%s: sending confirmation hash", __func__);
+       mm_request_send(sock, MONITOR_ANS_JPAKE_KEY_CONFIRM, m);
+
+       monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_CHECK_CONFIRM, 1);
+
+       return 0;
+}
+
+int
+mm_answer_jpake_check_confirm(int sock, Buffer *m)
+{
+       int authenticated = 0;
+       u_char *peer_confirm_hash;
+       u_int peer_confirm_hash_len;
+       struct jpake_ctx *pctx = authctxt->jpake_ctx;
+
+       if (pctx == NULL)
+               fatal("%s: pctx == NULL", __func__);
+
+       peer_confirm_hash = buffer_get_string(m, &peer_confirm_hash_len);
+
+       authenticated = jpake_check_confirm(pctx->k,
+           pctx->client_id, pctx->client_id_len,
+           session_id2, session_id2_len,
+           peer_confirm_hash, peer_confirm_hash_len) && authctxt->valid;
+
+       JPAKE_DEBUG_CTX((pctx, "check_confirm done in %s", __func__));
+
+       bzero(peer_confirm_hash, peer_confirm_hash_len);
+       xfree(peer_confirm_hash);
+
+       buffer_clear(m);
+       buffer_put_int(m, authenticated);
+
+       debug3("%s: sending result %d", __func__, authenticated);
+       mm_request_send(sock, MONITOR_ANS_JPAKE_CHECK_CONFIRM, m);
+
+       monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 1);
+
+       auth_method = "jpake-01@openssh.com";
+       return authenticated;
+}
+
+#endif /* JPAKE */
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor.h b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor.h
new file mode 100644 (file)
index 0000000..42887eb
--- /dev/null
@@ -0,0 +1,101 @@
+/* $OpenBSD: monitor.h,v 1.16 2011/06/17 21:44:31 djm Exp $ */
+
+/*
+ * Copyright 2002 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.
+ */
+
+#ifndef _MONITOR_H_
+#define _MONITOR_H_
+
+enum monitor_reqtype {
+       MONITOR_REQ_MODULI, MONITOR_ANS_MODULI,
+       MONITOR_REQ_FREE, MONITOR_REQ_AUTHSERV, MONITOR_REQ_AUTHROLE,
+       MONITOR_REQ_SIGN, MONITOR_ANS_SIGN,
+       MONITOR_REQ_PWNAM, MONITOR_ANS_PWNAM,
+       MONITOR_REQ_AUTH2_READ_BANNER, MONITOR_ANS_AUTH2_READ_BANNER,
+       MONITOR_REQ_AUTHPASSWORD, MONITOR_ANS_AUTHPASSWORD,
+       MONITOR_REQ_BSDAUTHQUERY, MONITOR_ANS_BSDAUTHQUERY,
+       MONITOR_REQ_BSDAUTHRESPOND, MONITOR_ANS_BSDAUTHRESPOND,
+       MONITOR_REQ_SKEYQUERY, MONITOR_ANS_SKEYQUERY,
+       MONITOR_REQ_SKEYRESPOND, MONITOR_ANS_SKEYRESPOND,
+       MONITOR_REQ_KEYALLOWED, MONITOR_ANS_KEYALLOWED,
+       MONITOR_REQ_KEYVERIFY, MONITOR_ANS_KEYVERIFY,
+       MONITOR_REQ_KEYEXPORT,
+       MONITOR_REQ_PTY, MONITOR_ANS_PTY,
+       MONITOR_REQ_PTYCLEANUP,
+       MONITOR_REQ_SESSKEY, MONITOR_ANS_SESSKEY,
+       MONITOR_REQ_SESSID,
+       MONITOR_REQ_RSAKEYALLOWED, MONITOR_ANS_RSAKEYALLOWED,
+       MONITOR_REQ_RSACHALLENGE, MONITOR_ANS_RSACHALLENGE,
+       MONITOR_REQ_RSARESPONSE, MONITOR_ANS_RSARESPONSE,
+       MONITOR_REQ_GSSSETUP, MONITOR_ANS_GSSSETUP,
+       MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP,
+       MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK,
+       MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC,
+       MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN,
+       MONITOR_REQ_GSSUPCREDS, MONITOR_ANS_GSSUPCREDS,
+       MONITOR_REQ_PAM_START,
+       MONITOR_REQ_PAM_ACCOUNT, MONITOR_ANS_PAM_ACCOUNT,
+       MONITOR_REQ_PAM_INIT_CTX, MONITOR_ANS_PAM_INIT_CTX,
+       MONITOR_REQ_PAM_QUERY, MONITOR_ANS_PAM_QUERY,
+       MONITOR_REQ_PAM_RESPOND, MONITOR_ANS_PAM_RESPOND,
+       MONITOR_REQ_PAM_FREE_CTX, MONITOR_ANS_PAM_FREE_CTX,
+       MONITOR_REQ_AUDIT_EVENT, MONITOR_REQ_AUDIT_COMMAND,
+       MONITOR_REQ_TERM,
+       MONITOR_REQ_JPAKE_STEP1, MONITOR_ANS_JPAKE_STEP1,
+       MONITOR_REQ_JPAKE_GET_PWDATA, MONITOR_ANS_JPAKE_GET_PWDATA,
+       MONITOR_REQ_JPAKE_STEP2, MONITOR_ANS_JPAKE_STEP2,
+       MONITOR_REQ_JPAKE_KEY_CONFIRM, MONITOR_ANS_JPAKE_KEY_CONFIRM,
+       MONITOR_REQ_JPAKE_CHECK_CONFIRM, MONITOR_ANS_JPAKE_CHECK_CONFIRM,
+};
+
+struct mm_master;
+struct monitor {
+       int                      m_recvfd;
+       int                      m_sendfd;
+       int                      m_log_recvfd;
+       int                      m_log_sendfd;
+       struct mm_master        *m_zback;
+       struct mm_master        *m_zlib;
+       struct Kex              **m_pkex;
+       pid_t                    m_pid;
+};
+
+struct monitor *monitor_init(void);
+void monitor_reinit(struct monitor *);
+void monitor_sync(struct monitor *);
+
+struct Authctxt;
+void monitor_child_preauth(struct Authctxt *, struct monitor *);
+void monitor_child_postauth(struct monitor *);
+
+struct mon_table;
+int monitor_read(struct monitor*, struct mon_table *, struct mon_table **);
+
+/* Prototypes for request sending and receiving */
+void mm_request_send(int, enum monitor_reqtype, Buffer *);
+void mm_request_receive(int, Buffer *);
+void mm_request_receive_expect(int, enum monitor_reqtype, Buffer *);
+
+#endif /* _MONITOR_H_ */
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor_wrap.c b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor_wrap.c
new file mode 100644 (file)
index 0000000..f46be66
--- /dev/null
@@ -0,0 +1,1515 @@
+/* $OpenBSD: monitor_wrap.c,v 1.73 2011/06/17 21:44:31 djm Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * Copyright 2002 Markus Friedl <markus@openbsd.org>
+ * 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"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#include <openssl/evp.h>
+
+#include "openbsd-compat/sys-queue.h"
+#include "xmalloc.h"
+#include "ssh.h"
+#include "dh.h"
+#include "buffer.h"
+#include "key.h"
+#include "cipher.h"
+#include "kex.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "auth-options.h"
+#include "packet.h"
+#include "mac.h"
+#include "log.h"
+#ifdef TARGET_OS_MAC    /* XXX Broken krb5 headers on Mac */
+#undef TARGET_OS_MAC
+#include "zlib.h"
+#define TARGET_OS_MAC 1
+#else
+#include "zlib.h"
+#endif
+#include "monitor.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+#include "monitor_wrap.h"
+#include "atomicio.h"
+#include "monitor_fdpass.h"
+#include "misc.h"
+#include "schnorr.h"
+#include "jpake.h"
+#include "uuencode.h"
+
+#include "channels.h"
+#include "session.h"
+#include "servconf.h"
+#include "roaming.h"
+
+/* Imports */
+extern int compat20;
+extern z_stream incoming_stream;
+extern z_stream outgoing_stream;
+extern struct monitor *pmonitor;
+extern Buffer loginmsg;
+extern ServerOptions options;
+
+void
+mm_log_handler(LogLevel level, const char *msg, void *ctx)
+{
+       Buffer log_msg;
+       struct monitor *mon = (struct monitor *)ctx;
+
+       if (mon->m_log_sendfd == -1)
+               fatal("%s: no log channel", __func__);
+
+       buffer_init(&log_msg);
+       /*
+        * Placeholder for packet length. Will be filled in with the actual
+        * packet length once the packet has been constucted. This saves
+        * fragile math.
+        */
+       buffer_put_int(&log_msg, 0);
+
+       buffer_put_int(&log_msg, level);
+       buffer_put_cstring(&log_msg, msg);
+       put_u32(buffer_ptr(&log_msg), buffer_len(&log_msg) - 4);
+       if (atomicio(vwrite, mon->m_log_sendfd, buffer_ptr(&log_msg),
+           buffer_len(&log_msg)) != buffer_len(&log_msg))
+               fatal("%s: write: %s", __func__, strerror(errno));
+       buffer_free(&log_msg);
+}
+
+int
+mm_is_monitor(void)
+{
+       /*
+        * m_pid is only set in the privileged part, and
+        * points to the unprivileged child.
+        */
+       return (pmonitor && pmonitor->m_pid > 0);
+}
+
+void
+mm_request_send(int sock, enum monitor_reqtype type, Buffer *m)
+{
+       u_int mlen = buffer_len(m);
+       u_char buf[5];
+
+       debug3("%s entering: type %d", __func__, type);
+
+       put_u32(buf, mlen + 1);
+       buf[4] = (u_char) type;         /* 1st byte of payload is mesg-type */
+       if (atomicio(vwrite, sock, buf, sizeof(buf)) != sizeof(buf))
+               fatal("%s: write: %s", __func__, strerror(errno));
+       if (atomicio(vwrite, sock, buffer_ptr(m), mlen) != mlen)
+               fatal("%s: write: %s", __func__, strerror(errno));
+}
+
+void
+mm_request_receive(int sock, Buffer *m)
+{
+       u_char buf[4];
+       u_int msg_len;
+
+       debug3("%s entering", __func__);
+
+       if (atomicio(read, sock, buf, sizeof(buf)) != sizeof(buf)) {
+               if (errno == EPIPE)
+                       cleanup_exit(255);
+               fatal("%s: read: %s", __func__, strerror(errno));
+       }
+       msg_len = get_u32(buf);
+       if (msg_len > 256 * 1024)
+               fatal("%s: read: bad msg_len %d", __func__, msg_len);
+       buffer_clear(m);
+       buffer_append_space(m, msg_len);
+       if (atomicio(read, sock, buffer_ptr(m), msg_len) != msg_len)
+               fatal("%s: read: %s", __func__, strerror(errno));
+}
+
+void
+mm_request_receive_expect(int sock, enum monitor_reqtype type, Buffer *m)
+{
+       u_char rtype;
+
+       debug3("%s entering: type %d", __func__, type);
+
+       mm_request_receive(sock, m);
+       rtype = buffer_get_char(m);
+       if (rtype != type)
+               fatal("%s: read: rtype %d != type %d", __func__,
+                   rtype, type);
+}
+
+DH *
+mm_choose_dh(int min, int nbits, int max)
+{
+       BIGNUM *p, *g;
+       int success = 0;
+       Buffer m;
+
+       buffer_init(&m);
+       buffer_put_int(&m, min);
+       buffer_put_int(&m, nbits);
+       buffer_put_int(&m, max);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_MODULI, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_MODULI", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_MODULI, &m);
+
+       success = buffer_get_char(&m);
+       if (success == 0)
+               fatal("%s: MONITOR_ANS_MODULI failed", __func__);
+
+       if ((p = BN_new()) == NULL)
+               fatal("%s: BN_new failed", __func__);
+       if ((g = BN_new()) == NULL)
+               fatal("%s: BN_new failed", __func__);
+       buffer_get_bignum2(&m, p);
+       buffer_get_bignum2(&m, g);
+
+       debug3("%s: remaining %d", __func__, buffer_len(&m));
+       buffer_free(&m);
+
+       return (dh_new_group(g, p));
+}
+
+int
+mm_key_sign(Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen)
+{
+       Kex *kex = *pmonitor->m_pkex;
+       Buffer m;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       buffer_put_int(&m, kex->host_key_index(key));
+       buffer_put_string(&m, data, datalen);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SIGN, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_SIGN", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SIGN, &m);
+       *sigp  = buffer_get_string(&m, lenp);
+       buffer_free(&m);
+
+       return (0);
+}
+
+struct passwd *
+mm_getpwnamallow(const char *username)
+{
+       Buffer m;
+       struct passwd *pw;
+       u_int len, i;
+       ServerOptions *newopts;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       buffer_put_cstring(&m, username);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PWNAM, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_PWNAM", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PWNAM, &m);
+
+       if (buffer_get_char(&m) == 0) {
+               pw = NULL;
+               goto out;
+       }
+       pw = buffer_get_string(&m, &len);
+       if (len != sizeof(struct passwd))
+               fatal("%s: struct passwd size mismatch", __func__);
+       pw->pw_name = buffer_get_string(&m, NULL);
+       pw->pw_passwd = buffer_get_string(&m, NULL);
+       pw->pw_gecos = buffer_get_string(&m, NULL);
+#ifdef HAVE_PW_CLASS_IN_PASSWD
+       pw->pw_class = buffer_get_string(&m, NULL);
+#endif
+       pw->pw_dir = buffer_get_string(&m, NULL);
+       pw->pw_shell = buffer_get_string(&m, NULL);
+
+out:
+       /* copy options block as a Match directive may have changed some */
+       newopts = buffer_get_string(&m, &len);
+       if (len != sizeof(*newopts))
+               fatal("%s: option block size mismatch", __func__);
+
+#define M_CP_STROPT(x) do { \
+               if (newopts->x != NULL) \
+                       newopts->x = buffer_get_string(&m, NULL); \
+       } while (0)
+#define M_CP_STRARRAYOPT(x, nx) do { \
+               for (i = 0; i < newopts->nx; i++) \
+                       newopts->x[i] = buffer_get_string(&m, NULL); \
+       } while (0)
+       /* See comment in servconf.h */
+       COPY_MATCH_STRING_OPTS();
+#undef M_CP_STROPT
+#undef M_CP_STRARRAYOPT
+
+       copy_set_server_options(&options, newopts, 1);
+       xfree(newopts);
+
+       buffer_free(&m);
+
+       return (pw);
+}
+
+char *
+mm_auth2_read_banner(void)
+{
+       Buffer m;
+       char *banner;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTH2_READ_BANNER, &m);
+       buffer_clear(&m);
+
+       mm_request_receive_expect(pmonitor->m_recvfd,
+           MONITOR_ANS_AUTH2_READ_BANNER, &m);
+       banner = buffer_get_string(&m, NULL);
+       buffer_free(&m);
+
+       /* treat empty banner as missing banner */
+       if (strlen(banner) == 0) {
+               xfree(banner);
+               banner = NULL;
+       }
+       return (banner);
+}
+
+/* Inform the privileged process about service, style, and role */
+
+void
+mm_inform_authserv(char *service, char *style, char *role)
+{
+       Buffer m;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       buffer_put_cstring(&m, service);
+       buffer_put_cstring(&m, style ? style : "");
+       buffer_put_cstring(&m, role ? role : "");
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHSERV, &m);
+
+       buffer_free(&m);
+}
+
+/* Inform the privileged process about role */
+
+void
+mm_inform_authrole(char *role)
+{
+       Buffer m;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       buffer_put_cstring(&m, role ? role : "");
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHROLE, &m);
+
+       buffer_free(&m);
+}
+
+/* Do the password authentication */
+int
+mm_auth_password(Authctxt *authctxt, char *password)
+{
+       Buffer m;
+       int authenticated = 0;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       buffer_put_cstring(&m, password);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHPASSWORD, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_AUTHPASSWORD", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUTHPASSWORD, &m);
+
+       authenticated = buffer_get_int(&m);
+
+       buffer_free(&m);
+
+       debug3("%s: user %sauthenticated",
+           __func__, authenticated ? "" : "not ");
+       return (authenticated);
+}
+
+int
+mm_user_key_allowed(struct passwd *pw, Key *key)
+{
+       return (mm_key_allowed(MM_USERKEY, NULL, NULL, key));
+}
+
+int
+mm_hostbased_key_allowed(struct passwd *pw, char *user, char *host,
+    Key *key)
+{
+       return (mm_key_allowed(MM_HOSTKEY, user, host, key));
+}
+
+int
+mm_auth_rhosts_rsa_key_allowed(struct passwd *pw, char *user,
+    char *host, Key *key)
+{
+       int ret;
+
+       key->type = KEY_RSA; /* XXX hack for key_to_blob */
+       ret = mm_key_allowed(MM_RSAHOSTKEY, user, host, key);
+       key->type = KEY_RSA1;
+       return (ret);
+}
+
+int
+mm_key_allowed(enum mm_keytype type, char *user, char *host, Key *key)
+{
+       Buffer m;
+       u_char *blob;
+       u_int len;
+       int allowed = 0, have_forced = 0;
+
+       debug3("%s entering", __func__);
+
+       /* Convert the key to a blob and the pass it over */
+       if (!key_to_blob(key, &blob, &len))
+               return (0);
+
+       buffer_init(&m);
+       buffer_put_int(&m, type);
+       buffer_put_cstring(&m, user ? user : "");
+       buffer_put_cstring(&m, host ? host : "");
+       buffer_put_string(&m, blob, len);
+       xfree(blob);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYALLOWED, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_KEYALLOWED", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_KEYALLOWED, &m);
+
+       allowed = buffer_get_int(&m);
+
+       /* fake forced command */
+       auth_clear_options();
+       have_forced = buffer_get_int(&m);
+       forced_command = have_forced ? xstrdup("true") : NULL;
+
+       buffer_free(&m);
+
+       return (allowed);
+}
+
+/*
+ * This key verify needs to send the key type along, because the
+ * privileged parent makes the decision if the key is allowed
+ * for authentication.
+ */
+
+int
+mm_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen)
+{
+       Buffer m;
+       u_char *blob;
+       u_int len;
+       int verified = 0;
+
+       debug3("%s entering", __func__);
+
+       /* Convert the key to a blob and the pass it over */
+       if (!key_to_blob(key, &blob, &len))
+               return (0);
+
+       buffer_init(&m);
+       buffer_put_string(&m, blob, len);
+       buffer_put_string(&m, sig, siglen);
+       buffer_put_string(&m, data, datalen);
+       xfree(blob);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYVERIFY, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_KEYVERIFY", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_KEYVERIFY, &m);
+
+       verified = buffer_get_int(&m);
+
+       buffer_free(&m);
+
+       return (verified);
+}
+
+/* Export key state after authentication */
+Newkeys *
+mm_newkeys_from_blob(u_char *blob, int blen)
+{
+       Buffer b;
+       u_int len;
+       Newkeys *newkey = NULL;
+       Enc *enc;
+       Mac *mac;
+       Comp *comp;
+
+       debug3("%s: %p(%d)", __func__, blob, blen);
+#ifdef DEBUG_PK
+       dump_base64(stderr, blob, blen);
+#endif
+       buffer_init(&b);
+       buffer_append(&b, blob, blen);
+
+       newkey = xmalloc(sizeof(*newkey));
+       enc = &newkey->enc;
+       mac = &newkey->mac;
+       comp = &newkey->comp;
+
+       /* Enc structure */
+       enc->name = buffer_get_string(&b, NULL);
+       buffer_get(&b, &enc->cipher, sizeof(enc->cipher));
+       enc->enabled = buffer_get_int(&b);
+       enc->block_size = buffer_get_int(&b);
+       enc->key = buffer_get_string(&b, &enc->key_len);
+       enc->iv = buffer_get_string(&b, &len);
+       if (len != enc->block_size)
+               fatal("%s: bad ivlen: expected %u != %u", __func__,
+                   enc->block_size, len);
+
+       if (enc->name == NULL || cipher_by_name(enc->name) != enc->cipher)
+               fatal("%s: bad cipher name %s or pointer %p", __func__,
+                   enc->name, enc->cipher);
+
+       /* Mac structure */
+       mac->name = buffer_get_string(&b, NULL);
+       if (mac->name == NULL || mac_setup(mac, mac->name) == -1)
+               fatal("%s: can not setup mac %s", __func__, mac->name);
+       mac->enabled = buffer_get_int(&b);
+       mac->key = buffer_get_string(&b, &len);
+       if (len > mac->key_len)
+               fatal("%s: bad mac key length: %u > %d", __func__, len,
+                   mac->key_len);
+       mac->key_len = len;
+
+       /* Comp structure */
+       comp->type = buffer_get_int(&b);
+       comp->enabled = buffer_get_int(&b);
+       comp->name = buffer_get_string(&b, NULL);
+
+       len = buffer_len(&b);
+       if (len != 0)
+               error("newkeys_from_blob: remaining bytes in blob %u", len);
+       buffer_free(&b);
+       return (newkey);
+}
+
+int
+mm_newkeys_to_blob(int mode, u_char **blobp, u_int *lenp)
+{
+       Buffer b;
+       int len;
+       Enc *enc;
+       Mac *mac;
+       Comp *comp;
+       Newkeys *newkey = (Newkeys *)packet_get_newkeys(mode);
+
+       debug3("%s: converting %p", __func__, newkey);
+
+       if (newkey == NULL) {
+               error("%s: newkey == NULL", __func__);
+               return 0;
+       }
+       enc = &newkey->enc;
+       mac = &newkey->mac;
+       comp = &newkey->comp;
+
+       buffer_init(&b);
+       /* Enc structure */
+       buffer_put_cstring(&b, enc->name);
+       /* The cipher struct is constant and shared, you export pointer */
+       buffer_append(&b, &enc->cipher, sizeof(enc->cipher));
+       buffer_put_int(&b, enc->enabled);
+       buffer_put_int(&b, enc->block_size);
+       buffer_put_string(&b, enc->key, enc->key_len);
+       packet_get_keyiv(mode, enc->iv, enc->block_size);
+       buffer_put_string(&b, enc->iv, enc->block_size);
+
+       /* Mac structure */
+       buffer_put_cstring(&b, mac->name);
+       buffer_put_int(&b, mac->enabled);
+       buffer_put_string(&b, mac->key, mac->key_len);
+
+       /* Comp structure */
+       buffer_put_int(&b, comp->type);
+       buffer_put_int(&b, comp->enabled);
+       buffer_put_cstring(&b, comp->name);
+
+       len = buffer_len(&b);
+       if (lenp != NULL)
+               *lenp = len;
+       if (blobp != NULL) {
+               *blobp = xmalloc(len);
+               memcpy(*blobp, buffer_ptr(&b), len);
+       }
+       memset(buffer_ptr(&b), 0, len);
+       buffer_free(&b);
+       return len;
+}
+
+static void
+mm_send_kex(Buffer *m, Kex *kex)
+{
+       buffer_put_string(m, kex->session_id, kex->session_id_len);
+       buffer_put_int(m, kex->we_need);
+       buffer_put_int(m, kex->hostkey_type);
+       buffer_put_int(m, kex->kex_type);
+       buffer_put_string(m, buffer_ptr(&kex->my), buffer_len(&kex->my));
+       buffer_put_string(m, buffer_ptr(&kex->peer), buffer_len(&kex->peer));
+       buffer_put_int(m, kex->flags);
+       buffer_put_cstring(m, kex->client_version_string);
+       buffer_put_cstring(m, kex->server_version_string);
+}
+
+void
+mm_send_keystate(struct monitor *monitor)
+{
+       Buffer m, *input, *output;
+       u_char *blob, *p;
+       u_int bloblen, plen;
+       u_int32_t seqnr, packets;
+       u_int64_t blocks, bytes;
+
+       buffer_init(&m);
+
+       if (!compat20) {
+               u_char iv[24];
+               u_char *key;
+               u_int ivlen, keylen;
+
+               buffer_put_int(&m, packet_get_protocol_flags());
+
+               buffer_put_int(&m, packet_get_ssh1_cipher());
+
+               debug3("%s: Sending ssh1 KEY+IV", __func__);
+               keylen = packet_get_encryption_key(NULL);
+               key = xmalloc(keylen+1);        /* add 1 if keylen == 0 */
+               keylen = packet_get_encryption_key(key);
+               buffer_put_string(&m, key, keylen);
+               memset(key, 0, keylen);
+               xfree(key);
+
+               ivlen = packet_get_keyiv_len(MODE_OUT);
+               packet_get_keyiv(MODE_OUT, iv, ivlen);
+               buffer_put_string(&m, iv, ivlen);
+               ivlen = packet_get_keyiv_len(MODE_OUT);
+               packet_get_keyiv(MODE_IN, iv, ivlen);
+               buffer_put_string(&m, iv, ivlen);
+               goto skip;
+       } else {
+               /* Kex for rekeying */
+               mm_send_kex(&m, *monitor->m_pkex);
+       }
+
+       debug3("%s: Sending new keys: %p %p",
+           __func__, packet_get_newkeys(MODE_OUT),
+           packet_get_newkeys(MODE_IN));
+
+       /* Keys from Kex */
+       if (!mm_newkeys_to_blob(MODE_OUT, &blob, &bloblen))
+               fatal("%s: conversion of newkeys failed", __func__);
+
+       buffer_put_string(&m, blob, bloblen);
+       xfree(blob);
+
+       if (!mm_newkeys_to_blob(MODE_IN, &blob, &bloblen))
+               fatal("%s: conversion of newkeys failed", __func__);
+
+       buffer_put_string(&m, blob, bloblen);
+       xfree(blob);
+
+       packet_get_state(MODE_OUT, &seqnr, &blocks, &packets, &bytes);
+       buffer_put_int(&m, seqnr);
+       buffer_put_int64(&m, blocks);
+       buffer_put_int(&m, packets);
+       buffer_put_int64(&m, bytes);
+       packet_get_state(MODE_IN, &seqnr, &blocks, &packets, &bytes);
+       buffer_put_int(&m, seqnr);
+       buffer_put_int64(&m, blocks);
+       buffer_put_int(&m, packets);
+       buffer_put_int64(&m, bytes);
+
+       debug3("%s: New keys have been sent", __func__);
+ skip:
+       /* More key context */
+       plen = packet_get_keycontext(MODE_OUT, NULL);
+       p = xmalloc(plen+1);
+       packet_get_keycontext(MODE_OUT, p);
+       buffer_put_string(&m, p, plen);
+       xfree(p);
+
+       plen = packet_get_keycontext(MODE_IN, NULL);
+       p = xmalloc(plen+1);
+       packet_get_keycontext(MODE_IN, p);
+       buffer_put_string(&m, p, plen);
+       xfree(p);
+
+       /* Compression state */
+       debug3("%s: Sending compression state", __func__);
+       buffer_put_string(&m, &outgoing_stream, sizeof(outgoing_stream));
+       buffer_put_string(&m, &incoming_stream, sizeof(incoming_stream));
+
+       /* Network I/O buffers */
+       input = (Buffer *)packet_get_input();
+       output = (Buffer *)packet_get_output();
+       buffer_put_string(&m, buffer_ptr(input), buffer_len(input));
+       buffer_put_string(&m, buffer_ptr(output), buffer_len(output));
+
+       /* Roaming */
+       if (compat20) {
+               buffer_put_int64(&m, get_sent_bytes());
+               buffer_put_int64(&m, get_recv_bytes());
+       }
+
+       mm_request_send(monitor->m_recvfd, MONITOR_REQ_KEYEXPORT, &m);
+       debug3("%s: Finished sending state", __func__);
+
+       buffer_free(&m);
+}
+
+int
+mm_pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
+{
+       Buffer m;
+       char *p, *msg;
+       int success = 0, tmp1 = -1, tmp2 = -1;
+
+       /* Kludge: ensure there are fds free to receive the pty/tty */
+       if ((tmp1 = dup(pmonitor->m_recvfd)) == -1 ||
+           (tmp2 = dup(pmonitor->m_recvfd)) == -1) {
+               error("%s: cannot allocate fds for pty", __func__);
+               if (tmp1 > 0)
+                       close(tmp1);
+               if (tmp2 > 0)
+                       close(tmp2);
+               return 0;
+       }
+       close(tmp1);
+       close(tmp2);
+
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PTY, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_PTY", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PTY, &m);
+
+       success = buffer_get_int(&m);
+       if (success == 0) {
+               debug3("%s: pty alloc failed", __func__);
+               buffer_free(&m);
+               return (0);
+       }
+       p = buffer_get_string(&m, NULL);
+       msg = buffer_get_string(&m, NULL);
+       buffer_free(&m);
+
+       strlcpy(namebuf, p, namebuflen); /* Possible truncation */
+       xfree(p);
+
+       buffer_append(&loginmsg, msg, strlen(msg));
+       xfree(msg);
+
+       if ((*ptyfd = mm_receive_fd(pmonitor->m_recvfd)) == -1 ||
+           (*ttyfd = mm_receive_fd(pmonitor->m_recvfd)) == -1)
+               fatal("%s: receive fds failed", __func__);
+
+       /* Success */
+       return (1);
+}
+
+void
+mm_session_pty_cleanup2(Session *s)
+{
+       Buffer m;
+
+       if (s->ttyfd == -1)
+               return;
+       buffer_init(&m);
+       buffer_put_cstring(&m, s->tty);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PTYCLEANUP, &m);
+       buffer_free(&m);
+
+       /* closed dup'ed master */
+       if (s->ptymaster != -1 && close(s->ptymaster) < 0)
+               error("close(s->ptymaster/%d): %s",
+                   s->ptymaster, strerror(errno));
+
+       /* unlink pty from session */
+       s->ttyfd = -1;
+}
+
+#ifdef USE_PAM
+void
+mm_start_pam(Authctxt *authctxt)
+{
+       Buffer m;
+
+       debug3("%s entering", __func__);
+       if (!options.use_pam)
+               fatal("UsePAM=no, but ended up in %s anyway", __func__);
+
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_START, &m);
+
+       buffer_free(&m);
+}
+
+u_int
+mm_do_pam_account(void)
+{
+       Buffer m;
+       u_int ret;
+       char *msg;
+
+       debug3("%s entering", __func__);
+       if (!options.use_pam)
+               fatal("UsePAM=no, but ended up in %s anyway", __func__);
+
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_ACCOUNT, &m);
+
+       mm_request_receive_expect(pmonitor->m_recvfd,
+           MONITOR_ANS_PAM_ACCOUNT, &m);
+       ret = buffer_get_int(&m);
+       msg = buffer_get_string(&m, NULL);
+       buffer_append(&loginmsg, msg, strlen(msg));
+       xfree(msg);
+
+       buffer_free(&m);
+
+       debug3("%s returning %d", __func__, ret);
+
+       return (ret);
+}
+
+void *
+mm_sshpam_init_ctx(Authctxt *authctxt)
+{
+       Buffer m;
+       int success;
+
+       debug3("%s", __func__);
+       buffer_init(&m);
+       buffer_put_cstring(&m, authctxt->user);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_INIT_CTX, &m);
+       debug3("%s: waiting for MONITOR_ANS_PAM_INIT_CTX", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_INIT_CTX, &m);
+       success = buffer_get_int(&m);
+       if (success == 0) {
+               debug3("%s: pam_init_ctx failed", __func__);
+               buffer_free(&m);
+               return (NULL);
+       }
+       buffer_free(&m);
+       return (authctxt);
+}
+
+int
+mm_sshpam_query(void *ctx, char **name, char **info,
+    u_int *num, char ***prompts, u_int **echo_on)
+{
+       Buffer m;
+       u_int i;
+       int ret;
+
+       debug3("%s", __func__);
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_QUERY, &m);
+       debug3("%s: waiting for MONITOR_ANS_PAM_QUERY", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_QUERY, &m);
+       ret = buffer_get_int(&m);
+       debug3("%s: pam_query returned %d", __func__, ret);
+       *name = buffer_get_string(&m, NULL);
+       *info = buffer_get_string(&m, NULL);
+       *num = buffer_get_int(&m);
+       if (*num > PAM_MAX_NUM_MSG)
+               fatal("%s: recieved %u PAM messages, expected <= %u",
+                   __func__, *num, PAM_MAX_NUM_MSG);
+       *prompts = xcalloc((*num + 1), sizeof(char *));
+       *echo_on = xcalloc((*num + 1), sizeof(u_int));
+       for (i = 0; i < *num; ++i) {
+               (*prompts)[i] = buffer_get_string(&m, NULL);
+               (*echo_on)[i] = buffer_get_int(&m);
+       }
+       buffer_free(&m);
+       return (ret);
+}
+
+int
+mm_sshpam_respond(void *ctx, u_int num, char **resp)
+{
+       Buffer m;
+       u_int i;
+       int ret;
+
+       debug3("%s", __func__);
+       buffer_init(&m);
+       buffer_put_int(&m, num);
+       for (i = 0; i < num; ++i)
+               buffer_put_cstring(&m, resp[i]);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_RESPOND, &m);
+       debug3("%s: waiting for MONITOR_ANS_PAM_RESPOND", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_RESPOND, &m);
+       ret = buffer_get_int(&m);
+       debug3("%s: pam_respond returned %d", __func__, ret);
+       buffer_free(&m);
+       return (ret);
+}
+
+void
+mm_sshpam_free_ctx(void *ctxtp)
+{
+       Buffer m;
+
+       debug3("%s", __func__);
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_FREE_CTX, &m);
+       debug3("%s: waiting for MONITOR_ANS_PAM_FREE_CTX", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_FREE_CTX, &m);
+       buffer_free(&m);
+}
+#endif /* USE_PAM */
+
+/* Request process termination */
+
+void
+mm_terminate(void)
+{
+       Buffer m;
+
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_TERM, &m);
+       buffer_free(&m);
+}
+
+int
+mm_ssh1_session_key(BIGNUM *num)
+{
+       int rsafail;
+       Buffer m;
+
+       buffer_init(&m);
+       buffer_put_bignum2(&m, num);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SESSKEY, &m);
+
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SESSKEY, &m);
+
+       rsafail = buffer_get_int(&m);
+       buffer_get_bignum2(&m, num);
+
+       buffer_free(&m);
+
+       return (rsafail);
+}
+
+static void
+mm_chall_setup(char **name, char **infotxt, u_int *numprompts,
+    char ***prompts, u_int **echo_on)
+{
+       *name = xstrdup("");
+       *infotxt = xstrdup("");
+       *numprompts = 1;
+       *prompts = xcalloc(*numprompts, sizeof(char *));
+       *echo_on = xcalloc(*numprompts, sizeof(u_int));
+       (*echo_on)[0] = 0;
+}
+
+int
+mm_bsdauth_query(void *ctx, char **name, char **infotxt,
+   u_int *numprompts, char ***prompts, u_int **echo_on)
+{
+       Buffer m;
+       u_int success;
+       char *challenge;
+
+       debug3("%s: entering", __func__);
+
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_BSDAUTHQUERY, &m);
+
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_BSDAUTHQUERY,
+           &m);
+       success = buffer_get_int(&m);
+       if (success == 0) {
+               debug3("%s: no challenge", __func__);
+               buffer_free(&m);
+               return (-1);
+       }
+
+       /* Get the challenge, and format the response */
+       challenge  = buffer_get_string(&m, NULL);
+       buffer_free(&m);
+
+       mm_chall_setup(name, infotxt, numprompts, prompts, echo_on);
+       (*prompts)[0] = challenge;
+
+       debug3("%s: received challenge: %s", __func__, challenge);
+
+       return (0);
+}
+
+int
+mm_bsdauth_respond(void *ctx, u_int numresponses, char **responses)
+{
+       Buffer m;
+       int authok;
+
+       debug3("%s: entering", __func__);
+       if (numresponses != 1)
+               return (-1);
+
+       buffer_init(&m);
+       buffer_put_cstring(&m, responses[0]);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_BSDAUTHRESPOND, &m);
+
+       mm_request_receive_expect(pmonitor->m_recvfd,
+           MONITOR_ANS_BSDAUTHRESPOND, &m);
+
+       authok = buffer_get_int(&m);
+       buffer_free(&m);
+
+       return ((authok == 0) ? -1 : 0);
+}
+
+#ifdef SKEY
+int
+mm_skey_query(void *ctx, char **name, char **infotxt,
+   u_int *numprompts, char ***prompts, u_int **echo_on)
+{
+       Buffer m;
+       u_int success;
+       char *challenge;
+
+       debug3("%s: entering", __func__);
+
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SKEYQUERY, &m);
+
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SKEYQUERY,
+           &m);
+       success = buffer_get_int(&m);
+       if (success == 0) {
+               debug3("%s: no challenge", __func__);
+               buffer_free(&m);
+               return (-1);
+       }
+
+       /* Get the challenge, and format the response */
+       challenge  = buffer_get_string(&m, NULL);
+       buffer_free(&m);
+
+       debug3("%s: received challenge: %s", __func__, challenge);
+
+       mm_chall_setup(name, infotxt, numprompts, prompts, echo_on);
+
+       xasprintf(*prompts, "%s%s", challenge, SKEY_PROMPT);
+       xfree(challenge);
+
+       return (0);
+}
+
+int
+mm_skey_respond(void *ctx, u_int numresponses, char **responses)
+{
+       Buffer m;
+       int authok;
+
+       debug3("%s: entering", __func__);
+       if (numresponses != 1)
+               return (-1);
+
+       buffer_init(&m);
+       buffer_put_cstring(&m, responses[0]);
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SKEYRESPOND, &m);
+
+       mm_request_receive_expect(pmonitor->m_recvfd,
+           MONITOR_ANS_SKEYRESPOND, &m);
+
+       authok = buffer_get_int(&m);
+       buffer_free(&m);
+
+       return ((authok == 0) ? -1 : 0);
+}
+#endif /* SKEY */
+
+void
+mm_ssh1_session_id(u_char session_id[16])
+{
+       Buffer m;
+       int i;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       for (i = 0; i < 16; i++)
+               buffer_put_char(&m, session_id[i]);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SESSID, &m);
+       buffer_free(&m);
+}
+
+int
+mm_auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey)
+{
+       Buffer m;
+       Key *key;
+       u_char *blob;
+       u_int blen;
+       int allowed = 0, have_forced = 0;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       buffer_put_bignum2(&m, client_n);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_RSAKEYALLOWED, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_RSAKEYALLOWED, &m);
+
+       allowed = buffer_get_int(&m);
+
+       /* fake forced command */
+       auth_clear_options();
+       have_forced = buffer_get_int(&m);
+       forced_command = have_forced ? xstrdup("true") : NULL;
+
+       if (allowed && rkey != NULL) {
+               blob = buffer_get_string(&m, &blen);
+               if ((key = key_from_blob(blob, blen)) == NULL)
+                       fatal("%s: key_from_blob failed", __func__);
+               *rkey = key;
+               xfree(blob);
+       }
+       buffer_free(&m);
+
+       return (allowed);
+}
+
+BIGNUM *
+mm_auth_rsa_generate_challenge(Key *key)
+{
+       Buffer m;
+       BIGNUM *challenge;
+       u_char *blob;
+       u_int blen;
+
+       debug3("%s entering", __func__);
+
+       if ((challenge = BN_new()) == NULL)
+               fatal("%s: BN_new failed", __func__);
+
+       key->type = KEY_RSA;    /* XXX cheat for key_to_blob */
+       if (key_to_blob(key, &blob, &blen) == 0)
+               fatal("%s: key_to_blob failed", __func__);
+       key->type = KEY_RSA1;
+
+       buffer_init(&m);
+       buffer_put_string(&m, blob, blen);
+       xfree(blob);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_RSACHALLENGE, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_RSACHALLENGE, &m);
+
+       buffer_get_bignum2(&m, challenge);
+       buffer_free(&m);
+
+       return (challenge);
+}
+
+int
+mm_auth_rsa_verify_response(Key *key, BIGNUM *p, u_char response[16])
+{
+       Buffer m;
+       u_char *blob;
+       u_int blen;
+       int success = 0;
+
+       debug3("%s entering", __func__);
+
+       key->type = KEY_RSA;    /* XXX cheat for key_to_blob */
+       if (key_to_blob(key, &blob, &blen) == 0)
+               fatal("%s: key_to_blob failed", __func__);
+       key->type = KEY_RSA1;
+
+       buffer_init(&m);
+       buffer_put_string(&m, blob, blen);
+       buffer_put_string(&m, response, 16);
+       xfree(blob);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_RSARESPONSE, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_RSARESPONSE, &m);
+
+       success = buffer_get_int(&m);
+       buffer_free(&m);
+
+       return (success);
+}
+
+#ifdef SSH_AUDIT_EVENTS
+void
+mm_audit_event(ssh_audit_event_t event)
+{
+       Buffer m;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       buffer_put_int(&m, event);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_EVENT, &m);
+       buffer_free(&m);
+}
+
+void
+mm_audit_run_command(const char *command)
+{
+       Buffer m;
+
+       debug3("%s entering command %s", __func__, command);
+
+       buffer_init(&m);
+       buffer_put_cstring(&m, command);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_COMMAND, &m);
+       buffer_free(&m);
+}
+#endif /* SSH_AUDIT_EVENTS */
+
+#ifdef GSSAPI
+OM_uint32
+mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID goid)
+{
+       Buffer m;
+       OM_uint32 major;
+
+       /* Client doesn't get to see the context */
+       *ctx = NULL;
+
+       buffer_init(&m);
+       buffer_put_string(&m, goid->elements, goid->length);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSETUP, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSETUP, &m);
+
+       major = buffer_get_int(&m);
+
+       buffer_free(&m);
+       return (major);
+}
+
+OM_uint32
+mm_ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *in,
+    gss_buffer_desc *out, OM_uint32 *flags)
+{
+       Buffer m;
+       OM_uint32 major;
+       u_int len;
+
+       buffer_init(&m);
+       buffer_put_string(&m, in->value, in->length);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSTEP, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSTEP, &m);
+
+       major = buffer_get_int(&m);
+       out->value = buffer_get_string(&m, &len);
+       out->length = len;
+       if (flags)
+               *flags = buffer_get_int(&m);
+
+       buffer_free(&m);
+
+       return (major);
+}
+
+OM_uint32
+mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
+{
+       Buffer m;
+       OM_uint32 major;
+
+       buffer_init(&m);
+       buffer_put_string(&m, gssbuf->value, gssbuf->length);
+       buffer_put_string(&m, gssmic->value, gssmic->length);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSCHECKMIC, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSCHECKMIC,
+           &m);
+
+       major = buffer_get_int(&m);
+       buffer_free(&m);
+       return(major);
+}
+
+int
+mm_ssh_gssapi_userok(char *user, struct passwd *pw)
+{
+       Buffer m;
+       int authenticated = 0;
+
+       buffer_init(&m);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK,
+                                 &m);
+
+       authenticated = buffer_get_int(&m);
+
+       buffer_free(&m);
+       debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not ");
+       return (authenticated);
+}
+
+OM_uint32
+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
+{
+       Buffer m;
+       OM_uint32 major;
+       u_int len;
+
+       buffer_init(&m);
+       buffer_put_string(&m, data->value, data->length);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m);
+
+       major = buffer_get_int(&m);
+       hash->value = buffer_get_string(&m, &len);
+       hash->length = len;
+
+       buffer_free(&m);
+
+       return(major);
+}
+
+int
+mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
+{
+       Buffer m;
+       int ok;
+
+       buffer_init(&m);
+
+       buffer_put_cstring(&m, store->filename ? store->filename : "");
+       buffer_put_cstring(&m, store->envvar ? store->envvar : "");
+       buffer_put_cstring(&m, store->envval ? store->envval : "");
+       
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m);
+
+       ok = buffer_get_int(&m);
+
+       buffer_free(&m);
+       
+       return (ok);
+}
+
+#endif /* GSSAPI */
+
+#ifdef JPAKE
+void
+mm_auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s,
+    char **hash_scheme, char **salt)
+{
+       Buffer m;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd,
+           MONITOR_REQ_JPAKE_GET_PWDATA, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_JPAKE_GET_PWDATA", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd,
+           MONITOR_ANS_JPAKE_GET_PWDATA, &m);
+
+       *hash_scheme = buffer_get_string(&m, NULL);
+       *salt = buffer_get_string(&m, NULL);
+
+       buffer_free(&m);
+}
+
+void
+mm_jpake_step1(struct modp_group *grp,
+    u_char **id, u_int *id_len,
+    BIGNUM **priv1, BIGNUM **priv2, BIGNUM **g_priv1, BIGNUM **g_priv2,
+    u_char **priv1_proof, u_int *priv1_proof_len,
+    u_char **priv2_proof, u_int *priv2_proof_len)
+{
+       Buffer m;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       mm_request_send(pmonitor->m_recvfd,
+           MONITOR_REQ_JPAKE_STEP1, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_JPAKE_STEP1", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd,
+           MONITOR_ANS_JPAKE_STEP1, &m);
+
+       if ((*priv1 = BN_new()) == NULL ||
+           (*priv2 = BN_new()) == NULL ||
+           (*g_priv1 = BN_new()) == NULL ||
+           (*g_priv2 = BN_new()) == NULL)
+               fatal("%s: BN_new", __func__);
+
+       *id = buffer_get_string(&m, id_len);
+       /* priv1 and priv2 are, well, private */
+       buffer_get_bignum2(&m, *g_priv1);
+       buffer_get_bignum2(&m, *g_priv2);
+       *priv1_proof = buffer_get_string(&m, priv1_proof_len);
+       *priv2_proof = buffer_get_string(&m, priv2_proof_len);
+
+       buffer_free(&m);
+}
+
+void
+mm_jpake_step2(struct modp_group *grp, BIGNUM *s,
+    BIGNUM *mypub1, BIGNUM *theirpub1, BIGNUM *theirpub2, BIGNUM *mypriv2,
+    const u_char *theirid, u_int theirid_len,
+    const u_char *myid, u_int myid_len,
+    const u_char *theirpub1_proof, u_int theirpub1_proof_len,
+    const u_char *theirpub2_proof, u_int theirpub2_proof_len,
+    BIGNUM **newpub,
+    u_char **newpub_exponent_proof, u_int *newpub_exponent_proof_len)
+{
+       Buffer m;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       /* monitor already has all bignums except theirpub1, theirpub2 */
+       buffer_put_bignum2(&m, theirpub1);
+       buffer_put_bignum2(&m, theirpub2);
+       /* monitor already knows our id */
+       buffer_put_string(&m, theirid, theirid_len);
+       buffer_put_string(&m, theirpub1_proof, theirpub1_proof_len);
+       buffer_put_string(&m, theirpub2_proof, theirpub2_proof_len);
+
+       mm_request_send(pmonitor->m_recvfd,
+           MONITOR_REQ_JPAKE_STEP2, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_JPAKE_STEP2", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd,
+           MONITOR_ANS_JPAKE_STEP2, &m);
+
+       if ((*newpub = BN_new()) == NULL)
+               fatal("%s: BN_new", __func__);
+
+       buffer_get_bignum2(&m, *newpub);
+       *newpub_exponent_proof = buffer_get_string(&m,
+           newpub_exponent_proof_len);
+
+       buffer_free(&m);
+}
+
+void
+mm_jpake_key_confirm(struct modp_group *grp, BIGNUM *s, BIGNUM *step2_val,
+    BIGNUM *mypriv2, BIGNUM *mypub1, BIGNUM *mypub2,
+    BIGNUM *theirpub1, BIGNUM *theirpub2,
+    const u_char *my_id, u_int my_id_len,
+    const u_char *their_id, u_int their_id_len,
+    const u_char *sess_id, u_int sess_id_len,
+    const u_char *theirpriv2_s_proof, u_int theirpriv2_s_proof_len,
+    BIGNUM **k,
+    u_char **confirm_hash, u_int *confirm_hash_len)
+{
+       Buffer m;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       /* monitor already has all bignums except step2_val */
+       buffer_put_bignum2(&m, step2_val);
+       /* monitor already knows all the ids */
+       buffer_put_string(&m, theirpriv2_s_proof, theirpriv2_s_proof_len);
+
+       mm_request_send(pmonitor->m_recvfd,
+           MONITOR_REQ_JPAKE_KEY_CONFIRM, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_JPAKE_KEY_CONFIRM", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd,
+           MONITOR_ANS_JPAKE_KEY_CONFIRM, &m);
+
+       /* 'k' is sensitive and stays in the monitor */
+       *confirm_hash = buffer_get_string(&m, confirm_hash_len);
+
+       buffer_free(&m);
+}
+
+int
+mm_jpake_check_confirm(const BIGNUM *k,
+    const u_char *peer_id, u_int peer_id_len,
+    const u_char *sess_id, u_int sess_id_len,
+    const u_char *peer_confirm_hash, u_int peer_confirm_hash_len)
+{
+       Buffer m;
+       int success = 0;
+
+       debug3("%s entering", __func__);
+
+       buffer_init(&m);
+       /* k is dummy in slave, ignored */
+       /* monitor knows all the ids */
+       buffer_put_string(&m, peer_confirm_hash, peer_confirm_hash_len);
+       mm_request_send(pmonitor->m_recvfd,
+           MONITOR_REQ_JPAKE_CHECK_CONFIRM, &m);
+
+       debug3("%s: waiting for MONITOR_ANS_JPAKE_CHECK_CONFIRM", __func__);
+       mm_request_receive_expect(pmonitor->m_recvfd,
+           MONITOR_ANS_JPAKE_CHECK_CONFIRM, &m);
+
+       success = buffer_get_int(&m);
+       buffer_free(&m);
+
+       debug3("%s: success = %d", __func__, success);
+       return success;
+}
+#endif /* JPAKE */
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor_wrap.h b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/monitor_wrap.h
new file mode 100644 (file)
index 0000000..4d12e29
--- /dev/null
@@ -0,0 +1,134 @@
+/* $OpenBSD: monitor_wrap.h,v 1.23 2011/06/17 21:44:31 djm Exp $ */
+
+/*
+ * Copyright 2002 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.
+ */
+
+#ifndef _MM_WRAP_H_
+#define _MM_WRAP_H_
+
+extern int use_privsep;
+#define PRIVSEP(x)     (use_privsep ? mm_##x : x)
+
+enum mm_keytype {MM_NOKEY, MM_HOSTKEY, MM_USERKEY, MM_RSAHOSTKEY, MM_RSAUSERKEY};
+
+struct monitor;
+struct mm_master;
+struct Authctxt;
+
+void mm_log_handler(LogLevel, const char *, void *);
+int mm_is_monitor(void);
+DH *mm_choose_dh(int, int, int);
+int mm_key_sign(Key *, u_char **, u_int *, u_char *, u_int);
+void mm_inform_authserv(char *, char *, char *);
+void mm_inform_authrole(char *);
+struct passwd *mm_getpwnamallow(const char *);
+char *mm_auth2_read_banner(void);
+int mm_auth_password(struct Authctxt *, char *);
+int mm_key_allowed(enum mm_keytype, char *, char *, Key *);
+int mm_user_key_allowed(struct passwd *, Key *);
+int mm_hostbased_key_allowed(struct passwd *, char *, char *, Key *);
+int mm_auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *);
+int mm_key_verify(Key *, u_char *, u_int, u_char *, u_int);
+int mm_auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **);
+int mm_auth_rsa_verify_response(Key *, BIGNUM *, u_char *);
+BIGNUM *mm_auth_rsa_generate_challenge(Key *);
+
+#ifdef GSSAPI
+OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
+OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
+   gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
+int mm_ssh_gssapi_userok(char *user, struct passwd *);
+OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
+int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
+#endif
+
+#ifdef USE_PAM
+void mm_start_pam(struct Authctxt *);
+u_int mm_do_pam_account(void);
+void *mm_sshpam_init_ctx(struct Authctxt *);
+int mm_sshpam_query(void *, char **, char **, u_int *, char ***, u_int **);
+int mm_sshpam_respond(void *, u_int, char **);
+void mm_sshpam_free_ctx(void *);
+#endif
+
+#ifdef SSH_AUDIT_EVENTS
+#include "audit.h"
+void mm_audit_event(ssh_audit_event_t);
+void mm_audit_run_command(const char *);
+#endif
+
+struct Session;
+void mm_terminate(void);
+int mm_pty_allocate(int *, int *, char *, size_t);
+void mm_session_pty_cleanup2(struct Session *);
+
+/* SSHv1 interfaces */
+void mm_ssh1_session_id(u_char *);
+int mm_ssh1_session_key(BIGNUM *);
+
+/* Key export functions */
+struct Newkeys *mm_newkeys_from_blob(u_char *, int);
+int mm_newkeys_to_blob(int, u_char **, u_int *);
+
+void monitor_apply_keystate(struct monitor *);
+void mm_get_keystate(struct monitor *);
+void mm_send_keystate(struct monitor*);
+
+/* bsdauth */
+int mm_bsdauth_query(void *, char **, char **, u_int *, char ***, u_int **);
+int mm_bsdauth_respond(void *, u_int, char **);
+
+/* skey */
+int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **);
+int mm_skey_respond(void *, u_int, char **);
+
+/* jpake */
+struct modp_group;
+void mm_auth2_jpake_get_pwdata(struct Authctxt *, BIGNUM **, char **, char **);
+void mm_jpake_step1(struct modp_group *, u_char **, u_int *,
+    BIGNUM **, BIGNUM **, BIGNUM **, BIGNUM **,
+    u_char **, u_int *, u_char **, u_int *);
+void mm_jpake_step2(struct modp_group *, BIGNUM *,
+    BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *,
+    const u_char *, u_int, const u_char *, u_int,
+    const u_char *, u_int, const u_char *, u_int,
+    BIGNUM **, u_char **, u_int *);
+void mm_jpake_key_confirm(struct modp_group *, BIGNUM *, BIGNUM *,
+    BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *,
+    const u_char *, u_int, const u_char *, u_int,
+    const u_char *, u_int, const u_char *, u_int,
+    BIGNUM **, u_char **, u_int *);
+int mm_jpake_check_confirm(const BIGNUM *,
+    const u_char *, u_int, const u_char *, u_int, const u_char *, u_int);
+
+
+/* zlib allocation hooks */
+
+void *mm_zalloc(struct mm_master *, u_int, u_int);
+void mm_zfree(struct mm_master *, void *);
+void mm_init_compression(struct mm_master *);
+
+#endif /* _MM_WRAP_H_ */
diff --git a/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/ssh-gss.h b/.pc/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch/ssh-gss.h
new file mode 100644 (file)
index 0000000..31d5a08
--- /dev/null
@@ -0,0 +1,162 @@
+/* $OpenBSD: ssh-gss.h,v 1.10 2007/06/12 08:20:00 djm Exp $ */
+/*
+ * Copyright (c) 2001-2009 Simon Wilkinson. 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.
+ */
+
+#ifndef _SSH_GSS_H
+#define _SSH_GSS_H
+
+#ifdef GSSAPI
+
+#ifdef HAVE_GSSAPI_H
+#include <gssapi.h>
+#elif defined(HAVE_GSSAPI_GSSAPI_H)
+#include <gssapi/gssapi.h>
+#endif
+
+#ifdef KRB5
+# ifndef HEIMDAL
+#  ifdef HAVE_GSSAPI_GENERIC_H
+#   include <gssapi_generic.h>
+#  elif defined(HAVE_GSSAPI_GSSAPI_GENERIC_H)
+#   include <gssapi/gssapi_generic.h>
+#  endif
+
+/* MIT Kerberos doesn't seem to define GSS_NT_HOSTBASED_SERVICE */
+
+#ifndef GSS_C_NT_HOSTBASED_SERVICE
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#endif /* GSS_C_NT_... */
+#endif /* !HEIMDAL */
+#endif /* KRB5 */
+
+/* draft-ietf-secsh-gsskeyex-06 */
+#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE              60
+#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN                 61
+#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE     63
+#define SSH2_MSG_USERAUTH_GSSAPI_ERROR                 64
+#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK                        65
+#define SSH2_MSG_USERAUTH_GSSAPI_MIC                   66
+
+#define SSH_GSS_OIDTYPE 0x06
+
+#define SSH2_MSG_KEXGSS_INIT                            30
+#define SSH2_MSG_KEXGSS_CONTINUE                        31
+#define SSH2_MSG_KEXGSS_COMPLETE                        32
+#define SSH2_MSG_KEXGSS_HOSTKEY                         33
+#define SSH2_MSG_KEXGSS_ERROR                           34
+#define SSH2_MSG_KEXGSS_GROUPREQ                       40
+#define SSH2_MSG_KEXGSS_GROUP                          41
+#define KEX_GSS_GRP1_SHA1_ID                           "gss-group1-sha1-"
+#define KEX_GSS_GRP14_SHA1_ID                          "gss-group14-sha1-"
+#define KEX_GSS_GEX_SHA1_ID                            "gss-gex-sha1-"
+
+typedef struct {
+       char *filename;
+       char *envvar;
+       char *envval;
+       struct passwd *owner;
+       void *data;
+} ssh_gssapi_ccache;
+
+typedef struct {
+       gss_buffer_desc displayname;
+       gss_buffer_desc exportedname;
+       gss_cred_id_t creds;
+       gss_name_t name;
+       struct ssh_gssapi_mech_struct *mech;
+       ssh_gssapi_ccache store;
+       int used;
+       int updated;
+} ssh_gssapi_client;
+
+typedef struct ssh_gssapi_mech_struct {
+       char *enc_name;
+       char *name;
+       gss_OID_desc oid;
+       int (*dochild) (ssh_gssapi_client *);
+       int (*userok) (ssh_gssapi_client *, char *);
+       int (*localname) (ssh_gssapi_client *, char **);
+       void (*storecreds) (ssh_gssapi_client *);
+       int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *);
+} ssh_gssapi_mech;
+
+typedef struct {
+       OM_uint32       major; /* both */
+       OM_uint32       minor; /* both */
+       gss_ctx_id_t    context; /* both */
+       gss_name_t      name; /* both */
+       gss_OID         oid; /* client */
+       gss_cred_id_t   creds; /* server */
+       gss_name_t      client; /* server */
+       gss_cred_id_t   client_creds; /* both */
+} Gssctxt;
+
+extern ssh_gssapi_mech *supported_mechs[];
+extern Gssctxt *gss_kex_context;
+
+int  ssh_gssapi_check_oid(Gssctxt *, void *, size_t);
+void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t);
+void ssh_gssapi_set_oid(Gssctxt *, gss_OID);
+void ssh_gssapi_supported_oids(gss_OID_set *);
+ssh_gssapi_mech *ssh_gssapi_get_ctype(Gssctxt *);
+
+OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *);
+OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int,
+    gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
+OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *,
+    gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *);
+OM_uint32 ssh_gssapi_getclient(Gssctxt *, ssh_gssapi_client *);
+void ssh_gssapi_error(Gssctxt *);
+char *ssh_gssapi_last_error(Gssctxt *, OM_uint32 *, OM_uint32 *);
+void ssh_gssapi_build_ctx(Gssctxt **);
+void ssh_gssapi_delete_ctx(Gssctxt **);
+OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
+void ssh_gssapi_buildmic(Buffer *, const char *, const char *, const char *);
+int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
+OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
+int ssh_gssapi_credentials_updated(Gssctxt *);
+
+/* In the server */
+typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, 
+    const char *);
+char *ssh_gssapi_client_mechanisms(const char *, const char *);
+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
+    const char *);
+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int);
+int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, 
+    const char *);
+OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
+int ssh_gssapi_userok(char *name, struct passwd *);
+OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
+void ssh_gssapi_do_child(char ***, u_int *);
+void ssh_gssapi_cleanup_creds(void);
+void ssh_gssapi_storecreds(void);
+
+char *ssh_gssapi_server_mechanisms(void);
+int ssh_gssapi_oid_table_ok();
+
+int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
+#endif /* GSSAPI */
+
+#endif /* _SSH_GSS_H */
index 77b03a8..fe74a5a 100644 (file)
@@ -20,3 +20,4 @@ doc-hash-tab-completion.patch
 auth-log-verbosity.patch
 gnome-ssh-askpass2-icon.patch
 debian-config.patch
+0001-initial-empty-usernames-on-top-of-keyex-and-role.patch
diff --git a/.pc/auth-log-verbosity.patch/.timestamp b/.pc/auth-log-verbosity.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/authorized-keys-man-symlink.patch/.timestamp b/.pc/authorized-keys-man-symlink.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/debian-banner.patch/.timestamp b/.pc/debian-banner.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/debian-config.patch/.timestamp b/.pc/debian-config.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/dnssec-sshfp.patch/.timestamp b/.pc/dnssec-sshfp.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/doc-hash-tab-completion.patch/.timestamp b/.pc/doc-hash-tab-completion.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/gnome-ssh-askpass2-icon.patch/.timestamp b/.pc/gnome-ssh-askpass2-icon.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/gssapi.patch/.timestamp b/.pc/gssapi.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/gssapi_generic/gss-serv.c b/.pc/gssapi_generic/gss-serv.c
new file mode 100644 (file)
index 0000000..380895e
--- /dev/null
@@ -0,0 +1,531 @@
+/* $OpenBSD: gss-serv.c,v 1.23 2011/08/01 19:18:15 markus Exp $ */
+
+/*
+ * Copyright (c) 2001-2009 Simon Wilkinson. 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"
+
+#ifdef GSSAPI
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "openbsd-compat/sys-queue.h"
+#include "xmalloc.h"
+#include "buffer.h"
+#include "key.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "log.h"
+#include "channels.h"
+#include "session.h"
+#include "misc.h"
+#include "servconf.h"
+#include "uidswap.h"
+
+#include "ssh-gss.h"
+#include "monitor_wrap.h"
+
+extern ServerOptions options;
+
+static ssh_gssapi_client gssapi_client =
+    { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
+    GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME,  NULL, {NULL, NULL, NULL}, 0, 0};
+
+ssh_gssapi_mech gssapi_null_mech =
+    { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL};
+
+#ifdef KRB5
+extern ssh_gssapi_mech gssapi_kerberos_mech;
+#endif
+
+ssh_gssapi_mech* supported_mechs[]= {
+#ifdef KRB5
+       &gssapi_kerberos_mech,
+#endif
+       &gssapi_null_mech,
+};
+
+
+/*
+ * Acquire credentials for a server running on the current host.
+ * Requires that the context structure contains a valid OID
+ */
+
+/* Returns a GSSAPI error code */
+/* Privileged (called from ssh_gssapi_server_ctx) */
+static OM_uint32
+ssh_gssapi_acquire_cred(Gssctxt *ctx)
+{
+       OM_uint32 status;
+       char lname[MAXHOSTNAMELEN];
+       gss_OID_set oidset;
+
+       if (options.gss_strict_acceptor) {
+               gss_create_empty_oid_set(&status, &oidset);
+               gss_add_oid_set_member(&status, ctx->oid, &oidset);
+
+               if (gethostname(lname, MAXHOSTNAMELEN)) {
+                       gss_release_oid_set(&status, &oidset);
+                       return (-1);
+               }
+
+               if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
+                       gss_release_oid_set(&status, &oidset);
+                       return (ctx->major);
+               }
+
+               if ((ctx->major = gss_acquire_cred(&ctx->minor,
+                   ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, 
+                   NULL, NULL)))
+                       ssh_gssapi_error(ctx);
+
+               gss_release_oid_set(&status, &oidset);
+               return (ctx->major);
+       } else {
+               ctx->name = GSS_C_NO_NAME;
+               ctx->creds = GSS_C_NO_CREDENTIAL;
+       }
+       return GSS_S_COMPLETE;
+}
+
+/* Privileged */
+OM_uint32
+ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
+{
+       if (*ctx)
+               ssh_gssapi_delete_ctx(ctx);
+       ssh_gssapi_build_ctx(ctx);
+       ssh_gssapi_set_oid(*ctx, oid);
+       return (ssh_gssapi_acquire_cred(*ctx));
+}
+
+/* Unprivileged */
+char *
+ssh_gssapi_server_mechanisms() {
+       gss_OID_set     supported;
+
+       ssh_gssapi_supported_oids(&supported);
+       return (ssh_gssapi_kex_mechs(supported, &ssh_gssapi_server_check_mech,
+           NULL, NULL));
+}
+
+/* Unprivileged */
+int
+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data,
+    const char *dummy) {
+       Gssctxt *ctx = NULL;
+       int res;
+       res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid)));
+       ssh_gssapi_delete_ctx(&ctx);
+
+       return (res);
+}
+
+/* Unprivileged */
+void
+ssh_gssapi_supported_oids(gss_OID_set *oidset)
+{
+       int i = 0;
+       OM_uint32 min_status;
+       int present;
+       gss_OID_set supported;
+
+       gss_create_empty_oid_set(&min_status, oidset);
+
+       if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported)))
+               return;
+
+       while (supported_mechs[i]->name != NULL) {
+               if (GSS_ERROR(gss_test_oid_set_member(&min_status,
+                   &supported_mechs[i]->oid, supported, &present)))
+                       present = 0;
+               if (present)
+                       gss_add_oid_set_member(&min_status,
+                           &supported_mechs[i]->oid, oidset);
+               i++;
+       }
+
+       gss_release_oid_set(&min_status, &supported);
+}
+
+
+/* Wrapper around accept_sec_context
+ * Requires that the context contains:
+ *    oid
+ *    credentials      (from ssh_gssapi_acquire_cred)
+ */
+/* Privileged */
+OM_uint32
+ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok,
+    gss_buffer_desc *send_tok, OM_uint32 *flags)
+{
+       OM_uint32 status;
+       gss_OID mech;
+
+       ctx->major = gss_accept_sec_context(&ctx->minor,
+           &ctx->context, ctx->creds, recv_tok,
+           GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech,
+           send_tok, flags, NULL, &ctx->client_creds);
+
+       if (GSS_ERROR(ctx->major))
+               ssh_gssapi_error(ctx);
+
+       if (ctx->client_creds)
+               debug("Received some client credentials");
+       else
+               debug("Got no client credentials");
+
+       status = ctx->major;
+
+       /* Now, if we're complete and we have the right flags, then
+        * we flag the user as also having been authenticated
+        */
+
+       if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) &&
+           (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) {
+               if (ssh_gssapi_getclient(ctx, &gssapi_client))
+                       fatal("Couldn't convert client name");
+       }
+
+       return (status);
+}
+
+/*
+ * This parses an exported name, extracting the mechanism specific portion
+ * to use for ACL checking. It verifies that the name belongs the mechanism
+ * originally selected.
+ */
+static OM_uint32
+ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name)
+{
+       u_char *tok;
+       OM_uint32 offset;
+       OM_uint32 oidl;
+
+       tok = ename->value;
+
+       /*
+        * Check that ename is long enough for all of the fixed length
+        * header, and that the initial ID bytes are correct
+        */
+
+       if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0)
+               return GSS_S_FAILURE;
+
+       /*
+        * Extract the OID, and check it. Here GSSAPI breaks with tradition
+        * and does use the OID type and length bytes. To confuse things
+        * there are two lengths - the first including these, and the
+        * second without.
+        */
+
+       oidl = get_u16(tok+2); /* length including next two bytes */
+       oidl = oidl-2; /* turn it into the _real_ length of the variable OID */
+
+       /*
+        * Check the BER encoding for correct type and length, that the
+        * string is long enough and that the OID matches that in our context
+        */
+       if (tok[4] != 0x06 || tok[5] != oidl ||
+           ename->length < oidl+6 ||
+           !ssh_gssapi_check_oid(ctx, tok+6, oidl))
+               return GSS_S_FAILURE;
+
+       offset = oidl+6;
+
+       if (ename->length < offset+4)
+               return GSS_S_FAILURE;
+
+       name->length = get_u32(tok+offset);
+       offset += 4;
+
+       if (UINT_MAX - offset < name->length)
+               return GSS_S_FAILURE;
+       if (ename->length < offset+name->length)
+               return GSS_S_FAILURE;
+
+       name->value = xmalloc(name->length+1);
+       memcpy(name->value, tok+offset, name->length);
+       ((char *)name->value)[name->length] = 0;
+
+       return GSS_S_COMPLETE;
+}
+
+/* Extract the client details from a given context. This can only reliably
+ * be called once for a context */
+
+/* Privileged (called from accept_secure_ctx) */
+OM_uint32
+ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
+{
+       int i = 0;
+       int equal = 0;
+       gss_name_t new_name = GSS_C_NO_NAME;
+       gss_buffer_desc ename = GSS_C_EMPTY_BUFFER;
+
+       if (options.gss_store_rekey && client->used && ctx->client_creds) {
+               if (client->mech->oid.length != ctx->oid->length ||
+                   (memcmp(client->mech->oid.elements,
+                    ctx->oid->elements, ctx->oid->length) !=0)) {
+                       debug("Rekeyed credentials have different mechanism");
+                       return GSS_S_COMPLETE;
+               }
+
+               if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, 
+                   ctx->client_creds, ctx->oid, &new_name, 
+                   NULL, NULL, NULL))) {
+                       ssh_gssapi_error(ctx);
+                       return (ctx->major);
+               }
+
+               ctx->major = gss_compare_name(&ctx->minor, client->name, 
+                   new_name, &equal);
+
+               if (GSS_ERROR(ctx->major)) {
+                       ssh_gssapi_error(ctx);
+                       return (ctx->major);
+               }
+               if (!equal) {
+                       debug("Rekeyed credentials have different name");
+                       return GSS_S_COMPLETE;
+               }
+
+               debug("Marking rekeyed credentials for export");
+
+               gss_release_name(&ctx->minor, &client->name);
+               gss_release_cred(&ctx->minor, &client->creds);
+               client->name = new_name;
+               client->creds = ctx->client_creds;
+               ctx->client_creds = GSS_C_NO_CREDENTIAL;
+               client->updated = 1;
+               return GSS_S_COMPLETE;
+       }
+
+       client->mech = NULL;
+
+       while (supported_mechs[i]->name != NULL) {
+               if (supported_mechs[i]->oid.length == ctx->oid->length &&
+                   (memcmp(supported_mechs[i]->oid.elements,
+                   ctx->oid->elements, ctx->oid->length) == 0))
+                       client->mech = supported_mechs[i];
+               i++;
+       }
+
+       if (client->mech == NULL)
+               return GSS_S_FAILURE;
+
+       if (ctx->client_creds &&
+           (ctx->major = gss_inquire_cred_by_mech(&ctx->minor,
+            ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) {
+               ssh_gssapi_error(ctx);
+               return (ctx->major);
+       }
+
+       if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
+           &client->displayname, NULL))) {
+               ssh_gssapi_error(ctx);
+               return (ctx->major);
+       }
+
+       if ((ctx->major = gss_export_name(&ctx->minor, ctx->client,
+           &ename))) {
+               ssh_gssapi_error(ctx);
+               return (ctx->major);
+       }
+
+       if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
+           &client->exportedname))) {
+               return (ctx->major);
+       }
+
+       gss_release_buffer(&ctx->minor, &ename);
+
+       /* We can't copy this structure, so we just move the pointer to it */
+       client->creds = ctx->client_creds;
+       ctx->client_creds = GSS_C_NO_CREDENTIAL;
+       return (ctx->major);
+}
+
+/* As user - called on fatal/exit */
+void
+ssh_gssapi_cleanup_creds(void)
+{
+       if (gssapi_client.store.filename != NULL) {
+               /* Unlink probably isn't sufficient */
+               debug("removing gssapi cred file\"%s\"",
+                   gssapi_client.store.filename);
+               unlink(gssapi_client.store.filename);
+       }
+}
+
+/* As user */
+void
+ssh_gssapi_storecreds(void)
+{
+       if (gssapi_client.mech && gssapi_client.mech->storecreds) {
+               (*gssapi_client.mech->storecreds)(&gssapi_client);
+       } else
+               debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
+}
+
+/* This allows GSSAPI methods to do things to the childs environment based
+ * on the passed authentication process and credentials.
+ */
+/* As user */
+void
+ssh_gssapi_do_child(char ***envp, u_int *envsizep)
+{
+
+       if (gssapi_client.store.envvar != NULL &&
+           gssapi_client.store.envval != NULL) {
+               debug("Setting %s to %s", gssapi_client.store.envvar,
+                   gssapi_client.store.envval);
+               child_set_env(envp, envsizep, gssapi_client.store.envvar,
+                   gssapi_client.store.envval);
+       }
+}
+
+/* Privileged */
+int
+ssh_gssapi_userok(char *user, struct passwd *pw)
+{
+       OM_uint32 lmin;
+
+       if (gssapi_client.exportedname.length == 0 ||
+           gssapi_client.exportedname.value == NULL) {
+               debug("No suitable client data");
+               return 0;
+       }
+       if (gssapi_client.mech && gssapi_client.mech->userok)
+               if ((*gssapi_client.mech->userok)(&gssapi_client, user)) {
+                       gssapi_client.used = 1;
+                       gssapi_client.store.owner = pw;
+                       return 1;
+               } else {
+                       /* Destroy delegated credentials if userok fails */
+                       gss_release_buffer(&lmin, &gssapi_client.displayname);
+                       gss_release_buffer(&lmin, &gssapi_client.exportedname);
+                       gss_release_cred(&lmin, &gssapi_client.creds);
+                       memset(&gssapi_client, 0, sizeof(ssh_gssapi_client));
+                       return 0;
+               }
+       else
+               debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
+       return (0);
+}
+
+/* These bits are only used for rekeying. The unpriviledged child is running 
+ * as the user, the monitor is root.
+ *
+ * In the child, we want to :
+ *    *) Ask the monitor to store our credentials into the store we specify
+ *    *) If it succeeds, maybe do a PAM update
+ */
+
+/* Stuff for PAM */
+
+#ifdef USE_PAM
+static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, 
+    struct pam_response **resp, void *data)
+{
+       return (PAM_CONV_ERR);
+}
+#endif
+
+void
+ssh_gssapi_rekey_creds() {
+       int ok;
+       int ret;
+#ifdef USE_PAM
+       pam_handle_t *pamh = NULL;
+       struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
+       char *envstr;
+#endif
+
+       if (gssapi_client.store.filename == NULL && 
+           gssapi_client.store.envval == NULL &&
+           gssapi_client.store.envvar == NULL)
+               return;
+       ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
+
+       if (!ok)
+               return;
+
+       debug("Rekeyed credentials stored successfully");
+
+       /* Actually managing to play with the ssh pam stack from here will
+        * be next to impossible. In any case, we may want different options
+        * for rekeying. So, use our own :)
+        */
+#ifdef USE_PAM 
+       if (!use_privsep) {
+               debug("Not even going to try and do PAM with privsep disabled");
+               return;
+       }
+
+       ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
+           &pamconv, &pamh);
+       if (ret)
+               return;
+
+       xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, 
+           gssapi_client.store.envval);
+
+       ret = pam_putenv(pamh, envstr);
+       if (!ret)
+               pam_setcred(pamh, PAM_REINITIALIZE_CRED);
+       pam_end(pamh, PAM_SUCCESS);
+#endif
+}
+
+int 
+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
+       int ok = 0;
+
+       /* Check we've got credentials to store */
+       if (!gssapi_client.updated)
+               return 0;
+
+       gssapi_client.updated = 0;
+
+       temporarily_use_uid(gssapi_client.store.owner);
+       if (gssapi_client.mech && gssapi_client.mech->updatecreds)
+               ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
+       else
+               debug("No update function for this mechanism");
+
+       restore_uid();
+
+       return ok;
+}
+
+#endif
diff --git a/.pc/helpful-wait-terminate.patch/.timestamp b/.pc/helpful-wait-terminate.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/keepalive-extensions.patch/.timestamp b/.pc/keepalive-extensions.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/lintian-symlink-pickiness.patch/.timestamp b/.pc/lintian-symlink-pickiness.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/openbsd-docs.patch/.timestamp b/.pc/openbsd-docs.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/package-versioning.patch/.timestamp b/.pc/package-versioning.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/quieter-signals.patch/.timestamp b/.pc/quieter-signals.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/scp-quoting.patch/.timestamp b/.pc/scp-quoting.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/selinux-role.patch/.timestamp b/.pc/selinux-role.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/shell-path.patch/.timestamp b/.pc/shell-path.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/ssh-argv0.patch/.timestamp b/.pc/ssh-argv0.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/ssh-vulnkey.patch/.timestamp b/.pc/ssh-vulnkey.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/ssh1-keepalive.patch/.timestamp b/.pc/ssh1-keepalive.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/syslog-level-silent.patch/.timestamp b/.pc/syslog-level-silent.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/user-group-modes.patch/.timestamp b/.pc/user-group-modes.patch/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
index 675006e..55f70b7 100644 (file)
@@ -30,7 +30,7 @@
  */
 /*
  * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
- * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au>
+ * Copyright (c) 2003,2004,2006 Darren Tucker <dtucker@zip.com.au>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -122,6 +122,10 @@ extern u_int utmp_len;
  */
 typedef pthread_t sp_pthread_t;
 #else
+#define pthread_create openssh_pthread_create
+#define pthread_exit openssh_pthread_exit
+#define pthread_cancel openssh_pthread_cancel
+#define pthread_join openssh_pthread_join
 typedef pid_t sp_pthread_t;
 #endif
 
@@ -272,6 +276,49 @@ sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
 # define pam_chauthtok(a,b)    (sshpam_chauthtok_ruid((a), (b)))
 #endif
 
+struct passwd *
+sshpam_getpw(const char *user)
+{
+       struct passwd *pw;
+
+       if ((pw = getpwnam(user)) != NULL)
+               return(pw);
+
+       debug("PAM: faking passwd struct for user '%.100s'", user);
+       if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL)
+               return NULL;
+       pw->pw_name = xstrdup(user);    /* XXX leak */
+       pw->pw_shell = "/bin/true";
+       pw->pw_gecos = "sshd fake PAM user";
+       return (pw);
+}
+
+void
+sshpam_check_userchanged(void)
+{
+       int sshpam_err;
+       struct passwd *pw;
+       const char *user;
+
+       debug("sshpam_check_userchanged");
+       sshpam_err = pam_get_item(sshpam_handle, PAM_USER, &user);
+       if (sshpam_err != PAM_SUCCESS)
+               fatal("PAM: could not get PAM_USER: %s",
+                   pam_strerror(sshpam_handle, sshpam_err));
+       if (strcmp(user, sshpam_authctxt->pw->pw_name) != 0) {
+               debug("PAM: user mapped from '%.100s' to '%.100s'",
+                   sshpam_authctxt->pw->pw_name, user);
+               if ((pw = getpwnam(user)) == NULL)
+                       fatal("PAM: could not get passwd entry for user "
+                           "'%.100s' provided by PAM_USER", user);
+               pwfree(sshpam_authctxt->pw);
+               sshpam_authctxt->pw = pw;
+               sshpam_authctxt->valid = allowed_user(pw);
+               debug("PAM: user '%.100s' now %svalid", user,
+                   sshpam_authctxt->valid ? "" : "in");
+       }
+}
+
 void
 sshpam_password_change_required(int reqd)
 {
@@ -294,7 +341,7 @@ sshpam_password_change_required(int reqd)
 static void
 import_environments(Buffer *b)
 {
-       char *env;
+       char *env, *user;
        u_int i, num_env;
        int err;
 
index a1a2b52..d075512 100644 (file)
@@ -46,5 +46,6 @@ void sshpam_thread_cleanup(void);
 void sshpam_cleanup(void);
 int sshpam_auth_passwd(Authctxt *, const char *);
 int is_pam_session_open(void);
+struct passwd *sshpam_getpw(const char *);
 
 #endif /* USE_PAM */
index 7dc87db..c1d02b6 100644 (file)
@@ -47,6 +47,7 @@
 
 extern ServerOptions options;
 
+static void ssh_gssapi_userauth_error(Gssctxt *ctxt);
 static void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
 static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
 static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
@@ -59,8 +60,8 @@ static int
 userauth_gsskeyex(Authctxt *authctxt)
 {
        int authenticated = 0;
-       Buffer b;
-       gss_buffer_desc mic, gssbuf;
+       Buffer b, b2;
+       gss_buffer_desc mic, gssbuf, gssbuf2;
        u_int len;
 
        mic.value = packet_get_string(&len);
@@ -74,13 +75,26 @@ userauth_gsskeyex(Authctxt *authctxt)
        gssbuf.value = buffer_ptr(&b);
        gssbuf.length = buffer_len(&b);
 
+       /* client may have used empty username to determine target
+          name from GSSAPI context */
+       ssh_gssapi_buildmic(&b2, "", authctxt->service, "gssapi-keyex");
+
+       gssbuf2.value = buffer_ptr(&b2);
+       gssbuf2.length = buffer_len(&b2);
+
        /* gss_kex_context is NULL with privsep, so we can't check it here */
        if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, 
-           &gssbuf, &mic))))
-               authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
-                   authctxt->pw));
+                                                  &gssbuf, &mic))) ||
+           !GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, 
+                                                  &gssbuf2, &mic)))) {
+           if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+            authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+                                                      authctxt->pw));
+           }
+       }
        
        buffer_free(&b);
+       buffer_free(&b2);
        xfree(mic.value);
 
        return (authenticated);
@@ -102,7 +116,10 @@ userauth_gssapi(Authctxt *authctxt)
        u_int len;
        u_char *doid = NULL;
 
-       if (!authctxt->valid || authctxt->user == NULL)
+       /* authctxt->valid may be 0 if we haven't yet determined
+          username from gssapi context. */
+
+       if (authctxt->user == NULL)
                return (0);
 
        mechs = packet_get_int();
@@ -172,7 +189,7 @@ input_gssapi_token(int type, u_int32_t plen, void *ctxt)
        Gssctxt *gssctxt;
        gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
        gss_buffer_desc recv_tok;
-       OM_uint32 maj_status, min_status, flags;
+       OM_uint32 maj_status, min_status, flags=0;
        u_int len;
 
        if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
@@ -190,6 +207,7 @@ input_gssapi_token(int type, u_int32_t plen, void *ctxt)
        xfree(recv_tok.value);
 
        if (GSS_ERROR(maj_status)) {
+               ssh_gssapi_userauth_error(gssctxt);
                if (send_tok.length != 0) {
                        packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
                        packet_put_string(send_tok.value, send_tok.length);
@@ -253,6 +271,32 @@ input_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
        gss_release_buffer(&maj_status, &send_tok);
 }
 
+static void
+gssapi_set_username(Authctxt *authctxt)
+{
+    char *lname = NULL;
+
+    if ((authctxt->user == NULL) || (authctxt->user[0] == '\0')) {
+        PRIVSEP(ssh_gssapi_localname(&lname));
+        if (lname && lname[0] != '\0') {
+            if (authctxt->user) xfree(authctxt->user);
+            authctxt->user = lname;
+            debug("set username to %s from gssapi context", lname);
+            authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user));
+            if (authctxt->pw) {
+                authctxt->valid = 1;
+#ifdef USE_PAM
+                if (options.use_pam)
+                    PRIVSEP(start_pam(authctxt));
+#endif
+            }
+        } else {
+            debug("failed to set username from gssapi context");
+            packet_send_debug("failed to set username from gssapi context");
+        }
+    }
+}
+
 /*
  * This is called when the client thinks we've completed authentication.
  * It should only be enabled in the dispatch handler by the function above,
@@ -269,6 +313,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
        if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
                fatal("No authentication or GSSAPI context");
 
+       gssapi_set_username(authctxt);
+
        gssctxt = authctxt->methoddata;
 
        /*
@@ -278,8 +324,13 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
 
        packet_check_eom();
 
-       authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
-           authctxt->pw));
+       /* user should be set if valid but we double-check here */
+       if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+           authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+                                                     authctxt->pw));
+       } else {
+           authenticated = 0;
+       }
 
        authctxt->postponed = 0;
        dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
@@ -313,9 +364,15 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
        gssbuf.value = buffer_ptr(&b);
        gssbuf.length = buffer_len(&b);
 
+    gssapi_set_username(authctxt);
+
        if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
-               authenticated = 
-                   PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
+           if (authctxt->valid && authctxt->user && authctxt->user[0]) {
+            authenticated =
+             PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
+           } else {
+            authenticated = 0;
+           }
        else
                logit("GSSAPI MIC check failed");
 
@@ -330,6 +387,23 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
        userauth_finish(authctxt, authenticated, "gssapi-with-mic");
 }
 
+static void ssh_gssapi_userauth_error(Gssctxt *ctxt) {
+       char *errstr;
+       OM_uint32 maj,min;
+       
+       errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min));
+       if (errstr) {
+               packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR);
+               packet_put_int(maj);
+               packet_put_int(min);
+               packet_put_cstring(errstr);
+               packet_put_cstring("");
+               packet_send();
+               packet_write_wait();
+               xfree(errstr);
+       }
+}
+
 Authmethod method_gsskeyex = {
        "gssapi-keyex",
        userauth_gsskeyex,
diff --git a/auth2.c b/auth2.c
index 52acf46..50f1352 100644 (file)
--- a/auth2.c
+++ b/auth2.c
@@ -223,10 +223,30 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
        if (authctxt == NULL)
                fatal("input_userauth_request: no authctxt");
 
-       user = packet_get_cstring(NULL);
-       service = packet_get_cstring(NULL);
-       method = packet_get_cstring(NULL);
-       debug("userauth-request for user %s service %s method %s", user, service, method);
+       user = packet_get_string(NULL);
+       service = packet_get_string(NULL);
+       method = packet_get_string(NULL);
+
+#ifdef GSSAPI
+       if (user[0] == '\0') {
+           debug("received empty username for %s", method);
+           if (strcmp(method, "gssapi-keyex") == 0) {
+               char *lname = NULL;
+               PRIVSEP(ssh_gssapi_localname(&lname));
+               if (lname && lname[0] != '\0') {
+                   xfree(user);
+                   user = lname;
+                   debug("set username to %s from gssapi context", user);
+               } else {
+                   debug("failed to set username from gssapi context");
+                   packet_send_debug("failed to set username from gssapi context");
+               }
+           }
+       }
+#endif
+
+       debug("userauth-request for user %s service %s method %s",
+             user[0] ? user : "<implicit>", service, method);
        debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
 
        if ((role = strchr(user, '/')) != NULL)
@@ -237,11 +257,32 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
        else if (role && (style = strchr(role, ':')) != NULL)
                *style++ = '\0';
 
-       if (authctxt->attempt++ == 0) {
-               /* setup auth context */
+       /* If first time or username changed or empty username,
+          setup/reset authentication context. */
+       if ((authctxt->attempt++ == 0) ||
+           (strcmp(user, authctxt->user) != 0) ||
+           (strcmp(user, "") == 0)) {
+               if (authctxt->user) {
+                   xfree(authctxt->user);
+                   authctxt->user = NULL;
+               }
+               authctxt->valid = 0;
+        authctxt->user = xstrdup(user);
+        if (strcmp(service, "ssh-connection") != 0) {
+            packet_disconnect("Unsupported service %s", service);
+        }
+#ifdef GSSAPI
+               /* If we're going to set the username based on the
+                  GSSAPI context later, then wait until then to
+                  verify it. Just put in placeholders for now. */
+               if ((strcmp(user, "") == 0) &&
+                   ((strcmp(method, "gssapi") == 0) ||
+                    (strcmp(method, "gssapi-with-mic") == 0))) {
+                       authctxt->pw = fakepw();
+               } else {
+#endif
                authctxt->pw = PRIVSEP(getpwnamallow(user));
-               authctxt->user = xstrdup(user);
-               if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
+               if (authctxt->pw) {
                        authctxt->valid = 1;
                        debug2("input_userauth_request: setting up authctxt for %s", user);
                } else {
@@ -251,21 +292,26 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
                        PRIVSEP(audit_event(SSH_INVALID_USER));
 #endif
                }
+#ifdef GSSAPI
+               } /* endif for setting username based on GSSAPI context */
+#endif
 #ifdef USE_PAM
                if (options.use_pam)
                        PRIVSEP(start_pam(authctxt));
 #endif
                setproctitle("%s%s", authctxt->valid ? user : "unknown",
                    use_privsep ? " [net]" : "");
-               authctxt->service = xstrdup(service);
-               authctxt->style = style ? xstrdup(style) : NULL;
-               authctxt->role = role ? xstrdup(role) : NULL;
-               if (use_privsep)
-                       mm_inform_authserv(service, style, role);
-               userauth_banner();
-       } else if (strcmp(user, authctxt->user) != 0 ||
-           strcmp(service, authctxt->service) != 0) {
-               packet_disconnect("Change of username or service not allowed: "
+               if (authctxt->attempt == 1) {
+            authctxt->service = xstrdup(service);
+            authctxt->style = style ? xstrdup(style) : NULL;
+           authctxt->role = role ? xstrdup(role) : NULL;
+            if (use_privsep)
+             mm_inform_authserv(service, style, role);
+            userauth_banner();
+               }
+       }
+       if (strcmp(service, authctxt->service) != 0) {
+               packet_disconnect("Change of service not allowed: "
                    "(%s,%s) -> (%s,%s)",
                    authctxt->user, authctxt->service, user, service);
        }
diff --git a/debian/patches/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch b/debian/patches/0001-initial-empty-usernames-on-top-of-keyex-and-role.patch
new file mode 100644 (file)
index 0000000..d0bf651
--- /dev/null
@@ -0,0 +1,691 @@
+From 4693de3d73bce0c0f1000bf2c0efd4c06190ecdf Mon Sep 17 00:00:00 2001
+From: Sam Hartman <hartmans@debian.org>
+Date: Mon, 19 Sep 2011 16:35:48 -0400
+Subject: [PATCH] initial empty usernames on top of  keyex and role
+
+---
+ auth-pam.c  |   51 +++++++++++++++++++++++++++++-
+ auth-pam.h  |    1 +
+ auth2-gss.c |   98 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
+ auth2.c     |   86 +++++++++++++++++++++++++++++++++++++++++----------
+ 4 files changed, 206 insertions(+), 30 deletions(-)
+
+Index: openssh/auth-pam.c
+===================================================================
+--- openssh.orig/auth-pam.c    2011-09-21 18:55:39.000000000 -0400
++++ openssh/auth-pam.c 2011-09-21 18:57:32.000000000 -0400
+@@ -30,7 +30,7 @@
+  */
+ /*
+  * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
+- * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au>
++ * Copyright (c) 2003,2004,2006 Darren Tucker <dtucker@zip.com.au>
+  *
+  * Permission to use, copy, modify, and distribute this software for any
+  * purpose with or without fee is hereby granted, provided that the above
+@@ -122,6 +122,10 @@
+  */
+ typedef pthread_t sp_pthread_t;
+ #else
++#define pthread_create openssh_pthread_create
++#define pthread_exit openssh_pthread_exit
++#define pthread_cancel openssh_pthread_cancel
++#define pthread_join openssh_pthread_join
+ typedef pid_t sp_pthread_t;
+ #endif
+@@ -272,6 +276,49 @@
+ # define pam_chauthtok(a,b)   (sshpam_chauthtok_ruid((a), (b)))
+ #endif
++struct passwd *
++sshpam_getpw(const char *user)
++{
++      struct passwd *pw;
++
++      if ((pw = getpwnam(user)) != NULL)
++              return(pw);
++
++      debug("PAM: faking passwd struct for user '%.100s'", user);
++      if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL)
++              return NULL;
++      pw->pw_name = xstrdup(user);    /* XXX leak */
++      pw->pw_shell = "/bin/true";
++      pw->pw_gecos = "sshd fake PAM user";
++      return (pw);
++}
++
++void
++sshpam_check_userchanged(void)
++{
++      int sshpam_err;
++      struct passwd *pw;
++      const char *user;
++
++      debug("sshpam_check_userchanged");
++      sshpam_err = pam_get_item(sshpam_handle, PAM_USER, &user);
++      if (sshpam_err != PAM_SUCCESS)
++              fatal("PAM: could not get PAM_USER: %s",
++                  pam_strerror(sshpam_handle, sshpam_err));
++      if (strcmp(user, sshpam_authctxt->pw->pw_name) != 0) {
++              debug("PAM: user mapped from '%.100s' to '%.100s'",
++                  sshpam_authctxt->pw->pw_name, user);
++              if ((pw = getpwnam(user)) == NULL)
++                      fatal("PAM: could not get passwd entry for user "
++                          "'%.100s' provided by PAM_USER", user);
++              pwfree(sshpam_authctxt->pw);
++              sshpam_authctxt->pw = pw;
++              sshpam_authctxt->valid = allowed_user(pw);
++              debug("PAM: user '%.100s' now %svalid", user,
++                  sshpam_authctxt->valid ? "" : "in");
++      }
++}
++
+ void
+ sshpam_password_change_required(int reqd)
+ {
+@@ -294,7 +341,7 @@
+ static void
+ import_environments(Buffer *b)
+ {
+-      char *env;
++      char *env, *user;
+       u_int i, num_env;
+       int err;
+Index: openssh/auth-pam.h
+===================================================================
+--- openssh.orig/auth-pam.h    2011-09-21 18:55:39.000000000 -0400
++++ openssh/auth-pam.h 2011-09-21 18:57:32.000000000 -0400
+@@ -46,5 +46,6 @@
+ void sshpam_cleanup(void);
+ int sshpam_auth_passwd(Authctxt *, const char *);
+ int is_pam_session_open(void);
++struct passwd *sshpam_getpw(const char *);
+ #endif /* USE_PAM */
+Index: openssh/auth2-gss.c
+===================================================================
+--- openssh.orig/auth2-gss.c   2011-09-21 18:55:39.000000000 -0400
++++ openssh/auth2-gss.c        2011-09-21 18:57:32.000000000 -0400
+@@ -47,6 +47,7 @@
+ extern ServerOptions options;
++static void ssh_gssapi_userauth_error(Gssctxt *ctxt);
+ static void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
+ static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
+ static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
+@@ -59,8 +60,8 @@
+ userauth_gsskeyex(Authctxt *authctxt)
+ {
+       int authenticated = 0;
+-      Buffer b;
+-      gss_buffer_desc mic, gssbuf;
++      Buffer b, b2;
++      gss_buffer_desc mic, gssbuf, gssbuf2;
+       u_int len;
+       mic.value = packet_get_string(&len);
+@@ -74,13 +75,26 @@
+       gssbuf.value = buffer_ptr(&b);
+       gssbuf.length = buffer_len(&b);
++      /* client may have used empty username to determine target
++         name from GSSAPI context */
++      ssh_gssapi_buildmic(&b2, "", authctxt->service, "gssapi-keyex");
++
++      gssbuf2.value = buffer_ptr(&b2);
++      gssbuf2.length = buffer_len(&b2);
++
+       /* gss_kex_context is NULL with privsep, so we can't check it here */
+       if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, 
+-          &gssbuf, &mic))))
+-              authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+-                  authctxt->pw));
++                                                 &gssbuf, &mic))) ||
++          !GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, 
++                                                 &gssbuf2, &mic)))) {
++          if (authctxt->valid && authctxt->user && authctxt->user[0]) {
++            authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
++                                                      authctxt->pw));
++          }
++      }
+       
+       buffer_free(&b);
++      buffer_free(&b2);
+       xfree(mic.value);
+       return (authenticated);
+@@ -102,7 +116,10 @@
+       u_int len;
+       u_char *doid = NULL;
+-      if (!authctxt->valid || authctxt->user == NULL)
++      /* authctxt->valid may be 0 if we haven't yet determined
++         username from gssapi context. */
++
++      if (authctxt->user == NULL)
+               return (0);
+       mechs = packet_get_int();
+@@ -172,7 +189,7 @@
+       Gssctxt *gssctxt;
+       gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc recv_tok;
+-      OM_uint32 maj_status, min_status, flags;
++      OM_uint32 maj_status, min_status, flags=0;
+       u_int len;
+       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+@@ -190,6 +207,7 @@
+       xfree(recv_tok.value);
+       if (GSS_ERROR(maj_status)) {
++              ssh_gssapi_userauth_error(gssctxt);
+               if (send_tok.length != 0) {
+                       packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
+                       packet_put_string(send_tok.value, send_tok.length);
+@@ -253,6 +271,32 @@
+       gss_release_buffer(&maj_status, &send_tok);
+ }
++static void
++gssapi_set_username(Authctxt *authctxt)
++{
++    char *lname = NULL;
++
++    if ((authctxt->user == NULL) || (authctxt->user[0] == '\0')) {
++        PRIVSEP(ssh_gssapi_localname(&lname));
++        if (lname && lname[0] != '\0') {
++            if (authctxt->user) xfree(authctxt->user);
++            authctxt->user = lname;
++            debug("set username to %s from gssapi context", lname);
++            authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user));
++            if (authctxt->pw) {
++                authctxt->valid = 1;
++#ifdef USE_PAM
++                if (options.use_pam)
++                    PRIVSEP(start_pam(authctxt));
++#endif
++            }
++        } else {
++            debug("failed to set username from gssapi context");
++            packet_send_debug("failed to set username from gssapi context");
++        }
++    }
++}
++
+ /*
+  * This is called when the client thinks we've completed authentication.
+  * It should only be enabled in the dispatch handler by the function above,
+@@ -269,6 +313,8 @@
+       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+               fatal("No authentication or GSSAPI context");
++      gssapi_set_username(authctxt);
++
+       gssctxt = authctxt->methoddata;
+       /*
+@@ -278,8 +324,13 @@
+       packet_check_eom();
+-      authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
+-          authctxt->pw));
++      /* user should be set if valid but we double-check here */
++      if (authctxt->valid && authctxt->user && authctxt->user[0]) {
++          authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
++                                                    authctxt->pw));
++      } else {
++          authenticated = 0;
++      }
+       authctxt->postponed = 0;
+       dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
+@@ -313,9 +364,15 @@
+       gssbuf.value = buffer_ptr(&b);
+       gssbuf.length = buffer_len(&b);
++    gssapi_set_username(authctxt);
++
+       if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
+-              authenticated = 
+-                  PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
++          if (authctxt->valid && authctxt->user && authctxt->user[0]) {
++            authenticated =
++            PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw));
++          } else {
++            authenticated = 0;
++          }
+       else
+               logit("GSSAPI MIC check failed");
+@@ -330,6 +387,23 @@
+       userauth_finish(authctxt, authenticated, "gssapi-with-mic");
+ }
++static void ssh_gssapi_userauth_error(Gssctxt *ctxt) {
++      char *errstr;
++      OM_uint32 maj,min;
++      
++      errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min));
++      if (errstr) {
++              packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR);
++              packet_put_int(maj);
++              packet_put_int(min);
++              packet_put_cstring(errstr);
++              packet_put_cstring("");
++              packet_send();
++              packet_write_wait();
++              xfree(errstr);
++      }
++}
++
+ Authmethod method_gsskeyex = {
+       "gssapi-keyex",
+       userauth_gsskeyex,
+Index: openssh/auth2.c
+===================================================================
+--- openssh.orig/auth2.c       2011-09-21 18:57:31.000000000 -0400
++++ openssh/auth2.c    2011-09-21 18:57:32.000000000 -0400
+@@ -223,10 +223,30 @@
+       if (authctxt == NULL)
+               fatal("input_userauth_request: no authctxt");
+-      user = packet_get_cstring(NULL);
+-      service = packet_get_cstring(NULL);
+-      method = packet_get_cstring(NULL);
+-      debug("userauth-request for user %s service %s method %s", user, service, method);
++      user = packet_get_string(NULL);
++      service = packet_get_string(NULL);
++      method = packet_get_string(NULL);
++
++#ifdef GSSAPI
++      if (user[0] == '\0') {
++          debug("received empty username for %s", method);
++          if (strcmp(method, "gssapi-keyex") == 0) {
++              char *lname = NULL;
++              PRIVSEP(ssh_gssapi_localname(&lname));
++              if (lname && lname[0] != '\0') {
++                  xfree(user);
++                  user = lname;
++                  debug("set username to %s from gssapi context", user);
++              } else {
++                  debug("failed to set username from gssapi context");
++                  packet_send_debug("failed to set username from gssapi context");
++              }
++          }
++      }
++#endif
++
++      debug("userauth-request for user %s service %s method %s",
++            user[0] ? user : "<implicit>", service, method);
+       debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
+       if ((role = strchr(user, '/')) != NULL)
+@@ -237,11 +257,32 @@
+       else if (role && (style = strchr(role, ':')) != NULL)
+               *style++ = '\0';
+-      if (authctxt->attempt++ == 0) {
+-              /* setup auth context */
++      /* If first time or username changed or empty username,
++         setup/reset authentication context. */
++      if ((authctxt->attempt++ == 0) ||
++          (strcmp(user, authctxt->user) != 0) ||
++          (strcmp(user, "") == 0)) {
++              if (authctxt->user) {
++                  xfree(authctxt->user);
++                  authctxt->user = NULL;
++              }
++              authctxt->valid = 0;
++        authctxt->user = xstrdup(user);
++        if (strcmp(service, "ssh-connection") != 0) {
++            packet_disconnect("Unsupported service %s", service);
++        }
++#ifdef GSSAPI
++              /* If we're going to set the username based on the
++                 GSSAPI context later, then wait until then to
++                 verify it. Just put in placeholders for now. */
++              if ((strcmp(user, "") == 0) &&
++                  ((strcmp(method, "gssapi") == 0) ||
++                   (strcmp(method, "gssapi-with-mic") == 0))) {
++                      authctxt->pw = fakepw();
++              } else {
++#endif
+               authctxt->pw = PRIVSEP(getpwnamallow(user));
+-              authctxt->user = xstrdup(user);
+-              if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
++              if (authctxt->pw) {
+                       authctxt->valid = 1;
+                       debug2("input_userauth_request: setting up authctxt for %s", user);
+               } else {
+@@ -251,21 +292,26 @@
+                       PRIVSEP(audit_event(SSH_INVALID_USER));
+ #endif
+               }
++#ifdef GSSAPI
++              } /* endif for setting username based on GSSAPI context */
++#endif
+ #ifdef USE_PAM
+               if (options.use_pam)
+                       PRIVSEP(start_pam(authctxt));
+ #endif
+               setproctitle("%s%s", authctxt->valid ? user : "unknown",
+                   use_privsep ? " [net]" : "");
+-              authctxt->service = xstrdup(service);
+-              authctxt->style = style ? xstrdup(style) : NULL;
+-              authctxt->role = role ? xstrdup(role) : NULL;
+-              if (use_privsep)
+-                      mm_inform_authserv(service, style, role);
+-              userauth_banner();
+-      } else if (strcmp(user, authctxt->user) != 0 ||
+-          strcmp(service, authctxt->service) != 0) {
+-              packet_disconnect("Change of username or service not allowed: "
++              if (authctxt->attempt == 1) {
++            authctxt->service = xstrdup(service);
++            authctxt->style = style ? xstrdup(style) : NULL;
++          authctxt->role = role ? xstrdup(role) : NULL;
++            if (use_privsep)
++            mm_inform_authserv(service, style, role);
++            userauth_banner();
++              }
++      }
++      if (strcmp(service, authctxt->service) != 0) {
++              packet_disconnect("Change of service not allowed: "
+                   "(%s,%s) -> (%s,%s)",
+                   authctxt->user, authctxt->service, user, service);
+       }
+Index: openssh/gss-serv.c
+===================================================================
+--- openssh.orig/gss-serv.c    2011-09-21 18:55:39.000000000 -0400
++++ openssh/gss-serv.c 2011-09-21 18:57:32.000000000 -0400
+@@ -495,6 +495,25 @@
+       return (0);
+ }
++
++/* Privileged */
++int
++ssh_gssapi_localname(char **user)
++{
++      *user = NULL;
++      if (gssapi_client.displayname.length==0 || 
++          gssapi_client.displayname.value==NULL) {
++              debug("No suitable client data");
++              return(0);;
++      }
++      if (gssapi_client.mech && gssapi_client.mech->localname) {
++              return((*gssapi_client.mech->localname)(&gssapi_client,user));
++      } else {
++              debug("Unknown client authentication type");
++      }
++      return(0);
++}
++
+ /* These bits are only used for rekeying. The unpriviledged child is running 
+  * as the user, the monitor is root.
+  *
+Index: openssh/monitor.c
+===================================================================
+--- openssh.orig/monitor.c     2011-09-21 18:57:31.000000000 -0400
++++ openssh/monitor.c  2011-09-21 18:57:32.000000000 -0400
+@@ -182,6 +182,8 @@
+ int mm_answer_gss_userok(int, Buffer *);
+ int mm_answer_gss_checkmic(int, Buffer *);
+ int mm_answer_gss_sign(int, Buffer *);
++int mm_answer_gss_error(int, Buffer *);
++int mm_answer_gss_localname(int, Buffer *);
+ int mm_answer_gss_updatecreds(int, Buffer *);
+ #endif
+@@ -256,6 +258,8 @@
+     {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
+     {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
+     {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
++    {MONITOR_REQ_GSSERR, MON_ISAUTH | MON_ONCE, mm_answer_gss_error},
++    {MONITOR_REQ_GSSLOCALNAME, MON_ISAUTH, mm_answer_gss_localname},
+ #endif
+ #ifdef JPAKE
+     {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata},
+@@ -272,6 +276,7 @@
+     {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
+     {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
+     {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
++    {MONITOR_REQ_GSSERR, 0, mm_answer_gss_error},
+     {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
+ #endif
+     {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
+@@ -303,8 +308,14 @@
+     {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery},
+     {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond},
+ #endif
++#ifdef GSSAPI
++    {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx},
++    {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
++    {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
++    {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
++#endif
+ #ifdef USE_PAM
+-    {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start},
++    {MONITOR_REQ_PAM_START, MON_ISAUTH, mm_answer_pam_start},
+     {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account},
+     {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx},
+     {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query},
+@@ -382,9 +393,10 @@
+               /* Permit requests for moduli and signatures */
+               monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
+-#ifdef GSSAPI
++#ifdef GSSAPI         
+               /* and for the GSSAPI key exchange */
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
++              monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
+ #endif
+       } else {
+               mon_dispatch = mon_dispatch_proto15;
+@@ -483,14 +495,20 @@
+               monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
++
+ #ifdef GSSAPI
+               /* and for the GSSAPI key exchange */
+-              monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
+-#endif                
++              monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP,1);
++              monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR,1);
++#endif
++
+       } else {
+               mon_dispatch = mon_dispatch_postauth15;
+               monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
+       }
++#ifdef GSSAPI         
++      monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
++#endif
+       if (!no_pty_flag) {
+               monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1);
+@@ -2167,6 +2185,45 @@
+       return (authenticated);
+ }
++int
++mm_answer_gss_error(int socket, Buffer *m) {
++        OM_uint32 major,minor;
++        char *msg;
++
++      msg=ssh_gssapi_last_error(gsscontext,&major,&minor);
++      buffer_clear(m);
++      buffer_put_int(m,major);
++      buffer_put_int(m,minor);
++      buffer_put_cstring(m,msg);
++
++      mm_request_send(socket,MONITOR_ANS_GSSERR,m);
++
++      xfree(msg);
++      
++        return(0);
++}
++
++int
++mm_answer_gss_localname(int socket, Buffer *m) {
++      char *name;
++
++      ssh_gssapi_localname(&name);
++
++        buffer_clear(m);
++      if (name) {
++          buffer_put_cstring(m, name);
++          debug3("%s: sending result %s", __func__, name);
++          xfree(name);
++      } else {
++          buffer_put_cstring(m, "");
++          debug3("%s: sending result \"\"", __func__);
++      }
++
++        mm_request_send(socket, MONITOR_ANS_GSSLOCALNAME, m);
++
++        return(0);
++}
++
+ int 
+ mm_answer_gss_sign(int socket, Buffer *m)
+ {
+Index: openssh/monitor.h
+===================================================================
+--- openssh.orig/monitor.h     2011-09-21 18:57:31.000000000 -0400
++++ openssh/monitor.h  2011-09-21 18:57:32.000000000 -0400
+@@ -52,6 +52,8 @@
+       MONITOR_REQ_GSSSETUP, MONITOR_ANS_GSSSETUP,
+       MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP,
+       MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK,
++      MONITOR_REQ_GSSLOCALNAME, MONITOR_ANS_GSSLOCALNAME,
++      MONITOR_REQ_GSSERR, MONITOR_ANS_GSSERR,
+       MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC,
+       MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN,
+       MONITOR_REQ_GSSUPCREDS, MONITOR_ANS_GSSUPCREDS,
+Index: openssh/monitor_wrap.c
+===================================================================
+--- openssh.orig/monitor_wrap.c        2011-09-21 18:57:31.000000000 -0400
++++ openssh/monitor_wrap.c     2011-09-21 18:57:32.000000000 -0400
+@@ -1306,6 +1306,54 @@
+       return (authenticated);
+ }
++char *
++mm_ssh_gssapi_last_error(Gssctxt *ctx, OM_uint32 *major, OM_uint32 *minor) {
++      Buffer m;
++      OM_uint32 maj,min;
++      char *errstr;
++      
++      buffer_init(&m);
++
++      mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSERR, &m);
++      mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSERR, &m);
++
++      maj = buffer_get_int(&m);
++      min = buffer_get_int(&m);
++
++      if (major) *major=maj;
++      if (minor) *minor=min;
++      
++      errstr=buffer_get_string(&m,NULL);
++
++      buffer_free(&m);
++      
++      return(errstr);
++}     
++
++int
++mm_ssh_gssapi_localname(char **lname)
++{
++        Buffer m;
++
++      buffer_init(&m);
++        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSLOCALNAME, &m);
++
++        debug3("%s: waiting for MONITOR_ANS_GSSLOCALNAME", __func__);
++        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSLOCALNAME,
++                                  &m);
++
++      *lname = buffer_get_string(&m, NULL);
++
++        buffer_free(&m);
++      if (lname[0] == '\0') {
++          debug3("%s: gssapi identity mapping failed", __func__);
++      } else {
++          debug3("%s: gssapi identity mapped to %s", __func__, *lname);
++      }
++      
++        return(0);
++}     
++
+ OM_uint32
+ mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
+ {
+Index: openssh/monitor_wrap.h
+===================================================================
+--- openssh.orig/monitor_wrap.h        2011-09-21 18:57:31.000000000 -0400
++++ openssh/monitor_wrap.h     2011-09-21 18:57:32.000000000 -0400
+@@ -62,6 +62,8 @@
+ int mm_ssh_gssapi_userok(char *user, struct passwd *);
+ OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
+ OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
++int mm_ssh_gssapi_localname(char **user);
++char *mm_ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min);
+ int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
+ #endif
+Index: openssh/misc.c
+===================================================================
+--- openssh.orig/misc.c        2011-09-21 18:57:31.000000000 -0400
++++ openssh/misc.c     2011-09-21 18:57:32.000000000 -0400
+@@ -224,6 +224,20 @@
+       return copy;
+ }
++void
++pwfree(struct passwd *pw)
++{
++      xfree(pw->pw_name);
++      xfree(pw->pw_passwd);
++      xfree(pw->pw_gecos);
++#ifdef HAVE_PW_CLASS_IN_PASSWD
++      xfree(pw->pw_class);
++#endif
++      xfree(pw->pw_dir);
++      xfree(pw->pw_shell);
++      xfree(pw);
++}
++
+ /*
+  * Convert ASCII string to TCP/IP port number.
+  * Port must be >=0 and <=65535.
+Index: openssh/misc.h
+===================================================================
+--- openssh.orig/misc.h        2011-09-21 18:57:31.000000000 -0400
++++ openssh/misc.h     2011-09-21 18:57:32.000000000 -0400
+@@ -38,6 +38,7 @@
+ void   sock_set_v6only(int);
+ struct passwd *pwcopy(struct passwd *);
++void   pwfree(struct passwd *);
+ const char *ssh_gai_strerror(int);
+ typedef struct arglist arglist;
+Index: openssh/ssh-gss.h
+===================================================================
+--- openssh.orig/ssh-gss.h     2011-09-21 18:58:35.000000000 -0400
++++ openssh/ssh-gss.h  2011-09-21 18:58:49.000000000 -0400
+@@ -141,6 +141,7 @@
+ OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
+ int ssh_gssapi_credentials_updated(Gssctxt *);
++int ssh_gssapi_localname(char **name);
+ /* In the server */
+ typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, 
+     const char *);
index 2be7cf1..e7a404a 100644 (file)
@@ -41,3 +41,4 @@ auth-log-verbosity.patch
 # Debian-specific configuration
 gnome-ssh-askpass2-icon.patch
 debian-config.patch
+0001-initial-empty-usernames-on-top-of-keyex-and-role.patch
index 380895e..06ddca3 100644 (file)
@@ -442,6 +442,25 @@ ssh_gssapi_userok(char *user, struct passwd *pw)
        return (0);
 }
 
+
+/* Privileged */
+int
+ssh_gssapi_localname(char **user)
+{
+       *user = NULL;
+       if (gssapi_client.displayname.length==0 || 
+           gssapi_client.displayname.value==NULL) {
+               debug("No suitable client data");
+               return(0);;
+       }
+       if (gssapi_client.mech && gssapi_client.mech->localname) {
+               return((*gssapi_client.mech->localname)(&gssapi_client,user));
+       } else {
+               debug("Unknown client authentication type");
+       }
+       return(0);
+}
+
 /* These bits are only used for rekeying. The unpriviledged child is running 
  * as the user, the monitor is root.
  *
diff --git a/misc.c b/misc.c
index 1814ae3..7dbb277 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -224,6 +224,20 @@ pwcopy(struct passwd *pw)
        return copy;
 }
 
+void
+pwfree(struct passwd *pw)
+{
+       xfree(pw->pw_name);
+       xfree(pw->pw_passwd);
+       xfree(pw->pw_gecos);
+#ifdef HAVE_PW_CLASS_IN_PASSWD
+       xfree(pw->pw_class);
+#endif
+       xfree(pw->pw_dir);
+       xfree(pw->pw_shell);
+       xfree(pw);
+}
+
 /*
  * Convert ASCII string to TCP/IP port number.
  * Port must be >=0 and <=65535.
diff --git a/misc.h b/misc.h
index 904edc7..dea3ae3 100644 (file)
--- a/misc.h
+++ b/misc.h
@@ -38,6 +38,7 @@ void   ms_to_timeval(struct timeval *, int);
 void    sock_set_v6only(int);
 
 struct passwd *pwcopy(struct passwd *);
+void    pwfree(struct passwd *);
 const char *ssh_gai_strerror(int);
 
 typedef struct arglist arglist;
index 985916b..634a7f3 100644 (file)
--- a/monitor.c
+++ b/monitor.c
@@ -182,6 +182,8 @@ int mm_answer_gss_accept_ctx(int, Buffer *);
 int mm_answer_gss_userok(int, Buffer *);
 int mm_answer_gss_checkmic(int, Buffer *);
 int mm_answer_gss_sign(int, Buffer *);
+int mm_answer_gss_error(int, Buffer *);
+int mm_answer_gss_localname(int, Buffer *);
 int mm_answer_gss_updatecreds(int, Buffer *);
 #endif
 
@@ -256,6 +258,8 @@ struct mon_table mon_dispatch_proto20[] = {
     {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
     {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
     {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
+    {MONITOR_REQ_GSSERR, MON_ISAUTH | MON_ONCE, mm_answer_gss_error},
+    {MONITOR_REQ_GSSLOCALNAME, MON_ISAUTH, mm_answer_gss_localname},
 #endif
 #ifdef JPAKE
     {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata},
@@ -272,6 +276,7 @@ struct mon_table mon_dispatch_postauth20[] = {
     {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx},
     {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx},
     {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign},
+    {MONITOR_REQ_GSSERR, 0, mm_answer_gss_error},
     {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds},
 #endif
     {MONITOR_REQ_MODULI, 0, mm_answer_moduli},
@@ -303,8 +308,14 @@ struct mon_table mon_dispatch_proto15[] = {
     {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery},
     {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond},
 #endif
+#ifdef GSSAPI
+    {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx},
+    {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
+    {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign},
+    {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
+#endif
 #ifdef USE_PAM
-    {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start},
+    {MONITOR_REQ_PAM_START, MON_ISAUTH, mm_answer_pam_start},
     {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account},
     {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx},
     {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query},
@@ -382,9 +393,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
                /* Permit requests for moduli and signatures */
                monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
                monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
-#ifdef GSSAPI
+#ifdef GSSAPI          
                /* and for the GSSAPI key exchange */
                monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
 #endif
        } else {
                mon_dispatch = mon_dispatch_proto15;
@@ -483,14 +495,20 @@ monitor_child_postauth(struct monitor *pmonitor)
                monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
                monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
                monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
+
 #ifdef GSSAPI
                /* and for the GSSAPI key exchange */
-               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1);
-#endif         
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP,1);
+               monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR,1);
+#endif
+
        } else {
                mon_dispatch = mon_dispatch_postauth15;
                monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
        }
+#ifdef GSSAPI          
+       monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1);
+#endif
        if (!no_pty_flag) {
                monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
                monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1);
@@ -2167,6 +2185,45 @@ mm_answer_gss_userok(int sock, Buffer *m)
        return (authenticated);
 }
 
+int
+mm_answer_gss_error(int socket, Buffer *m) {
+        OM_uint32 major,minor;
+        char *msg;
+
+       msg=ssh_gssapi_last_error(gsscontext,&major,&minor);
+       buffer_clear(m);
+       buffer_put_int(m,major);
+       buffer_put_int(m,minor);
+       buffer_put_cstring(m,msg);
+
+       mm_request_send(socket,MONITOR_ANS_GSSERR,m);
+
+       xfree(msg);
+       
+        return(0);
+}
+
+int
+mm_answer_gss_localname(int socket, Buffer *m) {
+       char *name;
+
+       ssh_gssapi_localname(&name);
+
+        buffer_clear(m);
+       if (name) {
+           buffer_put_cstring(m, name);
+           debug3("%s: sending result %s", __func__, name);
+           xfree(name);
+       } else {
+           buffer_put_cstring(m, "");
+           debug3("%s: sending result \"\"", __func__);
+       }
+
+        mm_request_send(socket, MONITOR_ANS_GSSLOCALNAME, m);
+
+        return(0);
+}
+
 int 
 mm_answer_gss_sign(int socket, Buffer *m)
 {
index 42887eb..7aeb9ed 100644 (file)
--- a/monitor.h
+++ b/monitor.h
@@ -52,6 +52,8 @@ enum monitor_reqtype {
        MONITOR_REQ_GSSSETUP, MONITOR_ANS_GSSSETUP,
        MONITOR_REQ_GSSSTEP, MONITOR_ANS_GSSSTEP,
        MONITOR_REQ_GSSUSEROK, MONITOR_ANS_GSSUSEROK,
+       MONITOR_REQ_GSSLOCALNAME, MONITOR_ANS_GSSLOCALNAME,
+       MONITOR_REQ_GSSERR, MONITOR_ANS_GSSERR,
        MONITOR_REQ_GSSCHECKMIC, MONITOR_ANS_GSSCHECKMIC,
        MONITOR_REQ_GSSSIGN, MONITOR_ANS_GSSSIGN,
        MONITOR_REQ_GSSUPCREDS, MONITOR_ANS_GSSUPCREDS,
index f46be66..af268d6 100644 (file)
@@ -1306,6 +1306,54 @@ mm_ssh_gssapi_userok(char *user, struct passwd *pw)
        return (authenticated);
 }
 
+char *
+mm_ssh_gssapi_last_error(Gssctxt *ctx, OM_uint32 *major, OM_uint32 *minor) {
+       Buffer m;
+       OM_uint32 maj,min;
+       char *errstr;
+       
+       buffer_init(&m);
+
+       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSERR, &m);
+       mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSERR, &m);
+
+       maj = buffer_get_int(&m);
+       min = buffer_get_int(&m);
+
+       if (major) *major=maj;
+       if (minor) *minor=min;
+       
+       errstr=buffer_get_string(&m,NULL);
+
+       buffer_free(&m);
+       
+       return(errstr);
+}      
+
+int
+mm_ssh_gssapi_localname(char **lname)
+{
+        Buffer m;
+
+       buffer_init(&m);
+        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSLOCALNAME, &m);
+
+        debug3("%s: waiting for MONITOR_ANS_GSSLOCALNAME", __func__);
+        mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSLOCALNAME,
+                                  &m);
+
+       *lname = buffer_get_string(&m, NULL);
+
+        buffer_free(&m);
+       if (lname[0] == '\0') {
+           debug3("%s: gssapi identity mapping failed", __func__);
+       } else {
+           debug3("%s: gssapi identity mapped to %s", __func__, *lname);
+       }
+       
+        return(0);
+}      
+
 OM_uint32
 mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash)
 {
index 4d12e29..6bfd43b 100644 (file)
@@ -62,6 +62,8 @@ OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
 int mm_ssh_gssapi_userok(char *user, struct passwd *);
 OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t);
 OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t);
+int mm_ssh_gssapi_localname(char **user);
+char *mm_ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *maj, OM_uint32 *min);
 int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *);
 #endif
 
index 31d5a08..363ab8b 100644 (file)
--- a/ssh-gss.h
+++ b/ssh-gss.h
@@ -137,6 +137,7 @@ int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
 OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
 int ssh_gssapi_credentials_updated(Gssctxt *);
 
+int ssh_gssapi_localname(char **name);
 /* In the server */
 typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, 
     const char *);