diff --git a/.gitignore b/.gitignore index 9ad2894..6dd51a0 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ pam_ssh_agent_auth-0.9.2.tar.bz2 /openssh-7.1p1.tar.gz /openssh-7.1p2.tar.gz /pam_ssh_agent_auth-0.10.2.tar.bz2 +/openssh-7.2p1.tar.gz diff --git a/openssh-6.6.1p1-log-in-chroot.patch b/openssh-6.6.1p1-log-in-chroot.patch index 7590812..5889005 100644 --- a/openssh-6.6.1p1-log-in-chroot.patch +++ b/openssh-6.6.1p1-log-in-chroot.patch @@ -117,15 +117,14 @@ diff -up openssh-6.8p1/monitor.h.log-in-chroot openssh-6.8p1/monitor.h diff -up openssh-6.8p1/session.c.log-in-chroot openssh-6.8p1/session.c --- openssh-6.8p1/session.c.log-in-chroot 2015-03-18 12:59:29.675022359 +0100 +++ openssh-6.8p1/session.c 2015-03-18 12:59:29.696022308 +0100 -@@ -161,6 +161,8 @@ login_cap_t *lc; +@@ -161,6 +161,7 @@ login_cap_t *lc; static int is_child = 0; - + static int in_chroot = 0; +static int have_dev_log = 1; -+ + /* Name and directory of socket for authentication agent forwarding. */ static char *auth_sock_name = NULL; - static char *auth_sock_dir = NULL; @@ -506,8 +508,8 @@ do_exec_no_pty(Session *s, const char *c is_child = 1; @@ -150,8 +149,8 @@ diff -up openssh-6.8p1/session.c.log-in-chroot openssh-6.8p1/session.c @@ -780,6 +782,7 @@ do_exec(Session *s, const char *command) int ret; - const char *forced = NULL; - char session_type[1024], *tty = NULL; + const char *forced = NULL, *tty = NULL; + char session_type[1024]; + struct stat dev_log_stat; if (options.adm_forced_command) { @@ -164,7 +163,7 @@ diff -up openssh-6.8p1/session.c.log-in-chroot openssh-6.8p1/session.c + have_dev_log = 0; + } + - verbose("Starting session: %s%s%s for %s from %.200s port %d", + verbose("Starting session: %s%s%s for %s from %.200s port %d id %d", session_type, tty == NULL ? "" : " on ", @@ -1678,14 +1685,6 @@ child_close_fds(void) @@ -233,8 +232,8 @@ diff -up openssh-6.8p1/sftp-server.c.log-in-chroot openssh-6.8p1/sftp-server.c fd_set *rset, *wset; int i, r, in, out, max, ch, skipargs = 0, log_stderr = 0; @@ -1515,7 +1515,7 @@ sftp_server_main(int argc, char **argv, - extern char *__progname; + ssh_malloc_init(); /* must be called before any mallocs */ __progname = ssh_get_progname(argv[0]); - log_init(__progname, log_level, log_facility, log_stderr); + log_init_handler(__progname, log_level, log_facility, log_stderr, reset_handler); diff --git a/openssh-6.6.1p1-selinux-contexts.patch b/openssh-6.6.1p1-selinux-contexts.patch index 052843f..5a9233b 100644 --- a/openssh-6.6.1p1-selinux-contexts.patch +++ b/openssh-6.6.1p1-selinux-contexts.patch @@ -115,7 +115,7 @@ index 2871fe9..39b9c08 100644 + sshd_selinux_change_privsep_preauth_context(); #endif - /* Change our root directory */ + /* Demote the child */ diff --git a/openbsd-compat/port-linux.c b/openbsd-compat/port-linux.c index 12c014e..c5ef2ff 100644 --- a/openbsd-compat/port-linux.c diff --git a/openssh-6.6p1-GSSAPIEnablek5users.patch b/openssh-6.6p1-GSSAPIEnablek5users.patch index ae0e5b2..16333c3 100644 --- a/openssh-6.6p1-GSSAPIEnablek5users.patch +++ b/openssh-6.6p1-GSSAPIEnablek5users.patch @@ -106,9 +106,9 @@ diff -up openssh-7.0p1/sshd_config.5.GSSAPIEnablek5users openssh-7.0p1/sshd_conf --- openssh-7.0p1/sshd_config.5.GSSAPIEnablek5users 2015-08-12 11:27:44.023407950 +0200 +++ openssh-7.0p1/sshd_config.5 2015-08-12 11:27:44.048407911 +0200 @@ -633,6 +633,12 @@ on logout. + on logout. The default is .Dq yes . - Note that this option applies to protocol version 2 only. +.It Cm GSSAPIEnablek5users +Specifies whether to look at .k5users file for GSSAPI authentication +access control. Further details are described in diff --git a/openssh-6.6p1-ctr-cavstest.patch b/openssh-6.6p1-ctr-cavstest.patch index db9f47b..591ae6c 100644 --- a/openssh-6.6p1-ctr-cavstest.patch +++ b/openssh-6.6p1-ctr-cavstest.patch @@ -25,8 +25,8 @@ diff -up openssh-6.8p1/Makefile.in.ctr-cavs openssh-6.8p1/Makefile.in +ctr-cavstest$(EXEEXT): $(LIBCOMPAT) libssh.a ctr-cavstest.o + $(LD) -o $@ ctr-cavstest.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(LIBS) + - ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o roaming_dummy.o - $(LD) -o $@ ssh-keyscan.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) @@ -326,6 +330,7 @@ install-files: $(INSTALL) -m 0700 ssh-ldap-wrapper $(DESTDIR)$(SSH_LDAP_WRAPPER) ; \ diff --git a/openssh-6.6p1-gsskex.patch b/openssh-6.6p1-gsskex.patch deleted file mode 100644 index 2feb7b5..0000000 --- a/openssh-6.6p1-gsskex.patch +++ /dev/null @@ -1,2744 +0,0 @@ -diff -up openssh-7.0p1/auth2.c.gsskex openssh-7.0p1/auth2.c ---- openssh-7.0p1/auth2.c.gsskex 2015-08-12 11:15:43.625548999 +0200 -+++ openssh-7.0p1/auth2.c 2015-08-12 11:15:43.692548892 +0200 -@@ -70,6 +70,7 @@ extern Authmethod method_passwd; - extern Authmethod method_kbdint; - extern Authmethod method_hostbased; - #ifdef GSSAPI -+extern Authmethod method_gsskeyex; - extern Authmethod method_gssapi; - #endif - -@@ -77,6 +78,7 @@ Authmethod *authmethods[] = { - &method_none, - &method_pubkey, - #ifdef GSSAPI -+ &method_gsskeyex, - &method_gssapi, - #endif - &method_passwd, -diff -up openssh-7.0p1/auth2-gss.c.gsskex openssh-7.0p1/auth2-gss.c ---- openssh-7.0p1/auth2-gss.c.gsskex 2015-08-12 11:15:43.624549001 +0200 -+++ openssh-7.0p1/auth2-gss.c 2015-08-12 11:15:43.692548892 +0200 -@@ -31,6 +31,7 @@ - #include - - #include -+#include - - #include "xmalloc.h" - #include "key.h" -@@ -53,6 +54,40 @@ static int input_gssapi_mic(int type, u_ - static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); - static int 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); -+ free(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) -@@ -238,7 +273,8 @@ input_gssapi_exchange_complete(int type, - - packet_check_eom(); - -- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); -+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -+ authctxt->pw)); - - authctxt->postponed = 0; - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); -@@ -281,7 +317,8 @@ input_gssapi_mic(int type, u_int32_t ple - gssbuf.length = buffer_len(&b); - - if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) -- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); -+ authenticated = -+ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); - else - logit("GSSAPI MIC check failed"); - -@@ -299,6 +336,12 @@ input_gssapi_mic(int type, u_int32_t ple - return 0; - } - -+Authmethod method_gsskeyex = { -+ "gssapi-keyex", -+ userauth_gsskeyex, -+ &options.gss_authentication -+}; -+ - Authmethod method_gssapi = { - "gssapi-with-mic", - userauth_gssapi, -diff -up openssh-7.0p1/clientloop.c.gsskex openssh-7.0p1/clientloop.c ---- openssh-7.0p1/clientloop.c.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/clientloop.c 2015-08-12 11:15:43.693548890 +0200 -@@ -115,6 +115,10 @@ - #include "ssherr.h" - #include "hostfile.h" - -+#ifdef GSSAPI -+#include "ssh-gss.h" -+#endif -+ - /* import options */ - extern Options options; - -@@ -1610,6 +1614,15 @@ client_loop(int have_pty, int escape_cha - /* Do channel operations unless rekeying in progress. */ - if (!rekeying) { - channel_after_select(readset, writeset); -+ -+#ifdef GSSAPI -+ if (options.gss_renewal_rekey && -+ ssh_gssapi_credentials_updated(GSS_C_NO_CONTEXT)) { -+ debug("credentials updated - forcing rekey"); -+ need_rekeying = 1; -+ } -+#endif -+ - if (need_rekeying || packet_need_rekeying()) { - debug("need rekeying"); - active_state->kex->done = 0; -diff -up openssh-7.0p1/configure.ac.gsskex openssh-7.0p1/configure.ac ---- openssh-7.0p1/configure.ac.gsskex 2015-08-12 11:15:43.675548919 +0200 -+++ openssh-7.0p1/configure.ac 2015-08-12 11:15:43.694548889 +0200 -@@ -625,6 +625,30 @@ main() { if (NSVersionOfRunTimeLibrary(" - [Use tunnel device compatibility to OpenBSD]) - AC_DEFINE([SSH_TUN_PREPEND_AF], [1], - [Prepend the address family to IP tunnel traffic]) -+ AC_MSG_CHECKING(if we have the Security Authorization Session API) -+ AC_TRY_COMPILE([#include ], -+ [SessionCreate(0, 0);], -+ [ac_cv_use_security_session_api="yes" -+ AC_DEFINE(USE_SECURITY_SESSION_API, 1, -+ [platform has the Security Authorization Session API]) -+ LIBS="$LIBS -framework Security" -+ AC_MSG_RESULT(yes)], -+ [ac_cv_use_security_session_api="no" -+ AC_MSG_RESULT(no)]) -+ AC_MSG_CHECKING(if we have an in-memory credentials cache) -+ AC_TRY_COMPILE( -+ [#include ], -+ [cc_context_t c; -+ (void) cc_initialize (&c, 0, NULL, NULL);], -+ [AC_DEFINE(USE_CCAPI, 1, -+ [platform uses an in-memory credentials cache]) -+ LIBS="$LIBS -framework Security" -+ AC_MSG_RESULT(yes) -+ if test "x$ac_cv_use_security_session_api" = "xno"; then -+ AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***) -+ fi], -+ [AC_MSG_RESULT(no)] -+ ) - m4_pattern_allow([AU_IPv]) - AC_CHECK_DECL([AU_IPv4], [], - AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) -diff -up openssh-7.0p1/gss-genr.c.gsskex openssh-7.0p1/gss-genr.c ---- openssh-7.0p1/gss-genr.c.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/gss-genr.c 2015-08-12 11:15:43.694548889 +0200 -@@ -41,12 +41,167 @@ - #include "buffer.h" - #include "log.h" - #include "ssh2.h" -+#include "cipher.h" -+#include "key.h" -+#include "kex.h" -+#include - - #include "ssh-gss.h" - - extern u_char *session_id2; - extern u_int session_id2_len; - -+typedef struct { -+ char *encoded; -+ gss_OID oid; -+} ssh_gss_kex_mapping; -+ -+/* -+ * XXX - It would be nice to find a more elegant way of handling the -+ * XXX passing of the key exchange context to the userauth routines -+ */ -+ -+Gssctxt *gss_kex_context = NULL; -+ -+static ssh_gss_kex_mapping *gss_enc2oid = NULL; -+ -+int -+ssh_gssapi_oid_table_ok() { -+ return (gss_enc2oid != NULL); -+} -+ -+/* -+ * Return a list of the gss-group1-sha1 mechanisms supported by this program -+ * -+ * We test mechanisms to ensure that we can use them, to avoid starting -+ * a key exchange with a bad mechanism -+ */ -+ -+char * -+ssh_gssapi_client_mechanisms(const char *host, const char *client) { -+ gss_OID_set gss_supported; -+ OM_uint32 min_status; -+ -+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) -+ return NULL; -+ -+ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, -+ host, client)); -+} -+ -+char * -+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, -+ const char *host, const char *client) { -+ Buffer buf; -+ size_t i; -+ int oidpos, enclen; -+ char *mechs, *encoded; -+ u_char digest[EVP_MAX_MD_SIZE]; -+ char deroid[2]; -+ const EVP_MD *evp_md = EVP_md5(); -+ EVP_MD_CTX md; -+ -+ if (gss_enc2oid != NULL) { -+ for (i = 0; gss_enc2oid[i].encoded != NULL; i++) -+ free(gss_enc2oid[i].encoded); -+ free(gss_enc2oid); -+ } -+ -+ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * -+ (gss_supported->count + 1)); -+ -+ buffer_init(&buf); -+ -+ oidpos = 0; -+ for (i = 0; i < gss_supported->count; i++) { -+ if (gss_supported->elements[i].length < 128 && -+ (*check)(NULL, &(gss_supported->elements[i]), host, client)) { -+ -+ deroid[0] = SSH_GSS_OIDTYPE; -+ deroid[1] = gss_supported->elements[i].length; -+ -+ EVP_DigestInit(&md, evp_md); -+ EVP_DigestUpdate(&md, deroid, 2); -+ EVP_DigestUpdate(&md, -+ gss_supported->elements[i].elements, -+ gss_supported->elements[i].length); -+ EVP_DigestFinal(&md, digest, NULL); -+ -+ encoded = xmalloc(EVP_MD_size(evp_md) * 2); -+ enclen = __b64_ntop(digest, EVP_MD_size(evp_md), -+ encoded, EVP_MD_size(evp_md) * 2); -+ -+ if (oidpos != 0) -+ buffer_put_char(&buf, ','); -+ -+ buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, -+ sizeof(KEX_GSS_GEX_SHA1_ID) - 1); -+ buffer_append(&buf, encoded, enclen); -+ buffer_put_char(&buf, ','); -+ buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, -+ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); -+ buffer_append(&buf, encoded, enclen); -+ buffer_put_char(&buf, ','); -+ buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, -+ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); -+ buffer_append(&buf, encoded, enclen); -+ -+ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); -+ gss_enc2oid[oidpos].encoded = encoded; -+ oidpos++; -+ } -+ } -+ gss_enc2oid[oidpos].oid = NULL; -+ gss_enc2oid[oidpos].encoded = NULL; -+ -+ buffer_put_char(&buf, '\0'); -+ -+ mechs = xmalloc(buffer_len(&buf)); -+ buffer_get(&buf, mechs, buffer_len(&buf)); -+ buffer_free(&buf); -+ -+ if (strlen(mechs) == 0) { -+ free(mechs); -+ mechs = NULL; -+ } -+ -+ return (mechs); -+} -+ -+gss_OID -+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { -+ int i = 0; -+ -+ switch (kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; -+ break; -+ case KEX_GSS_GRP14_SHA1: -+ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; -+ break; -+ case KEX_GSS_GEX_SHA1: -+ if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; -+ break; -+ default: -+ return GSS_C_NO_OID; -+ } -+ -+ while (gss_enc2oid[i].encoded != NULL && -+ strcmp(name, gss_enc2oid[i].encoded) != 0) -+ i++; -+ -+ if (gss_enc2oid[i].oid != NULL && ctx != NULL) -+ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); -+ -+ return gss_enc2oid[i].oid; -+} -+ - /* Check that the OID in a data stream matches that in the context */ - int - ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) -@@ -199,7 +354,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int de - } - - ctx->major = gss_init_sec_context(&ctx->minor, -- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, -+ ctx->client_creds, &ctx->context, ctx->name, ctx->oid, - GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, - 0, NULL, recv_tok, NULL, send_tok, flags, NULL); - -@@ -229,8 +384,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, con - } - - OM_uint32 -+ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) -+{ -+ gss_buffer_desc gssbuf; -+ gss_name_t gssname; -+ OM_uint32 status; -+ gss_OID_set oidset; -+ -+ gssbuf.value = (void *) name; -+ gssbuf.length = strlen(gssbuf.value); -+ -+ gss_create_empty_oid_set(&status, &oidset); -+ gss_add_oid_set_member(&status, ctx->oid, &oidset); -+ -+ ctx->major = gss_import_name(&ctx->minor, &gssbuf, -+ GSS_C_NT_USER_NAME, &gssname); -+ -+ if (!ctx->major) -+ ctx->major = gss_acquire_cred(&ctx->minor, -+ gssname, 0, oidset, GSS_C_INITIATE, -+ &ctx->client_creds, NULL, NULL); -+ -+ gss_release_name(&status, &gssname); -+ gss_release_oid_set(&status, &oidset); -+ -+ if (ctx->major) -+ ssh_gssapi_error(ctx); -+ -+ return(ctx->major); -+} -+ -+OM_uint32 - ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) - { -+ if (ctx == NULL) -+ return -1; -+ - if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, - GSS_C_QOP_DEFAULT, buffer, hash))) - ssh_gssapi_error(ctx); -@@ -238,6 +427,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer - return (ctx->major); - } - -+/* Priviledged when used by server */ -+OM_uint32 -+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) -+{ -+ if (ctx == NULL) -+ return -1; -+ -+ ctx->major = gss_verify_mic(&ctx->minor, ctx->context, -+ gssbuf, gssmic, NULL); -+ -+ return (ctx->major); -+} -+ - void - ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, - const char *context) -@@ -251,11 +453,16 @@ ssh_gssapi_buildmic(Buffer *b, const cha - } - - int --ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) -+ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, -+ const char *client) - { - gss_buffer_desc token = GSS_C_EMPTY_BUFFER; - OM_uint32 major, minor; - gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; -+ Gssctxt *intctx = NULL; -+ -+ if (ctx == NULL) -+ ctx = &intctx; - - /* RFC 4462 says we MUST NOT do SPNEGO */ - if (oid->length == spnego_oid.length && -@@ -265,6 +472,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx - ssh_gssapi_build_ctx(ctx); - ssh_gssapi_set_oid(*ctx, oid); - major = ssh_gssapi_import_name(*ctx, host); -+ -+ if (!GSS_ERROR(major) && client) -+ major = ssh_gssapi_client_identity(*ctx, client); -+ - if (!GSS_ERROR(major)) { - major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, - NULL); -@@ -274,10 +485,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx - GSS_C_NO_BUFFER); - } - -- if (GSS_ERROR(major)) -+ if (GSS_ERROR(major) || intctx != NULL) - ssh_gssapi_delete_ctx(ctx); - - return (!GSS_ERROR(major)); - } - -+int -+ssh_gssapi_credentials_updated(Gssctxt *ctxt) { -+ static gss_name_t saved_name = GSS_C_NO_NAME; -+ static OM_uint32 saved_lifetime = 0; -+ static gss_OID saved_mech = GSS_C_NO_OID; -+ static gss_name_t name; -+ static OM_uint32 last_call = 0; -+ OM_uint32 lifetime, now, major, minor; -+ int equal; -+ -+ now = time(NULL); -+ -+ if (ctxt) { -+ debug("Rekey has happened - updating saved versions"); -+ -+ if (saved_name != GSS_C_NO_NAME) -+ gss_release_name(&minor, &saved_name); -+ -+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, -+ &saved_name, &saved_lifetime, NULL, NULL); -+ -+ if (!GSS_ERROR(major)) { -+ saved_mech = ctxt->oid; -+ saved_lifetime+= now; -+ } else { -+ /* Handle the error */ -+ } -+ return 0; -+ } -+ -+ if (now - last_call < 10) -+ return 0; -+ -+ last_call = now; -+ -+ if (saved_mech == GSS_C_NO_OID) -+ return 0; -+ -+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, -+ &name, &lifetime, NULL, NULL); -+ if (major == GSS_S_CREDENTIALS_EXPIRED) -+ return 0; -+ else if (GSS_ERROR(major)) -+ return 0; -+ -+ major = gss_compare_name(&minor, saved_name, name, &equal); -+ gss_release_name(&minor, &name); -+ if (GSS_ERROR(major)) -+ return 0; -+ -+ if (equal && (saved_lifetime < lifetime + now - 10)) -+ return 1; -+ -+ return 0; -+} -+ - #endif /* GSSAPI */ -diff -up openssh-7.0p1/gss-serv.c.gsskex openssh-7.0p1/gss-serv.c ---- openssh-7.0p1/gss-serv.c.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/gss-serv.c 2015-08-12 11:15:43.694548889 +0200 -@@ -45,17 +45,19 @@ - #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, NULL, {NULL, NULL, NULL, NULL}}; -+ GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, {NULL, NULL, NULL}, 0, 0}; - - ssh_gssapi_mech gssapi_null_mech = -- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; -+ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; - - #ifdef KRB5 - extern ssh_gssapi_mech gssapi_kerberos_mech; -@@ -142,6 +144,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss - } - - /* 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) - { -@@ -151,7 +176,9 @@ ssh_gssapi_supported_oids(gss_OID_set *o - gss_OID_set supported; - - gss_create_empty_oid_set(&min_status, oidset); -- gss_indicate_mechs(&min_status, &supported); -+ -+ 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, -@@ -277,8 +304,48 @@ 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); -+ } - -- gss_buffer_desc ename; -+ 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; - -@@ -293,6 +360,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g - 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); -@@ -310,6 +384,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g - 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; -@@ -320,11 +396,20 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g - 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); -+ krb5_ccache ccache = NULL; -+ krb5_error_code problem; -+ -+ if (gssapi_client.store.data != NULL) { -+ if ((problem = krb5_cc_resolve(gssapi_client.store.data, gssapi_client.store.envval, &ccache))) { -+ debug("%s: krb5_cc_resolve(): %.100s", __func__, -+ krb5_get_err_text(gssapi_client.store.data, problem)); -+ } else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) { -+ debug("%s: krb5_cc_resolve(): %.100s", __func__, -+ krb5_get_err_text(gssapi_client.store.data, problem)); -+ } else { -+ krb5_free_context(gssapi_client.store.data); -+ gssapi_client.store.data = NULL; -+ } - } - } - -@@ -357,7 +442,7 @@ ssh_gssapi_do_child(char ***envp, u_int - - /* Privileged */ - int --ssh_gssapi_userok(char *user) -+ssh_gssapi_userok(char *user, struct passwd *pw) - { - OM_uint32 lmin; - -@@ -367,9 +452,11 @@ ssh_gssapi_userok(char *user) - return 0; - } - if (gssapi_client.mech && gssapi_client.mech->userok) -- if ((*gssapi_client.mech->userok)(&gssapi_client, user)) -+ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { -+ gssapi_client.used = 1; -+ gssapi_client.store.owner = pw; - return 1; -- else { -+ } else { - /* Destroy delegated credentials if userok fails */ - gss_release_buffer(&lmin, &gssapi_client.displayname); - gss_release_buffer(&lmin, &gssapi_client.exportedname); -@@ -383,14 +470,90 @@ ssh_gssapi_userok(char *user) - return (0); - } - --/* Privileged */ --OM_uint32 --ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) -+/* 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) - { -- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, -- gssbuf, gssmic, NULL); -+ return (PAM_CONV_ERR); -+} -+#endif - -- return (ctx->major); -+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 -up openssh-7.0p1/gss-serv-krb5.c.gsskex openssh-7.0p1/gss-serv-krb5.c ---- openssh-7.0p1/gss-serv-krb5.c.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/gss-serv-krb5.c 2015-08-12 11:15:43.694548889 +0200 -@@ -121,7 +121,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl - krb5_error_code problem; - krb5_principal princ; - OM_uint32 maj_status, min_status; -- int len; -+ const char *new_ccname, *new_cctype; - const char *errmsg; - - if (client->creds == NULL) { -@@ -181,11 +181,26 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl - return; - } - -- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); -+ new_cctype = krb5_cc_get_type(krb_context, ccache); -+ new_ccname = krb5_cc_get_name(krb_context, ccache); -+ - client->store.envvar = "KRB5CCNAME"; -- len = strlen(client->store.filename) + 6; -- client->store.envval = xmalloc(len); -- snprintf(client->store.envval, len, "FILE:%s", client->store.filename); -+#ifdef USE_CCAPI -+ xasprintf(&client->store.envval, "API:%s", new_ccname); -+ client->store.filename = NULL; -+#else -+ if (new_ccname[0] == ':') -+ new_ccname++; -+ xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname); -+ if (strcmp(new_cctype, "DIR") == 0) { -+ char *p; -+ p = strrchr(client->store.envval, '/'); -+ if (p) -+ *p = '\0'; -+ } -+ if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0)) -+ client->store.filename = xstrdup(new_ccname); -+#endif - - #ifdef USE_PAM - if (options.use_pam) -@@ -194,9 +209,76 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl - - krb5_cc_close(krb_context, ccache); - -+ client->store.data = krb_context; -+ - return; - } - -+int -+ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, -+ ssh_gssapi_client *client) -+{ -+ krb5_ccache ccache = NULL; -+ krb5_principal principal = NULL; -+ char *name = NULL; -+ krb5_error_code problem; -+ OM_uint32 maj_status, min_status; -+ -+ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) { -+ logit("krb5_cc_resolve(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ return 0; -+ } -+ -+ /* Find out who the principal in this cache is */ -+ if ((problem = krb5_cc_get_principal(krb_context, ccache, -+ &principal))) { -+ logit("krb5_cc_get_principal(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ if ((problem = krb5_unparse_name(krb_context, principal, &name))) { -+ logit("krb5_unparse_name(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ krb5_free_principal(krb_context, principal); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ -+ if (strcmp(name,client->exportedname.value)!=0) { -+ debug("Name in local credentials cache differs. Not storing"); -+ krb5_free_principal(krb_context, principal); -+ krb5_cc_close(krb_context, ccache); -+ krb5_free_unparsed_name(krb_context, name); -+ return 0; -+ } -+ krb5_free_unparsed_name(krb_context, name); -+ -+ /* Name matches, so lets get on with it! */ -+ -+ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) { -+ logit("krb5_cc_initialize(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ krb5_free_principal(krb_context, principal); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ krb5_free_principal(krb_context, principal); -+ -+ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds, -+ ccache))) { -+ logit("gss_krb5_copy_ccache() failed. Sorry!"); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ return 1; -+} -+ - ssh_gssapi_mech gssapi_kerberos_mech = { - "toWM5Slw5Ew8Mqkay+al2g==", - "Kerberos", -@@ -204,7 +286,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = { - NULL, - &ssh_gssapi_krb5_userok, - NULL, -- &ssh_gssapi_krb5_storecreds -+ &ssh_gssapi_krb5_storecreds, -+ &ssh_gssapi_krb5_updatecreds - }; - - #endif /* KRB5 */ -diff -up openssh-7.0p1/kex.c.gsskex openssh-7.0p1/kex.c ---- openssh-7.0p1/kex.c.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/kex.c 2015-08-12 11:15:43.695548887 +0200 -@@ -55,6 +55,10 @@ - #include "sshbuf.h" - #include "digest.h" - -+#ifdef GSSAPI -+#include "ssh-gss.h" -+#endif -+ - #if OPENSSL_VERSION_NUMBER >= 0x00907000L - # if defined(HAVE_EVP_SHA256) - # define evp_ssh_sha256 EVP_sha256 -@@ -95,6 +99,11 @@ static const struct kexalg kexalgs[] = { - #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL) - { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, - #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ -+#ifdef GSSAPI -+ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, -+ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, -+ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, -+#endif - { NULL, -1, -1, -1}, - }; - -@@ -128,6 +137,12 @@ kex_alg_by_name(const char *name) - for (k = kexalgs; k->name != NULL; k++) { - if (strcmp(k->name, name) == 0) - return k; -+#ifdef GSSAPI -+ if (strncmp(name, "gss-", 4) == 0) { -+ if (strncmp(k->name, name, strlen(k->name)) == 0) -+ return k; -+ } -+#endif - } - return NULL; - } -diff -up openssh-7.0p1/kexgssc.c.gsskex openssh-7.0p1/kexgssc.c ---- openssh-7.0p1/kexgssc.c.gsskex 2015-08-12 11:15:43.695548887 +0200 -+++ openssh-7.0p1/kexgssc.c 2015-08-12 11:15:43.695548887 +0200 -@@ -0,0 +1,338 @@ -+/* -+ * 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 "includes.h" -+ -+#include -+#include -+ -+#include -+ -+#include "xmalloc.h" -+#include "buffer.h" -+#include "ssh2.h" -+#include "key.h" -+#include "cipher.h" -+#include "kex.h" -+#include "log.h" -+#include "packet.h" -+#include "dh.h" -+#include "digest.h" -+ -+#include "ssh-gss.h" -+ -+int -+kexgss_client(struct ssh *ssh) { -+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; -+ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; -+ Gssctxt *ctxt; -+ OM_uint32 maj_status, min_status, ret_flags; -+ u_int klen, kout, slen = 0, strlen; -+ DH *dh; -+ BIGNUM *dh_server_pub = NULL; -+ BIGNUM *shared_secret = NULL; -+ BIGNUM *p = NULL; -+ BIGNUM *g = NULL; -+ u_char *kbuf; -+ u_char *serverhostkey = NULL; -+ u_char *empty = ""; -+ char *msg; -+ char *lang; -+ int type = 0; -+ int first = 1; -+ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; -+ u_char hash[SSH_DIGEST_MAX_LENGTH]; -+ size_t hashlen; -+ -+ /* Initialise our GSSAPI world */ -+ ssh_gssapi_build_ctx(&ctxt); -+ if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type) -+ == GSS_C_NO_OID) -+ fatal("Couldn't identify host exchange"); -+ -+ if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host)) -+ fatal("Couldn't import hostname"); -+ -+ if (ssh->kex->gss_client && -+ ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client)) -+ fatal("Couldn't acquire client credentials"); -+ -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ dh = dh_new_group1(); -+ break; -+ case KEX_GSS_GRP14_SHA1: -+ dh = dh_new_group14(); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ debug("Doing group exchange\n"); -+ nbits = dh_estimate(ssh->kex->we_need * 8); -+ packet_start(SSH2_MSG_KEXGSS_GROUPREQ); -+ packet_put_int(min); -+ packet_put_int(nbits); -+ packet_put_int(max); -+ -+ packet_send(); -+ -+ packet_read_expect(SSH2_MSG_KEXGSS_GROUP); -+ -+ if ((p = BN_new()) == NULL) -+ fatal("BN_new() failed"); -+ packet_get_bignum2(p); -+ if ((g = BN_new()) == NULL) -+ fatal("BN_new() failed"); -+ packet_get_bignum2(g); -+ packet_check_eom(); -+ -+ if (BN_num_bits(p) < min || BN_num_bits(p) > max) -+ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", -+ min, BN_num_bits(p), max); -+ -+ dh = dh_new_group(g, p); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ /* Step 1 - e is dh->pub_key */ -+ dh_gen_key(dh, ssh->kex->we_need * 8); -+ -+ /* This is f, we initialise it now to make life easier */ -+ dh_server_pub = BN_new(); -+ if (dh_server_pub == NULL) -+ fatal("dh_server_pub == NULL"); -+ -+ token_ptr = GSS_C_NO_BUFFER; -+ -+ do { -+ debug("Calling gss_init_sec_context"); -+ -+ maj_status = ssh_gssapi_init_ctx(ctxt, -+ ssh->kex->gss_deleg_creds, token_ptr, &send_tok, -+ &ret_flags); -+ -+ if (GSS_ERROR(maj_status)) { -+ if (send_tok.length != 0) { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ } -+ fatal("gss_init_context failed"); -+ } -+ -+ /* If we've got an old receive buffer get rid of it */ -+ if (token_ptr != GSS_C_NO_BUFFER) -+ free(recv_tok.value); -+ -+ if (maj_status == GSS_S_COMPLETE) { -+ /* If mutual state flag is not true, kex fails */ -+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -+ fatal("Mutual authentication failed"); -+ -+ /* If integ avail flag is not true kex fails */ -+ if (!(ret_flags & GSS_C_INTEG_FLAG)) -+ fatal("Integrity check failed"); -+ } -+ -+ /* -+ * If we have data to send, then the last message that we -+ * received cannot have been a 'complete'. -+ */ -+ if (send_tok.length != 0) { -+ if (first) { -+ packet_start(SSH2_MSG_KEXGSS_INIT); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ packet_put_bignum2(dh->pub_key); -+ first = 0; -+ } else { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ } -+ packet_send(); -+ gss_release_buffer(&min_status, &send_tok); -+ -+ /* If we've sent them data, they should reply */ -+ do { -+ type = packet_read(); -+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { -+ debug("Received KEXGSS_HOSTKEY"); -+ if (serverhostkey) -+ fatal("Server host key received more than once"); -+ serverhostkey = -+ packet_get_string(&slen); -+ } -+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); -+ -+ switch (type) { -+ case SSH2_MSG_KEXGSS_CONTINUE: -+ debug("Received GSSAPI_CONTINUE"); -+ if (maj_status == GSS_S_COMPLETE) -+ fatal("GSSAPI Continue received from server when complete"); -+ recv_tok.value = packet_get_string(&strlen); -+ recv_tok.length = strlen; -+ break; -+ case SSH2_MSG_KEXGSS_COMPLETE: -+ debug("Received GSSAPI_COMPLETE"); -+ packet_get_bignum2(dh_server_pub); -+ msg_tok.value = packet_get_string(&strlen); -+ msg_tok.length = strlen; -+ -+ /* Is there a token included? */ -+ if (packet_get_char()) { -+ recv_tok.value= -+ packet_get_string(&strlen); -+ recv_tok.length = strlen; -+ /* If we're already complete - protocol error */ -+ if (maj_status == GSS_S_COMPLETE) -+ packet_disconnect("Protocol error: received token when complete"); -+ } else { -+ /* No token included */ -+ if (maj_status != GSS_S_COMPLETE) -+ packet_disconnect("Protocol error: did not receive final token"); -+ } -+ break; -+ case SSH2_MSG_KEXGSS_ERROR: -+ debug("Received Error"); -+ maj_status = packet_get_int(); -+ min_status = packet_get_int(); -+ msg = packet_get_string(NULL); -+ lang = packet_get_string(NULL); -+ fatal("GSSAPI Error: \n%.400s",msg); -+ default: -+ packet_disconnect("Protocol error: didn't expect packet type %d", -+ type); -+ } -+ token_ptr = &recv_tok; -+ } else { -+ /* No data, and not complete */ -+ if (maj_status != GSS_S_COMPLETE) -+ fatal("Not complete, and no token output"); -+ } -+ } while (maj_status & GSS_S_CONTINUE_NEEDED); -+ -+ /* -+ * We _must_ have received a COMPLETE message in reply from the -+ * server, which will have set dh_server_pub and msg_tok -+ */ -+ -+ if (type != SSH2_MSG_KEXGSS_COMPLETE) -+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); -+ -+ /* Check f in range [1, p-1] */ -+ if (!dh_pub_is_valid(dh, dh_server_pub)) -+ packet_disconnect("bad server public DH value"); -+ -+ /* compute K=f^x mod p */ -+ klen = DH_size(dh); -+ kbuf = xmalloc(klen); -+ kout = DH_compute_key(kbuf, dh_server_pub, dh); -+ if ((int)kout < 0) -+ fatal("DH_compute_key: failed"); -+ -+ shared_secret = BN_new(); -+ if (shared_secret == NULL) -+ fatal("kexgss_client: BN_new failed"); -+ -+ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) -+ fatal("kexdh_client: BN_bin2bn failed"); -+ -+ memset(kbuf, 0, klen); -+ free(kbuf); -+ -+ hashlen = sizeof(hash); -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ case KEX_GSS_GRP14_SHA1: -+ kex_dh_hash( ssh->kex->client_version_string, -+ ssh->kex->server_version_string, -+ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), -+ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), -+ (serverhostkey ? serverhostkey : empty), slen, -+ dh->pub_key, /* e */ -+ dh_server_pub, /* f */ -+ shared_secret, /* K */ -+ hash, &hashlen -+ ); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ kexgex_hash( -+ ssh->kex->hash_alg, -+ ssh->kex->client_version_string, -+ ssh->kex->server_version_string, -+ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), -+ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), -+ (serverhostkey ? serverhostkey : empty), slen, -+ min, nbits, max, -+ dh->p, dh->g, -+ dh->pub_key, -+ dh_server_pub, -+ shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ gssbuf.value = hash; -+ gssbuf.length = hashlen; -+ -+ /* Verify that the hash matches the MIC we just got. */ -+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) -+ packet_disconnect("Hash's MIC didn't verify"); -+ -+ free(msg_tok.value); -+ -+ DH_free(dh); -+ if (serverhostkey) -+ free(serverhostkey); -+ BN_clear_free(dh_server_pub); -+ -+ /* save session id */ -+ if (ssh->kex->session_id == NULL) { -+ ssh->kex->session_id_len = hashlen; -+ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); -+ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); -+ } -+ -+ if (ssh->kex->gss_deleg_creds) -+ ssh_gssapi_credentials_updated(ctxt); -+ -+ if (gss_kex_context == NULL) -+ gss_kex_context = ctxt; -+ else -+ ssh_gssapi_delete_ctx(&ctxt); -+ -+ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); -+ BN_clear_free(shared_secret); -+ return kex_send_newkeys(ssh); -+} -+ -+#endif /* GSSAPI */ -diff -up openssh-7.0p1/kexgsss.c.gsskex openssh-7.0p1/kexgsss.c ---- openssh-7.0p1/kexgsss.c.gsskex 2015-08-12 11:15:43.695548887 +0200 -+++ openssh-7.0p1/kexgsss.c 2015-08-12 11:15:43.695548887 +0200 -@@ -0,0 +1,295 @@ -+/* -+ * 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 -+ -+#include -+#include -+ -+#include "xmalloc.h" -+#include "buffer.h" -+#include "ssh2.h" -+#include "key.h" -+#include "cipher.h" -+#include "kex.h" -+#include "log.h" -+#include "packet.h" -+#include "dh.h" -+#include "ssh-gss.h" -+#include "monitor_wrap.h" -+#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ -+#include "servconf.h" -+#include "ssh-gss.h" -+#include "digest.h" -+ -+extern ServerOptions options; -+ -+int -+kexgss_server(struct ssh *ssh) -+{ -+ OM_uint32 maj_status, min_status; -+ -+ /* -+ * Some GSSAPI implementations use the input value of ret_flags (an -+ * output variable) as a means of triggering mechanism specific -+ * features. Initializing it to zero avoids inadvertently -+ * activating this non-standard behaviour. -+ */ -+ -+ OM_uint32 ret_flags = 0; -+ gss_buffer_desc gssbuf, recv_tok, msg_tok; -+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; -+ Gssctxt *ctxt = NULL; -+ u_int slen, klen, kout; -+ u_char *kbuf; -+ DH *dh; -+ int min = -1, max = -1, nbits = -1; -+ BIGNUM *shared_secret = NULL; -+ BIGNUM *dh_client_pub = NULL; -+ int type = 0; -+ gss_OID oid; -+ char *mechs; -+ u_char hash[SSH_DIGEST_MAX_LENGTH]; -+ size_t hashlen; -+ -+ /* Initialise GSSAPI */ -+ -+ /* If we're rekeying, privsep means that some of the private structures -+ * in the GSSAPI code are no longer available. This kludges them back -+ * into life -+ */ -+ if (!ssh_gssapi_oid_table_ok()) -+ if ((mechs = ssh_gssapi_server_mechanisms())) -+ free(mechs); -+ -+ debug2("%s: Identifying %s", __func__, ssh->kex->name); -+ oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type); -+ if (oid == GSS_C_NO_OID) -+ fatal("Unknown gssapi mechanism"); -+ -+ debug2("%s: Acquiring credentials", __func__); -+ -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) -+ fatal("Unable to acquire credentials for the server"); -+ -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ dh = dh_new_group1(); -+ break; -+ case KEX_GSS_GRP14_SHA1: -+ dh = dh_new_group14(); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ debug("Doing group exchange"); -+ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); -+ min = packet_get_int(); -+ nbits = packet_get_int(); -+ max = packet_get_int(); -+ min = MAX(DH_GRP_MIN, min); -+ max = MIN(DH_GRP_MAX, max); -+ packet_check_eom(); -+ if (max < min || nbits < min || max < nbits) -+ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", -+ min, nbits, max); -+ dh = PRIVSEP(choose_dh(min, nbits, max)); -+ if (dh == NULL) -+ packet_disconnect("Protocol error: no matching group found"); -+ -+ packet_start(SSH2_MSG_KEXGSS_GROUP); -+ packet_put_bignum2(dh->p); -+ packet_put_bignum2(dh->g); -+ packet_send(); -+ -+ packet_write_wait(); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ dh_gen_key(dh, ssh->kex->we_need * 8); -+ -+ do { -+ debug("Wait SSH2_MSG_GSSAPI_INIT"); -+ type = packet_read(); -+ switch(type) { -+ case SSH2_MSG_KEXGSS_INIT: -+ if (dh_client_pub != NULL) -+ fatal("Received KEXGSS_INIT after initialising"); -+ recv_tok.value = packet_get_string(&slen); -+ recv_tok.length = slen; -+ -+ if ((dh_client_pub = BN_new()) == NULL) -+ fatal("dh_client_pub == NULL"); -+ -+ packet_get_bignum2(dh_client_pub); -+ -+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ -+ break; -+ case SSH2_MSG_KEXGSS_CONTINUE: -+ recv_tok.value = packet_get_string(&slen); -+ recv_tok.length = slen; -+ break; -+ default: -+ packet_disconnect( -+ "Protocol error: didn't expect packet type %d", -+ type); -+ } -+ -+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, -+ &send_tok, &ret_flags)); -+ -+ free(recv_tok.value); -+ -+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) -+ fatal("Zero length token output when incomplete"); -+ -+ if (dh_client_pub == NULL) -+ fatal("No client public key"); -+ -+ if (maj_status & GSS_S_CONTINUE_NEEDED) { -+ debug("Sending GSSAPI_CONTINUE"); -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, send_tok.length); -+ packet_send(); -+ gss_release_buffer(&min_status, &send_tok); -+ } -+ } while (maj_status & GSS_S_CONTINUE_NEEDED); -+ -+ if (GSS_ERROR(maj_status)) { -+ if (send_tok.length > 0) { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, send_tok.length); -+ packet_send(); -+ } -+ fatal("accept_ctx died"); -+ } -+ -+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -+ fatal("Mutual Authentication flag wasn't set"); -+ -+ if (!(ret_flags & GSS_C_INTEG_FLAG)) -+ fatal("Integrity flag wasn't set"); -+ -+ if (!dh_pub_is_valid(dh, dh_client_pub)) -+ packet_disconnect("bad client public DH value"); -+ -+ klen = DH_size(dh); -+ kbuf = xmalloc(klen); -+ kout = DH_compute_key(kbuf, dh_client_pub, dh); -+ if ((int)kout < 0) -+ fatal("DH_compute_key: failed"); -+ -+ shared_secret = BN_new(); -+ if (shared_secret == NULL) -+ fatal("kexgss_server: BN_new failed"); -+ -+ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) -+ fatal("kexgss_server: BN_bin2bn failed"); -+ -+ memset(kbuf, 0, klen); -+ free(kbuf); -+ -+ hashlen = sizeof(hash); -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ case KEX_GSS_GRP14_SHA1: -+ kex_dh_hash( -+ ssh->kex->client_version_string, ssh->kex->server_version_string, -+ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), -+ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), -+ NULL, 0, /* Change this if we start sending host keys */ -+ dh_client_pub, dh->pub_key, shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ kexgex_hash( -+ ssh->kex->hash_alg, -+ ssh->kex->client_version_string, ssh->kex->server_version_string, -+ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), -+ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), -+ NULL, 0, -+ min, nbits, max, -+ dh->p, dh->g, -+ dh_client_pub, -+ dh->pub_key, -+ shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ BN_clear_free(dh_client_pub); -+ -+ if (ssh->kex->session_id == NULL) { -+ ssh->kex->session_id_len = hashlen; -+ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); -+ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); -+ } -+ -+ gssbuf.value = hash; -+ gssbuf.length = hashlen; -+ -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) -+ fatal("Couldn't get MIC"); -+ -+ packet_start(SSH2_MSG_KEXGSS_COMPLETE); -+ packet_put_bignum2(dh->pub_key); -+ packet_put_string(msg_tok.value,msg_tok.length); -+ -+ if (send_tok.length != 0) { -+ packet_put_char(1); /* true */ -+ packet_put_string(send_tok.value, send_tok.length); -+ } else { -+ packet_put_char(0); /* false */ -+ } -+ packet_send(); -+ -+ gss_release_buffer(&min_status, &send_tok); -+ gss_release_buffer(&min_status, &msg_tok); -+ -+ if (gss_kex_context == NULL) -+ gss_kex_context = ctxt; -+ else -+ ssh_gssapi_delete_ctx(&ctxt); -+ -+ DH_free(dh); -+ -+ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); -+ BN_clear_free(shared_secret); -+ kex_send_newkeys(ssh); -+ -+ /* If this was a rekey, then save out any delegated credentials we -+ * just exchanged. */ -+ if (options.gss_store_rekey) -+ ssh_gssapi_rekey_creds(); -+ return 0; -+} -+#endif /* GSSAPI */ -diff -up openssh-7.0p1/kex.h.gsskex openssh-7.0p1/kex.h ---- openssh-7.0p1/kex.h.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/kex.h 2015-08-12 11:17:44.694354889 +0200 -@@ -93,6 +93,11 @@ enum kex_exchange { - KEX_DH_GEX_SHA256, - KEX_ECDH_SHA2, - KEX_C25519_SHA256, -+#ifdef GSSAPI -+ KEX_GSS_GRP1_SHA1, -+ KEX_GSS_GRP14_SHA1, -+ KEX_GSS_GEX_SHA1, -+#endif - KEX_MAX - }; - -@@ -139,6 +144,12 @@ struct kex { - u_int flags; - int hash_alg; - int ec_nid; -+#ifdef GSSAPI -+ int gss_deleg_creds; -+ int gss_trust_dns; -+ char *gss_host; -+ char *gss_client; -+#endif - char *client_version_string; - char *server_version_string; - char *failed_choice; -@@ -186,6 +197,10 @@ int kexecdh_client(struct ssh *); - int kexecdh_server(struct ssh *); - int kexc25519_client(struct ssh *); - int kexc25519_server(struct ssh *); -+#ifdef GSSAPI -+int kexgss_client(struct ssh *); -+int kexgss_server(struct ssh *); -+#endif - - int kex_dh_hash(const char *, const char *, - const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, -diff -up openssh-7.0p1/Makefile.in.gsskex openssh-7.0p1/Makefile.in ---- openssh-7.0p1/Makefile.in.gsskex 2015-08-12 11:15:43.686548901 +0200 -+++ openssh-7.0p1/Makefile.in 2015-08-12 11:15:43.695548887 +0200 -@@ -90,6 +90,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ - readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ - atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o \ - monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ -+ kexgssc.o \ - msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ - ssh-pkcs11.o smult_curve25519_ref.o \ - poly1305.o chacha.o cipher-chachapoly.o \ -@@ -111,7 +112,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw - auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ - auth2-none.o auth2-passwd.o auth2-pubkey.o \ - monitor_mm.o monitor.o monitor_wrap.o auth-krb5.o \ -- auth2-gss.o gss-serv.o gss-serv-krb5.o \ -+ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \ - loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ - sftp-server.o sftp-common.o \ - roaming_common.o roaming_serv.o \ -diff -up openssh-7.0p1/monitor.c.gsskex openssh-7.0p1/monitor.c ---- openssh-7.0p1/monitor.c.gsskex 2015-08-12 11:15:43.626548998 +0200 -+++ openssh-7.0p1/monitor.c 2015-08-12 11:15:43.696548885 +0200 -@@ -160,6 +160,8 @@ 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 -@@ -240,11 +242,18 @@ struct mon_table mon_dispatch_proto20[] - {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 - {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 - #ifdef WITH_OPENSSL - {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, - #endif -@@ -359,6 +368,10 @@ monitor_child_preauth(Authctxt *_authctx - /* 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; - -@@ -467,6 +480,10 @@ monitor_child_postauth(struct monitor *p - 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); -@@ -1896,6 +1913,13 @@ monitor_apply_keystate(struct monitor *p - # endif - #endif /* WITH_OPENSSL */ - kex->kex[KEX_C25519_SHA256] = kexc25519_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->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; -@@ -1995,6 +2019,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer - 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; - -@@ -2022,6 +2049,9 @@ mm_answer_gss_accept_ctx(int sock, Buffe - 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); -@@ -2039,6 +2069,7 @@ mm_answer_gss_accept_ctx(int sock, Buffe - 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); - } -@@ -2050,6 +2081,9 @@ mm_answer_gss_checkmic(int sock, Buffer - 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); -@@ -2076,7 +2110,11 @@ mm_answer_gss_userok(int sock, Buffer *m - { - int authenticated; - -- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); -+ 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); -@@ -2089,5 +2127,73 @@ mm_answer_gss_userok(int sock, Buffer *m - /* 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); -+ -+ free(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); -+ -+ free(store.filename); -+ free(store.envvar); -+ free(store.envval); -+ -+ buffer_clear(m); -+ buffer_put_int(m, ok); -+ -+ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); -+ -+ return(0); -+} -+ - #endif /* GSSAPI */ - -diff -up openssh-7.0p1/monitor.h.gsskex openssh-7.0p1/monitor.h ---- openssh-7.0p1/monitor.h.gsskex 2015-08-12 11:15:43.626548998 +0200 -+++ openssh-7.0p1/monitor.h 2015-08-12 11:15:43.696548885 +0200 -@@ -60,6 +60,8 @@ enum monitor_reqtype { - #ifdef WITH_SELINUX - MONITOR_REQ_AUTHROLE = 80, - #endif -+ MONITOR_REQ_GSSSIGN = 82, MONITOR_ANS_GSSSIGN = 83, -+ MONITOR_REQ_GSSUPCREDS = 84, MONITOR_ANS_GSSUPCREDS = 85, - - MONITOR_REQ_PAM_START = 100, - MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103, -diff -up openssh-7.0p1/monitor_wrap.c.gsskex openssh-7.0p1/monitor_wrap.c ---- openssh-7.0p1/monitor_wrap.c.gsskex 2015-08-12 11:15:43.626548998 +0200 -+++ openssh-7.0p1/monitor_wrap.c 2015-08-12 11:15:43.697548884 +0200 -@@ -1087,7 +1087,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss - } - - int --mm_ssh_gssapi_userok(char *user) -+mm_ssh_gssapi_userok(char *user, struct passwd *pw) - { - Buffer m; - int authenticated = 0; -@@ -1104,5 +1104,50 @@ mm_ssh_gssapi_userok(char *user) - 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 */ - -diff -up openssh-7.0p1/monitor_wrap.h.gsskex openssh-7.0p1/monitor_wrap.h ---- openssh-7.0p1/monitor_wrap.h.gsskex 2015-08-12 11:15:43.626548998 +0200 -+++ openssh-7.0p1/monitor_wrap.h 2015-08-12 11:15:43.697548884 +0200 -@@ -61,8 +61,10 @@ BIGNUM *mm_auth_rsa_generate_challenge(K - 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); -+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 -diff -up openssh-7.0p1/readconf.c.gsskex openssh-7.0p1/readconf.c ---- openssh-7.0p1/readconf.c.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/readconf.c 2015-08-12 11:15:43.697548884 +0200 -@@ -147,6 +147,8 @@ typedef enum { - oClearAllForwardings, oNoHostAuthenticationForLocalhost, - oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, - oAddressFamily, oGssAuthentication, oGssDelegateCreds, -+ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, -+ oGssServerIdentity, - oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oControlPath, oControlMaster, oControlPersist, - oHashKnownHosts, -@@ -192,10 +194,19 @@ static struct { - { "afstokenpassing", oUnsupported }, - #if defined(GSSAPI) - { "gssapiauthentication", oGssAuthentication }, -+ { "gssapikeyexchange", oGssKeyEx }, - { "gssapidelegatecredentials", oGssDelegateCreds }, -+ { "gssapitrustdns", oGssTrustDns }, -+ { "gssapiclientidentity", oGssClientIdentity }, -+ { "gssapiserveridentity", oGssServerIdentity }, -+ { "gssapirenewalforcesrekey", oGssRenewalRekey }, - #else - { "gssapiauthentication", oUnsupported }, -+ { "gssapikeyexchange", oUnsupported }, - { "gssapidelegatecredentials", oUnsupported }, -+ { "gssapitrustdns", oUnsupported }, -+ { "gssapiclientidentity", oUnsupported }, -+ { "gssapirenewalforcesrekey", oUnsupported }, - #endif - { "fallbacktorsh", oDeprecated }, - { "usersh", oDeprecated }, -@@ -894,10 +905,30 @@ parse_time: - intptr = &options->gss_authentication; - goto parse_flag; - -+ case oGssKeyEx: -+ intptr = &options->gss_keyex; -+ goto parse_flag; -+ - case oGssDelegateCreds: - intptr = &options->gss_deleg_creds; - goto parse_flag; - -+ case oGssTrustDns: -+ intptr = &options->gss_trust_dns; -+ goto parse_flag; -+ -+ case oGssClientIdentity: -+ charptr = &options->gss_client_identity; -+ goto parse_string; -+ -+ case oGssServerIdentity: -+ charptr = &options->gss_server_identity; -+ goto parse_string; -+ -+ case oGssRenewalRekey: -+ intptr = &options->gss_renewal_rekey; -+ goto parse_flag; -+ - case oBatchMode: - intptr = &options->batch_mode; - goto parse_flag; -@@ -1601,7 +1632,12 @@ initialize_options(Options * options) - options->pubkey_authentication = -1; - options->challenge_response_authentication = -1; - options->gss_authentication = -1; -+ options->gss_keyex = -1; - options->gss_deleg_creds = -1; -+ options->gss_trust_dns = -1; -+ options->gss_renewal_rekey = -1; -+ options->gss_client_identity = NULL; -+ options->gss_server_identity = NULL; - options->password_authentication = -1; - options->kbd_interactive_authentication = -1; - options->kbd_interactive_devices = NULL; -@@ -1729,8 +1765,14 @@ fill_default_options(Options * options) - options->challenge_response_authentication = 1; - if (options->gss_authentication == -1) - options->gss_authentication = 0; -+ if (options->gss_keyex == -1) -+ options->gss_keyex = 0; - if (options->gss_deleg_creds == -1) - options->gss_deleg_creds = 0; -+ if (options->gss_trust_dns == -1) -+ options->gss_trust_dns = 0; -+ if (options->gss_renewal_rekey == -1) -+ options->gss_renewal_rekey = 0; - if (options->password_authentication == -1) - options->password_authentication = 1; - if (options->kbd_interactive_authentication == -1) -diff -up openssh-7.0p1/readconf.h.gsskex openssh-7.0p1/readconf.h ---- openssh-7.0p1/readconf.h.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/readconf.h 2015-08-12 11:15:43.697548884 +0200 -@@ -45,7 +45,12 @@ typedef struct { - int challenge_response_authentication; - /* Try S/Key or TIS, authentication. */ - int gss_authentication; /* Try GSS authentication */ -+ int gss_keyex; /* Try GSS key exchange */ - int gss_deleg_creds; /* Delegate GSS credentials */ -+ int gss_trust_dns; /* Trust DNS for GSS canonicalization */ -+ int gss_renewal_rekey; /* Credential renewal forces rekey */ -+ char *gss_client_identity; /* Principal to initiate GSSAPI with */ -+ char *gss_server_identity; /* GSSAPI target principal */ - int password_authentication; /* Try password - * authentication. */ - int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ -diff -up openssh-7.0p1/regress/cert-hostkey.sh.gsskex openssh-7.0p1/regress/cert-hostkey.sh ---- openssh-7.0p1/regress/cert-hostkey.sh.gsskex 2015-08-12 11:15:43.698548882 +0200 -+++ openssh-7.0p1/regress/cert-hostkey.sh 2015-08-12 11:16:52.511438554 +0200 -@@ -46,7 +46,7 @@ touch $OBJ/host_revoked_plain - touch $OBJ/host_revoked_cert - cp $OBJ/host_ca_key.pub $OBJ/host_revoked_ca - --PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` -+PLAIN_TYPES=`$SSH -Q key-plain | grep -v null | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` - - # Prepare certificate, plain key and CA KRLs - ${SSHKEYGEN} -kf $OBJ/host_krl_empty || fatal "KRL init failed" -diff -up openssh-7.0p1/regress/cert-userkey.sh.gsskex openssh-7.0p1/regress/cert-userkey.sh ---- openssh-7.0p1/regress/cert-userkey.sh.gsskex 2015-08-12 11:15:43.698548882 +0200 -+++ openssh-7.0p1/regress/cert-userkey.sh 2015-08-12 11:20:30.110089677 +0200 -@@ -7,7 +7,7 @@ rm -f $OBJ/authorized_keys_$USER $OBJ/us - cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak - cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak - --PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` -+PLAIN_TYPES=`$SSH -Q key-plain | grep -v null | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` - - kname() { - n=`echo "$1" | sed 's/^dsa/ssh-dss/;s/^rsa/ssh-rsa/;s/^ed/ssh-ed/'` -diff -up openssh-7.0p1/regress/kextype.sh.gsskex openssh-7.0p1/regress/kextype.sh ---- openssh-7.0p1/regress/kextype.sh.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/regress/kextype.sh 2015-08-12 11:15:43.698548882 +0200 -@@ -14,6 +14,9 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/ssh - - tries="1 2 3 4" - for k in `${SSH} -Q kex`; do -+ if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o $k = "gss-group14-sha1-" ]; then -+ continue -+ fi - verbose "kex $k" - for i in $tries; do - ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true -diff -up openssh-7.0p1/regress/rekey.sh.gsskex openssh-7.0p1/regress/rekey.sh ---- openssh-7.0p1/regress/rekey.sh.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/regress/rekey.sh 2015-08-12 11:15:43.698548882 +0200 -@@ -38,6 +38,9 @@ increase_datafile_size 300 - - opts="" - for i in `${SSH} -Q kex`; do -+ if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o $i = "gss-group14-sha1-" ]; then -+ continue -+ fi - opts="$opts KexAlgorithms=$i" - done - for i in `${SSH} -Q cipher`; do -@@ -56,6 +59,9 @@ done - if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then - for c in `${SSH} -Q cipher-auth`; do - for kex in `${SSH} -Q kex`; do -+ if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o $kex = "gss-group14-sha1-" ]; then -+ continue -+ fi - verbose "client rekey $c $kex" - ssh_data_rekeying "KexAlgorithms=$kex" -oRekeyLimit=256k -oCiphers=$c - done -diff -up openssh-7.0p1/servconf.c.gsskex openssh-7.0p1/servconf.c ---- openssh-7.0p1/servconf.c.gsskex 2015-08-12 11:15:43.676548918 +0200 -+++ openssh-7.0p1/servconf.c 2015-08-12 11:22:32.686893730 +0200 -@@ -117,8 +117,10 @@ initialize_server_options(ServerOptions - options->kerberos_ticket_cleanup = -1; - options->kerberos_get_afs_token = -1; - options->gss_authentication=-1; -+ options->gss_keyex = -1; - options->gss_cleanup_creds = -1; - options->gss_strict_acceptor = -1; -+ options->gss_store_rekey = -1; - options->password_authentication = -1; - options->kbd_interactive_authentication = -1; - options->challenge_response_authentication = -1; -@@ -276,10 +278,14 @@ fill_default_server_options(ServerOption - options->kerberos_get_afs_token = 0; - if (options->gss_authentication == -1) - options->gss_authentication = 0; -+ if (options->gss_keyex == -1) -+ options->gss_keyex = 0; - if (options->gss_cleanup_creds == -1) - options->gss_cleanup_creds = 1; - if (options->gss_strict_acceptor == -1) - options->gss_strict_acceptor = 0; -+ if (options->gss_store_rekey == -1) -+ options->gss_store_rekey = 0; - if (options->password_authentication == -1) - options->password_authentication = 1; - if (options->kbd_interactive_authentication == -1) -@@ -415,7 +421,7 @@ typedef enum { - sHostKeyAlgorithms, - sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, - sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, -- sAcceptEnv, sPermitTunnel, -+ sGssKeyEx, sGssStoreRekey, sAcceptEnv, sPermitTunnel, - sMatch, sPermitOpen, sForceCommand, sChrootDirectory, - sUsePrivilegeSeparation, sAllowAgentForwarding, - sHostCertificate, -@@ -489,11 +495,17 @@ static struct { - { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, - { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, - { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, -+ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, -+ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, - #else - { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, - { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, - { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, -+ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, -+ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, - #endif -+ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, -+ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, - { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, - { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, - { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, -@@ -1235,6 +1247,10 @@ process_server_config_line(ServerOptions - intptr = &options->gss_authentication; - goto parse_flag; - -+ case sGssKeyEx: -+ intptr = &options->gss_keyex; -+ goto parse_flag; -+ - case sGssCleanupCreds: - intptr = &options->gss_cleanup_creds; - goto parse_flag; -@@ -1243,6 +1259,10 @@ process_server_config_line(ServerOptions - intptr = &options->gss_strict_acceptor; - goto parse_flag; - -+ case sGssStoreRekey: -+ intptr = &options->gss_store_rekey; -+ goto parse_flag; -+ - case sPasswordAuthentication: - intptr = &options->password_authentication; - goto parse_flag; -@@ -2255,6 +2275,9 @@ dump_config(ServerOptions *o) - #ifdef GSSAPI - dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); - dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); -+ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); -+ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); -+ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); - #endif - dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); - dump_cfg_fmtint(sKbdInteractiveAuthentication, -diff -up openssh-7.0p1/servconf.h.gsskex openssh-7.0p1/servconf.h ---- openssh-7.0p1/servconf.h.gsskex 2015-08-12 11:15:43.676548918 +0200 -+++ openssh-7.0p1/servconf.h 2015-08-12 11:15:43.700548879 +0200 -@@ -118,8 +118,10 @@ typedef struct { - int kerberos_get_afs_token; /* If true, try to get AFS token if - * authenticated with Kerberos. */ - int gss_authentication; /* If true, permit GSSAPI authentication */ -+ int gss_keyex; /* If true, permit GSSAPI key exchange */ - int gss_cleanup_creds; /* If true, destroy cred cache on logout */ - int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ -+ int gss_store_rekey; - int password_authentication; /* If true, permit password - * authentication. */ - int kbd_interactive_authentication; /* If true, permit */ -diff -up openssh-7.0p1/ssh_config.5.gsskex openssh-7.0p1/ssh_config.5 ---- openssh-7.0p1/ssh_config.5.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/ssh_config.5 2015-08-12 11:15:43.700548879 +0200 -@@ -749,11 +749,43 @@ Specifies whether user authentication ba - The default is - .Dq no . - Note that this option applies to protocol version 2 only. -+.It Cm GSSAPIKeyExchange -+Specifies whether key exchange based on GSSAPI may be used. When using -+GSSAPI key exchange the server need not have a host key. -+The default is -+.Dq no . -+Note that this option applies to protocol version 2 only. -+.It Cm GSSAPIClientIdentity -+If set, specifies the GSSAPI client identity that ssh should use when -+connecting to the server. The default is unset, which means that the default -+identity will be used. -+.It Cm GSSAPIServerIdentity -+If set, specifies the GSSAPI server identity that ssh should expect when -+connecting to the server. The default is unset, which means that the -+expected GSSAPI server identity will be determined from the target -+hostname. - .It Cm GSSAPIDelegateCredentials - Forward (delegate) credentials to the server. - The default is - .Dq no . --Note that this option applies to protocol version 2 only. -+Note that this option applies to protocol version 2 connections using GSSAPI. -+.It Cm GSSAPIRenewalForcesRekey -+If set to -+.Dq yes -+then renewal of the client's GSSAPI credentials will force the rekeying of the -+ssh connection. With a compatible server, this can delegate the renewed -+credentials to a session on the server. -+The default is -+.Dq no . -+.It Cm GSSAPITrustDns -+Set to -+.Dq yes to indicate that the DNS is trusted to securely canonicalize -+the name of the host being connected to. If -+.Dq no, the hostname entered on the -+command line will be passed untouched to the GSSAPI library. -+The default is -+.Dq no . -+This option only applies to protocol version 2 connections using GSSAPI. - .It Cm HashKnownHosts - Indicates that - .Xr ssh 1 -diff -up openssh-7.0p1/ssh_config.gsskex openssh-7.0p1/ssh_config ---- openssh-7.0p1/ssh_config.gsskex 2015-08-12 11:15:43.667548932 +0200 -+++ openssh-7.0p1/ssh_config 2015-08-12 11:15:43.700548879 +0200 -@@ -26,6 +26,8 @@ - # HostbasedAuthentication no - # GSSAPIAuthentication no - # GSSAPIDelegateCredentials no -+# GSSAPIKeyExchange no -+# GSSAPITrustDNS no - # BatchMode no - # CheckHostIP yes - # AddressFamily any -diff -up openssh-7.0p1/sshconnect2.c.gsskex openssh-7.0p1/sshconnect2.c ---- openssh-7.0p1/sshconnect2.c.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/sshconnect2.c 2015-08-12 11:25:12.486644393 +0200 -@@ -160,9 +160,34 @@ ssh_kex2(char *host, struct sockaddr *ho - struct kex *kex; - int r; - -+#ifdef GSSAPI -+ char *orig = NULL, *gss = NULL; -+ char *gss_host = NULL; -+#endif -+ - xxx_host = host; - xxx_hostaddr = hostaddr; - -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ /* Add the GSSAPI mechanisms currently supported on this -+ * client to the key exchange algorithm proposal */ -+ orig = options.kex_algorithms; -+ -+ if (options.gss_trust_dns) -+ gss_host = (char *)get_canonical_hostname(1); -+ else -+ gss_host = host; -+ -+ gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity); -+ if (gss) { -+ debug("Offering GSSAPI proposal: %s", gss); -+ xasprintf(&options.kex_algorithms, -+ "%s,%s", gss, orig); -+ } -+ } -+#endif -+ - myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( - options.kex_algorithms); - myproposal[PROPOSAL_ENC_ALGS_CTOS] = -@@ -193,6 +218,17 @@ ssh_kex2(char *host, struct sockaddr *ho - order_hostkeyalgs(host, hostaddr, port)); - } - -+#ifdef GSSAPI -+ /* If we've got GSSAPI algorithms, then we also support the -+ * 'null' hostkey, as a last resort */ -+ if (options.gss_keyex && gss) { -+ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; -+ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], -+ "%s,null", orig); -+ free(gss); -+ } -+#endif -+ - if (options.rekey_limit || options.rekey_interval) - packet_set_rekey_limits((u_int32_t)options.rekey_limit, - (time_t)options.rekey_interval); -@@ -210,11 +246,31 @@ ssh_kex2(char *host, struct sockaddr *ho - kex->kex[KEX_ECDH_SHA2] = kexecdh_client; - # endif - #endif -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; -+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; -+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; -+ } -+#endif - kex->kex[KEX_C25519_SHA256] = kexc25519_client; - kex->client_version_string=client_version_string; - kex->server_version_string=server_version_string; - kex->verify_host_key=&verify_host_key_callback; - -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ kex->gss_deleg_creds = options.gss_deleg_creds; -+ kex->gss_trust_dns = options.gss_trust_dns; -+ kex->gss_client = options.gss_client_identity; -+ if (options.gss_server_identity) { -+ kex->gss_host = options.gss_server_identity; -+ } else { -+ kex->gss_host = gss_host; -+ } -+ } -+#endif -+ - dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); - - if (options.use_roaming && !kex->roaming) { -@@ -306,6 +362,7 @@ int input_gssapi_token(int type, u_int32 - int input_gssapi_hash(int type, u_int32_t, void *); - int input_gssapi_error(int, u_int32_t, void *); - int input_gssapi_errtok(int, u_int32_t, void *); -+int userauth_gsskeyex(Authctxt *authctxt); - #endif - - void userauth(Authctxt *, char *); -@@ -321,6 +378,11 @@ static char *authmethods_get(void); - - Authmethod authmethods[] = { - #ifdef GSSAPI -+ {"gssapi-keyex", -+ userauth_gsskeyex, -+ NULL, -+ &options.gss_authentication, -+ NULL}, - {"gssapi-with-mic", - userauth_gssapi, - NULL, -@@ -627,19 +689,31 @@ userauth_gssapi(Authctxt *authctxt) - static u_int mech = 0; - OM_uint32 min; - int ok = 0; -+ const char *gss_host; -+ -+ if (options.gss_server_identity) -+ gss_host = options.gss_server_identity; -+ else if (options.gss_trust_dns) -+ gss_host = get_canonical_hostname(1); -+ else -+ gss_host = authctxt->host; - - /* Try one GSSAPI method at a time, rather than sending them all at - * once. */ - - if (gss_supported == NULL) -- gss_indicate_mechs(&min, &gss_supported); -+ if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) { -+ gss_supported = NULL; -+ return 0; -+ } - - /* Check to see if the mechanism is usable before we offer it */ - while (mech < gss_supported->count && !ok) { - /* My DER encoding requires length<128 */ - if (gss_supported->elements[mech].length < 128 && - ssh_gssapi_check_mechanism(&gssctxt, -- &gss_supported->elements[mech], authctxt->host)) { -+ &gss_supported->elements[mech], gss_host, -+ options.gss_client_identity)) { - ok = 1; /* Mechanism works */ - } else { - mech++; -@@ -736,8 +810,8 @@ input_gssapi_response(int type, u_int32_ - { - Authctxt *authctxt = ctxt; - Gssctxt *gssctxt; -- int oidlen; -- char *oidv; -+ u_int oidlen; -+ u_char *oidv; - - if (authctxt == NULL) - fatal("input_gssapi_response: no authentication context"); -@@ -850,6 +924,48 @@ input_gssapi_error(int type, u_int32_t p - free(lang); - return 0; - } -+ -+int -+userauth_gsskeyex(Authctxt *authctxt) -+{ -+ Buffer b; -+ gss_buffer_desc gssbuf; -+ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; -+ OM_uint32 ms; -+ -+ static int attempt = 0; -+ if (attempt++ >= 1) -+ return (0); -+ -+ if (gss_kex_context == NULL) { -+ debug("No valid Key exchange context"); -+ return (0); -+ } -+ -+ ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, -+ "gssapi-keyex"); -+ -+ gssbuf.value = buffer_ptr(&b); -+ gssbuf.length = buffer_len(&b); -+ -+ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { -+ buffer_free(&b); -+ return (0); -+ } -+ -+ packet_start(SSH2_MSG_USERAUTH_REQUEST); -+ packet_put_cstring(authctxt->server_user); -+ packet_put_cstring(authctxt->service); -+ packet_put_cstring(authctxt->method->name); -+ packet_put_string(mic.value, mic.length); -+ packet_send(); -+ -+ buffer_free(&b); -+ gss_release_buffer(&ms, &mic); -+ -+ return (1); -+} -+ - #endif /* GSSAPI */ - - int -diff -up openssh-7.0p1/sshd.c.gsskex openssh-7.0p1/sshd.c ---- openssh-7.0p1/sshd.c.gsskex 2015-08-12 11:15:43.679548913 +0200 -+++ openssh-7.0p1/sshd.c 2015-08-12 11:15:43.702548876 +0200 -@@ -1043,8 +1043,9 @@ notify_hostkeys(struct ssh *ssh) - } - debug3("%s: sent %d hostkeys", __func__, nkeys); - if (nkeys == 0) -- fatal("%s: no hostkeys", __func__); -- packet_send(); -+ debug3("%s: no hostkeys", __func__); -+ else -+ packet_send(); - sshbuf_free(buf); - } - -@@ -1843,10 +1843,13 @@ main(int ac, char **av) - logit("Disabling protocol version 1. Could not load host key"); - options.protocol &= ~SSH_PROTO_1; - } -+#ifndef GSSAPI -+ /* The GSSAPI key exchange can run without a host key */ - if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { - logit("Disabling protocol version 2. Could not load host key"); - options.protocol &= ~SSH_PROTO_2; - } -+#endif - if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { - logit("sshd: no hostkeys available -- exiting."); - exit(1); -@@ -2582,6 +2585,48 @@ do_ssh2_kex(void) - myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( - list_hostkey_types()); - -+#ifdef GSSAPI -+ { -+ char *orig; -+ char *gss = NULL; -+ char *newstr = NULL; -+ orig = myproposal[PROPOSAL_KEX_ALGS]; -+ -+ /* -+ * If we don't have a host key, then there's no point advertising -+ * the other key exchange algorithms -+ */ -+ -+ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) -+ orig = NULL; -+ -+ if (options.gss_keyex) -+ gss = ssh_gssapi_server_mechanisms(); -+ else -+ gss = NULL; -+ -+ if (gss && orig) -+ xasprintf(&newstr, "%s,%s", gss, orig); -+ else if (gss) -+ newstr = gss; -+ else if (orig) -+ newstr = orig; -+ -+ /* -+ * If we've got GSSAPI mechanisms, then we've got the 'null' host -+ * key alg, but we can't tell people about it unless its the only -+ * host key algorithm we support -+ */ -+ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) -+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; -+ -+ if (newstr) -+ myproposal[PROPOSAL_KEX_ALGS] = newstr; -+ else -+ fatal("No supported key exchange algorithms"); -+ } -+#endif -+ - /* start key exchange */ - if ((r = kex_setup(active_state, myproposal)) != 0) - fatal("kex_setup: %s", ssh_err(r)); -@@ -2596,6 +2641,13 @@ do_ssh2_kex(void) - # endif - #endif - kex->kex[KEX_C25519_SHA256] = kexc25519_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->client_version_string=client_version_string; - kex->server_version_string=server_version_string; -diff -up openssh-7.0p1/sshd_config.5.gsskex openssh-7.0p1/sshd_config.5 ---- openssh-7.0p1/sshd_config.5.gsskex 2015-08-12 11:15:43.677548916 +0200 -+++ openssh-7.0p1/sshd_config.5 2015-08-12 11:15:43.702548876 +0200 -@@ -621,6 +621,12 @@ Specifies whether user authentication ba - The default is - .Dq no . - Note that this option applies to protocol version 2 only. -+.It Cm GSSAPIKeyExchange -+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange -+doesn't rely on ssh keys to verify host identity. -+The default is -+.Dq no . -+Note that this option applies to protocol version 2 only. - .It Cm GSSAPICleanupCredentials - Specifies whether to automatically destroy the user's credentials cache - on logout. -@@ -642,6 +648,11 @@ machine's default store. - This facility is provided to assist with operation on multi homed machines. - The default is - .Dq yes . -+.It Cm GSSAPIStoreCredentialsOnRekey -+Controls whether the user's GSSAPI credentials should be updated following a -+successful connection rekeying. This option can be used to accepted renewed -+or updated credentials from a compatible client. The default is -+.Dq no . - .It Cm HostbasedAcceptedKeyTypes - Specifies the key types that will be accepted for hostbased authentication - as a comma-separated pattern list. -diff -up openssh-7.0p1/sshd_config.gsskex openssh-7.0p1/sshd_config ---- openssh-7.0p1/sshd_config.gsskex 2015-08-12 11:15:43.679548913 +0200 -+++ openssh-7.0p1/sshd_config 2015-08-12 11:15:43.702548876 +0200 -@@ -91,6 +91,8 @@ ChallengeResponseAuthentication no - # GSSAPI options - GSSAPIAuthentication yes - GSSAPICleanupCredentials no -+#GSSAPIStrictAcceptorCheck yes -+#GSSAPIKeyExchange no - - # Set this to 'yes' to enable PAM authentication, account processing, - # and session processing. If this is enabled, PAM authentication will -diff -up openssh-7.0p1/ssh-gss.h.gsskex openssh-7.0p1/ssh-gss.h ---- openssh-7.0p1/ssh-gss.h.gsskex 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/ssh-gss.h 2015-08-12 11:15:43.702548876 +0200 -@@ -1,6 +1,6 @@ - /* $OpenBSD: ssh-gss.h,v 1.11 2014/02/26 20:28:44 djm Exp $ */ - /* -- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. -+ * 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 -@@ -61,10 +61,22 @@ - - #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; - -@@ -72,8 +84,11 @@ 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 { -@@ -84,6 +99,7 @@ typedef struct ssh_gssapi_mech_struct { - 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 { -@@ -94,10 +110,11 @@ typedef struct { - gss_OID oid; /* client */ - gss_cred_id_t creds; /* server */ - gss_name_t client; /* server */ -- gss_cred_id_t client_creds; /* 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); -@@ -119,16 +136,32 @@ 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 *); -+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); -+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); -+ -+void ssh_gssapi_rekey_creds(void); - #endif /* GSSAPI */ - - #endif /* _SSH_GSS_H */ - -diff -up openssh-7.1p1/sshkey.c.gsskex openssh-7.1p1/sshkey.c ---- openssh-7.1p1/sshkey.c.gsskex 2015-09-17 15:54:32.135673460 +0200 -+++ openssh-7.1p1/sshkey.c 2015-09-17 15:55:23.014666159 +0200 -@@ -112,6 +112,7 @@ static const struct keytype keytypes[] = - # endif /* OPENSSL_HAS_NISTP521 */ - # endif /* OPENSSL_HAS_ECC */ - #endif /* WITH_OPENSSL */ -+ { "null", "null", KEY_NULL, 0, 0 }, - { NULL, NULL, -1, -1, 0 } - }; - -diff -up openssh-7.1p1/sshkey.h.gsskex openssh-7.1p1/sshkey.h ---- openssh-7.1p1/sshkey.h.gsskex 2015-09-17 15:54:32.135673460 +0200 -+++ openssh-7.1p1/sshkey.h 2015-09-17 15:55:45.885662877 +0200 -@@ -62,6 +62,7 @@ enum sshkey_types { - KEY_DSA_CERT, - KEY_ECDSA_CERT, - KEY_ED25519_CERT, -+ KEY_NULL, - KEY_UNSPEC - }; - -diff --git a/auth.c b/auth.c -index 4d1fbbe..5db39c4 100644 ---- a/auth.c -+++ b/auth.c -@@ -354,6 +354,7 @@ auth_root_allowed(const char *method) - case PERMIT_NO_PASSWD: - if (strcmp(method, "publickey") == 0 || - strcmp(method, "hostbased") == 0 || -+ strcmp(method, "gssapi-keyex") == 0 || - strcmp(method, "gssapi-with-mic") == 0) - return 1; - break; diff --git a/openssh-6.6p1-keycat.patch b/openssh-6.6p1-keycat.patch index 6018554..8d86014 100644 --- a/openssh-6.6p1-keycat.patch +++ b/openssh-6.6p1-keycat.patch @@ -59,8 +59,8 @@ diff -up openssh/Makefile.in.keycat openssh/Makefile.in +ssh-keycat$(EXEEXT): $(LIBCOMPAT) $(SSHDOBJS) libssh.a ssh-keycat.o + $(LD) -o $@ ssh-keycat.o bufaux.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(SSHDLIBS) $(SSHLIBS) + - ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o roaming_dummy.o - $(LD) -o $@ ssh-keyscan.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) @@ -321,6 +325,7 @@ install-files: $(INSTALL) -m 0700 $(STRIP_OPT) ssh-ldap-helper $(DESTDIR)$(SSH_LDAP_HELPER) ; \ diff --git a/openssh-6.6p1-kuserok.patch b/openssh-6.6p1-kuserok.patch index 0e6215d..ebb0196 100644 --- a/openssh-6.6p1-kuserok.patch +++ b/openssh-6.6p1-kuserok.patch @@ -190,8 +190,8 @@ diff -up openssh-7.0p1/servconf.c.kuserok openssh-7.0p1/servconf.c + if (options->use_kuserok == -1) + options->use_kuserok = 1; - if (kex_assemble_names(KEX_SERVER_ENCRYPT, &options->ciphers) != 0 || - kex_assemble_names(KEX_SERVER_MAC, &options->macs) != 0 || + assemble_algorithms(options); + @@ -404,7 +407,7 @@ typedef enum { sKeyRegenerationTime, sPermitRootLogin, sLogFacility, sLogLevel, sRhostsRSAAuthentication, sRSAAuthentication, diff --git a/openssh-6.6p1-privsep-selinux.patch b/openssh-6.6p1-privsep-selinux.patch index d7817e5..3433fd6 100644 --- a/openssh-6.6p1-privsep-selinux.patch +++ b/openssh-6.6p1-privsep-selinux.patch @@ -47,15 +47,6 @@ diff --git a/session.c b/session.c index 2bcf818..b5dc144 100644 --- a/session.c +++ b/session.c -@@ -1532,7 +1532,7 @@ void - do_setusercontext(struct passwd *pw) - { - char *chroot_path, *tmp; --#ifdef USE_LIBIAF -+#if defined(USE_LIBIAF) || defined(WITH_SELINUX) - int doing_chroot = 0; - #endif - @@ -1538,6 +1538,9 @@ do_setusercontext(struct passwd *pw) pw->pw_uid); chroot_path = percent_expand(tmp, "h", pw->pw_dir, @@ -66,22 +57,13 @@ index 2bcf818..b5dc144 100644 safely_chroot(chroot_path, pw->pw_uid); free(tmp); free(chroot_path); -@@ -1557,7 +1557,7 @@ do_setusercontext(struct passwd *pw) - /* Make sure we don't attempt to chroot again */ - free(options.chroot_directory); - options.chroot_directory = NULL; --#ifdef USE_LIBIAF -+#if defined(USE_LIBIAF) || defined(WITH_SELINUX) - doing_chroot = 1; - #endif - } @@ -1565,6 +1568,11 @@ do_setusercontext(struct passwd *pw) /* Permanently switch to the desired uid. */ permanently_set_uid(pw); #endif + +#ifdef WITH_SELINUX -+ if (doing_chroot == 0) ++ if (in_chroot == 0) + sshd_selinux_copy_context(); +#endif } else if (options.chroot_directory != NULL && @@ -119,9 +101,9 @@ index 07f9926..a97f8b7 100644 + ssh_selinux_change_context("sshd_net_t"); +#endif + - /* Change our root directory */ - if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1) - fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR, + /* Demote the child */ + if (getuid() == 0 || geteuid() == 0) { + /* Change our root directory */ @@ -755,6 +755,9 @@ privsep_postauth(Authctxt *authctxt) #ifdef DISABLE_FD_PASSING diff --git a/openssh-6.6p1-role-mls.patch b/openssh-6.6p1-role-mls.patch index 0c9d422..5ec22c4 100644 --- a/openssh-6.6p1-role-mls.patch +++ b/openssh-6.6p1-role-mls.patch @@ -355,7 +355,7 @@ diff -up openssh-6.8p1/monitor_wrap.h.role-mls openssh-6.8p1/monitor_wrap.h +++ openssh-6.8p1/monitor_wrap.h 2015-03-18 11:10:32.343936171 +0100 @@ -42,6 +42,9 @@ int mm_is_monitor(void); DH *mm_choose_dh(int, int, int); - int mm_key_sign(Key *, u_char **, u_int *, const u_char *, u_int); + int mm_key_sign(Key *, u_char **, u_int *, const u_char *, u_int, const char *); void mm_inform_authserv(char *, char *); +#ifdef WITH_SELINUX +void mm_inform_authrole(char *); diff --git a/openssh-6.6p1-set_remote_ipaddr.patch b/openssh-6.6p1-set_remote_ipaddr.patch index 14ff48c..f601e3d 100644 --- a/openssh-6.6p1-set_remote_ipaddr.patch +++ b/openssh-6.6p1-set_remote_ipaddr.patch @@ -59,9 +59,9 @@ diff -up openssh-6.8p1/sshconnect.c.set_remote_ipaddr openssh-6.8p1/sshconnect.c --- openssh-6.8p1/sshconnect.c.set_remote_ipaddr 2015-03-17 06:49:20.000000000 +0100 +++ openssh-6.8p1/sshconnect.c 2015-03-18 12:40:58.096788804 +0100 @@ -65,6 +65,7 @@ - #include "version.h" #include "authfile.h" #include "ssherr.h" + #include "authfd.h" +#include "canohost.h" char *client_version_string = NULL; diff --git a/openssh-6.7p1-audit.patch b/openssh-6.7p1-audit.patch deleted file mode 100644 index 106cda1..0000000 --- a/openssh-6.7p1-audit.patch +++ /dev/null @@ -1,2329 +0,0 @@ -diff -up openssh-7.0p1/audit-bsm.c.audit openssh-7.0p1/audit-bsm.c ---- openssh-7.0p1/audit-bsm.c.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/audit-bsm.c 2015-08-12 11:33:00.409914290 +0200 -@@ -375,10 +375,23 @@ audit_connection_from(const char *host, - #endif - } - --void -+int - audit_run_command(const char *command) - { - /* not implemented */ -+ return 0; -+} -+ -+void -+audit_end_command(int handle, const char *command) -+{ -+ /* not implemented */ -+} -+ -+void -+audit_count_session_open(void) -+{ -+ /* not necessary */ - } - - void -@@ -393,6 +406,12 @@ audit_session_close(struct logininfo *li - /* not implemented */ - } - -+int -+audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv) -+{ -+ /* not implemented */ -+} -+ - void - audit_event(ssh_audit_event_t event) - { -@@ -454,4 +473,40 @@ audit_event(ssh_audit_event_t event) - debug("%s: unhandled event %d", __func__, event); - } - } -+ -+void -+audit_unsupported_body(int what) -+{ -+ /* not implemented */ -+} -+ -+void -+audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, uid_t uid) -+{ -+ /* not implemented */ -+} -+ -+void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) -+{ -+ /* not implemented */ -+} -+ -+void -+audit_destroy_sensitive_data(const char *fp) -+{ -+ /* not implemented */ -+} -+ -+void -+audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) -+{ -+ /* not implemented */ -+} -+ -+void -+audit_generate_ephemeral_server_key(const char *fp) -+{ -+ /* not implemented */ -+} - #endif /* BSM */ -diff -up openssh-7.0p1/audit.c.audit openssh-7.0p1/audit.c ---- openssh-7.0p1/audit.c.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/audit.c 2015-08-12 11:33:00.410914289 +0200 -@@ -28,6 +28,7 @@ - - #include - #include -+#include - - #ifdef SSH_AUDIT_EVENTS - -@@ -36,6 +37,11 @@ - #include "key.h" - #include "hostfile.h" - #include "auth.h" -+#include "ssh-gss.h" -+#include "monitor_wrap.h" -+#include "xmalloc.h" -+#include "misc.h" -+#include "servconf.h" - - /* - * Care must be taken when using this since it WILL NOT be initialized when -@@ -43,6 +49,7 @@ - * audit_event(CONNECTION_ABANDON) is called. Test for NULL before using. - */ - extern Authctxt *the_authctxt; -+extern ServerOptions options; - - /* Maybe add the audit class to struct Authmethod? */ - ssh_audit_event_t -@@ -71,13 +78,10 @@ audit_classify_auth(const char *method) - const char * - audit_username(void) - { -- static const char unknownuser[] = "(unknown user)"; -- static const char invaliduser[] = "(invalid user)"; -+ static const char unknownuser[] = "(unknown)"; - -- if (the_authctxt == NULL || the_authctxt->user == NULL) -+ if (the_authctxt == NULL || the_authctxt->user == NULL || !the_authctxt->valid) - return (unknownuser); -- if (!the_authctxt->valid) -- return (invaliduser); - return (the_authctxt->user); - } - -@@ -111,6 +115,40 @@ audit_event_lookup(ssh_audit_event_t ev) - return(event_lookup[i].name); - } - -+void -+audit_key(int host_user, int *rv, const Key *key) -+{ -+ char *fp; -+ const char *crypto_name; -+ -+ fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_HEX); -+ if (key->type == KEY_RSA1) -+ crypto_name = "ssh-rsa1"; -+ else -+ crypto_name = key_ssh_name(key); -+ if (audit_keyusage(host_user, crypto_name, key_size(key), fp, *rv) == 0) -+ *rv = 0; -+ free(fp); -+} -+ -+void -+audit_unsupported(int what) -+{ -+ PRIVSEP(audit_unsupported_body(what)); -+} -+ -+void -+audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) -+{ -+ PRIVSEP(audit_kex_body(ctos, enc, mac, comp, pfs, getpid(), getuid())); -+} -+ -+void -+audit_session_key_free(int ctos) -+{ -+ PRIVSEP(audit_session_key_free_body(ctos, getpid(), getuid())); -+} -+ - # ifndef CUSTOM_SSH_AUDIT_EVENTS - /* - * Null implementations of audit functions. -@@ -140,6 +178,17 @@ audit_event(ssh_audit_event_t event) - } - - /* -+ * Called when a child process has called, or will soon call, -+ * audit_session_open. -+ */ -+void -+audit_count_session_open(void) -+{ -+ debug("audit count session open euid %d user %s", geteuid(), -+ audit_username()); -+} -+ -+/* - * Called when a user session is started. Argument is the tty allocated to - * the session, or NULL if no tty was allocated. - * -@@ -174,13 +223,91 @@ audit_session_close(struct logininfo *li - /* - * This will be called when a user runs a non-interactive command. Note that - * it may be called multiple times for a single connection since SSH2 allows -- * multiple sessions within a single connection. -+ * multiple sessions within a single connection. Returns a "handle" for -+ * audit_end_command. - */ --void -+int - audit_run_command(const char *command) - { - debug("audit run command euid %d user %s command '%.200s'", geteuid(), - audit_username(), command); -+ return 0; -+} -+ -+/* -+ * This will be called when the non-interactive command finishes. Note that -+ * it may be called multiple times for a single connection since SSH2 allows -+ * multiple sessions within a single connection. "handle" should come from -+ * the corresponding audit_run_command. -+ */ -+void -+audit_end_command(int handle, const char *command) -+{ -+ debug("audit end nopty exec euid %d user %s command '%.200s'", geteuid(), -+ audit_username(), command); -+} -+ -+/* -+ * This will be called when user is successfully autherized by the RSA1/RSA/DSA key. -+ * -+ * Type is the key type, len is the key length(byte) and fp is the fingerprint of the key. -+ */ -+int -+audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv) -+{ -+ debug("audit %s key usage euid %d user %s key type %s key length %d fingerprint %s%s, result %d", -+ host_user ? "pubkey" : "hostbased", geteuid(), audit_username(), type, bits, -+ sshkey_fingerprint_prefix(), fp, rv); -+} -+ -+/* -+ * This will be called when the protocol negotiation fails. -+ */ -+void -+audit_unsupported_body(int what) -+{ -+ debug("audit unsupported protocol euid %d type %d", geteuid(), what); -+} -+ -+/* -+ * This will be called on succesfull protocol negotiation. -+ */ -+void -+audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, -+ uid_t uid) -+{ -+ debug("audit protocol negotiation euid %d direction %d cipher %s mac %s compresion %s pfs %s from pid %ld uid %u", -+ (unsigned)geteuid(), ctos, enc, mac, compress, pfs, (long)pid, -+ (unsigned)uid); -+} -+ -+/* -+ * This will be called on succesfull session key discard -+ */ -+void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) -+{ -+ debug("audit session key discard euid %u direction %d from pid %ld uid %u", -+ (unsigned)geteuid(), ctos, (long)pid, (unsigned)uid); -+} -+ -+/* -+ * This will be called on destroy private part of the server key -+ */ -+void -+audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) -+{ -+ debug("audit destroy sensitive data euid %d fingerprint %s from pid %ld uid %u", -+ geteuid(), fp, (long)pid, (unsigned)uid); -+} -+ -+/* -+ * This will be called on generation of the ephemeral server key -+ */ -+void -+audit_generate_ephemeral_server_key(const char *) -+{ -+ debug("audit create ephemeral server key euid %d fingerprint %s", geteuid(), fp); - } - # endif /* !defined CUSTOM_SSH_AUDIT_EVENTS */ - #endif /* SSH_AUDIT_EVENTS */ -diff -up openssh-7.0p1/audit.h.audit openssh-7.0p1/audit.h ---- openssh-7.0p1/audit.h.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/audit.h 2015-08-12 11:33:00.410914289 +0200 -@@ -28,6 +28,7 @@ - # define _SSH_AUDIT_H - - #include "loginrec.h" -+#include "key.h" - - enum ssh_audit_event_type { - SSH_LOGIN_EXCEED_MAXTRIES, -@@ -47,11 +48,25 @@ enum ssh_audit_event_type { - }; - typedef enum ssh_audit_event_type ssh_audit_event_t; - -+int listening_for_clients(void); -+ - void audit_connection_from(const char *, int); - void audit_event(ssh_audit_event_t); -+void audit_count_session_open(void); - void audit_session_open(struct logininfo *); - void audit_session_close(struct logininfo *); --void audit_run_command(const char *); -+int audit_run_command(const char *); -+void audit_end_command(int, const char *); - ssh_audit_event_t audit_classify_auth(const char *); -+int audit_keyusage(int, const char *, unsigned, char *, int); -+void audit_key(int, int *, const Key *); -+void audit_unsupported(int); -+void audit_kex(int, char *, char *, char *, char *); -+void audit_unsupported_body(int); -+void audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); -+void audit_session_key_free(int ctos); -+void audit_session_key_free_body(int ctos, pid_t, uid_t); -+void audit_destroy_sensitive_data(const char *, pid_t, uid_t); -+void audit_generate_ephemeral_server_key(const char *); - - #endif /* _SSH_AUDIT_H */ -diff -up openssh-7.0p1/audit-linux.c.audit openssh-7.0p1/audit-linux.c ---- openssh-7.0p1/audit-linux.c.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/audit-linux.c 2015-08-12 11:33:00.411914287 +0200 -@@ -35,13 +35,25 @@ - - #include "log.h" - #include "audit.h" -+#include "key.h" -+#include "hostfile.h" -+#include "auth.h" -+#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ -+#include "servconf.h" - #include "canohost.h" -+#include "packet.h" -+#include "cipher.h" - -+#define AUDIT_LOG_SIZE 256 -+ -+extern ServerOptions options; -+extern Authctxt *the_authctxt; -+extern u_int utmp_len; - const char* audit_username(void); - --int --linux_audit_record_event(int uid, const char *username, -- const char *hostname, const char *ip, const char *ttyn, int success) -+static void -+linux_audit_user_logxxx(int uid, const char *username, -+ const char *hostname, const char *ip, const char *ttyn, int success, int event) - { - int audit_fd, rc, saved_errno; - -@@ -49,11 +61,11 @@ linux_audit_record_event(int uid, const - if (audit_fd < 0) { - if (errno == EINVAL || errno == EPROTONOSUPPORT || - errno == EAFNOSUPPORT) -- return 1; /* No audit support in kernel */ -+ return; /* No audit support in kernel */ - else -- return 0; /* Must prevent login */ -+ goto fatal_report; /* Must prevent login */ - } -- rc = audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN, -+ rc = audit_log_acct_message(audit_fd, event, - NULL, "login", username ? username : "(unknown)", - username == NULL ? uid : -1, hostname, ip, ttyn, success); - saved_errno = errno; -@@ -65,35 +77,154 @@ linux_audit_record_event(int uid, const - if ((rc == -EPERM) && (geteuid() != 0)) - rc = 0; - errno = saved_errno; -- return (rc >= 0); -+ if (rc < 0) { -+fatal_report: -+ fatal("linux_audit_write_entry failed: %s", strerror(errno)); -+ } - } - -+static void -+linux_audit_user_auth(int uid, const char *username, -+ const char *hostname, const char *ip, const char *ttyn, int success, int event) -+{ -+ int audit_fd, rc, saved_errno; -+ static const char *event_name[] = { -+ "maxtries exceeded", -+ "root denied", -+ "success", -+ "none", -+ "password", -+ "challenge-response", -+ "pubkey", -+ "hostbased", -+ "gssapi", -+ "invalid user", -+ "nologin", -+ "connection closed", -+ "connection abandoned", -+ "unknown" -+ }; -+ -+ audit_fd = audit_open(); -+ if (audit_fd < 0) { -+ if (errno == EINVAL || errno == EPROTONOSUPPORT || -+ errno == EAFNOSUPPORT) -+ return; /* No audit support in kernel */ -+ else -+ goto fatal_report; /* Must prevent login */ -+ } -+ -+ if ((event < 0) || (event > SSH_AUDIT_UNKNOWN)) -+ event = SSH_AUDIT_UNKNOWN; -+ -+ rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, -+ NULL, event_name[event], username ? username : "(unknown)", -+ username == NULL ? uid : -1, hostname, ip, ttyn, success); -+ saved_errno = errno; -+ close(audit_fd); -+ /* -+ * Do not report error if the error is EPERM and sshd is run as non -+ * root user. -+ */ -+ if ((rc == -EPERM) && (geteuid() != 0)) -+ rc = 0; -+ errno = saved_errno; -+ if (rc < 0) { -+fatal_report: -+ fatal("linux_audit_write_entry failed: %s", strerror(errno)); -+ } -+} -+ -+int -+audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv) -+{ -+ char buf[AUDIT_LOG_SIZE]; -+ int audit_fd, rc, saved_errno; -+ -+ audit_fd = audit_open(); -+ if (audit_fd < 0) { -+ if (errno == EINVAL || errno == EPROTONOSUPPORT || -+ errno == EAFNOSUPPORT) -+ return 1; /* No audit support in kernel */ -+ else -+ return 0; /* Must prevent login */ -+ } -+ snprintf(buf, sizeof(buf), "%s_auth rport=%d", host_user ? "pubkey" : "hostbased", get_remote_port()); -+ rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL, -+ buf, audit_username(), -1, NULL, get_remote_ipaddr(), NULL, rv); -+ if ((rc < 0) && ((rc != -1) || (getuid() == 0))) -+ goto out; -+ /* is the fingerprint_prefix() still needed? -+ snprintf(buf, sizeof(buf), "key algo=%s size=%d fp=%s%s rport=%d", -+ type, bits, sshkey_fingerprint_prefix(), fp, get_remote_port()); -+ */ -+ snprintf(buf, sizeof(buf), "key algo=%s size=%d fp=%s rport=%d", -+ type, bits, fp, get_remote_port()); -+ rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL, -+ buf, audit_username(), -1, NULL, get_remote_ipaddr(), NULL, rv); -+out: -+ saved_errno = errno; -+ audit_close(audit_fd); -+ errno = saved_errno; -+ /* do not report error if the error is EPERM and sshd is run as non root user */ -+ return (rc >= 0) || ((rc == -EPERM) && (getuid() != 0)); -+} -+ -+static int user_login_count = 0; -+ - /* Below is the sshd audit API code */ - - void - audit_connection_from(const char *host, int port) - { --} - /* not implemented */ -+} - --void -+int - audit_run_command(const char *command) - { -- /* not implemented */ -+ if (!user_login_count++) -+ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), -+ NULL, "ssh", 1, AUDIT_USER_LOGIN); -+ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), -+ NULL, "ssh", 1, AUDIT_USER_START); -+ return 0; -+} -+ -+void -+audit_end_command(int handle, const char *command) -+{ -+ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), -+ NULL, "ssh", 1, AUDIT_USER_END); -+ if (user_login_count && !--user_login_count) -+ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), -+ NULL, "ssh", 1, AUDIT_USER_LOGOUT); -+} -+ -+void -+audit_count_session_open(void) -+{ -+ user_login_count++; - } - - void - audit_session_open(struct logininfo *li) - { -- if (linux_audit_record_event(li->uid, NULL, li->hostname, -- NULL, li->line, 1) == 0) -- fatal("linux_audit_write_entry failed: %s", strerror(errno)); -+ if (!user_login_count++) -+ linux_audit_user_logxxx(li->uid, NULL, li->hostname, -+ NULL, li->line, 1, AUDIT_USER_LOGIN); -+ linux_audit_user_logxxx(li->uid, NULL, li->hostname, -+ NULL, li->line, 1, AUDIT_USER_START); - } - - void - audit_session_close(struct logininfo *li) - { -- /* not implemented */ -+ linux_audit_user_logxxx(li->uid, NULL, li->hostname, -+ NULL, li->line, 1, AUDIT_USER_END); -+ if (user_login_count && !--user_login_count) -+ linux_audit_user_logxxx(li->uid, NULL, li->hostname, -+ NULL, li->line, 1, AUDIT_USER_LOGOUT); - } - - void -@@ -101,21 +232,43 @@ audit_event(ssh_audit_event_t event) - { - switch(event) { - case SSH_AUTH_SUCCESS: -- case SSH_CONNECTION_CLOSE: -+ linux_audit_user_auth(-1, audit_username(), NULL, -+ get_remote_ipaddr(), "ssh", 1, event); -+ break; -+ - case SSH_NOLOGIN: -- case SSH_LOGIN_EXCEED_MAXTRIES: - case SSH_LOGIN_ROOT_DENIED: -+ linux_audit_user_auth(-1, audit_username(), NULL, -+ get_remote_ipaddr(), "ssh", 0, event); -+ linux_audit_user_logxxx(-1, audit_username(), NULL, -+ get_remote_ipaddr(), "ssh", 0, AUDIT_USER_LOGIN); - break; - -+ case SSH_LOGIN_EXCEED_MAXTRIES: - case SSH_AUTH_FAIL_NONE: - case SSH_AUTH_FAIL_PASSWD: - case SSH_AUTH_FAIL_KBDINT: - case SSH_AUTH_FAIL_PUBKEY: - case SSH_AUTH_FAIL_HOSTBASED: - case SSH_AUTH_FAIL_GSSAPI: -+ linux_audit_user_auth(-1, audit_username(), NULL, -+ get_remote_ipaddr(), "ssh", 0, event); -+ break; -+ -+ case SSH_CONNECTION_CLOSE: -+ if (user_login_count) { -+ while (user_login_count--) -+ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), -+ NULL, "ssh", 1, AUDIT_USER_END); -+ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), -+ NULL, "ssh", 1, AUDIT_USER_LOGOUT); -+ } -+ break; -+ -+ case SSH_CONNECTION_ABANDON: - case SSH_INVALID_USER: -- linux_audit_record_event(-1, audit_username(), NULL, -- get_remote_ipaddr(), "sshd", 0); -+ linux_audit_user_logxxx(-1, audit_username(), NULL, -+ get_remote_ipaddr(), "ssh", 0, AUDIT_USER_LOGIN); - break; - - default: -@@ -123,4 +276,135 @@ audit_event(ssh_audit_event_t event) - } - } - -+void -+audit_unsupported_body(int what) -+{ -+#ifdef AUDIT_CRYPTO_SESSION -+ char buf[AUDIT_LOG_SIZE]; -+ const static char *name[] = { "cipher", "mac", "comp" }; -+ char *s; -+ int audit_fd; -+ -+ snprintf(buf, sizeof(buf), "op=unsupported-%s direction=? cipher=? ksize=? rport=%d laddr=%s lport=%d ", -+ name[what], get_remote_port(), (s = get_local_ipaddr(packet_get_connection_in())), -+ get_local_port()); -+ free(s); -+ audit_fd = audit_open(); -+ if (audit_fd < 0) -+ /* no problem, the next instruction will be fatal() */ -+ return; -+ audit_log_user_message(audit_fd, AUDIT_CRYPTO_SESSION, -+ buf, NULL, get_remote_ipaddr(), NULL, 0); -+ audit_close(audit_fd); -+#endif -+} -+ -+const static char *direction[] = { "from-server", "from-client", "both" }; -+ -+void -+audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, -+ uid_t uid) -+{ -+#ifdef AUDIT_CRYPTO_SESSION -+ char buf[AUDIT_LOG_SIZE]; -+ int audit_fd, audit_ok; -+ const struct sshcipher *cipher = cipher_by_name(enc); -+ char *s; -+ -+ snprintf(buf, sizeof(buf), "op=start direction=%s cipher=%s ksize=%d mac=%s pfs=%s spid=%jd suid=%jd rport=%d laddr=%s lport=%d ", -+ direction[ctos], enc, cipher ? 8 * cipher->key_len : 0, mac, pfs, -+ (intmax_t)pid, (intmax_t)uid, -+ get_remote_port(), (s = get_local_ipaddr(packet_get_connection_in())), get_local_port()); -+ free(s); -+ audit_fd = audit_open(); -+ if (audit_fd < 0) { -+ if (errno == EINVAL || errno == EPROTONOSUPPORT || -+ errno == EAFNOSUPPORT) -+ return; /* No audit support in kernel */ -+ else -+ fatal("cannot open audit"); /* Must prevent login */ -+ } -+ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_SESSION, -+ buf, NULL, get_remote_ipaddr(), NULL, 1); -+ audit_close(audit_fd); -+ /* do not abort if the error is EPERM and sshd is run as non root user */ -+ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) -+ fatal("cannot write into audit"); /* Must prevent login */ -+#endif -+} -+ -+void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) -+{ -+ char buf[AUDIT_LOG_SIZE]; -+ int audit_fd, audit_ok; -+ char *s; -+ -+ snprintf(buf, sizeof(buf), "op=destroy kind=session fp=? direction=%s spid=%jd suid=%jd rport=%d laddr=%s lport=%d ", -+ direction[ctos], (intmax_t)pid, (intmax_t)uid, -+ get_remote_port(), -+ (s = get_local_ipaddr(packet_get_connection_in())), -+ get_local_port()); -+ free(s); -+ audit_fd = audit_open(); -+ if (audit_fd < 0) { -+ if (errno != EINVAL && errno != EPROTONOSUPPORT && -+ errno != EAFNOSUPPORT) -+ error("cannot open audit"); -+ return; -+ } -+ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, -+ buf, NULL, get_remote_ipaddr(), NULL, 1); -+ audit_close(audit_fd); -+ /* do not abort if the error is EPERM and sshd is run as non root user */ -+ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) -+ error("cannot write into audit"); -+} -+ -+void -+audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) -+{ -+ char buf[AUDIT_LOG_SIZE]; -+ int audit_fd, audit_ok; -+ -+ snprintf(buf, sizeof(buf), "op=destroy kind=server fp=%s direction=? spid=%jd suid=%jd ", -+ fp, (intmax_t)pid, (intmax_t)uid); -+ audit_fd = audit_open(); -+ if (audit_fd < 0) { -+ if (errno != EINVAL && errno != EPROTONOSUPPORT && -+ errno != EAFNOSUPPORT) -+ error("cannot open audit"); -+ return; -+ } -+ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, -+ buf, NULL, -+ listening_for_clients() ? NULL : get_remote_ipaddr(), -+ NULL, 1); -+ audit_close(audit_fd); -+ /* do not abort if the error is EPERM and sshd is run as non root user */ -+ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) -+ error("cannot write into audit"); -+} -+ -+void -+audit_generate_ephemeral_server_key(const char *fp) -+{ -+ char buf[AUDIT_LOG_SIZE]; -+ int audit_fd, audit_ok; -+ -+ snprintf(buf, sizeof(buf), "op=create kind=server fp=%s direction=? ", fp); -+ audit_fd = audit_open(); -+ if (audit_fd < 0) { -+ if (errno != EINVAL && errno != EPROTONOSUPPORT && -+ errno != EAFNOSUPPORT) -+ error("cannot open audit"); -+ return; -+ } -+ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, -+ buf, NULL, 0, NULL, 1); -+ audit_close(audit_fd); -+ /* do not abort if the error is EPERM and sshd is run as non root user */ -+ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) -+ error("cannot write into audit"); -+} - #endif /* USE_LINUX_AUDIT */ -diff -up openssh-7.0p1/auditstub.c.audit openssh-7.0p1/auditstub.c ---- openssh-7.0p1/auditstub.c.audit 2015-08-12 11:33:00.411914287 +0200 -+++ openssh-7.0p1/auditstub.c 2015-08-12 11:33:00.411914287 +0200 -@@ -0,0 +1,50 @@ -+/* $Id: auditstub.c,v 1.1 jfch Exp $ */ -+ -+/* -+ * Copyright 2010 Red Hat, Inc. All rights reserved. -+ * Use is subject to license terms. -+ * -+ * 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. -+ * -+ * Red Hat author: Jan F. Chadima -+ */ -+ -+#include -+ -+void -+audit_unsupported(int n) -+{ -+} -+ -+void -+audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) -+{ -+} -+ -+void -+audit_session_key_free(int ctos) -+{ -+} -+ -+void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) -+{ -+} -diff -up openssh-7.0p1/auth2.c.audit openssh-7.0p1/auth2.c ---- openssh-7.0p1/auth2.c.audit 2015-08-12 11:33:00.349914384 +0200 -+++ openssh-7.0p1/auth2.c 2015-08-12 11:33:00.411914287 +0200 -@@ -249,9 +249,6 @@ input_userauth_request(int type, u_int32 - } 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) -diff -up openssh-7.0p1/auth2-hostbased.c.audit openssh-7.0p1/auth2-hostbased.c ---- openssh-7.0p1/auth2-hostbased.c.audit 2015-08-12 11:33:00.303914456 +0200 -+++ openssh-7.0p1/auth2-hostbased.c 2015-08-12 11:33:00.412914286 +0200 -@@ -146,7 +146,7 @@ userauth_hostbased(Authctxt *authctxt) - /* test for allowed key and correct signature */ - authenticated = 0; - if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) && -- PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), -+ PRIVSEP(hostbased_key_verify(key, sig, slen, buffer_ptr(&b), - buffer_len(&b))) == 1) - authenticated = 1; - -@@ -163,6 +163,18 @@ done: - return authenticated; - } - -+int -+hostbased_key_verify(const Key *key, const u_char *sig, u_int slen, const u_char *data, u_int datalen) -+{ -+ int rv; -+ -+ rv = key_verify(key, sig, slen, data, datalen); -+#ifdef SSH_AUDIT_EVENTS -+ audit_key(0, &rv, key); -+#endif -+ return rv; -+} -+ - /* return 1 if given hostkey is allowed */ - int - hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, -diff -up openssh-7.0p1/auth2-pubkey.c.audit openssh-7.0p1/auth2-pubkey.c ---- openssh-7.0p1/auth2-pubkey.c.audit 2015-08-12 11:33:00.318914432 +0200 -+++ openssh-7.0p1/auth2-pubkey.c 2015-08-12 11:33:00.412914286 +0200 -@@ -175,7 +175,7 @@ userauth_pubkey(Authctxt *authctxt) - /* test for correct signature */ - authenticated = 0; - if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && -- PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), -+ PRIVSEP(user_key_verify(key, sig, slen, buffer_ptr(&b), - buffer_len(&b))) == 1) { - authenticated = 1; - /* Record the successful key to prevent reuse */ -@@ -253,6 +253,18 @@ pubkey_auth_info(Authctxt *authctxt, con - free(extra); - } - -+int -+user_key_verify(const Key *key, const u_char *sig, u_int slen, const u_char *data, u_int datalen) -+{ -+ int rv; -+ -+ rv = key_verify(key, sig, slen, data, datalen); -+#ifdef SSH_AUDIT_EVENTS -+ audit_key(1, &rv, key); -+#endif -+ return rv; -+} -+ - /* - * Splits 's' into an argument vector. Handles quoted string and basic - * escape characters (\\, \", \'). Caller must free the argument vector -diff -up openssh-7.0p1/auth.c.audit openssh-7.0p1/auth.c ---- openssh-7.0p1/auth.c.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/auth.c 2015-08-12 11:33:00.412914286 +0200 -@@ -645,9 +645,6 @@ getpwnamallow(const char *user) - record_failed_login(user, - get_canonical_hostname(options.use_dns), "ssh"); - #endif --#ifdef SSH_AUDIT_EVENTS -- audit_event(SSH_INVALID_USER); --#endif /* SSH_AUDIT_EVENTS */ - return (NULL); - } - if (!allowed_user(pw)) -diff -up openssh-7.0p1/auth.h.audit openssh-7.0p1/auth.h ---- openssh-7.0p1/auth.h.audit 2015-08-12 11:33:00.302914457 +0200 -+++ openssh-7.0p1/auth.h 2015-08-12 11:33:00.412914286 +0200 -@@ -195,6 +195,7 @@ void abandon_challenge_response(Authctxt - - char *expand_authorized_keys(const char *, struct passwd *pw); - char *authorized_principals_file(struct passwd *); -+int user_key_verify(const Key *, const u_char *, u_int, const u_char *, u_int); - - FILE *auth_openkeyfile(const char *, struct passwd *, int); - FILE *auth_openprincipals(const char *, struct passwd *, int); -@@ -213,6 +214,7 @@ int get_hostkey_index(Key *, int, struc - int ssh1_session_key(BIGNUM *); - int sshd_hostkey_sign(Key *, Key *, u_char **, size_t *, - const u_char *, size_t, u_int); -+int hostbased_key_verify(const Key *, const u_char *, u_int, const u_char *, u_int); - - /* debug messages during authentication */ - void auth_debug_add(const char *fmt,...) __attribute__((format(printf, 1, 2))); -diff -up openssh-7.0p1/auth-rsa.c.audit openssh-7.0p1/auth-rsa.c ---- openssh-7.0p1/auth-rsa.c.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/auth-rsa.c 2015-08-12 11:33:00.412914286 +0200 -@@ -95,7 +95,10 @@ auth_rsa_verify_response(Key *key, BIGNU - { - u_char buf[32], mdbuf[16]; - struct ssh_digest_ctx *md; -- int len; -+ int len, rv; -+#ifdef SSH_AUDIT_EVENTS -+ char *fp; -+#endif - - /* don't allow short keys */ - if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { -@@ -119,12 +122,18 @@ auth_rsa_verify_response(Key *key, BIGNU - ssh_digest_free(md); - - /* Verify that the response is the original challenge. */ -- if (timingsafe_bcmp(response, mdbuf, 16) != 0) { -- /* Wrong answer. */ -- return (0); -+ rv = timingsafe_bcmp(response, mdbuf, 16) == 0; -+ -+#ifdef SSH_AUDIT_EVENTS -+ fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_HEX); -+ if (audit_keyusage(1, "ssh-rsa1", RSA_size(key->rsa) * 8, fp, rv) == 0) { -+ debug("unsuccessful audit"); -+ rv = 0; - } -- /* Correct answer. */ -- return (1); -+ free(fp); -+#endif -+ -+ return rv; - } - - /* -diff -up openssh-7.0p1/cipher.c.audit openssh-7.0p1/cipher.c ---- openssh-7.0p1/cipher.c.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/cipher.c 2015-08-12 11:33:00.412914286 +0200 -@@ -57,26 +57,6 @@ extern const EVP_CIPHER *evp_ssh1_3des(v - extern int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); - #endif - --struct sshcipher { -- char *name; -- int number; /* for ssh1 only */ -- u_int block_size; -- u_int key_len; -- u_int iv_len; /* defaults to block_size */ -- u_int auth_len; -- u_int discard_len; -- u_int flags; --#define CFLAG_CBC (1<<0) --#define CFLAG_CHACHAPOLY (1<<1) --#define CFLAG_AESCTR (1<<2) --#define CFLAG_NONE (1<<3) --#ifdef WITH_OPENSSL -- const EVP_CIPHER *(*evptype)(void); --#else -- void *ignored; --#endif --}; -- - static const struct sshcipher ciphers[] = { - #ifdef WITH_SSH1 - { "des", SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc }, -diff -up openssh-7.0p1/cipher.h.audit openssh-7.0p1/cipher.h ---- openssh-7.0p1/cipher.h.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/cipher.h 2015-08-12 11:33:00.413914284 +0200 -@@ -62,7 +62,26 @@ - #define CIPHER_ENCRYPT 1 - #define CIPHER_DECRYPT 0 - --struct sshcipher; -+struct sshcipher { -+ char *name; -+ int number; /* for ssh1 only */ -+ u_int block_size; -+ u_int key_len; -+ u_int iv_len; /* defaults to block_size */ -+ u_int auth_len; -+ u_int discard_len; -+ u_int flags; -+#define CFLAG_CBC (1<<0) -+#define CFLAG_CHACHAPOLY (1<<1) -+#define CFLAG_AESCTR (1<<2) -+#define CFLAG_NONE (1<<3) -+#ifdef WITH_OPENSSL -+ const EVP_CIPHER *(*evptype)(void); -+#else -+ void *ignored; -+#endif -+}; -+ - struct sshcipher_ctx { - int plaintext; - int encrypt; -diff -up openssh-7.0p1/kex.c.audit openssh-7.0p1/kex.c ---- openssh-7.0p1/kex.c.audit 2015-08-12 11:33:00.351914381 +0200 -+++ openssh-7.0p1/kex.c 2015-08-12 11:33:00.413914284 +0200 -@@ -54,6 +54,7 @@ - #include "ssherr.h" - #include "sshbuf.h" - #include "digest.h" -+#include "audit.h" - - #ifdef GSSAPI - #include "ssh-gss.h" -@@ -549,8 +550,12 @@ choose_enc(struct sshenc *enc, char *cli - { - char *name = match_list(client, server, NULL); - -- if (name == NULL) -+ if (name == NULL) { -+#ifdef SSH_AUDIT_EVENTS -+ audit_unsupported(0); -+#endif - return SSH_ERR_NO_CIPHER_ALG_MATCH; -+ } - if ((enc->cipher = cipher_by_name(name)) == NULL) - return SSH_ERR_INTERNAL_ERROR; - enc->name = name; -@@ -568,8 +573,12 @@ choose_mac(struct ssh *ssh, struct sshma - { - char *name = match_list(client, server, NULL); - -- if (name == NULL) -+ if (name == NULL) { -+#ifdef SSH_AUDIT_EVENTS -+ audit_unsupported(1); -+#endif - return SSH_ERR_NO_MAC_ALG_MATCH; -+ } - if (mac_setup(mac, name) < 0) - return SSH_ERR_INTERNAL_ERROR; - /* truncate the key */ -@@ -586,8 +595,12 @@ choose_comp(struct sshcomp *comp, char * - { - char *name = match_list(client, server, NULL); - -- if (name == NULL) -+ if (name == NULL) { -+#ifdef SSH_AUDIT_EVENTS -+ audit_unsupported(2); -+#endif - return SSH_ERR_NO_COMPRESS_ALG_MATCH; -+ } - if (strcmp(name, "zlib@openssh.com") == 0) { - comp->type = COMP_DELAYED; - } else if (strcmp(name, "zlib") == 0) { -@@ -753,6 +766,10 @@ kex_choose_conf(struct ssh *ssh) - dh_need = MAX(dh_need, newkeys->enc.block_size); - dh_need = MAX(dh_need, newkeys->enc.iv_len); - dh_need = MAX(dh_need, newkeys->mac.key_len); -+ debug("kex: %s need=%d dh_need=%d", kex->name, need, dh_need); -+#ifdef SSH_AUDIT_EVENTS -+ audit_kex(mode, newkeys->enc.name, newkeys->mac.name, newkeys->comp.name, kex->name); -+#endif - } - /* XXX need runden? */ - kex->we_need = need; -@@ -928,3 +945,34 @@ dump_digest(char *msg, u_char *digest, i - sshbuf_dump_data(digest, len, stderr); - } - #endif -+ -+static void -+enc_destroy(struct sshenc *enc) -+{ -+ if (enc == NULL) -+ return; -+ -+ if (enc->key) { -+ memset(enc->key, 0, enc->key_len); -+ free(enc->key); -+ } -+ -+ if (enc->iv) { -+ memset(enc->iv, 0, enc->block_size); -+ free(enc->iv); -+ } -+ -+ memset(enc, 0, sizeof(*enc)); -+} -+ -+void -+newkeys_destroy(struct newkeys *newkeys) -+{ -+ if (newkeys == NULL) -+ return; -+ -+ enc_destroy(&newkeys->enc); -+ mac_destroy(&newkeys->mac); -+ memset(&newkeys->comp, 0, sizeof(newkeys->comp)); -+} -+ -diff -up openssh-7.0p1/kex.h.audit openssh-7.0p1/kex.h ---- openssh-7.0p1/kex.h.audit 2015-08-12 11:33:00.352914379 +0200 -+++ openssh-7.0p1/kex.h 2015-08-12 11:33:00.413914284 +0200 -@@ -202,6 +202,8 @@ int kexgss_client(struct ssh *); - int kexgss_server(struct ssh *); - #endif - -+void newkeys_destroy(struct newkeys *newkeys); -+ - int kex_dh_hash(const char *, const char *, - const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, - const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); -diff -up openssh-7.0p1/key.h.audit openssh-7.0p1/key.h ---- openssh-7.0p1/key.h.audit 2015-08-12 11:33:00.413914284 +0200 -+++ openssh-7.0p1/key.h 2015-08-12 11:33:45.908843298 +0200 -@@ -50,6 +50,7 @@ typedef struct sshkey Key; - #define key_ecdsa_bits_to_nid sshkey_ecdsa_bits_to_nid - #define key_ecdsa_key_to_nid sshkey_ecdsa_key_to_nid - #define key_is_cert sshkey_is_cert -+#define key_is_private sshkey_is_private - #define key_type_plain sshkey_type_plain - #define key_curve_name_to_nid sshkey_curve_name_to_nid - #define key_curve_nid_to_bits sshkey_curve_nid_to_bits -diff -up openssh-7.0p1/mac.c.audit openssh-7.0p1/mac.c ---- openssh-7.0p1/mac.c.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/mac.c 2015-08-12 11:33:00.413914284 +0200 -@@ -226,6 +226,20 @@ mac_clear(struct sshmac *mac) - mac->umac_ctx = NULL; - } - -+void -+mac_destroy(struct sshmac *mac) -+{ -+ if (mac == NULL) -+ return; -+ -+ if (mac->key) { -+ memset(mac->key, 0, mac->key_len); -+ free(mac->key); -+ } -+ -+ memset(mac, 0, sizeof(*mac)); -+} -+ - /* XXX copied from ciphers_valid */ - #define MAC_SEP "," - int -diff -up openssh-7.0p1/mac.h.audit openssh-7.0p1/mac.h ---- openssh-7.0p1/mac.h.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/mac.h 2015-08-12 11:33:00.413914284 +0200 -@@ -47,5 +47,6 @@ int mac_init(struct sshmac *); - int mac_compute(struct sshmac *, u_int32_t, const u_char *, int, - u_char *, size_t); - void mac_clear(struct sshmac *); -+void mac_destroy(struct sshmac *); - - #endif /* SSHMAC_H */ -diff -up openssh-7.0p1/Makefile.in.audit openssh-7.0p1/Makefile.in ---- openssh-7.0p1/Makefile.in.audit 2015-08-12 11:33:00.402914301 +0200 -+++ openssh-7.0p1/Makefile.in 2015-08-12 11:33:00.414914283 +0200 -@@ -98,7 +98,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ - sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \ - kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ - kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ -- kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o -+ kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o auditstub.o - - SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ - sshconnect.o sshconnect1.o sshconnect2.o mux.o \ -diff -up openssh-7.0p1/monitor.c.audit openssh-7.0p1/monitor.c ---- openssh-7.0p1/monitor.c.audit 2015-08-12 11:33:00.378914339 +0200 -+++ openssh-7.0p1/monitor.c 2015-08-12 11:33:00.414914283 +0200 -@@ -102,6 +102,7 @@ - #include "ssh2.h" - #include "roaming.h" - #include "authfd.h" -+#include "audit.h" - #include "match.h" - #include "ssherr.h" - -@@ -117,6 +118,8 @@ extern Buffer auth_debug; - extern int auth_debug_init; - extern Buffer loginmsg; - -+extern void destroy_sensitive_data(int); -+ - /* State exported from the child */ - static struct sshbuf *child_state; - -@@ -167,6 +170,11 @@ int mm_answer_gss_updatecreds(int, Buffe - #ifdef SSH_AUDIT_EVENTS - int mm_answer_audit_event(int, Buffer *); - int mm_answer_audit_command(int, Buffer *); -+int mm_answer_audit_end_command(int, Buffer *); -+int mm_answer_audit_unsupported_body(int, Buffer *); -+int mm_answer_audit_kex_body(int, Buffer *); -+int mm_answer_audit_session_key_free_body(int, Buffer *); -+int mm_answer_audit_server_key_free(int, Buffer *); - #endif - - static int monitor_read_log(struct monitor *); -@@ -226,6 +234,10 @@ struct mon_table mon_dispatch_proto20[] - #endif - #ifdef SSH_AUDIT_EVENTS - {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, -+ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, -+ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, -+ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, -+ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, - #endif - #ifdef BSD_AUTH - {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, -@@ -264,6 +276,11 @@ struct mon_table mon_dispatch_postauth20 - #ifdef SSH_AUDIT_EVENTS - {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, - {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command}, -+ {MONITOR_REQ_AUDIT_END_COMMAND, MON_PERMIT, mm_answer_audit_end_command}, -+ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, -+ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, -+ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, -+ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, - #endif - {0, 0, NULL} - }; -@@ -296,6 +313,10 @@ struct mon_table mon_dispatch_proto15[] - #endif - #ifdef SSH_AUDIT_EVENTS - {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, -+ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, -+ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, -+ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, -+ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, - #endif - #endif /* WITH_SSH1 */ - {0, 0, NULL} -@@ -309,6 +330,11 @@ struct mon_table mon_dispatch_postauth15 - #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}, -+ {MONITOR_REQ_AUDIT_END_COMMAND, MON_PERMIT, mm_answer_audit_end_command}, -+ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, -+ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, -+ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, -+ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, - #endif - #endif /* WITH_SSH1 */ - {0, 0, NULL} -@@ -1467,9 +1493,11 @@ mm_answer_keyverify(int sock, Buffer *m) - Key *key; - u_char *signature, *data, *blob; - u_int signaturelen, datalen, bloblen; -+ int type = 0; - int verified = 0; - int valid_data = 0; - -+ type = buffer_get_int(m); - blob = buffer_get_string(m, &bloblen); - signature = buffer_get_string(m, &signaturelen); - data = buffer_get_string(m, &datalen); -@@ -1477,6 +1505,8 @@ mm_answer_keyverify(int sock, Buffer *m) - if (hostbased_cuser == NULL || hostbased_chost == NULL || - !monitor_allowed_key(blob, bloblen)) - fatal("%s: bad key, not previously allowed", __func__); -+ if (type != key_blobtype) -+ fatal("%s: bad key type", __func__); - - key = key_from_blob(blob, bloblen); - if (key == NULL) -@@ -1497,7 +1527,17 @@ mm_answer_keyverify(int sock, Buffer *m) - if (!valid_data) - fatal("%s: bad signature data blob", __func__); - -- verified = key_verify(key, signature, signaturelen, data, datalen); -+ switch (key_blobtype) { -+ case MM_USERKEY: -+ verified = user_key_verify(key, signature, signaturelen, data, datalen); -+ break; -+ case MM_HOSTKEY: -+ verified = hostbased_key_verify(key, signature, signaturelen, data, datalen); -+ break; -+ default: -+ verified = 0; -+ break; -+ } - debug3("%s: key %p signature %s", - __func__, key, (verified == 1) ? "verified" : "unverified"); - -@@ -1558,6 +1598,12 @@ mm_session_close(Session *s) - debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd); - session_pty_cleanup2(s); - } -+#ifdef SSH_AUDIT_EVENTS -+ if (s->command != NULL) { -+ debug3("%s: command %d", __func__, s->command_handle); -+ session_end_command2(s); -+ } -+#endif - session_unused(s->self); - } - -@@ -1840,6 +1886,8 @@ mm_answer_term(int sock, Buffer *req) - sshpam_cleanup(); - #endif - -+ destroy_sensitive_data(0); -+ - while (waitpid(pmonitor->m_pid, &status, 0) == -1) - if (errno != EINTR) - exit(1); -@@ -1882,11 +1930,43 @@ mm_answer_audit_command(int socket, Buff - { - u_int len; - char *cmd; -+ Session *s; - - debug3("%s entering", __func__); - cmd = buffer_get_string(m, &len); -+ - /* sanity check command, if so how? */ -- audit_run_command(cmd); -+ s = session_new(); -+ if (s == NULL) -+ fatal("%s: error allocating a session", __func__); -+ s->command = cmd; -+ s->command_handle = audit_run_command(cmd); -+ -+ buffer_clear(m); -+ buffer_put_int(m, s->self); -+ -+ mm_request_send(socket, MONITOR_ANS_AUDIT_COMMAND, m); -+ -+ return (0); -+} -+ -+int -+mm_answer_audit_end_command(int socket, Buffer *m) -+{ -+ int handle; -+ u_int len; -+ char *cmd; -+ Session *s; -+ -+ debug3("%s entering", __func__); -+ handle = buffer_get_int(m); -+ cmd = buffer_get_string(m, &len); -+ -+ s = session_by_id(handle); -+ if (s == NULL || s->ttyfd != -1 || s->command == NULL || -+ strcmp(s->command, cmd) != 0) -+ fatal("%s: invalid handle", __func__); -+ mm_session_close(s); - free(cmd); - return (0); - } -@@ -1943,6 +2023,7 @@ monitor_apply_keystate(struct monitor *p - void - mm_get_keystate(struct monitor *pmonitor) - { -+ Buffer m; - debug3("%s: Waiting for new keys", __func__); - - if ((child_state = sshbuf_new()) == NULL) -@@ -1950,6 +2031,21 @@ mm_get_keystate(struct monitor *pmonitor - mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, - child_state); - debug3("%s: GOT new keys", __func__); -+ -+#ifdef SSH_AUDIT_EVENTS -+ if (compat20) { -+ buffer_init(&m); -+ mm_request_receive_expect(pmonitor->m_sendfd, -+ MONITOR_REQ_AUDIT_SESSION_KEY_FREE, &m); -+ mm_answer_audit_session_key_free_body(pmonitor->m_sendfd, &m); -+ buffer_free(&m); -+ } -+#endif -+ -+ /* Drain any buffered messages from the child */ -+ while (pmonitor->m_log_recvfd >= 0 && monitor_read_log(pmonitor) == 0) -+ ; -+ - } - - -@@ -2216,3 +2312,86 @@ mm_answer_gss_updatecreds(int socket, Bu - - #endif /* GSSAPI */ - -+#ifdef SSH_AUDIT_EVENTS -+int -+mm_answer_audit_unsupported_body(int sock, Buffer *m) -+{ -+ int what; -+ -+ what = buffer_get_int(m); -+ -+ audit_unsupported_body(what); -+ -+ buffer_clear(m); -+ -+ mm_request_send(sock, MONITOR_ANS_AUDIT_UNSUPPORTED, m); -+ return 0; -+} -+ -+int -+mm_answer_audit_kex_body(int sock, Buffer *m) -+{ -+ int ctos, len; -+ char *cipher, *mac, *compress, *pfs; -+ pid_t pid; -+ uid_t uid; -+ -+ ctos = buffer_get_int(m); -+ cipher = buffer_get_string(m, &len); -+ mac = buffer_get_string(m, &len); -+ compress = buffer_get_string(m, &len); -+ pfs = buffer_get_string(m, &len); -+ pid = buffer_get_int64(m); -+ uid = buffer_get_int64(m); -+ -+ audit_kex_body(ctos, cipher, mac, compress, pfs, pid, uid); -+ -+ free(cipher); -+ free(mac); -+ free(compress); -+ free(pfs); -+ buffer_clear(m); -+ -+ mm_request_send(sock, MONITOR_ANS_AUDIT_KEX, m); -+ return 0; -+} -+ -+int -+mm_answer_audit_session_key_free_body(int sock, Buffer *m) -+{ -+ int ctos; -+ pid_t pid; -+ uid_t uid; -+ -+ ctos = buffer_get_int(m); -+ pid = buffer_get_int64(m); -+ uid = buffer_get_int64(m); -+ -+ audit_session_key_free_body(ctos, pid, uid); -+ -+ buffer_clear(m); -+ -+ mm_request_send(sock, MONITOR_ANS_AUDIT_SESSION_KEY_FREE, m); -+ return 0; -+} -+ -+int -+mm_answer_audit_server_key_free(int sock, Buffer *m) -+{ -+ int len; -+ char *fp; -+ pid_t pid; -+ uid_t uid; -+ -+ fp = buffer_get_string(m, &len); -+ pid = buffer_get_int64(m); -+ uid = buffer_get_int64(m); -+ -+ audit_destroy_sensitive_data(fp, pid, uid); -+ -+ free(fp); -+ buffer_clear(m); -+ -+ return 0; -+} -+#endif /* SSH_AUDIT_EVENTS */ -diff -up openssh-7.0p1/monitor.h.audit openssh-7.0p1/monitor.h ---- openssh-7.0p1/monitor.h.audit 2015-08-12 11:33:00.378914339 +0200 -+++ openssh-7.0p1/monitor.h 2015-08-12 11:33:00.414914283 +0200 -@@ -69,7 +69,13 @@ enum monitor_reqtype { - MONITOR_REQ_PAM_QUERY = 106, MONITOR_ANS_PAM_QUERY = 107, - MONITOR_REQ_PAM_RESPOND = 108, MONITOR_ANS_PAM_RESPOND = 109, - MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, -- MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, -+ MONITOR_REQ_AUDIT_EVENT = 112, -+ MONITOR_REQ_AUDIT_COMMAND = 114, MONITOR_ANS_AUDIT_COMMAND = 115, -+ MONITOR_REQ_AUDIT_END_COMMAND = 116, -+ MONITOR_REQ_AUDIT_UNSUPPORTED = 118, MONITOR_ANS_AUDIT_UNSUPPORTED = 119, -+ MONITOR_REQ_AUDIT_KEX = 120, MONITOR_ANS_AUDIT_KEX = 121, -+ MONITOR_REQ_AUDIT_SESSION_KEY_FREE = 122, MONITOR_ANS_AUDIT_SESSION_KEY_FREE = 123, -+ MONITOR_REQ_AUDIT_SERVER_KEY_FREE = 124 - - }; - -diff -up openssh-7.0p1/monitor_wrap.c.audit openssh-7.0p1/monitor_wrap.c ---- openssh-7.0p1/monitor_wrap.c.audit 2015-08-12 11:33:00.353914378 +0200 -+++ openssh-7.0p1/monitor_wrap.c 2015-08-12 11:33:00.414914283 +0200 -@@ -462,7 +462,7 @@ mm_key_allowed(enum mm_keytype type, cha - */ - - int --mm_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) -+mm_key_verify(enum mm_keytype type, Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) - { - Buffer m; - u_char *blob; -@@ -476,6 +476,7 @@ mm_key_verify(Key *key, u_char *sig, u_i - return (0); - - buffer_init(&m); -+ buffer_put_int(&m, type); - buffer_put_string(&m, blob, len); - buffer_put_string(&m, sig, siglen); - buffer_put_string(&m, data, datalen); -@@ -493,6 +494,18 @@ mm_key_verify(Key *key, u_char *sig, u_i - return (verified); - } - -+int -+mm_hostbased_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) -+{ -+ return mm_key_verify(MM_HOSTKEY, key, sig, siglen, data, datalen); -+} -+ -+int -+mm_user_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) -+{ -+ return mm_key_verify(MM_USERKEY, key, sig, siglen, data, datalen); -+} -+ - void - mm_send_keystate(struct monitor *monitor) - { -@@ -1005,10 +1018,11 @@ mm_audit_event(ssh_audit_event_t event) - buffer_free(&m); - } - --void -+int - mm_audit_run_command(const char *command) - { - Buffer m; -+ int handle; - - debug3("%s entering command %s", __func__, command); - -@@ -1016,6 +1030,26 @@ mm_audit_run_command(const char *command - buffer_put_cstring(&m, command); - - mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_COMMAND, &m); -+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_COMMAND, &m); -+ -+ handle = buffer_get_int(&m); -+ buffer_free(&m); -+ -+ return (handle); -+} -+ -+void -+mm_audit_end_command(int handle, const char *command) -+{ -+ Buffer m; -+ -+ debug3("%s entering command %s", __func__, command); -+ -+ buffer_init(&m); -+ buffer_put_int(&m, handle); -+ buffer_put_cstring(&m, command); -+ -+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_END_COMMAND, &m); - buffer_free(&m); - } - #endif /* SSH_AUDIT_EVENTS */ -@@ -1151,3 +1185,70 @@ mm_ssh_gssapi_update_creds(ssh_gssapi_cc - - #endif /* GSSAPI */ - -+#ifdef SSH_AUDIT_EVENTS -+void -+mm_audit_unsupported_body(int what) -+{ -+ Buffer m; -+ -+ buffer_init(&m); -+ buffer_put_int(&m, what); -+ -+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_UNSUPPORTED, &m); -+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_UNSUPPORTED, -+ &m); -+ -+ buffer_free(&m); -+} -+ -+void -+mm_audit_kex_body(int ctos, char *cipher, char *mac, char *compress, char *fps, pid_t pid, -+ uid_t uid) -+{ -+ Buffer m; -+ -+ buffer_init(&m); -+ buffer_put_int(&m, ctos); -+ buffer_put_cstring(&m, cipher); -+ buffer_put_cstring(&m, (mac ? mac : "")); -+ buffer_put_cstring(&m, compress); -+ buffer_put_cstring(&m, fps); -+ buffer_put_int64(&m, pid); -+ buffer_put_int64(&m, uid); -+ -+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_KEX, &m); -+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_KEX, -+ &m); -+ -+ buffer_free(&m); -+} -+ -+void -+mm_audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) -+{ -+ Buffer m; -+ -+ buffer_init(&m); -+ buffer_put_int(&m, ctos); -+ buffer_put_int64(&m, pid); -+ buffer_put_int64(&m, uid); -+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_SESSION_KEY_FREE, &m); -+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_SESSION_KEY_FREE, -+ &m); -+ buffer_free(&m); -+} -+ -+void -+mm_audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) -+{ -+ Buffer m; -+ -+ buffer_init(&m); -+ buffer_put_cstring(&m, fp); -+ buffer_put_int64(&m, pid); -+ buffer_put_int64(&m, uid); -+ -+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_SERVER_KEY_FREE, &m); -+ buffer_free(&m); -+} -+#endif /* SSH_AUDIT_EVENTS */ -diff -up openssh-7.0p1/monitor_wrap.h.audit openssh-7.0p1/monitor_wrap.h ---- openssh-7.0p1/monitor_wrap.h.audit 2015-08-12 11:33:00.353914378 +0200 -+++ openssh-7.0p1/monitor_wrap.h 2015-08-12 11:33:00.415914281 +0200 -@@ -52,7 +52,8 @@ int mm_key_allowed(enum mm_keytype, char - int mm_user_key_allowed(struct passwd *, Key *, int); - 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_hostbased_key_verify(Key *, u_char *, u_int, u_char *, u_int); -+int mm_user_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 *); -@@ -79,7 +80,12 @@ void mm_sshpam_free_ctx(void *); - #ifdef SSH_AUDIT_EVENTS - #include "audit.h" - void mm_audit_event(ssh_audit_event_t); --void mm_audit_run_command(const char *); -+int mm_audit_run_command(const char *); -+void mm_audit_end_command(int, const char *); -+void mm_audit_unsupported_body(int); -+void mm_audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); -+void mm_audit_session_key_free_body(int, pid_t, uid_t); -+void mm_audit_destroy_sensitive_data(const char *, pid_t, uid_t); - #endif - - struct Session; -diff -up openssh-7.0p1/packet.c.audit openssh-7.0p1/packet.c ---- openssh-7.0p1/packet.c.audit 2015-08-12 11:33:00.288914479 +0200 -+++ openssh-7.0p1/packet.c 2015-08-12 11:33:00.415914281 +0200 -@@ -67,6 +67,7 @@ - #include "key.h" /* typedefs XXX */ - - #include "xmalloc.h" -+#include "audit.h" - #include "crc32.h" - #include "deattack.h" - #include "compat.h" -@@ -449,6 +450,13 @@ ssh_packet_get_connection_out(struct ssh - return ssh->state->connection_out; - } - -+static int -+packet_state_has_keys (const struct session_state *state) -+{ -+ return state != NULL && -+ (state->newkeys[MODE_IN] != NULL || state->newkeys[MODE_OUT] != NULL); -+} -+ - /* - * Returns the IP-address of the remote host as a string. The returned - * string must not be freed. -@@ -479,13 +487,6 @@ ssh_packet_close(struct ssh *ssh) - if (!state->initialized) - return; - state->initialized = 0; -- if (state->connection_in == state->connection_out) { -- shutdown(state->connection_out, SHUT_RDWR); -- close(state->connection_out); -- } else { -- close(state->connection_in); -- close(state->connection_out); -- } - sshbuf_free(state->input); - sshbuf_free(state->output); - sshbuf_free(state->outgoing_packet); -@@ -517,14 +518,24 @@ ssh_packet_close(struct ssh *ssh) - inflateEnd(stream); - } - } -- if ((r = cipher_cleanup(&state->send_context)) != 0) -- error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); -- if ((r = cipher_cleanup(&state->receive_context)) != 0) -- error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); -+ if (packet_state_has_keys(state)) { -+ if ((r = cipher_cleanup(&state->send_context)) != 0) -+ error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); -+ if ((r = cipher_cleanup(&state->receive_context)) != 0) -+ error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); -+ audit_session_key_free(2); -+ } - if (ssh->remote_ipaddr) { - free(ssh->remote_ipaddr); - ssh->remote_ipaddr = NULL; - } -+ if (state->connection_in == state->connection_out) { -+ shutdown(state->connection_out, SHUT_RDWR); -+ close(state->connection_out); -+ } else { -+ close(state->connection_in); -+ close(state->connection_out); -+ } - free(ssh->state); - ssh->state = NULL; - } -@@ -944,6 +955,7 @@ ssh_set_newkeys(struct ssh *ssh, int mod - } - if (state->newkeys[mode] != NULL) { - debug("set_newkeys: rekeying"); -+ audit_session_key_free(mode); - if ((r = cipher_cleanup(cc)) != 0) - return r; - enc = &state->newkeys[mode]->enc; -@@ -2292,6 +2304,75 @@ ssh_packet_get_output(struct ssh *ssh) - return (void *)ssh->state->output; - } - -+static void -+newkeys_destroy_and_free(struct newkeys *newkeys) -+{ -+ if (newkeys == NULL) -+ return; -+ -+ free(newkeys->enc.name); -+ -+ if (newkeys->mac.enabled) { -+ mac_clear(&newkeys->mac); -+ free(newkeys->mac.name); -+ } -+ -+ free(newkeys->comp.name); -+ -+ newkeys_destroy(newkeys); -+ free(newkeys); -+} -+ -+static void -+packet_destroy_state(struct session_state *state) -+{ -+ if (state == NULL) -+ return; -+ -+ cipher_cleanup(&state->receive_context); -+ cipher_cleanup(&state->send_context); -+ -+ buffer_free(state->input); -+ state->input = NULL; -+ buffer_free(state->output); -+ state->output = NULL; -+ buffer_free(state->outgoing_packet); -+ state->outgoing_packet = NULL; -+ buffer_free(state->incoming_packet); -+ state->incoming_packet = NULL; -+ if( state->compression_buffer ) { -+ buffer_free(state->compression_buffer); -+ state->compression_buffer = NULL; -+ } -+ newkeys_destroy_and_free(state->newkeys[MODE_IN]); -+ state->newkeys[MODE_IN] = NULL; -+ newkeys_destroy_and_free(state->newkeys[MODE_OUT]); -+ state->newkeys[MODE_OUT] = NULL; -+ mac_destroy(state->packet_discard_mac); -+// TAILQ_HEAD(, packet) outgoing; -+// memset(state, 0, sizeof(state)); -+} -+ -+void -+packet_destroy_all(int audit_it, int privsep) -+{ -+ if (audit_it) -+ audit_it = (active_state != NULL && packet_state_has_keys(active_state->state)) -+ || (backup_state != NULL && packet_state_has_keys(backup_state->state)); -+ if (active_state != NULL) -+ packet_destroy_state(active_state->state); -+ if (backup_state != NULL) -+ packet_destroy_state(backup_state->state); -+ if (audit_it) { -+#ifdef SSH_AUDIT_EVENTS -+ if (privsep) -+ audit_session_key_free(2); -+ else -+ audit_session_key_free_body(2, getpid(), getuid()); -+#endif -+ } -+} -+ - /* XXX TODO update roaming to new API (does not work anyway) */ - /* - * Save the state for the real connection, and use a separate state when -@@ -2301,18 +2382,12 @@ void - ssh_packet_backup_state(struct ssh *ssh, - struct ssh *backup_state) - { -- struct ssh *tmp; -- - close(ssh->state->connection_in); - ssh->state->connection_in = -1; - close(ssh->state->connection_out); - ssh->state->connection_out = -1; -- if (backup_state) -- tmp = backup_state; -- else -- tmp = ssh_alloc_session_state(); - backup_state = ssh; -- ssh = tmp; -+ ssh = ssh_alloc_session_state(); - } - - /* XXX FIXME FIXME FIXME */ -@@ -2331,9 +2406,7 @@ ssh_packet_restore_state(struct ssh *ssh - backup_state = ssh; - ssh = tmp; - ssh->state->connection_in = backup_state->state->connection_in; -- backup_state->state->connection_in = -1; - ssh->state->connection_out = backup_state->state->connection_out; -- backup_state->state->connection_out = -1; - len = sshbuf_len(backup_state->state->input); - if (len > 0) { - if ((r = sshbuf_putb(ssh->state->input, -@@ -2342,6 +2415,11 @@ ssh_packet_restore_state(struct ssh *ssh - sshbuf_reset(backup_state->state->input); - add_recv_bytes(len); - } -+ backup_state->state->connection_in = -1; -+ backup_state->state->connection_out = -1; -+ packet_destroy_state(backup_state->state); -+ free(backup_state); -+ backup_state = NULL; - } - - /* Reset after_authentication and reset compression in post-auth privsep */ -diff -up openssh-7.0p1/packet.h.audit openssh-7.0p1/packet.h ---- openssh-7.0p1/packet.h.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/packet.h 2015-08-12 11:33:00.415914281 +0200 -@@ -189,7 +189,7 @@ int sshpkt_get_end(struct ssh *ssh); - const u_char *sshpkt_ptr(struct ssh *, size_t *lenp); - - /* OLD API */ --extern struct ssh *active_state; -+extern struct ssh *active_state, *backup_state; - #include "opacket.h" - - #if !defined(WITH_OPENSSL) -@@ -203,4 +203,5 @@ extern struct ssh *active_state; - # undef EC_POINT - #endif - -+void packet_destroy_all(int, int); - #endif /* PACKET_H */ -diff -up openssh-7.0p1/sandbox-seccomp-filter.c.audit openssh-7.0p1/sandbox-seccomp-filter.c ---- openssh-7.0p1/sandbox-seccomp-filter.c.audit 2015-08-12 11:33:00.394914314 +0200 -+++ openssh-7.0p1/sandbox-seccomp-filter.c 2015-08-12 11:33:00.415914281 +0200 -@@ -150,6 +150,12 @@ static const struct sock_filter preauth_ - #ifdef __NR_gettimeofday - SC_ALLOW(gettimeofday), - #endif -+#ifdef SSH_AUDIT_EVENTS -+ SC_ALLOW(getuid), -+#ifdef __NR_getuid32 /* not defined on x86_64 */ -+ SC_ALLOW(getuid32), -+#endif -+#endif - #ifdef __NR_madvise - SC_ALLOW(madvise), - #endif -diff -up openssh-7.0p1/session.c.audit openssh-7.0p1/session.c ---- openssh-7.0p1/session.c.audit 2015-08-12 11:33:00.379914337 +0200 -+++ openssh-7.0p1/session.c 2015-08-12 11:33:00.416914280 +0200 -@@ -139,7 +139,7 @@ extern int log_stderr; - extern int debug_flag; - extern u_int utmp_len; - extern int startup_pipe; --extern void destroy_sensitive_data(void); -+extern void destroy_sensitive_data(int); - extern Buffer loginmsg; - - /* original command from peer. */ -@@ -731,6 +731,14 @@ do_exec_pty(Session *s, const char *comm - /* Parent. Close the slave side of the pseudo tty. */ - close(ttyfd); - -+#ifndef HAVE_OSF_SIA -+ /* do_login in the child did not affect state in this process, -+ compensate. From an architectural standpoint, this is extremely -+ ugly. */ -+ if (!(options.use_login && command == NULL)) -+ audit_count_session_open(); -+#endif -+ - /* Enter interactive session. */ - s->ptymaster = ptymaster; - packet_set_interactive(1, -@@ -853,15 +861,19 @@ do_exec(Session *s, const char *command) - get_remote_port()); - - #ifdef SSH_AUDIT_EVENTS -+ if (s->command != NULL || s->command_handle != -1) -+ fatal("do_exec: command already set"); - if (command != NULL) -- PRIVSEP(audit_run_command(command)); -+ s->command = xstrdup(command); - else if (s->ttyfd == -1) { - char *shell = s->pw->pw_shell; - - if (shell[0] == '\0') /* empty shell means /bin/sh */ - shell =_PATH_BSHELL; -- PRIVSEP(audit_run_command(shell)); -+ s->command = xstrdup(shell); - } -+ if (s->command != NULL && s->ptyfd == -1) -+ s->command_handle = PRIVSEP(audit_run_command(s->command)); - #endif - if (s->ttyfd != -1) - ret = do_exec_pty(s, command); -@@ -1704,7 +1716,10 @@ do_child(Session *s, const char *command - int r = 0; - - /* remove hostkey from the child's memory */ -- destroy_sensitive_data(); -+ destroy_sensitive_data(1); -+ /* Don't audit this - both us and the parent would be talking to the -+ monitor over a single socket, with no synchronization. */ -+ packet_destroy_all(0, 1); - - /* Force a password change */ - if (s->authctxt->force_pwchange) { -@@ -1934,6 +1949,7 @@ session_unused(int id) - sessions[id].ttyfd = -1; - sessions[id].ptymaster = -1; - sessions[id].x11_chanids = NULL; -+ sessions[id].command_handle = -1; - sessions[id].next_unused = sessions_first_unused; - sessions_first_unused = id; - } -@@ -2016,6 +2032,19 @@ session_open(Authctxt *authctxt, int cha - } - - Session * -+session_by_id(int id) -+{ -+ if (id >= 0 && id < sessions_nalloc) { -+ Session *s = &sessions[id]; -+ if (s->used) -+ return s; -+ } -+ debug("session_by_id: unknown id %d", id); -+ session_dump(); -+ return NULL; -+} -+ -+Session * - session_by_tty(char *tty) - { - int i; -@@ -2532,6 +2561,32 @@ session_exit_message(Session *s, int sta - chan_write_failed(c); - } - -+#ifdef SSH_AUDIT_EVENTS -+void -+session_end_command2(Session *s) -+{ -+ if (s->command != NULL) { -+ if (s->command_handle != -1) -+ audit_end_command(s->command_handle, s->command); -+ free(s->command); -+ s->command = NULL; -+ s->command_handle = -1; -+ } -+} -+ -+static void -+session_end_command(Session *s) -+{ -+ if (s->command != NULL) { -+ if (s->command_handle != -1) -+ PRIVSEP(audit_end_command(s->command_handle, s->command)); -+ free(s->command); -+ s->command = NULL; -+ s->command_handle = -1; -+ } -+} -+#endif -+ - void - session_close(Session *s) - { -@@ -2540,6 +2595,10 @@ session_close(Session *s) - debug("session_close: session %d pid %ld", s->self, (long)s->pid); - if (s->ttyfd != -1) - session_pty_cleanup(s); -+#ifdef SSH_AUDIT_EVENTS -+ if (s->command) -+ session_end_command(s); -+#endif - free(s->term); - free(s->display); - free(s->x11_chanids); -@@ -2754,6 +2813,15 @@ do_authenticated2(Authctxt *authctxt) - server_loop2(authctxt); - } - -+static void -+do_cleanup_one_session(Session *s) -+{ -+ session_pty_cleanup2(s); -+#ifdef SSH_AUDIT_EVENTS -+ session_end_command2(s); -+#endif -+} -+ - void - do_cleanup(Authctxt *authctxt) - { -@@ -2802,5 +2870,5 @@ do_cleanup(Authctxt *authctxt) - * or if running in monitor. - */ - if (!use_privsep || mm_is_monitor()) -- session_destroy_all(session_pty_cleanup2); -+ session_destroy_all(do_cleanup_one_session); - } -diff -up openssh-7.0p1/session.h.audit openssh-7.0p1/session.h ---- openssh-7.0p1/session.h.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/session.h 2015-08-12 11:33:00.416914280 +0200 -@@ -61,6 +61,12 @@ struct Session { - char *name; - char *val; - } *env; -+ -+ /* exec */ -+#ifdef SSH_AUDIT_EVENTS -+ int command_handle; -+ char *command; -+#endif - }; - - void do_authenticated(Authctxt *); -@@ -73,8 +79,10 @@ void session_close_by_pid(pid_t, int); - void session_close_by_channel(int, void *); - void session_destroy_all(void (*)(Session *)); - void session_pty_cleanup2(Session *); -+void session_end_command2(Session *); - - Session *session_new(void); -+Session *session_by_id(int); - Session *session_by_tty(char *); - void session_close(Session *); - void do_setusercontext(struct passwd *); -diff -up openssh-7.0p1/sshd.c.audit openssh-7.0p1/sshd.c ---- openssh-7.0p1/sshd.c.audit 2015-08-12 11:33:00.388914323 +0200 -+++ openssh-7.0p1/sshd.c 2015-08-12 11:33:00.417914278 +0200 -@@ -122,6 +122,7 @@ - #endif - #include "monitor_wrap.h" - #include "roaming.h" -+#include "audit.h" - #include "ssh-sandbox.h" - #include "version.h" - #include "ssherr.h" -@@ -261,7 +262,7 @@ Buffer loginmsg; - struct passwd *privsep_pw = NULL; - - /* Prototypes for various functions defined later in this file. */ --void destroy_sensitive_data(void); -+void destroy_sensitive_data(int); - void demote_sensitive_data(void); - - #ifdef WITH_SSH1 -@@ -282,6 +283,15 @@ close_listen_socks(void) - num_listen_socks = -1; - } - -+/* -+ * Is this process listening for clients (i.e. not specific to any specific -+ * client connection?) -+ */ -+int listening_for_clients(void) -+{ -+ return num_listen_socks > 0; -+} -+ - static void - close_startup_pipes(void) - { -@@ -561,22 +571,45 @@ sshd_exchange_identification(int sock_in - } - } - --/* Destroy the host and server keys. They will no longer be needed. */ -+/* -+ * Destroy the host and server keys. They will no longer be needed. Careful, -+ * this can be called from cleanup_exit() - i.e. from just about anywhere. -+ */ - void --destroy_sensitive_data(void) -+destroy_sensitive_data(int privsep) - { - int i; -+ pid_t pid; -+ uid_t uid; - - if (sensitive_data.server_key) { - key_free(sensitive_data.server_key); - sensitive_data.server_key = NULL; - } -+ pid = getpid(); -+ uid = getuid(); - for (i = 0; i < options.num_host_key_files; i++) { - if (sensitive_data.host_keys[i]) { -+ char *fp; -+ -+ if (key_is_private(sensitive_data.host_keys[i])) -+ fp = sshkey_fingerprint(sensitive_data.host_keys[i], options.fingerprint_hash, SSH_FP_HEX); -+ else -+ fp = NULL; - key_free(sensitive_data.host_keys[i]); - sensitive_data.host_keys[i] = NULL; -+ if (fp != NULL) { -+ if (privsep) -+ PRIVSEP(audit_destroy_sensitive_data(fp, -+ pid, uid)); -+ else -+ audit_destroy_sensitive_data(fp, -+ pid, uid); -+ free(fp); -+ } - } -- if (sensitive_data.host_certificates[i]) { -+ if (sensitive_data.host_certificates -+ && sensitive_data.host_certificates[i]) { - key_free(sensitive_data.host_certificates[i]); - sensitive_data.host_certificates[i] = NULL; - } -@@ -590,6 +623,8 @@ void - demote_sensitive_data(void) - { - Key *tmp; -+ pid_t pid; -+ uid_t uid; - int i; - - if (sensitive_data.server_key) { -@@ -598,13 +633,25 @@ demote_sensitive_data(void) - sensitive_data.server_key = tmp; - } - -+ pid = getpid(); -+ uid = getuid(); - for (i = 0; i < options.num_host_key_files; i++) { - if (sensitive_data.host_keys[i]) { -+ char *fp; -+ -+ if (key_is_private(sensitive_data.host_keys[i])) -+ fp = sshkey_fingerprint(sensitive_data.host_keys[i], options.fingerprint_hash, SSH_FP_HEX); -+ else -+ fp = NULL; - tmp = key_demote(sensitive_data.host_keys[i]); - key_free(sensitive_data.host_keys[i]); - sensitive_data.host_keys[i] = tmp; - if (tmp->type == KEY_RSA1) - sensitive_data.ssh1_host_key = tmp; -+ if (fp != NULL) { -+ audit_destroy_sensitive_data(fp, pid, uid); -+ free(fp); -+ } - } - /* Certs do not need demotion */ - } -@@ -676,7 +723,7 @@ privsep_preauth(Authctxt *authctxt) - - if (use_privsep == PRIVSEP_ON) - box = ssh_sandbox_init(pmonitor); -- pid = fork(); -+ pmonitor->m_pid = pid = fork(); - if (pid == -1) { - fatal("fork of unprivileged child failed"); - } else if (pid != 0) { -@@ -760,6 +807,12 @@ privsep_postauth(Authctxt *authctxt) - else if (pmonitor->m_pid != 0) { - verbose("User child is on pid %ld", (long)pmonitor->m_pid); - buffer_clear(&loginmsg); -+ if (*pmonitor->m_pkex != NULL ){ -+ newkeys_destroy((*pmonitor->m_pkex)->newkeys[MODE_OUT]); -+ newkeys_destroy((*pmonitor->m_pkex)->newkeys[MODE_IN]); -+ audit_session_key_free_body(2, getpid(), getuid()); -+ packet_destroy_all(0, 0); -+ } - monitor_child_postauth(pmonitor); - - /* NEVERREACHED */ -@@ -1292,6 +1345,7 @@ server_accept_loop(int *sock_in, int *so - if (received_sigterm) { - logit("Received signal %d; terminating.", - (int) received_sigterm); -+ destroy_sensitive_data(0); - close_listen_socks(); - if (options.pid_file != NULL) - unlink(options.pid_file); -@@ -2255,6 +2309,7 @@ main(int ac, char **av) - */ - if (use_privsep) { - mm_send_keystate(pmonitor); -+ packet_destroy_all(1, 1); - exit(0); - } - -@@ -2300,7 +2355,7 @@ main(int ac, char **av) - privsep_postauth(authctxt); - /* the monitor process [priv] will not return */ - if (!compat20) -- destroy_sensitive_data(); -+ destroy_sensitive_data(0); - } - - packet_set_timeout(options.client_alive_interval, -@@ -2314,6 +2369,9 @@ main(int ac, char **av) - do_authenticated(authctxt); - - /* The connection has been terminated. */ -+ packet_destroy_all(1, 1); -+ destroy_sensitive_data(1); -+ - packet_get_bytes(&ibytes, &obytes); - verbose("Transferred: sent %llu, received %llu bytes", - (unsigned long long)obytes, (unsigned long long)ibytes); -@@ -2474,6 +2532,10 @@ do_ssh1_kex(void) - if (cookie[i] != packet_get_char()) - packet_disconnect("IP Spoofing check bytes do not match."); - -+#ifdef SSH_AUDIT_EVENTS -+ audit_kex(2, cipher_name(cipher_type), "crc", "none", "none"); -+#endif -+ - debug("Encryption type: %.200s", cipher_name(cipher_type)); - - /* Get the encrypted integer. */ -@@ -2533,7 +2595,7 @@ do_ssh1_kex(void) - } - - /* Destroy the private and public keys. No longer. */ -- destroy_sensitive_data(); -+ destroy_sensitive_data(1); - - if (use_privsep) - mm_ssh1_session_id(session_id); -@@ -2705,6 +2767,16 @@ do_ssh2_kex(void) - void - cleanup_exit(int i) - { -+ static int in_cleanup = 0; -+ int is_privsep_child; -+ -+ /* cleanup_exit can be called at the very least from the privsep -+ wrappers used for auditing. Make sure we don't recurse -+ indefinitely. */ -+ if (in_cleanup) -+ _exit(i); -+ in_cleanup = 1; -+ - if (the_authctxt) { - do_cleanup(the_authctxt); - if (use_privsep && privsep_is_preauth && -@@ -2716,9 +2788,14 @@ cleanup_exit(int i) - pmonitor->m_pid, strerror(errno)); - } - } -+ is_privsep_child = use_privsep && pmonitor != NULL && pmonitor->m_pid == 0; -+ if (sensitive_data.host_keys != NULL) -+ destroy_sensitive_data(is_privsep_child); -+ packet_destroy_all(1, is_privsep_child); - #ifdef SSH_AUDIT_EVENTS - /* done after do_cleanup so it can cancel the PAM auth 'thread' */ -- if (!use_privsep || mm_is_monitor()) -+ if ((the_authctxt == NULL || !the_authctxt->authenticated) && -+ (!use_privsep || mm_is_monitor())) - audit_event(SSH_CONNECTION_ABANDON); - #endif - _exit(i); -diff -up openssh-7.0p1/sshkey.c.audit openssh-7.0p1/sshkey.c ---- openssh-7.0p1/sshkey.c.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/sshkey.c 2015-08-12 11:33:00.417914278 +0200 -@@ -299,6 +299,33 @@ sshkey_type_is_valid_ca(int type) - } - - int -+sshkey_is_private(const struct sshkey *k) -+{ -+ switch (k->type) { -+#ifdef WITH_OPENSSL -+ case KEY_RSA_CERT: -+ case KEY_RSA1: -+ case KEY_RSA: -+ return k->rsa->d != NULL; -+ case KEY_DSA_CERT: -+ case KEY_DSA: -+ return k->dsa->priv_key != NULL; -+#ifdef OPENSSL_HAS_ECC -+ case KEY_ECDSA_CERT: -+ case KEY_ECDSA: -+ return EC_KEY_get0_private_key(k->ecdsa) != NULL; -+#endif /* OPENSSL_HAS_ECC */ -+#endif /* WITH_OPENSSL */ -+ case KEY_ED25519_CERT: -+ case KEY_ED25519: -+ return (k->ed25519_pk != NULL); -+ default: -+ /* fatal("key_is_private: bad key type %d", k->type); */ -+ return 0; -+ } -+} -+ -+int - sshkey_is_cert(const struct sshkey *k) - { - if (k == NULL) -diff -up openssh-7.0p1/sshkey.h.audit openssh-7.0p1/sshkey.h ---- openssh-7.0p1/sshkey.h.audit 2015-08-11 10:57:29.000000000 +0200 -+++ openssh-7.0p1/sshkey.h 2015-08-12 11:33:00.417914278 +0200 -@@ -132,6 +132,7 @@ u_int sshkey_size(const struct sshkey - int sshkey_generate(int type, u_int bits, struct sshkey **keyp); - int sshkey_from_private(const struct sshkey *, struct sshkey **); - int sshkey_type_from_name(const char *); -+int sshkey_is_private(const struct sshkey *); - int sshkey_is_cert(const struct sshkey *); - int sshkey_type_is_cert(int); - int sshkey_type_plain(int); diff --git a/openssh-6.7p1-coverity.patch b/openssh-6.7p1-coverity.patch index 4b8a26f..8834903 100644 --- a/openssh-6.7p1-coverity.patch +++ b/openssh-6.7p1-coverity.patch @@ -415,8 +415,8 @@ diff -up openssh-6.8p1/sshd.c.coverity openssh-6.8p1/sshd.c --- openssh-6.8p1/sshd.c.coverity 2015-03-18 17:21:51.893264839 +0100 +++ openssh-6.8p1/sshd.c 2015-03-18 17:21:58.284251454 +0100 @@ -778,8 +778,10 @@ privsep_preauth(Authctxt *authctxt) - if (getuid() == 0 || geteuid() == 0) - privsep_preauth_child(); + + privsep_preauth_child(); setproctitle("%s", "[net]"); - if (box != NULL) + if (box != NULL) { diff --git a/openssh-6.7p1-fips.patch b/openssh-6.7p1-fips.patch index 62f9e26..4845a2a 100644 --- a/openssh-6.7p1-fips.patch +++ b/openssh-6.7p1-fips.patch @@ -92,7 +92,7 @@ diff -up openssh-7.0p1/dh.h.fips openssh-7.0p1/dh.h @@ -46,6 +46,7 @@ u_int dh_estimate(int); /* Min and max values from RFC4419. */ - #define DH_GRP_MIN 1024 + #define DH_GRP_MIN 2048 +#define DH_GRP_MIN_FIPS 2048 #define DH_GRP_MAX 8192 @@ -296,18 +296,18 @@ diff -up openssh-7.0p1/Makefile.in.fips openssh-7.0p1/Makefile.in - $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) - ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o roaming_dummy.o readconf.o -- $(LD) -o $@ ssh-keysign.o readconf.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) -+ $(LD) -o $@ ssh-keysign.o readconf.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) + ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o readconf.o +- $(LD) -o $@ ssh-keysign.o readconf.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ++ $(LD) -o $@ ssh-keysign.o readconf.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) @@ -204,7 +204,7 @@ ssh-cavs$(EXEEXT): $(LIBCOMPAT) libssh.a - $(LD) -o $@ ssh-cavs.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + $(LD) -o $@ ssh-cavs.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) - ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o roaming_dummy.o -- $(LD) -o $@ ssh-keyscan.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) -+ $(LD) -o $@ ssh-keyscan.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(LIBS) + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o +- $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) ++ $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(LIBS) sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-server.o sftp-server-main.o $(LD) -o $@ sftp-server.o sftp-common.o sftp-server-main.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) diff --git a/openssh-6.7p1-kdf-cavs.patch b/openssh-6.7p1-kdf-cavs.patch index d219791..2f5874a 100644 --- a/openssh-6.7p1-kdf-cavs.patch +++ b/openssh-6.7p1-kdf-cavs.patch @@ -22,11 +22,11 @@ diff -up openssh-6.8p1/Makefile.in.kdf-cavs openssh-6.8p1/Makefile.in ctr-cavstest$(EXEEXT): $(LIBCOMPAT) libssh.a ctr-cavstest.o $(LD) -o $@ ctr-cavstest.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(LIBS) -+ssh-cavs$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-cavs.o roaming_dummy.o -+ $(LD) -o $@ ssh-cavs.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ++ssh-cavs$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-cavs.o ++ $(LD) -o $@ ssh-cavs.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + - ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o roaming_dummy.o - $(LD) -o $@ ssh-keyscan.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) @@ -331,6 +335,8 @@ install-files: fi diff --git a/openssh-6.7p1-ldap.patch b/openssh-6.7p1-ldap.patch index a555579..1c3cb3e 100644 --- a/openssh-6.7p1-ldap.patch +++ b/openssh-6.7p1-ldap.patch @@ -148,8 +148,8 @@ diff -up openssh-6.8p1/Makefile.in.ldap openssh-6.8p1/Makefile.in LIBOPENSSH_OBJS=\ ssh_api.o \ @@ -112,8 +115,8 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw - sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ - sandbox-seccomp-filter.o sandbox-capsicum.o + sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \ + sandbox-solaris.o -MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out -MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5 @@ -165,8 +165,8 @@ diff -up openssh-6.8p1/Makefile.in.ldap openssh-6.8p1/Makefile.in +ssh-ldap-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o sshbuf-getput-basic.o ssherr.o + $(LD) -o $@ ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o sshbuf-getput-basic.o ssherr.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) + - ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o roaming_dummy.o - $(LD) -o $@ ssh-keyscan.o roaming_dummy.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) @@ -311,6 +317,10 @@ install-files: $(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT) @@ -187,9 +187,9 @@ diff -up openssh-6.8p1/Makefile.in.ldap openssh-6.8p1/Makefile.in + $(INSTALL) -m 644 ssh-ldap-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-ldap-helper.8 ; \ + $(INSTALL) -m 644 ssh-ldap.conf.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/ssh-ldap.conf.5 ; \ + fi - -rm -f $(DESTDIR)$(bindir)/slogin - ln -s ./ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin - -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 + + install-sysconf: + if [ ! -d $(DESTDIR)$(sysconfdir) ]; then \ @@ -356,6 +370,13 @@ install-sysconf: else \ echo "$(DESTDIR)$(sysconfdir)/moduli already exists, install will not overwrite"; \ @@ -218,9 +218,9 @@ diff -up openssh-6.8p1/Makefile.in.ldap openssh-6.8p1/Makefile.in -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-ldap-helper.8 - -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1 regress-prep: + [ -d `pwd`/regress ] || mkdir -p `pwd`/regress diff -up openssh-6.8p1/configure.ac.ldap openssh-6.8p1/configure.ac --- openssh-6.8p1/configure.ac.ldap 2015-03-17 06:49:20.000000000 +0100 +++ openssh-6.8p1/configure.ac 2015-03-18 11:11:29.030801464 +0100 diff --git a/openssh-7.0p1-gssKexAlgorithms.patch b/openssh-7.0p1-gssKexAlgorithms.patch index 6a469ff..6988f2f 100644 --- a/openssh-7.0p1-gssKexAlgorithms.patch +++ b/openssh-7.0p1-gssKexAlgorithms.patch @@ -95,9 +95,9 @@ diff -up openssh-7.0p1/kex.c.gsskexalg openssh-7.0p1/kex.c --- openssh-7.0p1/kex.c.gsskexalg 2015-08-19 12:28:38.078518839 +0200 +++ openssh-7.0p1/kex.c 2015-08-19 12:30:13.249306371 +0200 @@ -50,6 +50,7 @@ + #include "misc.h" #include "dispatch.h" #include "monitor.h" - #include "roaming.h" +#include "xmalloc.h" #include "ssherr.h" @@ -336,9 +336,9 @@ diff -up openssh-7.0p1/ssh_config.5.gsskexalg openssh-7.0p1/ssh_config.5 --- openssh-7.0p1/ssh_config.5.gsskexalg 2015-08-19 12:28:38.028518950 +0200 +++ openssh-7.0p1/ssh_config.5 2015-08-19 12:28:38.082518830 +0200 @@ -786,6 +786,18 @@ command line will be passed untouched to + command line will be passed untouched to the GSSAPI library. The default is .Dq no . - This option only applies to protocol version 2 connections using GSSAPI. +.It Cm GSSAPIKexAlgorithms +The list of key exchange algorithms that are offered for GSSAPI +key exchange. Possible values are diff --git a/openssh-7.0p1-show-more-fingerprints.patch b/openssh-7.0p1-show-more-fingerprints.patch index 52f9535..52c7d73 100644 --- a/openssh-7.0p1-show-more-fingerprints.patch +++ b/openssh-7.0p1-show-more-fingerprints.patch @@ -199,7 +199,7 @@ index f41960c..e12932f 100644 + options.fingerprint_hash[0], SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal("%s: sshkey_fingerprint fail", __func__); - logit("Host key fingerprint is %s\n%s\n", fp, ra); + logit("Host key fingerprint is %s\n%s", fp, ra); @@ -964,12 +964,6 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, else snprintf(msg1, sizeof(msg1), "."); @@ -295,14 +295,14 @@ index 7751031..82ed92e 100644 goto done; debug2("input_userauth_pk_ok: fp %s", fp); @@ -1009,7 +1009,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) - int have_sig = 1; + int matched, ret = -1, have_sig = 1; char *fp; - if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash, + if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) return 0; - debug3("sign_and_send_pubkey: %s %s", key_type(id->key), fp); + debug3("%s: %s %s", __func__, key_type(id->key), fp); @@ -1635,7 +1635,7 @@ userauth_hostbased(Authctxt *authctxt) goto out; } @@ -323,10 +323,23 @@ index 1dca3e2..23bff7d 100644 - if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, + if ((fp = sshkey_fingerprint(key, options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) - fatal("%s: sshkey_fingerprint failed", __func__); + fatal("%s: sshkey_fingerprint failed", __progname); fatal("no matching hostkey found for key %s %s", -- 2.1.0 +diff --git a/sshconnect.c b/sshconnect.c +index de7ace6..f16e606 100644 +--- a/sshconnect.c ++++ b/sshconnect.c +@@ -1262,7 +1262,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) + + if (sshkey_is_cert(host_key)) { + if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, +- options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { ++ options.fingerprint_hash[0], SSH_FP_DEFAULT)) == NULL) { + error("%s: fingerprint CA key: %s", + __func__, ssh_err(r)); + r = -1; diff --git a/openssh-7.1p1-hostkeyalgorithms.patch b/openssh-7.1p1-hostkeyalgorithms.patch deleted file mode 100644 index 4a22cdc..0000000 --- a/openssh-7.1p1-hostkeyalgorithms.patch +++ /dev/null @@ -1,46 +0,0 @@ -diff --git a/readconf.c b/readconf.c -index 374e741..23d74fb 100644 ---- a/readconf.c -+++ b/readconf.c -@@ -2229,6 +2229,10 @@ dump_client_config(Options *o, const char *host) - int i; - char vbuf[5]; - -+ /* This is normally prepared in ssh_kex2 */ -+ if (kex_assemble_names(KEX_DEFAULT_PK_ALG, &o->hostkeyalgorithms) != 0) -+ fatal("%s: kex_assemble_names failed", __func__); -+ - /* Most interesting options first: user, host, port */ - dump_cfg_string(oUser, o->user); - dump_cfg_string(oHostName, host); -@@ -2289,7 +2293,7 @@ dump_client_config(Options *o, const char *host) - dump_cfg_string(oBindAddress, o->bind_address); - dump_cfg_string(oCiphers, o->ciphers ? o->ciphers : KEX_CLIENT_ENCRYPT); - dump_cfg_string(oControlPath, o->control_path); -- dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms ? o->hostkeyalgorithms : KEX_DEFAULT_PK_ALG); -+ dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms); - dump_cfg_string(oHostKeyAlias, o->host_key_alias); - dump_cfg_string(oHostbasedKeyTypes, o->hostbased_key_types); - dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices); -diff --git a/servconf.c b/servconf.c -index 04404a4..08c8139 100644 ---- a/servconf.c -+++ b/servconf.c -@@ -242,8 +242,6 @@ fill_default_server_options(ServerOptions *options) - options->hostbased_authentication = 0; - if (options->hostbased_uses_name_from_packet_only == -1) - options->hostbased_uses_name_from_packet_only = 0; -- if (options->hostkeyalgorithms == NULL) -- options->hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG); - if (options->rsa_authentication == -1) - options->rsa_authentication = 1; - if (options->pubkey_authentication == -1) -@@ -329,6 +327,8 @@ fill_default_server_options(ServerOptions *options) - kex_assemble_names(KEX_SERVER_MAC, &options->macs) != 0 || - kex_assemble_names(KEX_SERVER_KEX, &options->kex_algorithms) != 0 || - kex_assemble_names(KEX_DEFAULT_PK_ALG, -+ &options->hostkeyalgorithms) != 0 || -+ kex_assemble_names(KEX_DEFAULT_PK_ALG, - &options->hostbased_key_types) != 0 || - kex_assemble_names(KEX_DEFAULT_PK_ALG, - &options->pubkey_key_types) != 0) diff --git a/openssh-7.1p1-ssh-copy-id.patch b/openssh-7.1p1-ssh-copy-id.patch deleted file mode 100644 index a4e6f7d..0000000 --- a/openssh-7.1p1-ssh-copy-id.patch +++ /dev/null @@ -1,532 +0,0 @@ -From 1a52b2d612b1d0c2a15dfcdc8da560704909ec72 Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Sat, 27 Jul 2013 14:16:52 +0100 -Subject: [PATCH] echo --> printf "%s: ERROR... (for consistency) - ---- - ssh-copy-id | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index ae88e99..516b87f 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -200,7 +200,7 @@ populate_new_ids() { - umask 0177 - local L_TMP_ID_FILE=$(mktemp ~/.ssh/ssh-copy-id_id.XXXXXXXXXX) - if test $? -ne 0 || test "x$L_TMP_ID_FILE" = "x" ; then -- echo "mktemp failed" 1>&2 -+ printf '%s: ERROR: mktemp failed\n' "$0" >&2 - exit 1 - fi - trap "rm -f $L_TMP_ID_FILE ${L_TMP_ID_FILE}.pub" EXIT TERM INT QUIT --- -1.9.1 - - - -From baebbb9e18e4a1af7554d939710eacb665a24b68 Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Wed, 25 Nov 2015 17:05:39 +0100 -Subject: [PATCH] Deal with remote user shell being e.g. tcsh (fixes: 2206) - -as suggested by Jakub Jelen ---- - ssh-copy-id | 6 ++---- - 1 file changed, 2 insertions(+), 4 deletions(-) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index 516b87f..6a0447a 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -269,10 +269,8 @@ case "$REMOTE_VERSION" in - *) - # Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect - populate_new_ids 0 -- [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | ssh "$@" " -- umask 077 ; -- mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; -- if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi" \ -+ [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \ -+ ssh "$@" "exec sh -c 'umask 077 ; mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ - || exit 1 - ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l) - ;; --- -1.9.1 - - - -From 35f05e39cda8670b3f6797330a3e521fda509a4c Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Wed, 25 Nov 2015 21:14:00 +0100 -Subject: [PATCH] set LogLevel to ensure that it's not set to 'None' (closes: - 2214) - -As pointed out by Sami Haahtinen , -the LogLevel is set to 'None' we'll not get the -Permission Denied we're looking for. ---- - ssh-copy-id | 1 + - 1 file changed, 1 insertion(+) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index 6a0447a..70d3866 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -215,6 +215,7 @@ populate_new_ids() { - # The point being that if file based, ssh needs the private key, which it cannot - # find if only given the contents of the .pub file in an unrelated tmpfile - ssh -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \ -+ -o LogLevel=INFO \ - -o PreferredAuthentications=publickey \ - -o IdentitiesOnly=yes "$@" exit 2>$L_TMP_ID_FILE.stderr -Date: Wed, 25 Nov 2015 22:30:43 +0100 -Subject: [PATCH] set ControlPath=none (closes: 2488) -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -Thanks to Salvador Fandiño for the patch -This seems to be the same problem as described in 2195 ---- - ssh-copy-id | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index 70d3866..7df7fad 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -215,6 +215,7 @@ populate_new_ids() { - # The point being that if file based, ssh needs the private key, which it cannot - # find if only given the contents of the .pub file in an unrelated tmpfile - ssh -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \ -+ -o ControlPath=none \ - -o LogLevel=INFO \ - -o PreferredAuthentications=publickey \ - -o IdentitiesOnly=yes "$@" exit 2>$L_TMP_ID_FILE.stderr &2 - } - --REMOTE_VERSION=$(ssh -v -o PreferredAuthentications=',' "$@" 2>&1 | -+REMOTE_VERSION=$(ssh -v -o PreferredAuthentications=',' -o ControlPath=none "$@" 2>&1 | - sed -ne 's/.*remote software version //p') - - case "$REMOTE_VERSION" in --- -1.9.1 - - - -From 6fa6f1e3dbec32636e77d01228ceecfa3851c7e8 Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Wed, 25 Nov 2015 23:24:13 +0100 -Subject: [PATCH] add -f (forced) option to install keys unconditionally - (closes: 2110) - -Thanks for the patch from Petr Lautrbach -which inspired this. ---- - ssh-copy-id | 15 +++++++++++++-- - ssh-copy-id.1 | 5 +++++ - 2 files changed, 18 insertions(+), 2 deletions(-) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index 7df7fad..3121171 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -59,7 +59,10 @@ fi - DEFAULT_PUB_ID_FILE=$(ls -t ${HOME}/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1) - - usage () { -- printf 'Usage: %s [-h|-?|-n] [-i [identity_file]] [-p port] [[-o ] ...] [user@]hostname\n' "$0" >&2 -+ printf 'Usage: %s [-h|-?|-f|-n] [-i [identity_file]] [-p port] [[-o ] ...] [user@]hostname\n' "$0" >&2 -+ printf '\t-f: force mode -- copy keys without trying to check if they are already installed\n' >&2 -+ printf '\t-n: dry run -- no keys are actually copied\n' >&2 -+ printf '\t-h|-?: print this help\n' >&2 - exit 1 - } - -@@ -121,7 +124,7 @@ do - } - shift - ;; -- -n|-h|-\?) -+ -f|-n|-h|-\?) - OPT="$1" - OPTARG= - shift -@@ -154,6 +157,9 @@ do - -o|-p) - SSH_OPTS="${SSH_OPTS:+$SSH_OPTS }$OPT '$(quote "$OPTARG")'" - ;; -+ -f) -+ FORCED=1 -+ ;; - -n) - DRY_RUN=1 - ;; -@@ -194,6 +200,11 @@ fi - populate_new_ids() { - local L_SUCCESS="$1" - -+ if [ "$FORCED" ] ; then -+ NEW_IDS=$(eval $GET_ID) -+ return -+ fi -+ - # repopulate "$@" inside this function - eval set -- "$SSH_OPTS" - -diff --git $1/contrib/ssh-copy-id.1 $1/contrib/ssh-copy-id.1 -index 67a59e4..8850cce 100644 ---- $1/contrib/ssh-copy-id.1 -+++ $1/contrib/ssh-copy-id.1 -@@ -29,6 +29,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - .Nd use locally available keys to authorise logins on a remote machine - .Sh SYNOPSIS - .Nm -+.Op Fl f - .Op Fl n - .Op Fl i Op Ar identity_file - .Op Fl p Ar port -@@ -76,6 +77,10 @@ is used. - Note that this can be used to ensure that the keys copied have the - comment one prefers and/or extra options applied, by ensuring that the - key file has these set as preferred before the copy is attempted. -+.It Fl f -+Forced mode: doesn't check if the keys are present on the remote server. -+This means that it does not need the private key. Of course, this can result -+in more than one copy of the key being installed on the remote system. - .It Fl n - do a dry-run. Instead of installing keys on the remote system simply - prints the key(s) that would have been installed. --- -1.9.1 - - - -From ab185eea5a03cdd846c909d83e5dd0a07a44fb54 Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Wed, 25 Nov 2015 23:47:06 +0100 -Subject: [PATCH] deal with #2331 by suggesting the use of the -f option - ---- - ssh-copy-id | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index 3121171..8666cea 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -250,7 +250,8 @@ populate_new_ids() { - exit 1 - fi - if [ -z "$NEW_IDS" ] ; then -- printf '\n%s: WARNING: All keys were skipped because they already exist on the remote system.\n\n' "$0" >&2 -+ printf '\n%s: WARNING: All keys were skipped because they already exist on the remote system.\n' "$0" >&2 -+ printf '\t\t(if you think this is a mistake, you may want to use -f option)\n\n' "$0" >&2 - exit 0 - fi - printf '%s: INFO: %d key(s) remain to be installed -- if you are prompted now it is to install the new keys\n' "$0" "$(printf '%s\n' "$NEW_IDS" | wc -l)" >&2 --- -1.9.1 - - - -From de78897ada50ed12f4b0c9faa6e935ce82ee49a6 Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Thu, 26 Nov 2015 00:25:56 +0100 -Subject: [PATCH] handle keys with missing trailing newline (closes: 2350) - ---- - ssh-copy-id | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index 8666cea..362b49b 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -218,7 +218,7 @@ populate_new_ids() { - printf '%s: INFO: attempting to log in with the new key(s), to filter out any that are already installed\n' "$0" >&2 - NEW_IDS=$( - eval $GET_ID | { -- while read ID ; do -+ while read ID || [ "$ID" ] ; do - printf '%s\n' "$ID" > $L_TMP_ID_FILE - - # the next line assumes $PRIV_ID_FILE only set if using a single id file - this --- -1.9.1 - - - -From 6b903ab99a3f0107bb0dbde748a4372033bab00c Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Thu, 26 Nov 2015 00:36:09 +0100 -Subject: [PATCH] add a cd to ensure we're in the remote's home directory - (closes: 2349) - ---- - ssh-copy-id | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index 362b49b..2932936 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -284,7 +284,7 @@ case "$REMOTE_VERSION" in - # Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect - populate_new_ids 0 - [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \ -- ssh "$@" "exec sh -c 'umask 077 ; mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ -+ ssh "$@" "exec sh -c 'cd ; umask 077 ; mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ - || exit 1 - ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l) - ;; --- -1.9.1 - - - -From 441892cbf4ff96fd96908582b8170f51890b5deb Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Sat, 28 Nov 2015 14:42:36 +0100 -Subject: [PATCH] add comment about why the ugly one-line remote command is as - it is - -In case anyone looks here for the details: - * tcsh doesn't support multi-line strings, - which is why it's a one-liner. - * tcsh doesn't do 2>&1, and fish doesn't do - 'command || command' which is why we're runnig this under - sh (which is very likely to be a POSIX shell on any vaguely - Unix-like system) - * The 'cd' is there to make sure we're in the home dir, because - there was a bug report about having a cd in ~/.bashrc that resulted - in a .ssh being created elsewhere. - * the 'exec' ensures that we're not relying on anything beyond the - (hopefully POSIX) shell that's available as 'sh' on the remote system ---- - ssh-copy-id | 1 + - 1 file changed, 1 insertion(+) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index 2932936..04c03eb 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -283,6 +283,7 @@ case "$REMOTE_VERSION" in - *) - # Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect - populate_new_ids 0 -+ # in ssh below - to defend against quirky remote shells: use 'exec sh -c' to get POSIX; 'cd' to be at $HOME; and all on one line, because tcsh. - [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \ - ssh "$@" "exec sh -c 'cd ; umask 077 ; mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ - || exit 1 --- -1.9.1 - - - -From 8b59b122d321b97badd15c41e1a22863aa922a02 Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Sat, 28 Nov 2015 14:46:47 +0100 -Subject: [PATCH] with '-f' there's no need to have access to the private key - ---- - ssh-copy-id | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index 04c03eb..d3ff83b 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -80,7 +80,7 @@ use_id_file() { - PUB_ID_FILE="$L_ID_FILE.pub" - fi - -- PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub) -+ [ "$FORCED" ] || PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub) - - # check that the files are readable - for f in $PUB_ID_FILE $PRIV_ID_FILE ; do --- -1.9.1 - - - -From 1b931894de0614099255244be789ad097fd0948a Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Sat, 28 Nov 2015 14:47:35 +0100 -Subject: [PATCH] if the private key is missing, point out that '-f' might be - what's needed - ---- - ssh-copy-id | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index d3ff83b..f0b01aa 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -85,7 +85,9 @@ use_id_file() { - # check that the files are readable - for f in $PUB_ID_FILE $PRIV_ID_FILE ; do - ErrMSG=$( { : < $f ; } 2>&1 ) || { -- printf "\n%s: ERROR: failed to open ID file '%s': %s\n\n" "$0" "$f" "$(printf "%s\n" "$ErrMSG" | sed -e 's/.*: *//')" -+ local L_PRIVMSG="" -+ [ "$f" = "$PRIV_ID_FILE" ] && L_PRIVMSG=" (to install the contents of '$PUB_ID_FILE' anyway, look at the -f option)" -+ printf "\n%s: ERROR: failed to open ID file '%s': %s\n" "$0" "$f" "$(printf "%s\n%s\n" "$ErrMSG" "$L_PRIVMSG" | sed -e 's/.*: *//')" - exit 1 - } - done --- -1.9.1 - - - -From fd3e8b115e160a1332773cd8e06a3305d0d680ab Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Sat, 28 Nov 2015 21:10:39 +0100 -Subject: [PATCH] +INFO message to mitigate the surprise described in #2196 - ---- - ssh-copy-id | 1 + - 1 file changed, 1 insertion(+) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index f0b01aa..994194e 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -91,6 +91,7 @@ use_id_file() { - exit 1 - } - done -+ printf '%s: INFO: Source of key(s) to be installed: "%s"\n' "$0" $PUB_ID_FILE >&2 - GET_ID="cat \"$PUB_ID_FILE\"" - } - --- -1.9.1 - - - -From 783ef08b0a757402aba67313f08f8dbfa9bf85f3 Mon Sep 17 00:00:00 2001 -From: Philip Hands -Date: Mon, 30 Nov 2015 20:46:19 +0100 -Subject: [PATCH] deal with $HOME and id filenames that include a space - ---- - ssh-copy-id | 15 ++++++++------- - 1 file changed, 8 insertions(+), 7 deletions(-) - -diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id -index 994194e..afde8b1 100755 ---- $1/contrib/ssh-copy-id -+++ $1/contrib/ssh-copy-id -@@ -56,7 +56,7 @@ then - fi - fi - --DEFAULT_PUB_ID_FILE=$(ls -t ${HOME}/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1) -+DEFAULT_PUB_ID_FILE="$HOME/$(cd "$HOME" ; ls -t .ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1)" - - usage () { - printf 'Usage: %s [-h|-?|-f|-n] [-i [identity_file]] [-p port] [[-o ] ...] [user@]hostname\n' "$0" >&2 -@@ -83,15 +83,15 @@ use_id_file() { - [ "$FORCED" ] || PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub) - - # check that the files are readable -- for f in $PUB_ID_FILE $PRIV_ID_FILE ; do -- ErrMSG=$( { : < $f ; } 2>&1 ) || { -+ for f in "$PUB_ID_FILE" ${PRIV_ID_FILE:+"$PRIV_ID_FILE"} ; do -+ ErrMSG=$( { : < "$f" ; } 2>&1 ) || { - local L_PRIVMSG="" - [ "$f" = "$PRIV_ID_FILE" ] && L_PRIVMSG=" (to install the contents of '$PUB_ID_FILE' anyway, look at the -f option)" - printf "\n%s: ERROR: failed to open ID file '%s': %s\n" "$0" "$f" "$(printf "%s\n%s\n" "$ErrMSG" "$L_PRIVMSG" | sed -e 's/.*: *//')" - exit 1 - } - done -- printf '%s: INFO: Source of key(s) to be installed: "%s"\n' "$0" $PUB_ID_FILE >&2 -+ printf '%s: INFO: Source of key(s) to be installed: "%s"\n' "$0" "$PUB_ID_FILE" >&2 - GET_ID="cat \"$PUB_ID_FILE\"" - } - -@@ -217,12 +217,13 @@ populate_new_ids() { - printf '%s: ERROR: mktemp failed\n' "$0" >&2 - exit 1 - fi -- trap "rm -f $L_TMP_ID_FILE ${L_TMP_ID_FILE}.pub" EXIT TERM INT QUIT -+ local L_CLEANUP="rm -f \"$L_TMP_ID_FILE\" \"${L_TMP_ID_FILE}.stderr\"" -+ trap "$L_CLEANUP" EXIT TERM INT QUIT - printf '%s: INFO: attempting to log in with the new key(s), to filter out any that are already installed\n' "$0" >&2 - NEW_IDS=$( - eval $GET_ID | { - while read ID || [ "$ID" ] ; do -- printf '%s\n' "$ID" > $L_TMP_ID_FILE -+ printf '%s\n' "$ID" > "$L_TMP_ID_FILE" - - # the next line assumes $PRIV_ID_FILE only set if using a single id file - this - # assumption will break if we implement the possibility of multiple -i options. -@@ -246,7 +247,7 @@ populate_new_ids() { - done - } - ) -- rm -f $L_TMP_ID_FILE* && trap - EXIT TERM INT QUIT -+ eval "$L_CLEANUP" && trap - EXIT TERM INT QUIT - - if expr "$NEW_IDS" : "^ERROR: " >/dev/null ; then - printf '\n%s: %s\n\n' "$0" "$NEW_IDS" >&2 --- -1.9.1 - - -diff --git a/contrib/ssh-copy-id b/contrib/ssh-copy-id -index afde8b1..cd52764 100644 ---- a/contrib/ssh-copy-id -+++ b/contrib/ssh-copy-id -@@ -99,6 +99,8 @@ if [ -n "$SSH_AUTH_SOCK" ] && ssh-add -L >/dev/null 2>&1 ; then - GET_ID="ssh-add -L" - fi - -+[ "x$SSH_COPY_ID_LEGACY" != "x" ] && FORCED=1 -+ - while test "$#" -gt 0 - do - [ "${SEEN_OPT_I}" ] && expr "$1" : "[-]i" >/dev/null && { -diff --git a/contrib/ssh-copy-id.1 b/contrib/ssh-copy-id.1 -index 8850cce..62f112d 100644 ---- a/contrib/ssh-copy-id.1 -+++ b/contrib/ssh-copy-id.1 -@@ -185,6 +185,22 @@ should prove enlightening (N.B. the modern approach is to use the - .Fl W - option, rather than - .Xr nc 1 ) . -+.Sh ENVIRONMENT -+.Bl -tag -width Ds -+.Pp -+.It Pa SSH_COPY_ID_LEGACY -+If the -+.Cm SSH_COPY_ID_LEGACY -+environment variable is set, the -+.Nm -+is run in a legacy mode. In this mode, the -+.Nm -+doesn't check an existence of a private key and doesn't do remote checks -+of the remote server versions or if public keys are already installed -+(equivalent to -+.Fl f -+switch). -+.El - .Sh "SEE ALSO" - .Xr ssh 1 , - .Xr ssh-agent 1 , diff --git a/openssh-7.1p2-audit-race-condition.patch b/openssh-7.1p2-audit-race-condition.patch index 04039a7..0a58fb4 100644 --- a/openssh-7.1p2-audit-race-condition.patch +++ b/openssh-7.1p2-audit-race-condition.patch @@ -77,7 +77,7 @@ index 8949fd1..9afb764 100644 +#endif + static int is_child = 0; - + static int in_chroot = 0; static int have_dev_log = 1; @@ -875,6 +879,8 @@ do_exec(Session *s, const char *command) } diff --git a/openssh-7.1p2-fallback-x11-untrusted.patch b/openssh-7.1p2-fallback-x11-untrusted.patch deleted file mode 100644 index 3fdf5fe..0000000 --- a/openssh-7.1p2-fallback-x11-untrusted.patch +++ /dev/null @@ -1,387 +0,0 @@ -From f98a09cacff7baad8748c9aa217afd155a4d493f Mon Sep 17 00:00:00 2001 -From: "mmcc@openbsd.org" -Date: Tue, 20 Oct 2015 03:36:35 +0000 -Subject: [PATCH] upstream commit - -Replace a function-local allocation with stack memory. - -ok djm@ - -Upstream-ID: c09fbbab637053a2ab9f33ca142b4e20a4c5a17e ---- - clientloop.c | 9 ++------- - 1 file changed, 2 insertions(+), 7 deletions(-) - -diff --git a/clientloop.c b/clientloop.c -index 87ceb3d..1e05cba 100644 ---- a/clientloop.c -+++ b/clientloop.c -@@ -311,11 +311,10 @@ client_x11_get_proto(const char *display, const char *xauth_path, - static char proto[512], data[512]; - FILE *f; - int got_data = 0, generated = 0, do_unlink = 0, i; -- char *xauthdir, *xauthfile; -+ char xauthdir[PATH_MAX] = "", xauthfile[PATH_MAX] = ""; - struct stat st; - u_int now, x11_timeout_real; - -- xauthdir = xauthfile = NULL; - *_proto = proto; - *_data = data; - proto[0] = data[0] = '\0'; -@@ -343,8 +342,6 @@ client_x11_get_proto(const char *display, const char *xauth_path, - display = xdisplay; - } - if (trusted == 0) { -- xauthdir = xmalloc(PATH_MAX); -- xauthfile = xmalloc(PATH_MAX); - mktemp_proto(xauthdir, PATH_MAX); - /* - * The authentication cookie should briefly outlive -@@ -407,8 +404,6 @@ client_x11_get_proto(const char *display, const char *xauth_path, - unlink(xauthfile); - rmdir(xauthdir); - } -- free(xauthdir); -- free(xauthfile); - - /* - * If we didn't get authentication data, just make up some --- -2.5.0 - -From ed4ce82dbfa8a3a3c8ea6fa0db113c71e234416c Mon Sep 17 00:00:00 2001 -From: "djm@openbsd.org" -Date: Wed, 13 Jan 2016 23:04:47 +0000 -Subject: [PATCH] upstream commit - -eliminate fallback from untrusted X11 forwarding to trusted - forwarding when the X server disables the SECURITY extension; Reported by - Thomas Hoger; ok deraadt@ - -Upstream-ID: f76195bd2064615a63ef9674a0e4096b0713f938 ---- - clientloop.c | 114 ++++++++++++++++++++++++++++++++++++----------------------- - clientloop.h | 4 +-- - mux.c | 22 ++++++------ - ssh.c | 23 +++++------- - 4 files changed, 93 insertions(+), 70 deletions(-) - -diff --git a/clientloop.c b/clientloop.c -index f555451..c0386d5 100644 ---- a/clientloop.c -+++ b/clientloop.c -@@ -288,6 +288,9 @@ client_x11_display_valid(const char *display) - { - size_t i, dlen; - -+ if (display == NULL) -+ return 0; -+ - dlen = strlen(display); - for (i = 0; i < dlen; i++) { - if (!isalnum((u_char)display[i]) && -@@ -301,34 +304,33 @@ client_x11_display_valid(const char *display) - - #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" - #define X11_TIMEOUT_SLACK 60 --void -+int - client_x11_get_proto(const char *display, const char *xauth_path, - u_int trusted, u_int timeout, char **_proto, char **_data) - { -- char cmd[1024]; -- char line[512]; -- char xdisplay[512]; -+ char cmd[1024], line[512], xdisplay[512]; -+ char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; - static char proto[512], data[512]; - FILE *f; -- int got_data = 0, generated = 0, do_unlink = 0, i; -- char xauthdir[PATH_MAX] = "", xauthfile[PATH_MAX] = ""; -+ int got_data = 0, generated = 0, do_unlink = 0, i, r; - struct stat st; - u_int now, x11_timeout_real; - - *_proto = proto; - *_data = data; -- proto[0] = data[0] = '\0'; -+ proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; - -- if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) { -- debug("No xauth program."); -- } else if (!client_x11_display_valid(display)) { -- logit("DISPLAY '%s' invalid, falling back to fake xauth data", -+ if (!client_x11_display_valid(display)) { -+ logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", - display); -- } else { -- if (display == NULL) { -- debug("x11_get_proto: DISPLAY not set"); -- return; -- } -+ return -1; -+ } -+ if (xauth_path != NULL && stat(xauth_path, &st) == -1) { -+ debug("No xauth program."); -+ xauth_path = NULL; -+ } -+ -+ if (xauth_path != NULL) { - /* - * Handle FamilyLocal case where $DISPLAY does - * not match an authorization entry. For this we -@@ -337,43 +339,60 @@ client_x11_get_proto(const char *display, const char *xauth_path, - * is not perfect. - */ - if (strncmp(display, "localhost:", 10) == 0) { -- snprintf(xdisplay, sizeof(xdisplay), "unix:%s", -- display + 10); -+ if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", -+ display + 10)) < 0 || -+ (size_t)r >= sizeof(xdisplay)) { -+ error("%s: display name too long", __func__); -+ return -1; -+ } - display = xdisplay; - } - if (trusted == 0) { -- mktemp_proto(xauthdir, PATH_MAX); - /* -+ * Generate an untrusted X11 auth cookie. -+ * - * The authentication cookie should briefly outlive - * ssh's willingness to forward X11 connections to - * avoid nasty fail-open behaviour in the X server. - */ -+ mktemp_proto(xauthdir, sizeof(xauthdir)); -+ if (mkdtemp(xauthdir) == NULL) { -+ error("%s: mkdtemp: %s", -+ __func__, strerror(errno)); -+ return -1; -+ } -+ do_unlink = 1; -+ if ((r = snprintf(xauthfile, sizeof(xauthfile), -+ "%s/xauthfile", xauthdir)) < 0 || -+ (size_t)r >= sizeof(xauthfile)) { -+ error("%s: xauthfile path too long", __func__); -+ unlink(xauthfile); -+ rmdir(xauthdir); -+ return -1; -+ } -+ - if (timeout >= UINT_MAX - X11_TIMEOUT_SLACK) - x11_timeout_real = UINT_MAX; - else - x11_timeout_real = timeout + X11_TIMEOUT_SLACK; -- if (mkdtemp(xauthdir) != NULL) { -- do_unlink = 1; -- snprintf(xauthfile, PATH_MAX, "%s/xauthfile", -- xauthdir); -- snprintf(cmd, sizeof(cmd), -- "%s -f %s generate %s " SSH_X11_PROTO -- " untrusted timeout %u 2>" _PATH_DEVNULL, -- xauth_path, xauthfile, display, -- x11_timeout_real); -- debug2("x11_get_proto: %s", cmd); -- if (x11_refuse_time == 0) { -- now = monotime() + 1; -- if (UINT_MAX - timeout < now) -- x11_refuse_time = UINT_MAX; -- else -- x11_refuse_time = now + timeout; -- channel_set_x11_refuse_time( -- x11_refuse_time); -- } -- if (system(cmd) == 0) -- generated = 1; -+ if ((r = snprintf(cmd, sizeof(cmd), -+ "%s -f %s generate %s " SSH_X11_PROTO -+ " untrusted timeout %u 2>" _PATH_DEVNULL, -+ xauth_path, xauthfile, display, -+ x11_timeout_real)) < 0 || -+ (size_t)r >= sizeof(cmd)) -+ fatal("%s: cmd too long", __func__); -+ debug2("%s: %s", __func__, cmd); -+ if (x11_refuse_time == 0) { -+ now = monotime() + 1; -+ if (UINT_MAX - timeout < now) -+ x11_refuse_time = UINT_MAX; -+ else -+ x11_refuse_time = now + timeout; -+ channel_set_x11_refuse_time(x11_refuse_time); - } -+ if (system(cmd) == 0) -+ generated = 1; - } - - /* -@@ -395,9 +414,7 @@ client_x11_get_proto(const char *display, const char *xauth_path, - got_data = 1; - if (f) - pclose(f); -- } else -- error("Warning: untrusted X11 forwarding setup failed: " -- "xauth key data not generated"); -+ } - } - - if (do_unlink) { -@@ -405,6 +422,13 @@ client_x11_get_proto(const char *display, const char *xauth_path, - rmdir(xauthdir); - } - -+ /* Don't fall back to fake X11 data for untrusted forwarding */ -+ if (!trusted && !got_data) { -+ error("Warning: untrusted X11 forwarding setup failed: " -+ "xauth key data not generated"); -+ return -1; -+ } -+ - /* - * If we didn't get authentication data, just make up some - * data. The forwarding code will check the validity of the -@@ -427,6 +451,8 @@ client_x11_get_proto(const char *display, const char *xauth_path, - rnd >>= 8; - } - } -+ -+ return 0; - } - - /* -diff --git a/clientloop.h b/clientloop.h -index 338d451..f4d4c69 100644 ---- a/clientloop.h -+++ b/clientloop.h -@@ -39,7 +39,7 @@ - - /* Client side main loop for the interactive session. */ - int client_loop(int, int, int); --void client_x11_get_proto(const char *, const char *, u_int, u_int, -+int client_x11_get_proto(const char *, const char *, u_int, u_int, - char **, char **); - void client_global_request_reply_fwd(int, u_int32_t, void *); - void client_session2_setup(int, int, int, const char *, struct termios *, -diff --git a/mux.c b/mux.c -index f9c3af6..6bf53eb 100644 ---- a/mux.c -+++ b/mux.c -@@ -1354,16 +1354,18 @@ mux_session_confirm(int id, int success, void *arg) - char *proto, *data; - - /* Get reasonable local authentication information. */ -- client_x11_get_proto(display, options.xauth_location, -+ if (client_x11_get_proto(display, options.xauth_location, - options.forward_x11_trusted, options.forward_x11_timeout, -- &proto, &data); -- /* Request forwarding with authentication spoofing. */ -- debug("Requesting X11 forwarding with authentication " -- "spoofing."); -- x11_request_forwarding_with_spoofing(id, display, proto, -- data, 1); -- client_expect_confirm(id, "X11 forwarding", CONFIRM_WARN); -- /* XXX exit_on_forward_failure */ -+ &proto, &data) == 0) { -+ /* Request forwarding with authentication spoofing. */ -+ debug("Requesting X11 forwarding with authentication " -+ "spoofing."); -+ x11_request_forwarding_with_spoofing(id, display, proto, -+ data, 1); -+ /* XXX exit_on_forward_failure */ -+ client_expect_confirm(id, "X11 forwarding", -+ CONFIRM_WARN); -+ } - } - - if (cctx->want_agent_fwd && options.forward_agent) { -diff --git a/ssh.c b/ssh.c -index 81704ab..096c5b5 100644 ---- a/ssh.c -+++ b/ssh.c -@@ -1626,6 +1626,7 @@ ssh_session(void) - struct winsize ws; - char *cp; - const char *display; -+ char *proto = NULL, *data = NULL; - - /* Enable compression if requested. */ - if (options.compression) { -@@ -1696,13 +1697,9 @@ ssh_session(void) - display = getenv("DISPLAY"); - if (display == NULL && options.forward_x11) - debug("X11 forwarding requested but DISPLAY not set"); -- if (options.forward_x11 && display != NULL) { -- char *proto, *data; -- /* Get reasonable local authentication information. */ -- client_x11_get_proto(display, options.xauth_location, -- options.forward_x11_trusted, -- options.forward_x11_timeout, -- &proto, &data); -+ if (options.forward_x11 && client_x11_get_proto(display, -+ options.xauth_location, options.forward_x11_trusted, -+ options.forward_x11_timeout, &proto, &data) == 0) { - /* Request forwarding with authentication spoofing. */ - debug("Requesting X11 forwarding with authentication " - "spoofing."); -@@ -1792,6 +1789,7 @@ ssh_session2_setup(int id, int success, void *arg) - extern char **environ; - const char *display; - int interactive = tty_flag; -+ char *proto = NULL, *data = NULL; - - if (!success) - return; /* No need for error message, channels code sens one */ -@@ -1799,12 +1797,9 @@ ssh_session2_setup(int id, int success, void *arg) - display = getenv("DISPLAY"); - if (display == NULL && options.forward_x11) - debug("X11 forwarding requested but DISPLAY not set"); -- if (options.forward_x11 && display != NULL) { -- char *proto, *data; -- /* Get reasonable local authentication information. */ -- client_x11_get_proto(display, options.xauth_location, -- options.forward_x11_trusted, -- options.forward_x11_timeout, &proto, &data); -+ if (options.forward_x11 && client_x11_get_proto(display, -+ options.xauth_location, options.forward_x11_trusted, -+ options.forward_x11_timeout, &proto, &data) == 0) { - /* Request forwarding with authentication spoofing. */ - debug("Requesting X11 forwarding with authentication " - "spoofing."); --- -2.5.0 - -From 5658ef2501e785fbbdf5de2dc33b1ff7a4dca73a Mon Sep 17 00:00:00 2001 -From: "millert@openbsd.org" -Date: Mon, 1 Feb 2016 21:18:17 +0000 -Subject: upstream commit - -Avoid ugly "DISPLAY "(null)" invalid; disabling X11 - forwarding" message when DISPLAY is not set. This could also result in a - crash on systems with a printf that doesn't handle NULL. OK djm@ - -Upstream-ID: 20ee0cfbda678a247264c20ed75362042b90b412 ---- - clientloop.c | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/clientloop.c b/clientloop.c -index f8f9a3f..f0a08f2 100644 ---- a/clientloop.c -+++ b/clientloop.c -@@ -318,8 +318,9 @@ client_x11_get_proto(const char *display, const char *xauth_path, - proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; - - if (!client_x11_display_valid(display)) { -- logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", -- display); -+ if (display != NULL) -+ logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", -+ display); - return -1; - } - if (xauth_path != NULL && stat(xauth_path, &st) == -1) { --- -cgit v0.11.2 - - diff --git a/openssh-7.2p1-audit.patch b/openssh-7.2p1-audit.patch new file mode 100644 index 0000000..e7574b5 --- /dev/null +++ b/openssh-7.2p1-audit.patch @@ -0,0 +1,2285 @@ +diff -up openssh-7.2p1/audit-bsm.c.audit openssh-7.2p1/audit-bsm.c +--- openssh-7.2p1/audit-bsm.c.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/audit-bsm.c 2016-02-12 18:24:34.212825181 +0100 +@@ -375,10 +375,23 @@ audit_connection_from(const char *host, + #endif + } + +-void ++int + audit_run_command(const char *command) + { + /* not implemented */ ++ return 0; ++} ++ ++void ++audit_end_command(int handle, const char *command) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_count_session_open(void) ++{ ++ /* not necessary */ + } + + void +@@ -393,6 +406,12 @@ audit_session_close(struct logininfo *li + /* not implemented */ + } + ++int ++audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv) ++{ ++ /* not implemented */ ++} ++ + void + audit_event(ssh_audit_event_t event) + { +@@ -454,4 +473,40 @@ audit_event(ssh_audit_event_t event) + debug("%s: unhandled event %d", __func__, event); + } + } ++ ++void ++audit_unsupported_body(int what) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, uid_t uid) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_destroy_sensitive_data(const char *fp) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_generate_ephemeral_server_key(const char *fp) ++{ ++ /* not implemented */ ++} + #endif /* BSM */ +diff -up openssh-7.2p1/audit.c.audit openssh-7.2p1/audit.c +--- openssh-7.2p1/audit.c.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/audit.c 2016-02-12 18:24:34.216825179 +0100 +@@ -28,6 +28,7 @@ + + #include + #include ++#include + + #ifdef SSH_AUDIT_EVENTS + +@@ -36,6 +37,11 @@ + #include "key.h" + #include "hostfile.h" + #include "auth.h" ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++#include "xmalloc.h" ++#include "misc.h" ++#include "servconf.h" + + /* + * Care must be taken when using this since it WILL NOT be initialized when +@@ -43,6 +49,7 @@ + * audit_event(CONNECTION_ABANDON) is called. Test for NULL before using. + */ + extern Authctxt *the_authctxt; ++extern ServerOptions options; + + /* Maybe add the audit class to struct Authmethod? */ + ssh_audit_event_t +@@ -71,13 +78,10 @@ audit_classify_auth(const char *method) + const char * + audit_username(void) + { +- static const char unknownuser[] = "(unknown user)"; +- static const char invaliduser[] = "(invalid user)"; ++ static const char unknownuser[] = "(unknown)"; + +- if (the_authctxt == NULL || the_authctxt->user == NULL) ++ if (the_authctxt == NULL || the_authctxt->user == NULL || !the_authctxt->valid) + return (unknownuser); +- if (!the_authctxt->valid) +- return (invaliduser); + return (the_authctxt->user); + } + +@@ -111,6 +115,40 @@ audit_event_lookup(ssh_audit_event_t ev) + return(event_lookup[i].name); + } + ++void ++audit_key(int host_user, int *rv, const Key *key) ++{ ++ char *fp; ++ const char *crypto_name; ++ ++ fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_HEX); ++ if (key->type == KEY_RSA1) ++ crypto_name = "ssh-rsa1"; ++ else ++ crypto_name = key_ssh_name(key); ++ if (audit_keyusage(host_user, crypto_name, key_size(key), fp, *rv) == 0) ++ *rv = 0; ++ free(fp); ++} ++ ++void ++audit_unsupported(int what) ++{ ++ PRIVSEP(audit_unsupported_body(what)); ++} ++ ++void ++audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) ++{ ++ PRIVSEP(audit_kex_body(ctos, enc, mac, comp, pfs, getpid(), getuid())); ++} ++ ++void ++audit_session_key_free(int ctos) ++{ ++ PRIVSEP(audit_session_key_free_body(ctos, getpid(), getuid())); ++} ++ + # ifndef CUSTOM_SSH_AUDIT_EVENTS + /* + * Null implementations of audit functions. +@@ -140,6 +178,17 @@ audit_event(ssh_audit_event_t event) + } + + /* ++ * Called when a child process has called, or will soon call, ++ * audit_session_open. ++ */ ++void ++audit_count_session_open(void) ++{ ++ debug("audit count session open euid %d user %s", geteuid(), ++ audit_username()); ++} ++ ++/* + * Called when a user session is started. Argument is the tty allocated to + * the session, or NULL if no tty was allocated. + * +@@ -174,13 +223,91 @@ audit_session_close(struct logininfo *li + /* + * This will be called when a user runs a non-interactive command. Note that + * it may be called multiple times for a single connection since SSH2 allows +- * multiple sessions within a single connection. ++ * multiple sessions within a single connection. Returns a "handle" for ++ * audit_end_command. + */ +-void ++int + audit_run_command(const char *command) + { + debug("audit run command euid %d user %s command '%.200s'", geteuid(), + audit_username(), command); ++ return 0; ++} ++ ++/* ++ * This will be called when the non-interactive command finishes. Note that ++ * it may be called multiple times for a single connection since SSH2 allows ++ * multiple sessions within a single connection. "handle" should come from ++ * the corresponding audit_run_command. ++ */ ++void ++audit_end_command(int handle, const char *command) ++{ ++ debug("audit end nopty exec euid %d user %s command '%.200s'", geteuid(), ++ audit_username(), command); ++} ++ ++/* ++ * This will be called when user is successfully autherized by the RSA1/RSA/DSA key. ++ * ++ * Type is the key type, len is the key length(byte) and fp is the fingerprint of the key. ++ */ ++int ++audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv) ++{ ++ debug("audit %s key usage euid %d user %s key type %s key length %d fingerprint %s%s, result %d", ++ host_user ? "pubkey" : "hostbased", geteuid(), audit_username(), type, bits, ++ sshkey_fingerprint_prefix(), fp, rv); ++} ++ ++/* ++ * This will be called when the protocol negotiation fails. ++ */ ++void ++audit_unsupported_body(int what) ++{ ++ debug("audit unsupported protocol euid %d type %d", geteuid(), what); ++} ++ ++/* ++ * This will be called on succesfull protocol negotiation. ++ */ ++void ++audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, ++ uid_t uid) ++{ ++ debug("audit protocol negotiation euid %d direction %d cipher %s mac %s compresion %s pfs %s from pid %ld uid %u", ++ (unsigned)geteuid(), ctos, enc, mac, compress, pfs, (long)pid, ++ (unsigned)uid); ++} ++ ++/* ++ * This will be called on succesfull session key discard ++ */ ++void ++audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++ debug("audit session key discard euid %u direction %d from pid %ld uid %u", ++ (unsigned)geteuid(), ctos, (long)pid, (unsigned)uid); ++} ++ ++/* ++ * This will be called on destroy private part of the server key ++ */ ++void ++audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++{ ++ debug("audit destroy sensitive data euid %d fingerprint %s from pid %ld uid %u", ++ geteuid(), fp, (long)pid, (unsigned)uid); ++} ++ ++/* ++ * This will be called on generation of the ephemeral server key ++ */ ++void ++audit_generate_ephemeral_server_key(const char *) ++{ ++ debug("audit create ephemeral server key euid %d fingerprint %s", geteuid(), fp); + } + # endif /* !defined CUSTOM_SSH_AUDIT_EVENTS */ + #endif /* SSH_AUDIT_EVENTS */ +diff -up openssh-7.2p1/audit.h.audit openssh-7.2p1/audit.h +--- openssh-7.2p1/audit.h.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/audit.h 2016-02-12 18:24:34.216825179 +0100 +@@ -28,6 +28,7 @@ + # define _SSH_AUDIT_H + + #include "loginrec.h" ++#include "key.h" + + enum ssh_audit_event_type { + SSH_LOGIN_EXCEED_MAXTRIES, +@@ -47,11 +48,25 @@ enum ssh_audit_event_type { + }; + typedef enum ssh_audit_event_type ssh_audit_event_t; + ++int listening_for_clients(void); ++ + void audit_connection_from(const char *, int); + void audit_event(ssh_audit_event_t); ++void audit_count_session_open(void); + void audit_session_open(struct logininfo *); + void audit_session_close(struct logininfo *); +-void audit_run_command(const char *); ++int audit_run_command(const char *); ++void audit_end_command(int, const char *); + ssh_audit_event_t audit_classify_auth(const char *); ++int audit_keyusage(int, const char *, unsigned, char *, int); ++void audit_key(int, int *, const Key *); ++void audit_unsupported(int); ++void audit_kex(int, char *, char *, char *, char *); ++void audit_unsupported_body(int); ++void audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); ++void audit_session_key_free(int ctos); ++void audit_session_key_free_body(int ctos, pid_t, uid_t); ++void audit_destroy_sensitive_data(const char *, pid_t, uid_t); ++void audit_generate_ephemeral_server_key(const char *); + + #endif /* _SSH_AUDIT_H */ +diff -up openssh-7.2p1/audit-linux.c.audit openssh-7.2p1/audit-linux.c +--- openssh-7.2p1/audit-linux.c.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/audit-linux.c 2016-02-12 18:24:34.219825178 +0100 +@@ -35,13 +35,25 @@ + + #include "log.h" + #include "audit.h" ++#include "key.h" ++#include "hostfile.h" ++#include "auth.h" ++#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ ++#include "servconf.h" + #include "canohost.h" ++#include "packet.h" ++#include "cipher.h" + ++#define AUDIT_LOG_SIZE 256 ++ ++extern ServerOptions options; ++extern Authctxt *the_authctxt; ++extern u_int utmp_len; + const char* audit_username(void); + +-int +-linux_audit_record_event(int uid, const char *username, +- const char *hostname, const char *ip, const char *ttyn, int success) ++static void ++linux_audit_user_logxxx(int uid, const char *username, ++ const char *hostname, const char *ip, const char *ttyn, int success, int event) + { + int audit_fd, rc, saved_errno; + +@@ -49,11 +61,11 @@ linux_audit_record_event(int uid, const + if (audit_fd < 0) { + if (errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT) +- return 1; /* No audit support in kernel */ ++ return; /* No audit support in kernel */ + else +- return 0; /* Must prevent login */ ++ goto fatal_report; /* Must prevent login */ + } +- rc = audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN, ++ rc = audit_log_acct_message(audit_fd, event, + NULL, "login", username ? username : "(unknown)", + username == NULL ? uid : -1, hostname, ip, ttyn, success); + saved_errno = errno; +@@ -65,35 +77,154 @@ linux_audit_record_event(int uid, const + if ((rc == -EPERM) && (geteuid() != 0)) + rc = 0; + errno = saved_errno; +- return (rc >= 0); ++ if (rc < 0) { ++fatal_report: ++ fatal("linux_audit_write_entry failed: %s", strerror(errno)); ++ } + } + ++static void ++linux_audit_user_auth(int uid, const char *username, ++ const char *hostname, const char *ip, const char *ttyn, int success, int event) ++{ ++ int audit_fd, rc, saved_errno; ++ static const char *event_name[] = { ++ "maxtries exceeded", ++ "root denied", ++ "success", ++ "none", ++ "password", ++ "challenge-response", ++ "pubkey", ++ "hostbased", ++ "gssapi", ++ "invalid user", ++ "nologin", ++ "connection closed", ++ "connection abandoned", ++ "unknown" ++ }; ++ ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno == EINVAL || errno == EPROTONOSUPPORT || ++ errno == EAFNOSUPPORT) ++ return; /* No audit support in kernel */ ++ else ++ goto fatal_report; /* Must prevent login */ ++ } ++ ++ if ((event < 0) || (event > SSH_AUDIT_UNKNOWN)) ++ event = SSH_AUDIT_UNKNOWN; ++ ++ rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, ++ NULL, event_name[event], username ? username : "(unknown)", ++ username == NULL ? uid : -1, hostname, ip, ttyn, success); ++ saved_errno = errno; ++ close(audit_fd); ++ /* ++ * Do not report error if the error is EPERM and sshd is run as non ++ * root user. ++ */ ++ if ((rc == -EPERM) && (geteuid() != 0)) ++ rc = 0; ++ errno = saved_errno; ++ if (rc < 0) { ++fatal_report: ++ fatal("linux_audit_write_entry failed: %s", strerror(errno)); ++ } ++} ++ ++int ++audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv) ++{ ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, rc, saved_errno; ++ ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno == EINVAL || errno == EPROTONOSUPPORT || ++ errno == EAFNOSUPPORT) ++ return 1; /* No audit support in kernel */ ++ else ++ return 0; /* Must prevent login */ ++ } ++ snprintf(buf, sizeof(buf), "%s_auth rport=%d", host_user ? "pubkey" : "hostbased", get_remote_port()); ++ rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL, ++ buf, audit_username(), -1, NULL, get_remote_ipaddr(), NULL, rv); ++ if ((rc < 0) && ((rc != -1) || (getuid() == 0))) ++ goto out; ++ /* is the fingerprint_prefix() still needed? ++ snprintf(buf, sizeof(buf), "key algo=%s size=%d fp=%s%s rport=%d", ++ type, bits, sshkey_fingerprint_prefix(), fp, get_remote_port()); ++ */ ++ snprintf(buf, sizeof(buf), "key algo=%s size=%d fp=%s rport=%d", ++ type, bits, fp, get_remote_port()); ++ rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL, ++ buf, audit_username(), -1, NULL, get_remote_ipaddr(), NULL, rv); ++out: ++ saved_errno = errno; ++ audit_close(audit_fd); ++ errno = saved_errno; ++ /* do not report error if the error is EPERM and sshd is run as non root user */ ++ return (rc >= 0) || ((rc == -EPERM) && (getuid() != 0)); ++} ++ ++static int user_login_count = 0; ++ + /* Below is the sshd audit API code */ + + void + audit_connection_from(const char *host, int port) + { +-} + /* not implemented */ ++} + +-void ++int + audit_run_command(const char *command) + { +- /* not implemented */ ++ if (!user_login_count++) ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_LOGIN); ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_START); ++ return 0; ++} ++ ++void ++audit_end_command(int handle, const char *command) ++{ ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_END); ++ if (user_login_count && !--user_login_count) ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_LOGOUT); ++} ++ ++void ++audit_count_session_open(void) ++{ ++ user_login_count++; + } + + void + audit_session_open(struct logininfo *li) + { +- if (linux_audit_record_event(li->uid, NULL, li->hostname, +- NULL, li->line, 1) == 0) +- fatal("linux_audit_write_entry failed: %s", strerror(errno)); ++ if (!user_login_count++) ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ NULL, li->line, 1, AUDIT_USER_LOGIN); ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ NULL, li->line, 1, AUDIT_USER_START); + } + + void + audit_session_close(struct logininfo *li) + { +- /* not implemented */ ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ NULL, li->line, 1, AUDIT_USER_END); ++ if (user_login_count && !--user_login_count) ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ NULL, li->line, 1, AUDIT_USER_LOGOUT); + } + + void +@@ -101,21 +232,43 @@ audit_event(ssh_audit_event_t event) + { + switch(event) { + case SSH_AUTH_SUCCESS: +- case SSH_CONNECTION_CLOSE: ++ linux_audit_user_auth(-1, audit_username(), NULL, ++ get_remote_ipaddr(), "ssh", 1, event); ++ break; ++ + case SSH_NOLOGIN: +- case SSH_LOGIN_EXCEED_MAXTRIES: + case SSH_LOGIN_ROOT_DENIED: ++ linux_audit_user_auth(-1, audit_username(), NULL, ++ get_remote_ipaddr(), "ssh", 0, event); ++ linux_audit_user_logxxx(-1, audit_username(), NULL, ++ get_remote_ipaddr(), "ssh", 0, AUDIT_USER_LOGIN); + break; + ++ case SSH_LOGIN_EXCEED_MAXTRIES: + case SSH_AUTH_FAIL_NONE: + case SSH_AUTH_FAIL_PASSWD: + case SSH_AUTH_FAIL_KBDINT: + case SSH_AUTH_FAIL_PUBKEY: + case SSH_AUTH_FAIL_HOSTBASED: + case SSH_AUTH_FAIL_GSSAPI: ++ linux_audit_user_auth(-1, audit_username(), NULL, ++ get_remote_ipaddr(), "ssh", 0, event); ++ break; ++ ++ case SSH_CONNECTION_CLOSE: ++ if (user_login_count) { ++ while (user_login_count--) ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_END); ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_LOGOUT); ++ } ++ break; ++ ++ case SSH_CONNECTION_ABANDON: + case SSH_INVALID_USER: +- linux_audit_record_event(-1, audit_username(), NULL, +- get_remote_ipaddr(), "sshd", 0); ++ linux_audit_user_logxxx(-1, audit_username(), NULL, ++ get_remote_ipaddr(), "ssh", 0, AUDIT_USER_LOGIN); + break; + + default: +@@ -123,4 +276,135 @@ audit_event(ssh_audit_event_t event) + } + } + ++void ++audit_unsupported_body(int what) ++{ ++#ifdef AUDIT_CRYPTO_SESSION ++ char buf[AUDIT_LOG_SIZE]; ++ const static char *name[] = { "cipher", "mac", "comp" }; ++ char *s; ++ int audit_fd; ++ ++ snprintf(buf, sizeof(buf), "op=unsupported-%s direction=? cipher=? ksize=? rport=%d laddr=%s lport=%d ", ++ name[what], get_remote_port(), (s = get_local_ipaddr(packet_get_connection_in())), ++ get_local_port()); ++ free(s); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) ++ /* no problem, the next instruction will be fatal() */ ++ return; ++ audit_log_user_message(audit_fd, AUDIT_CRYPTO_SESSION, ++ buf, NULL, get_remote_ipaddr(), NULL, 0); ++ audit_close(audit_fd); ++#endif ++} ++ ++const static char *direction[] = { "from-server", "from-client", "both" }; ++ ++void ++audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, ++ uid_t uid) ++{ ++#ifdef AUDIT_CRYPTO_SESSION ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, audit_ok; ++ const struct sshcipher *cipher = cipher_by_name(enc); ++ char *s; ++ ++ snprintf(buf, sizeof(buf), "op=start direction=%s cipher=%s ksize=%d mac=%s pfs=%s spid=%jd suid=%jd rport=%d laddr=%s lport=%d ", ++ direction[ctos], enc, cipher ? 8 * cipher->key_len : 0, mac, pfs, ++ (intmax_t)pid, (intmax_t)uid, ++ get_remote_port(), (s = get_local_ipaddr(packet_get_connection_in())), get_local_port()); ++ free(s); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno == EINVAL || errno == EPROTONOSUPPORT || ++ errno == EAFNOSUPPORT) ++ return; /* No audit support in kernel */ ++ else ++ fatal("cannot open audit"); /* Must prevent login */ ++ } ++ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_SESSION, ++ buf, NULL, get_remote_ipaddr(), NULL, 1); ++ audit_close(audit_fd); ++ /* do not abort if the error is EPERM and sshd is run as non root user */ ++ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) ++ fatal("cannot write into audit"); /* Must prevent login */ ++#endif ++} ++ ++void ++audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, audit_ok; ++ char *s; ++ ++ snprintf(buf, sizeof(buf), "op=destroy kind=session fp=? direction=%s spid=%jd suid=%jd rport=%d laddr=%s lport=%d ", ++ direction[ctos], (intmax_t)pid, (intmax_t)uid, ++ get_remote_port(), ++ (s = get_local_ipaddr(packet_get_connection_in())), ++ get_local_port()); ++ free(s); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno != EINVAL && errno != EPROTONOSUPPORT && ++ errno != EAFNOSUPPORT) ++ error("cannot open audit"); ++ return; ++ } ++ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, ++ buf, NULL, get_remote_ipaddr(), NULL, 1); ++ audit_close(audit_fd); ++ /* do not abort if the error is EPERM and sshd is run as non root user */ ++ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) ++ error("cannot write into audit"); ++} ++ ++void ++audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++{ ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, audit_ok; ++ ++ snprintf(buf, sizeof(buf), "op=destroy kind=server fp=%s direction=? spid=%jd suid=%jd ", ++ fp, (intmax_t)pid, (intmax_t)uid); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno != EINVAL && errno != EPROTONOSUPPORT && ++ errno != EAFNOSUPPORT) ++ error("cannot open audit"); ++ return; ++ } ++ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, ++ buf, NULL, ++ listening_for_clients() ? NULL : get_remote_ipaddr(), ++ NULL, 1); ++ audit_close(audit_fd); ++ /* do not abort if the error is EPERM and sshd is run as non root user */ ++ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) ++ error("cannot write into audit"); ++} ++ ++void ++audit_generate_ephemeral_server_key(const char *fp) ++{ ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, audit_ok; ++ ++ snprintf(buf, sizeof(buf), "op=create kind=server fp=%s direction=? ", fp); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno != EINVAL && errno != EPROTONOSUPPORT && ++ errno != EAFNOSUPPORT) ++ error("cannot open audit"); ++ return; ++ } ++ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, ++ buf, NULL, 0, NULL, 1); ++ audit_close(audit_fd); ++ /* do not abort if the error is EPERM and sshd is run as non root user */ ++ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) ++ error("cannot write into audit"); ++} + #endif /* USE_LINUX_AUDIT */ +diff -up openssh-7.2p1/auditstub.c.audit openssh-7.2p1/auditstub.c +--- openssh-7.2p1/auditstub.c.audit 2016-02-12 18:24:34.219825178 +0100 ++++ openssh-7.2p1/auditstub.c 2016-02-12 18:24:34.219825178 +0100 +@@ -0,0 +1,50 @@ ++/* $Id: auditstub.c,v 1.1 jfch Exp $ */ ++ ++/* ++ * Copyright 2010 Red Hat, Inc. All rights reserved. ++ * Use is subject to license terms. ++ * ++ * 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. ++ * ++ * Red Hat author: Jan F. Chadima ++ */ ++ ++#include ++ ++void ++audit_unsupported(int n) ++{ ++} ++ ++void ++audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) ++{ ++} ++ ++void ++audit_session_key_free(int ctos) ++{ ++} ++ ++void ++audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++} +diff -up openssh-7.2p1/auth2.c.audit openssh-7.2p1/auth2.c +--- openssh-7.2p1/auth2.c.audit 2016-02-12 18:24:34.148825205 +0100 ++++ openssh-7.2p1/auth2.c 2016-02-12 18:24:34.219825178 +0100 +@@ -249,9 +249,6 @@ input_userauth_request(int type, u_int32 + } 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) +diff -up openssh-7.2p1/auth2-hostbased.c.audit openssh-7.2p1/auth2-hostbased.c +--- openssh-7.2p1/auth2-hostbased.c.audit 2016-02-12 18:24:34.109825220 +0100 ++++ openssh-7.2p1/auth2-hostbased.c 2016-02-12 18:24:34.220825178 +0100 +@@ -146,7 +146,7 @@ userauth_hostbased(Authctxt *authctxt) + /* test for allowed key and correct signature */ + authenticated = 0; + if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) && +- PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), ++ PRIVSEP(hostbased_key_verify(key, sig, slen, buffer_ptr(&b), + buffer_len(&b))) == 1) + authenticated = 1; + +@@ -163,6 +163,18 @@ done: + return authenticated; + } + ++int ++hostbased_key_verify(const Key *key, const u_char *sig, u_int slen, const u_char *data, u_int datalen) ++{ ++ int rv; ++ ++ rv = key_verify(key, sig, slen, data, datalen); ++#ifdef SSH_AUDIT_EVENTS ++ audit_key(0, &rv, key); ++#endif ++ return rv; ++} ++ + /* return 1 if given hostkey is allowed */ + int + hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, +diff -up openssh-7.2p1/auth2-pubkey.c.audit openssh-7.2p1/auth2-pubkey.c +--- openssh-7.2p1/auth2-pubkey.c.audit 2016-02-12 18:24:34.122825215 +0100 ++++ openssh-7.2p1/auth2-pubkey.c 2016-02-12 18:24:34.220825178 +0100 +@@ -178,7 +178,7 @@ userauth_pubkey(Authctxt *authctxt) + /* test for correct signature */ + authenticated = 0; + if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && +- PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), ++ PRIVSEP(user_key_verify(key, sig, slen, buffer_ptr(&b), + buffer_len(&b))) == 1) { + authenticated = 1; + /* Record the successful key to prevent reuse */ +@@ -258,6 +258,18 @@ pubkey_auth_info(Authctxt *authctxt, con + free(extra); + } + ++int ++user_key_verify(const Key *key, const u_char *sig, u_int slen, const u_char *data, u_int datalen) ++{ ++ int rv; ++ ++ rv = key_verify(key, sig, slen, data, datalen); ++#ifdef SSH_AUDIT_EVENTS ++ audit_key(1, &rv, key); ++#endif ++ return rv; ++} ++ + /* + * Splits 's' into an argument vector. Handles quoted string and basic + * escape characters (\\, \", \'). Caller must free the argument vector +diff -up openssh-7.2p1/auth.c.audit openssh-7.2p1/auth.c +--- openssh-7.2p1/auth.c.audit 2016-02-12 18:24:34.148825205 +0100 ++++ openssh-7.2p1/auth.c 2016-02-12 18:24:34.220825178 +0100 +@@ -646,9 +646,6 @@ getpwnamallow(const char *user) + record_failed_login(user, + get_canonical_hostname(options.use_dns), "ssh"); + #endif +-#ifdef SSH_AUDIT_EVENTS +- audit_event(SSH_INVALID_USER); +-#endif /* SSH_AUDIT_EVENTS */ + return (NULL); + } + if (!allowed_user(pw)) +diff -up openssh-7.2p1/auth.h.audit openssh-7.2p1/auth.h +--- openssh-7.2p1/auth.h.audit 2016-02-12 18:24:34.108825221 +0100 ++++ openssh-7.2p1/auth.h 2016-02-12 18:32:46.085636046 +0100 +@@ -195,6 +195,7 @@ void abandon_challenge_response(Authctxt + + char *expand_authorized_keys(const char *, struct passwd *pw); + char *authorized_principals_file(struct passwd *); ++int user_key_verify(const Key *, const u_char *, u_int, const u_char *, u_int); + + FILE *auth_openkeyfile(const char *, struct passwd *, int); + FILE *auth_openprincipals(const char *, struct passwd *, int); +@@ -213,6 +214,7 @@ int get_hostkey_index(Key *, int, struc + int ssh1_session_key(BIGNUM *); + int sshd_hostkey_sign(Key *, Key *, u_char **, size_t *, + const u_char *, size_t, const char *, u_int); ++int hostbased_key_verify(const Key *, const u_char *, u_int, const u_char *, u_int); + + /* debug messages during authentication */ + void auth_debug_add(const char *fmt,...) __attribute__((format(printf, 1, 2))); +diff -up openssh-7.2p1/auth-rsa.c.audit openssh-7.2p1/auth-rsa.c +--- openssh-7.2p1/auth-rsa.c.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/auth-rsa.c 2016-02-12 18:24:34.221825177 +0100 +@@ -95,7 +95,10 @@ auth_rsa_verify_response(Key *key, BIGNU + { + u_char buf[32], mdbuf[16]; + struct ssh_digest_ctx *md; +- int len; ++ int len, rv; ++#ifdef SSH_AUDIT_EVENTS ++ char *fp; ++#endif + + /* don't allow short keys */ + if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { +@@ -119,12 +122,18 @@ auth_rsa_verify_response(Key *key, BIGNU + ssh_digest_free(md); + + /* Verify that the response is the original challenge. */ +- if (timingsafe_bcmp(response, mdbuf, 16) != 0) { +- /* Wrong answer. */ +- return (0); ++ rv = timingsafe_bcmp(response, mdbuf, 16) == 0; ++ ++#ifdef SSH_AUDIT_EVENTS ++ fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_HEX); ++ if (audit_keyusage(1, "ssh-rsa1", RSA_size(key->rsa) * 8, fp, rv) == 0) { ++ debug("unsuccessful audit"); ++ rv = 0; + } +- /* Correct answer. */ +- return (1); ++ free(fp); ++#endif ++ ++ return rv; + } + + /* +diff -up openssh-7.2p1/cipher.c.audit openssh-7.2p1/cipher.c +--- openssh-7.2p1/cipher.c.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/cipher.c 2016-02-12 18:24:34.221825177 +0100 +@@ -57,26 +57,6 @@ extern const EVP_CIPHER *evp_ssh1_3des(v + extern int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); + #endif + +-struct sshcipher { +- char *name; +- int number; /* for ssh1 only */ +- u_int block_size; +- u_int key_len; +- u_int iv_len; /* defaults to block_size */ +- u_int auth_len; +- u_int discard_len; +- u_int flags; +-#define CFLAG_CBC (1<<0) +-#define CFLAG_CHACHAPOLY (1<<1) +-#define CFLAG_AESCTR (1<<2) +-#define CFLAG_NONE (1<<3) +-#ifdef WITH_OPENSSL +- const EVP_CIPHER *(*evptype)(void); +-#else +- void *ignored; +-#endif +-}; +- + static const struct sshcipher ciphers[] = { + #ifdef WITH_SSH1 + { "des", SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc }, +diff -up openssh-7.2p1/cipher.h.audit openssh-7.2p1/cipher.h +--- openssh-7.2p1/cipher.h.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/cipher.h 2016-02-12 18:24:34.221825177 +0100 +@@ -62,7 +62,26 @@ + #define CIPHER_ENCRYPT 1 + #define CIPHER_DECRYPT 0 + +-struct sshcipher; ++struct sshcipher { ++ char *name; ++ int number; /* for ssh1 only */ ++ u_int block_size; ++ u_int key_len; ++ u_int iv_len; /* defaults to block_size */ ++ u_int auth_len; ++ u_int discard_len; ++ u_int flags; ++#define CFLAG_CBC (1<<0) ++#define CFLAG_CHACHAPOLY (1<<1) ++#define CFLAG_AESCTR (1<<2) ++#define CFLAG_NONE (1<<3) ++#ifdef WITH_OPENSSL ++ const EVP_CIPHER *(*evptype)(void); ++#else ++ void *ignored; ++#endif ++}; ++ + struct sshcipher_ctx { + int plaintext; + int encrypt; +diff -up openssh-7.2p1/kex.c.audit openssh-7.2p1/kex.c +--- openssh-7.2p1/kex.c.audit 2016-02-12 18:24:34.201825185 +0100 ++++ openssh-7.2p1/kex.c 2016-02-12 18:24:34.221825177 +0100 +@@ -54,6 +54,7 @@ + #include "ssherr.h" + #include "sshbuf.h" + #include "digest.h" ++#include "audit.h" + + #ifdef GSSAPI + #include "ssh-gss.h" +@@ -669,8 +670,12 @@ choose_enc(struct sshenc *enc, char *cli + { + char *name = match_list(client, server, NULL); + +- if (name == NULL) ++ if (name == NULL) { ++#ifdef SSH_AUDIT_EVENTS ++ audit_unsupported(0); ++#endif + return SSH_ERR_NO_CIPHER_ALG_MATCH; ++ } + if ((enc->cipher = cipher_by_name(name)) == NULL) + return SSH_ERR_INTERNAL_ERROR; + enc->name = name; +@@ -688,8 +693,12 @@ choose_mac(struct ssh *ssh, struct sshma + { + char *name = match_list(client, server, NULL); + +- if (name == NULL) ++ if (name == NULL) { ++#ifdef SSH_AUDIT_EVENTS ++ audit_unsupported(1); ++#endif + return SSH_ERR_NO_MAC_ALG_MATCH; ++ } + if (mac_setup(mac, name) < 0) + return SSH_ERR_INTERNAL_ERROR; + /* truncate the key */ +@@ -706,8 +715,12 @@ choose_comp(struct sshcomp *comp, char * + { + char *name = match_list(client, server, NULL); + +- if (name == NULL) ++ if (name == NULL) { ++#ifdef SSH_AUDIT_EVENTS ++ audit_unsupported(2); ++#endif + return SSH_ERR_NO_COMPRESS_ALG_MATCH; ++ } + if (strcmp(name, "zlib@openssh.com") == 0) { + comp->type = COMP_DELAYED; + } else if (strcmp(name, "zlib") == 0) { +@@ -878,6 +891,10 @@ kex_choose_conf(struct ssh *ssh) + dh_need = MAX(dh_need, newkeys->enc.block_size); + dh_need = MAX(dh_need, newkeys->enc.iv_len); + dh_need = MAX(dh_need, newkeys->mac.key_len); ++ debug("kex: %s need=%d dh_need=%d", kex->name, need, dh_need); ++#ifdef SSH_AUDIT_EVENTS ++ audit_kex(mode, newkeys->enc.name, newkeys->mac.name, newkeys->comp.name, kex->name); ++#endif + } + /* XXX need runden? */ + kex->we_need = need; +@@ -1052,3 +1069,34 @@ dump_digest(char *msg, u_char *digest, i + sshbuf_dump_data(digest, len, stderr); + } + #endif ++ ++static void ++enc_destroy(struct sshenc *enc) ++{ ++ if (enc == NULL) ++ return; ++ ++ if (enc->key) { ++ memset(enc->key, 0, enc->key_len); ++ free(enc->key); ++ } ++ ++ if (enc->iv) { ++ memset(enc->iv, 0, enc->iv_len); ++ free(enc->iv); ++ } ++ ++ memset(enc, 0, sizeof(*enc)); ++} ++ ++void ++newkeys_destroy(struct newkeys *newkeys) ++{ ++ if (newkeys == NULL) ++ return; ++ ++ enc_destroy(&newkeys->enc); ++ mac_destroy(&newkeys->mac); ++ memset(&newkeys->comp, 0, sizeof(newkeys->comp)); ++} ++ +diff -up openssh-7.2p1/kex.h.audit openssh-7.2p1/kex.h +--- openssh-7.2p1/kex.h.audit 2016-02-12 18:24:34.201825185 +0100 ++++ openssh-7.2p1/kex.h 2016-02-12 18:24:34.222825177 +0100 +@@ -206,6 +206,8 @@ int kexgss_client(struct ssh *); + int kexgss_server(struct ssh *); + #endif + ++void newkeys_destroy(struct newkeys *newkeys); ++ + int kex_dh_hash(const char *, const char *, + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, + const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); +diff -up openssh-7.2p1/key.h.audit openssh-7.2p1/key.h +--- openssh-7.2p1/key.h.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/key.h 2016-02-12 18:24:34.222825177 +0100 +@@ -50,6 +50,7 @@ typedef struct sshkey Key; + #define key_ecdsa_bits_to_nid sshkey_ecdsa_bits_to_nid + #define key_ecdsa_key_to_nid sshkey_ecdsa_key_to_nid + #define key_is_cert sshkey_is_cert ++#define key_is_private sshkey_is_private + #define key_type_plain sshkey_type_plain + #define key_curve_name_to_nid sshkey_curve_name_to_nid + #define key_curve_nid_to_bits sshkey_curve_nid_to_bits +diff -up openssh-7.2p1/mac.c.audit openssh-7.2p1/mac.c +--- openssh-7.2p1/mac.c.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/mac.c 2016-02-12 18:24:34.222825177 +0100 +@@ -226,6 +226,20 @@ mac_clear(struct sshmac *mac) + mac->umac_ctx = NULL; + } + ++void ++mac_destroy(struct sshmac *mac) ++{ ++ if (mac == NULL) ++ return; ++ ++ if (mac->key) { ++ memset(mac->key, 0, mac->key_len); ++ free(mac->key); ++ } ++ ++ memset(mac, 0, sizeof(*mac)); ++} ++ + /* XXX copied from ciphers_valid */ + #define MAC_SEP "," + int +diff -up openssh-7.2p1/mac.h.audit openssh-7.2p1/mac.h +--- openssh-7.2p1/mac.h.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/mac.h 2016-02-12 18:24:34.222825177 +0100 +@@ -47,5 +47,6 @@ int mac_init(struct sshmac *); + int mac_compute(struct sshmac *, u_int32_t, const u_char *, int, + u_char *, size_t); + void mac_clear(struct sshmac *); ++void mac_destroy(struct sshmac *); + + #endif /* SSHMAC_H */ +diff -up openssh-7.2p1/Makefile.in.audit openssh-7.2p1/Makefile.in +--- openssh-7.2p1/Makefile.in.audit 2016-02-12 18:24:34.222825177 +0100 ++++ openssh-7.2p1/Makefile.in 2016-02-12 18:33:38.858629492 +0100 +@@ -99,7 +99,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ + kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ + kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ +- platform-pledge.o ++ platform-pledge.o auditstub.o + + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect1.o sshconnect2.o mux.o +diff -up openssh-7.2p1/monitor.c.audit openssh-7.2p1/monitor.c +--- openssh-7.2p1/monitor.c.audit 2016-02-12 18:24:34.176825195 +0100 ++++ openssh-7.2p1/monitor.c 2016-02-12 18:34:05.184629882 +0100 +@@ -101,6 +101,7 @@ + #include "compat.h" + #include "ssh2.h" + #include "authfd.h" ++#include "audit.h" + #include "match.h" + #include "ssherr.h" + +@@ -116,6 +117,8 @@ extern Buffer auth_debug; + extern int auth_debug_init; + extern Buffer loginmsg; + ++extern void destroy_sensitive_data(int); ++ + /* State exported from the child */ + static struct sshbuf *child_state; + +@@ -166,6 +169,11 @@ int mm_answer_gss_updatecreds(int, Buffe + #ifdef SSH_AUDIT_EVENTS + int mm_answer_audit_event(int, Buffer *); + int mm_answer_audit_command(int, Buffer *); ++int mm_answer_audit_end_command(int, Buffer *); ++int mm_answer_audit_unsupported_body(int, Buffer *); ++int mm_answer_audit_kex_body(int, Buffer *); ++int mm_answer_audit_session_key_free_body(int, Buffer *); ++int mm_answer_audit_server_key_free(int, Buffer *); + #endif + + static int monitor_read_log(struct monitor *); +@@ -225,6 +233,10 @@ struct mon_table mon_dispatch_proto20[] + #endif + #ifdef SSH_AUDIT_EVENTS + {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, ++ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, ++ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, ++ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, ++ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, + #endif + #ifdef BSD_AUTH + {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, +@@ -263,6 +275,11 @@ struct mon_table mon_dispatch_postauth20 + #ifdef SSH_AUDIT_EVENTS + {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, + {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command}, ++ {MONITOR_REQ_AUDIT_END_COMMAND, MON_PERMIT, mm_answer_audit_end_command}, ++ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, ++ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, ++ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, ++ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, + #endif + {0, 0, NULL} + }; +@@ -295,6 +312,10 @@ struct mon_table mon_dispatch_proto15[] + #endif + #ifdef SSH_AUDIT_EVENTS + {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, ++ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, ++ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, ++ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, ++ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, + #endif + #endif /* WITH_SSH1 */ + {0, 0, NULL} +@@ -308,6 +329,11 @@ struct mon_table mon_dispatch_postauth15 + #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}, ++ {MONITOR_REQ_AUDIT_END_COMMAND, MON_PERMIT, mm_answer_audit_end_command}, ++ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, ++ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, ++ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, ++ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, + #endif + #endif /* WITH_SSH1 */ + {0, 0, NULL} +@@ -1464,9 +1490,11 @@ mm_answer_keyverify(int sock, Buffer *m) + Key *key; + u_char *signature, *data, *blob; + u_int signaturelen, datalen, bloblen; ++ int type = 0; + int verified = 0; + int valid_data = 0; + ++ type = buffer_get_int(m); + blob = buffer_get_string(m, &bloblen); + signature = buffer_get_string(m, &signaturelen); + data = buffer_get_string(m, &datalen); +@@ -1474,6 +1502,8 @@ mm_answer_keyverify(int sock, Buffer *m) + if (hostbased_cuser == NULL || hostbased_chost == NULL || + !monitor_allowed_key(blob, bloblen)) + fatal("%s: bad key, not previously allowed", __func__); ++ if (type != key_blobtype) ++ fatal("%s: bad key type", __func__); + + key = key_from_blob(blob, bloblen); + if (key == NULL) +@@ -1494,7 +1524,17 @@ mm_answer_keyverify(int sock, Buffer *m) + if (!valid_data) + fatal("%s: bad signature data blob", __func__); + +- verified = key_verify(key, signature, signaturelen, data, datalen); ++ switch (key_blobtype) { ++ case MM_USERKEY: ++ verified = user_key_verify(key, signature, signaturelen, data, datalen); ++ break; ++ case MM_HOSTKEY: ++ verified = hostbased_key_verify(key, signature, signaturelen, data, datalen); ++ break; ++ default: ++ verified = 0; ++ break; ++ } + debug3("%s: key %p signature %s", + __func__, key, (verified == 1) ? "verified" : "unverified"); + +@@ -1555,6 +1595,12 @@ mm_session_close(Session *s) + debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd); + session_pty_cleanup2(s); + } ++#ifdef SSH_AUDIT_EVENTS ++ if (s->command != NULL) { ++ debug3("%s: command %d", __func__, s->command_handle); ++ session_end_command2(s); ++ } ++#endif + session_unused(s->self); + } + +@@ -1837,6 +1883,8 @@ mm_answer_term(int sock, Buffer *req) + sshpam_cleanup(); + #endif + ++ destroy_sensitive_data(0); ++ + while (waitpid(pmonitor->m_pid, &status, 0) == -1) + if (errno != EINTR) + exit(1); +@@ -1879,11 +1927,43 @@ mm_answer_audit_command(int socket, Buff + { + u_int len; + char *cmd; ++ Session *s; + + debug3("%s entering", __func__); + cmd = buffer_get_string(m, &len); ++ + /* sanity check command, if so how? */ +- audit_run_command(cmd); ++ s = session_new(); ++ if (s == NULL) ++ fatal("%s: error allocating a session", __func__); ++ s->command = cmd; ++ s->command_handle = audit_run_command(cmd); ++ ++ buffer_clear(m); ++ buffer_put_int(m, s->self); ++ ++ mm_request_send(socket, MONITOR_ANS_AUDIT_COMMAND, m); ++ ++ return (0); ++} ++ ++int ++mm_answer_audit_end_command(int socket, Buffer *m) ++{ ++ int handle; ++ u_int len; ++ char *cmd; ++ Session *s; ++ ++ debug3("%s entering", __func__); ++ handle = buffer_get_int(m); ++ cmd = buffer_get_string(m, &len); ++ ++ s = session_by_id(handle); ++ if (s == NULL || s->ttyfd != -1 || s->command == NULL || ++ strcmp(s->command, cmd) != 0) ++ fatal("%s: invalid handle", __func__); ++ mm_session_close(s); + free(cmd); + return (0); + } +@@ -1940,6 +2020,7 @@ monitor_apply_keystate(struct monitor *p + void + mm_get_keystate(struct monitor *pmonitor) + { ++ Buffer m; + debug3("%s: Waiting for new keys", __func__); + + if ((child_state = sshbuf_new()) == NULL) +@@ -1947,6 +2028,21 @@ mm_get_keystate(struct monitor *pmonitor + mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, + child_state); + debug3("%s: GOT new keys", __func__); ++ ++#ifdef SSH_AUDIT_EVENTS ++ if (compat20) { ++ buffer_init(&m); ++ mm_request_receive_expect(pmonitor->m_sendfd, ++ MONITOR_REQ_AUDIT_SESSION_KEY_FREE, &m); ++ mm_answer_audit_session_key_free_body(pmonitor->m_sendfd, &m); ++ buffer_free(&m); ++ } ++#endif ++ ++ /* Drain any buffered messages from the child */ ++ while (pmonitor->m_log_recvfd >= 0 && monitor_read_log(pmonitor) == 0) ++ ; ++ + } + + +@@ -2213,3 +2309,86 @@ mm_answer_gss_updatecreds(int socket, Bu + + #endif /* GSSAPI */ + ++#ifdef SSH_AUDIT_EVENTS ++int ++mm_answer_audit_unsupported_body(int sock, Buffer *m) ++{ ++ int what; ++ ++ what = buffer_get_int(m); ++ ++ audit_unsupported_body(what); ++ ++ buffer_clear(m); ++ ++ mm_request_send(sock, MONITOR_ANS_AUDIT_UNSUPPORTED, m); ++ return 0; ++} ++ ++int ++mm_answer_audit_kex_body(int sock, Buffer *m) ++{ ++ int ctos, len; ++ char *cipher, *mac, *compress, *pfs; ++ pid_t pid; ++ uid_t uid; ++ ++ ctos = buffer_get_int(m); ++ cipher = buffer_get_string(m, &len); ++ mac = buffer_get_string(m, &len); ++ compress = buffer_get_string(m, &len); ++ pfs = buffer_get_string(m, &len); ++ pid = buffer_get_int64(m); ++ uid = buffer_get_int64(m); ++ ++ audit_kex_body(ctos, cipher, mac, compress, pfs, pid, uid); ++ ++ free(cipher); ++ free(mac); ++ free(compress); ++ free(pfs); ++ buffer_clear(m); ++ ++ mm_request_send(sock, MONITOR_ANS_AUDIT_KEX, m); ++ return 0; ++} ++ ++int ++mm_answer_audit_session_key_free_body(int sock, Buffer *m) ++{ ++ int ctos; ++ pid_t pid; ++ uid_t uid; ++ ++ ctos = buffer_get_int(m); ++ pid = buffer_get_int64(m); ++ uid = buffer_get_int64(m); ++ ++ audit_session_key_free_body(ctos, pid, uid); ++ ++ buffer_clear(m); ++ ++ mm_request_send(sock, MONITOR_ANS_AUDIT_SESSION_KEY_FREE, m); ++ return 0; ++} ++ ++int ++mm_answer_audit_server_key_free(int sock, Buffer *m) ++{ ++ int len; ++ char *fp; ++ pid_t pid; ++ uid_t uid; ++ ++ fp = buffer_get_string(m, &len); ++ pid = buffer_get_int64(m); ++ uid = buffer_get_int64(m); ++ ++ audit_destroy_sensitive_data(fp, pid, uid); ++ ++ free(fp); ++ buffer_clear(m); ++ ++ return 0; ++} ++#endif /* SSH_AUDIT_EVENTS */ +diff -up openssh-7.2p1/monitor.h.audit openssh-7.2p1/monitor.h +--- openssh-7.2p1/monitor.h.audit 2016-02-12 18:24:34.177825194 +0100 ++++ openssh-7.2p1/monitor.h 2016-02-12 18:24:34.224825176 +0100 +@@ -69,7 +69,13 @@ enum monitor_reqtype { + MONITOR_REQ_PAM_QUERY = 106, MONITOR_ANS_PAM_QUERY = 107, + MONITOR_REQ_PAM_RESPOND = 108, MONITOR_ANS_PAM_RESPOND = 109, + MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, +- MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, ++ MONITOR_REQ_AUDIT_EVENT = 112, ++ MONITOR_REQ_AUDIT_COMMAND = 114, MONITOR_ANS_AUDIT_COMMAND = 115, ++ MONITOR_REQ_AUDIT_END_COMMAND = 116, ++ MONITOR_REQ_AUDIT_UNSUPPORTED = 118, MONITOR_ANS_AUDIT_UNSUPPORTED = 119, ++ MONITOR_REQ_AUDIT_KEX = 120, MONITOR_ANS_AUDIT_KEX = 121, ++ MONITOR_REQ_AUDIT_SESSION_KEY_FREE = 122, MONITOR_ANS_AUDIT_SESSION_KEY_FREE = 123, ++ MONITOR_REQ_AUDIT_SERVER_KEY_FREE = 124 + + }; + +diff -up openssh-7.2p1/monitor_wrap.c.audit openssh-7.2p1/monitor_wrap.c +--- openssh-7.2p1/monitor_wrap.c.audit 2016-02-12 18:24:34.151825204 +0100 ++++ openssh-7.2p1/monitor_wrap.c 2016-02-12 18:24:34.224825176 +0100 +@@ -462,7 +462,7 @@ mm_key_allowed(enum mm_keytype type, cha + */ + + int +-mm_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) ++mm_key_verify(enum mm_keytype type, Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) + { + Buffer m; + u_char *blob; +@@ -476,6 +476,7 @@ mm_key_verify(Key *key, u_char *sig, u_i + return (0); + + buffer_init(&m); ++ buffer_put_int(&m, type); + buffer_put_string(&m, blob, len); + buffer_put_string(&m, sig, siglen); + buffer_put_string(&m, data, datalen); +@@ -493,6 +494,18 @@ mm_key_verify(Key *key, u_char *sig, u_i + return (verified); + } + ++int ++mm_hostbased_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) ++{ ++ return mm_key_verify(MM_HOSTKEY, key, sig, siglen, data, datalen); ++} ++ ++int ++mm_user_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) ++{ ++ return mm_key_verify(MM_USERKEY, key, sig, siglen, data, datalen); ++} ++ + void + mm_send_keystate(struct monitor *monitor) + { +@@ -1005,10 +1018,11 @@ mm_audit_event(ssh_audit_event_t event) + buffer_free(&m); + } + +-void ++int + mm_audit_run_command(const char *command) + { + Buffer m; ++ int handle; + + debug3("%s entering command %s", __func__, command); + +@@ -1016,6 +1030,26 @@ mm_audit_run_command(const char *command + buffer_put_cstring(&m, command); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_COMMAND, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_COMMAND, &m); ++ ++ handle = buffer_get_int(&m); ++ buffer_free(&m); ++ ++ return (handle); ++} ++ ++void ++mm_audit_end_command(int handle, const char *command) ++{ ++ Buffer m; ++ ++ debug3("%s entering command %s", __func__, command); ++ ++ buffer_init(&m); ++ buffer_put_int(&m, handle); ++ buffer_put_cstring(&m, command); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_END_COMMAND, &m); + buffer_free(&m); + } + #endif /* SSH_AUDIT_EVENTS */ +@@ -1151,3 +1185,70 @@ mm_ssh_gssapi_update_creds(ssh_gssapi_cc + + #endif /* GSSAPI */ + ++#ifdef SSH_AUDIT_EVENTS ++void ++mm_audit_unsupported_body(int what) ++{ ++ Buffer m; ++ ++ buffer_init(&m); ++ buffer_put_int(&m, what); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_UNSUPPORTED, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_UNSUPPORTED, ++ &m); ++ ++ buffer_free(&m); ++} ++ ++void ++mm_audit_kex_body(int ctos, char *cipher, char *mac, char *compress, char *fps, pid_t pid, ++ uid_t uid) ++{ ++ Buffer m; ++ ++ buffer_init(&m); ++ buffer_put_int(&m, ctos); ++ buffer_put_cstring(&m, cipher); ++ buffer_put_cstring(&m, (mac ? mac : "")); ++ buffer_put_cstring(&m, compress); ++ buffer_put_cstring(&m, fps); ++ buffer_put_int64(&m, pid); ++ buffer_put_int64(&m, uid); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_KEX, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_KEX, ++ &m); ++ ++ buffer_free(&m); ++} ++ ++void ++mm_audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++ Buffer m; ++ ++ buffer_init(&m); ++ buffer_put_int(&m, ctos); ++ buffer_put_int64(&m, pid); ++ buffer_put_int64(&m, uid); ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_SESSION_KEY_FREE, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_SESSION_KEY_FREE, ++ &m); ++ buffer_free(&m); ++} ++ ++void ++mm_audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++{ ++ Buffer m; ++ ++ buffer_init(&m); ++ buffer_put_cstring(&m, fp); ++ buffer_put_int64(&m, pid); ++ buffer_put_int64(&m, uid); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_SERVER_KEY_FREE, &m); ++ buffer_free(&m); ++} ++#endif /* SSH_AUDIT_EVENTS */ +diff -up openssh-7.2p1/monitor_wrap.h.audit openssh-7.2p1/monitor_wrap.h +--- openssh-7.2p1/monitor_wrap.h.audit 2016-02-12 18:24:34.152825204 +0100 ++++ openssh-7.2p1/monitor_wrap.h 2016-02-12 18:24:34.224825176 +0100 +@@ -52,7 +52,8 @@ int mm_key_allowed(enum mm_keytype, char + int mm_user_key_allowed(struct passwd *, Key *, int); + 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_hostbased_key_verify(Key *, u_char *, u_int, u_char *, u_int); ++int mm_user_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 *); +@@ -79,7 +80,12 @@ void mm_sshpam_free_ctx(void *); + #ifdef SSH_AUDIT_EVENTS + #include "audit.h" + void mm_audit_event(ssh_audit_event_t); +-void mm_audit_run_command(const char *); ++int mm_audit_run_command(const char *); ++void mm_audit_end_command(int, const char *); ++void mm_audit_unsupported_body(int); ++void mm_audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); ++void mm_audit_session_key_free_body(int, pid_t, uid_t); ++void mm_audit_destroy_sensitive_data(const char *, pid_t, uid_t); + #endif + + struct Session; +diff -up openssh-7.2p1/packet.c.audit openssh-7.2p1/packet.c +--- openssh-7.2p1/packet.c.audit 2016-02-12 18:24:34.095825226 +0100 ++++ openssh-7.2p1/packet.c 2016-02-12 18:43:47.268638489 +0100 +@@ -67,6 +67,7 @@ + #include "key.h" /* typedefs XXX */ + + #include "xmalloc.h" ++#include "audit.h" + #include "crc32.h" + #include "deattack.h" + #include "compat.h" +@@ -456,6 +457,13 @@ ssh_packet_get_connection_out(struct ssh + return ssh->state->connection_out; + } + ++static int ++packet_state_has_keys (const struct session_state *state) ++{ ++ return state != NULL && ++ (state->newkeys[MODE_IN] != NULL || state->newkeys[MODE_OUT] != NULL); ++} ++ + /* + * Returns the IP-address of the remote host as a string. The returned + * string must not be freed. +@@ -500,13 +508,6 @@ ssh_packet_close(struct ssh *ssh) + if (!state->initialized) + return; + state->initialized = 0; +- if (state->connection_in == state->connection_out) { +- shutdown(state->connection_out, SHUT_RDWR); +- close(state->connection_out); +- } else { +- close(state->connection_in); +- close(state->connection_out); +- } + sshbuf_free(state->input); + sshbuf_free(state->output); + sshbuf_free(state->outgoing_packet); +@@ -538,12 +539,22 @@ ssh_packet_close(struct ssh *ssh) + inflateEnd(stream); + } + } +- if ((r = cipher_cleanup(&state->send_context)) != 0) +- error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); +- if ((r = cipher_cleanup(&state->receive_context)) != 0) +- error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); ++ if (packet_state_has_keys(state)) { ++ if ((r = cipher_cleanup(&state->send_context)) != 0) ++ error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); ++ if ((r = cipher_cleanup(&state->receive_context)) != 0) ++ error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); ++ audit_session_key_free(2); ++ } + free(ssh->remote_ipaddr); + ssh->remote_ipaddr = NULL; ++ if (state->connection_in == state->connection_out) { ++ shutdown(state->connection_out, SHUT_RDWR); ++ close(state->connection_out); ++ } else { ++ close(state->connection_in); ++ close(state->connection_out); ++ } + free(ssh->state); + ssh->state = NULL; + } +@@ -968,6 +979,7 @@ ssh_set_newkeys(struct ssh *ssh, int mod + (unsigned long long)state->p_read.blocks, + (unsigned long long)state->p_send.bytes, + (unsigned long long)state->p_send.blocks); ++ audit_session_key_free(mode); + if ((r = cipher_cleanup(cc)) != 0) + return r; + enc = &state->newkeys[mode]->enc; +@@ -2408,6 +2420,75 @@ ssh_packet_get_output(struct ssh *ssh) + return (void *)ssh->state->output; + } + ++static void ++newkeys_destroy_and_free(struct newkeys *newkeys) ++{ ++ if (newkeys == NULL) ++ return; ++ ++ free(newkeys->enc.name); ++ ++ if (newkeys->mac.enabled) { ++ mac_clear(&newkeys->mac); ++ free(newkeys->mac.name); ++ } ++ ++ free(newkeys->comp.name); ++ ++ newkeys_destroy(newkeys); ++ free(newkeys); ++} ++ ++static void ++packet_destroy_state(struct session_state *state) ++{ ++ if (state == NULL) ++ return; ++ ++ cipher_cleanup(&state->receive_context); ++ cipher_cleanup(&state->send_context); ++ ++ buffer_free(state->input); ++ state->input = NULL; ++ buffer_free(state->output); ++ state->output = NULL; ++ buffer_free(state->outgoing_packet); ++ state->outgoing_packet = NULL; ++ buffer_free(state->incoming_packet); ++ state->incoming_packet = NULL; ++ if( state->compression_buffer ) { ++ buffer_free(state->compression_buffer); ++ state->compression_buffer = NULL; ++ } ++ newkeys_destroy_and_free(state->newkeys[MODE_IN]); ++ state->newkeys[MODE_IN] = NULL; ++ newkeys_destroy_and_free(state->newkeys[MODE_OUT]); ++ state->newkeys[MODE_OUT] = NULL; ++ mac_destroy(state->packet_discard_mac); ++// TAILQ_HEAD(, packet) outgoing; ++// memset(state, 0, sizeof(state)); ++} ++ ++void ++packet_destroy_all(int audit_it, int privsep) ++{ ++ if (audit_it) ++ audit_it = (active_state != NULL && packet_state_has_keys(active_state->state)) ++ || (backup_state != NULL && packet_state_has_keys(backup_state->state)); ++ if (active_state != NULL) ++ packet_destroy_state(active_state->state); ++ if (backup_state != NULL) ++ packet_destroy_state(backup_state->state); ++ if (audit_it) { ++#ifdef SSH_AUDIT_EVENTS ++ if (privsep) ++ audit_session_key_free(2); ++ else ++ audit_session_key_free_body(2, getpid(), getuid()); ++#endif ++ } ++} ++ + /* Reset after_authentication and reset compression in post-auth privsep */ + static int + ssh_packet_set_postauth(struct ssh *ssh) +diff -up openssh-7.2p1/packet.h.audit openssh-7.2p1/packet.h +--- openssh-7.2p1/packet.h.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/packet.h 2016-02-12 18:24:34.226825175 +0100 +@@ -186,7 +186,7 @@ int sshpkt_get_end(struct ssh *ssh); + const u_char *sshpkt_ptr(struct ssh *, size_t *lenp); + + /* OLD API */ +-extern struct ssh *active_state; ++extern struct ssh *active_state, *backup_state; + #include "opacket.h" + + #if !defined(WITH_OPENSSL) +@@ -200,4 +200,5 @@ extern struct ssh *active_state; + # undef EC_POINT + #endif + ++void packet_destroy_all(int, int); + #endif /* PACKET_H */ +diff -up openssh-7.2p1/sandbox-seccomp-filter.c.audit openssh-7.2p1/sandbox-seccomp-filter.c +--- openssh-7.2p1/sandbox-seccomp-filter.c.audit 2016-02-12 18:24:34.193825188 +0100 ++++ openssh-7.2p1/sandbox-seccomp-filter.c 2016-02-12 18:24:34.226825175 +0100 +@@ -153,6 +153,12 @@ static const struct sock_filter preauth_ + #ifdef __NR_gettimeofday + SC_ALLOW(gettimeofday), + #endif ++#ifdef SSH_AUDIT_EVENTS ++ SC_ALLOW(getuid), ++#ifdef __NR_getuid32 /* not defined on x86_64 */ ++ SC_ALLOW(getuid32), ++#endif ++#endif + #ifdef __NR_madvise + SC_ALLOW(madvise), + #endif +diff -up openssh-7.2p1/session.c.audit openssh-7.2p1/session.c +--- openssh-7.2p1/session.c.audit 2016-02-12 18:24:34.177825194 +0100 ++++ openssh-7.2p1/session.c 2016-02-12 18:24:34.226825175 +0100 +@@ -139,7 +139,7 @@ extern int log_stderr; + extern int debug_flag; + extern u_int utmp_len; + extern int startup_pipe; +-extern void destroy_sensitive_data(void); ++extern void destroy_sensitive_data(int); + extern Buffer loginmsg; + + /* original command from peer. */ +@@ -731,6 +731,14 @@ do_exec_pty(Session *s, const char *comm + /* Parent. Close the slave side of the pseudo tty. */ + close(ttyfd); + ++#ifndef HAVE_OSF_SIA ++ /* do_login in the child did not affect state in this process, ++ compensate. From an architectural standpoint, this is extremely ++ ugly. */ ++ if (!(options.use_login && command == NULL)) ++ audit_count_session_open(); ++#endif ++ + /* Enter interactive session. */ + s->ptymaster = ptymaster; + packet_set_interactive(1, +@@ -853,15 +861,19 @@ do_exec(Session *s, const char *command) + s->self); + + #ifdef SSH_AUDIT_EVENTS ++ if (s->command != NULL || s->command_handle != -1) ++ fatal("do_exec: command already set"); + if (command != NULL) +- PRIVSEP(audit_run_command(command)); ++ s->command = xstrdup(command); + else if (s->ttyfd == -1) { + char *shell = s->pw->pw_shell; + + if (shell[0] == '\0') /* empty shell means /bin/sh */ + shell =_PATH_BSHELL; +- PRIVSEP(audit_run_command(shell)); ++ s->command = xstrdup(shell); + } ++ if (s->command != NULL && s->ptyfd == -1) ++ s->command_handle = PRIVSEP(audit_run_command(s->command)); + #endif + if (s->ttyfd != -1) + ret = do_exec_pty(s, command); +@@ -1695,7 +1707,10 @@ do_child(Session *s, const char *command + int r = 0; + + /* remove hostkey from the child's memory */ +- destroy_sensitive_data(); ++ destroy_sensitive_data(1); ++ /* Don't audit this - both us and the parent would be talking to the ++ monitor over a single socket, with no synchronization. */ ++ packet_destroy_all(0, 1); + + /* Force a password change */ + if (s->authctxt->force_pwchange) { +@@ -1925,6 +1940,7 @@ session_unused(int id) + sessions[id].ttyfd = -1; + sessions[id].ptymaster = -1; + sessions[id].x11_chanids = NULL; ++ sessions[id].command_handle = -1; + sessions[id].next_unused = sessions_first_unused; + sessions_first_unused = id; + } +@@ -2007,6 +2023,19 @@ session_open(Authctxt *authctxt, int cha + } + + Session * ++session_by_id(int id) ++{ ++ if (id >= 0 && id < sessions_nalloc) { ++ Session *s = &sessions[id]; ++ if (s->used) ++ return s; ++ } ++ debug("session_by_id: unknown id %d", id); ++ session_dump(); ++ return NULL; ++} ++ ++Session * + session_by_tty(char *tty) + { + int i; +@@ -2523,6 +2552,32 @@ session_exit_message(Session *s, int sta + chan_write_failed(c); + } + ++#ifdef SSH_AUDIT_EVENTS ++void ++session_end_command2(Session *s) ++{ ++ if (s->command != NULL) { ++ if (s->command_handle != -1) ++ audit_end_command(s->command_handle, s->command); ++ free(s->command); ++ s->command = NULL; ++ s->command_handle = -1; ++ } ++} ++ ++static void ++session_end_command(Session *s) ++{ ++ if (s->command != NULL) { ++ if (s->command_handle != -1) ++ PRIVSEP(audit_end_command(s->command_handle, s->command)); ++ free(s->command); ++ s->command = NULL; ++ s->command_handle = -1; ++ } ++} ++#endif ++ + void + session_close(Session *s) + { +@@ -2531,6 +2586,10 @@ session_close(Session *s) + + if (s->ttyfd != -1) + session_pty_cleanup(s); ++#ifdef SSH_AUDIT_EVENTS ++ if (s->command) ++ session_end_command(s); ++#endif + free(s->term); + free(s->display); + free(s->x11_chanids); +@@ -2745,6 +2804,15 @@ do_authenticated2(Authctxt *authctxt) + server_loop2(authctxt); + } + ++static void ++do_cleanup_one_session(Session *s) ++{ ++ session_pty_cleanup2(s); ++#ifdef SSH_AUDIT_EVENTS ++ session_end_command2(s); ++#endif ++} ++ + void + do_cleanup(Authctxt *authctxt) + { +@@ -2793,5 +2861,5 @@ do_cleanup(Authctxt *authctxt) + * or if running in monitor. + */ + if (!use_privsep || mm_is_monitor()) +- session_destroy_all(session_pty_cleanup2); ++ session_destroy_all(do_cleanup_one_session); + } +diff -up openssh-7.2p1/session.h.audit openssh-7.2p1/session.h +--- openssh-7.2p1/session.h.audit 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/session.h 2016-02-12 18:24:34.226825175 +0100 +@@ -61,6 +61,12 @@ struct Session { + char *name; + char *val; + } *env; ++ ++ /* exec */ ++#ifdef SSH_AUDIT_EVENTS ++ int command_handle; ++ char *command; ++#endif + }; + + void do_authenticated(Authctxt *); +@@ -73,8 +79,10 @@ void session_close_by_pid(pid_t, int); + void session_close_by_channel(int, void *); + void session_destroy_all(void (*)(Session *)); + void session_pty_cleanup2(Session *); ++void session_end_command2(Session *); + + Session *session_new(void); ++Session *session_by_id(int); + Session *session_by_tty(char *); + void session_close(Session *); + void do_setusercontext(struct passwd *); +diff -up openssh-7.2p1/sshd.c.audit openssh-7.2p1/sshd.c +--- openssh-7.2p1/sshd.c.audit 2016-02-12 18:24:34.189825190 +0100 ++++ openssh-7.2p1/sshd.c 2016-02-12 18:44:44.650639338 +0100 +@@ -121,6 +121,7 @@ + #include "ssh-gss.h" + #endif + #include "monitor_wrap.h" ++#include "audit.h" + #include "ssh-sandbox.h" + #include "version.h" + #include "ssherr.h" +@@ -260,7 +261,7 @@ Buffer loginmsg; + struct passwd *privsep_pw = NULL; + + /* Prototypes for various functions defined later in this file. */ +-void destroy_sensitive_data(void); ++void destroy_sensitive_data(int); + void demote_sensitive_data(void); + + #ifdef WITH_SSH1 +@@ -281,6 +282,15 @@ close_listen_socks(void) + num_listen_socks = -1; + } + ++/* ++ * Is this process listening for clients (i.e. not specific to any specific ++ * client connection?) ++ */ ++int listening_for_clients(void) ++{ ++ return num_listen_socks > 0; ++} ++ + static void + close_startup_pipes(void) + { +@@ -560,22 +570,45 @@ sshd_exchange_identification(int sock_in + } + } + +-/* Destroy the host and server keys. They will no longer be needed. */ ++/* ++ * Destroy the host and server keys. They will no longer be needed. Careful, ++ * this can be called from cleanup_exit() - i.e. from just about anywhere. ++ */ + void +-destroy_sensitive_data(void) ++destroy_sensitive_data(int privsep) + { + int i; ++ pid_t pid; ++ uid_t uid; + + if (sensitive_data.server_key) { + key_free(sensitive_data.server_key); + sensitive_data.server_key = NULL; + } ++ pid = getpid(); ++ uid = getuid(); + for (i = 0; i < options.num_host_key_files; i++) { + if (sensitive_data.host_keys[i]) { ++ char *fp; ++ ++ if (key_is_private(sensitive_data.host_keys[i])) ++ fp = sshkey_fingerprint(sensitive_data.host_keys[i], options.fingerprint_hash, SSH_FP_HEX); ++ else ++ fp = NULL; + key_free(sensitive_data.host_keys[i]); + sensitive_data.host_keys[i] = NULL; ++ if (fp != NULL) { ++ if (privsep) ++ PRIVSEP(audit_destroy_sensitive_data(fp, ++ pid, uid)); ++ else ++ audit_destroy_sensitive_data(fp, ++ pid, uid); ++ free(fp); ++ } + } +- if (sensitive_data.host_certificates[i]) { ++ if (sensitive_data.host_certificates ++ && sensitive_data.host_certificates[i]) { + key_free(sensitive_data.host_certificates[i]); + sensitive_data.host_certificates[i] = NULL; + } +@@ -589,6 +622,8 @@ void + demote_sensitive_data(void) + { + Key *tmp; ++ pid_t pid; ++ uid_t uid; + int i; + + if (sensitive_data.server_key) { +@@ -597,13 +632,25 @@ demote_sensitive_data(void) + sensitive_data.server_key = tmp; + } + ++ pid = getpid(); ++ uid = getuid(); + for (i = 0; i < options.num_host_key_files; i++) { + if (sensitive_data.host_keys[i]) { ++ char *fp; ++ ++ if (key_is_private(sensitive_data.host_keys[i])) ++ fp = sshkey_fingerprint(sensitive_data.host_keys[i], options.fingerprint_hash, SSH_FP_HEX); ++ else ++ fp = NULL; + tmp = key_demote(sensitive_data.host_keys[i]); + key_free(sensitive_data.host_keys[i]); + sensitive_data.host_keys[i] = tmp; + if (tmp->type == KEY_RSA1) + sensitive_data.ssh1_host_key = tmp; ++ if (fp != NULL) { ++ audit_destroy_sensitive_data(fp, pid, uid); ++ free(fp); ++ } + } + /* Certs do not need demotion */ + } +@@ -675,7 +722,7 @@ privsep_preauth(Authctxt *authctxt) + + if (use_privsep == PRIVSEP_ON) + box = ssh_sandbox_init(pmonitor); +- pid = fork(); ++ pmonitor->m_pid = pid = fork(); + if (pid == -1) { + fatal("fork of unprivileged child failed"); + } else if (pid != 0) { +@@ -760,6 +807,12 @@ privsep_postauth(Authctxt *authctxt) + else if (pmonitor->m_pid != 0) { + verbose("User child is on pid %ld", (long)pmonitor->m_pid); + buffer_clear(&loginmsg); ++ if (*pmonitor->m_pkex != NULL ){ ++ newkeys_destroy((*pmonitor->m_pkex)->newkeys[MODE_OUT]); ++ newkeys_destroy((*pmonitor->m_pkex)->newkeys[MODE_IN]); ++ audit_session_key_free_body(2, getpid(), getuid()); ++ packet_destroy_all(0, 0); ++ } + monitor_child_postauth(pmonitor); + + /* NEVERREACHED */ +@@ -1293,6 +1346,7 @@ server_accept_loop(int *sock_in, int *so + if (received_sigterm) { + logit("Received signal %d; terminating.", + (int) received_sigterm); ++ destroy_sensitive_data(0); + close_listen_socks(); + if (options.pid_file != NULL) + unlink(options.pid_file); +@@ -2256,6 +2310,7 @@ main(int ac, char **av) + */ + if (use_privsep) { + mm_send_keystate(pmonitor); ++ packet_destroy_all(1, 1); + exit(0); + } + +@@ -2301,7 +2356,7 @@ main(int ac, char **av) + privsep_postauth(authctxt); + /* the monitor process [priv] will not return */ + if (!compat20) +- destroy_sensitive_data(); ++ destroy_sensitive_data(0); + } + + packet_set_timeout(options.client_alive_interval, +@@ -2315,6 +2370,9 @@ main(int ac, char **av) + do_authenticated(authctxt); + + /* The connection has been terminated. */ ++ packet_destroy_all(1, 1); ++ destroy_sensitive_data(1); ++ + packet_get_bytes(&ibytes, &obytes); + verbose("Transferred: sent %llu, received %llu bytes", + (unsigned long long)obytes, (unsigned long long)ibytes); +@@ -2475,6 +2533,10 @@ do_ssh1_kex(void) + if (cookie[i] != packet_get_char()) + packet_disconnect("IP Spoofing check bytes do not match."); + ++#ifdef SSH_AUDIT_EVENTS ++ audit_kex(2, cipher_name(cipher_type), "crc", "none", "none"); ++#endif ++ + debug("Encryption type: %.200s", cipher_name(cipher_type)); + + /* Get the encrypted integer. */ +@@ -2534,7 +2596,7 @@ do_ssh1_kex(void) + } + + /* Destroy the private and public keys. No longer. */ +- destroy_sensitive_data(); ++ destroy_sensitive_data(1); + + if (use_privsep) + mm_ssh1_session_id(session_id); +@@ -2708,6 +2770,16 @@ do_ssh2_kex(void) + void + cleanup_exit(int i) + { ++ static int in_cleanup = 0; ++ int is_privsep_child; ++ ++ /* cleanup_exit can be called at the very least from the privsep ++ wrappers used for auditing. Make sure we don't recurse ++ indefinitely. */ ++ if (in_cleanup) ++ _exit(i); ++ in_cleanup = 1; ++ + if (the_authctxt) { + do_cleanup(the_authctxt); + if (use_privsep && privsep_is_preauth && +@@ -2719,9 +2791,14 @@ cleanup_exit(int i) + pmonitor->m_pid, strerror(errno)); + } + } ++ is_privsep_child = use_privsep && pmonitor != NULL && pmonitor->m_pid == 0; ++ if (sensitive_data.host_keys != NULL) ++ destroy_sensitive_data(is_privsep_child); ++ packet_destroy_all(1, is_privsep_child); + #ifdef SSH_AUDIT_EVENTS + /* done after do_cleanup so it can cancel the PAM auth 'thread' */ +- if (!use_privsep || mm_is_monitor()) ++ if ((the_authctxt == NULL || !the_authctxt->authenticated) && ++ (!use_privsep || mm_is_monitor())) + audit_event(SSH_CONNECTION_ABANDON); + #endif + _exit(i); +diff -up openssh-7.2p1/sshkey.c.audit openssh-7.2p1/sshkey.c +--- openssh-7.2p1/sshkey.c.audit 2016-02-12 18:24:34.157825202 +0100 ++++ openssh-7.2p1/sshkey.c 2016-02-12 18:24:34.228825175 +0100 +@@ -303,6 +303,33 @@ sshkey_type_is_valid_ca(int type) + } + + int ++sshkey_is_private(const struct sshkey *k) ++{ ++ switch (k->type) { ++#ifdef WITH_OPENSSL ++ case KEY_RSA_CERT: ++ case KEY_RSA1: ++ case KEY_RSA: ++ return k->rsa->d != NULL; ++ case KEY_DSA_CERT: ++ case KEY_DSA: ++ return k->dsa->priv_key != NULL; ++#ifdef OPENSSL_HAS_ECC ++ case KEY_ECDSA_CERT: ++ case KEY_ECDSA: ++ return EC_KEY_get0_private_key(k->ecdsa) != NULL; ++#endif /* OPENSSL_HAS_ECC */ ++#endif /* WITH_OPENSSL */ ++ case KEY_ED25519_CERT: ++ case KEY_ED25519: ++ return (k->ed25519_pk != NULL); ++ default: ++ /* fatal("key_is_private: bad key type %d", k->type); */ ++ return 0; ++ } ++} ++ ++int + sshkey_is_cert(const struct sshkey *k) + { + if (k == NULL) +diff -up openssh-7.2p1/sshkey.h.audit openssh-7.2p1/sshkey.h +--- openssh-7.2p1/sshkey.h.audit 2016-02-12 18:24:34.157825202 +0100 ++++ openssh-7.2p1/sshkey.h 2016-02-12 18:24:34.228825175 +0100 +@@ -133,6 +133,7 @@ u_int sshkey_size(const struct sshkey + int sshkey_generate(int type, u_int bits, struct sshkey **keyp); + int sshkey_from_private(const struct sshkey *, struct sshkey **); + int sshkey_type_from_name(const char *); ++int sshkey_is_private(const struct sshkey *); + int sshkey_is_cert(const struct sshkey *); + int sshkey_type_is_cert(int); + int sshkey_type_plain(int); diff --git a/openssh-7.2p1-gsskex.patch b/openssh-7.2p1-gsskex.patch new file mode 100644 index 0000000..ab08b96 --- /dev/null +++ b/openssh-7.2p1-gsskex.patch @@ -0,0 +1,2741 @@ +diff -up openssh-7.2p1/auth2.c.gsskex openssh-7.2p1/auth2.c +--- openssh-7.2p1/auth2.c.gsskex 2016-02-19 10:01:04.829969345 +0100 ++++ openssh-7.2p1/auth2.c 2016-02-19 10:01:04.865969325 +0100 +@@ -70,6 +70,7 @@ extern Authmethod method_passwd; + extern Authmethod method_kbdint; + extern Authmethod method_hostbased; + #ifdef GSSAPI ++extern Authmethod method_gsskeyex; + extern Authmethod method_gssapi; + #endif + +@@ -77,6 +78,7 @@ Authmethod *authmethods[] = { + &method_none, + &method_pubkey, + #ifdef GSSAPI ++ &method_gsskeyex, + &method_gssapi, + #endif + &method_passwd, +diff -up openssh-7.2p1/auth2-gss.c.gsskex openssh-7.2p1/auth2-gss.c +--- openssh-7.2p1/auth2-gss.c.gsskex 2016-02-19 10:01:04.829969345 +0100 ++++ openssh-7.2p1/auth2-gss.c 2016-02-19 10:01:04.865969325 +0100 +@@ -31,6 +31,7 @@ + #include + + #include ++#include + + #include "xmalloc.h" + #include "key.h" +@@ -53,6 +54,40 @@ static int input_gssapi_mic(int type, u_ + static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); + static int 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); ++ free(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) +@@ -238,7 +273,8 @@ input_gssapi_exchange_complete(int type, + + packet_check_eom(); + +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw)); + + authctxt->postponed = 0; + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); +@@ -281,7 +317,8 @@ input_gssapi_mic(int type, u_int32_t ple + gssbuf.length = buffer_len(&b); + + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = ++ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); + else + logit("GSSAPI MIC check failed"); + +@@ -299,6 +336,12 @@ input_gssapi_mic(int type, u_int32_t ple + return 0; + } + ++Authmethod method_gsskeyex = { ++ "gssapi-keyex", ++ userauth_gsskeyex, ++ &options.gss_authentication ++}; ++ + Authmethod method_gssapi = { + "gssapi-with-mic", + userauth_gssapi, +diff -up openssh-7.2p1/auth.c.gsskex openssh-7.2p1/auth.c +--- openssh-7.2p1/auth.c.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/auth.c 2016-02-19 10:01:04.866969324 +0100 +@@ -354,6 +354,7 @@ auth_root_allowed(const char *method) + case PERMIT_NO_PASSWD: + if (strcmp(method, "publickey") == 0 || + strcmp(method, "hostbased") == 0 || ++ strcmp(method, "gssapi-keyex") == 0 || + strcmp(method, "gssapi-with-mic") == 0) + return 1; + break; +diff -up openssh-7.2p1/clientloop.c.gsskex openssh-7.2p1/clientloop.c +--- openssh-7.2p1/clientloop.c.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/clientloop.c 2016-02-19 10:01:04.866969324 +0100 +@@ -114,6 +114,10 @@ + #include "ssherr.h" + #include "hostfile.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* import options */ + extern Options options; + +@@ -1662,9 +1666,18 @@ client_loop(int have_pty, int escape_cha + break; + + /* Do channel operations unless rekeying in progress. */ +- if (!ssh_packet_is_rekeying(active_state)) ++ if (!ssh_packet_is_rekeying(active_state)) { + channel_after_select(readset, writeset); + ++#ifdef GSSAPI ++ if (options.gss_renewal_rekey && ++ ssh_gssapi_credentials_updated(GSS_C_NO_CONTEXT)) { ++ debug("credentials updated - forcing rekey"); ++ need_rekeying = 1; ++ } ++#endif ++ } ++ + /* Buffer input from the connection. */ + client_process_net_input(readset); + +diff -up openssh-7.2p1/configure.ac.gsskex openssh-7.2p1/configure.ac +--- openssh-7.2p1/configure.ac.gsskex 2016-02-19 10:01:04.857969329 +0100 ++++ openssh-7.2p1/configure.ac 2016-02-19 10:01:04.867969323 +0100 +@@ -632,6 +632,30 @@ main() { if (NSVersionOfRunTimeLibrary(" + [Use tunnel device compatibility to OpenBSD]) + AC_DEFINE([SSH_TUN_PREPEND_AF], [1], + [Prepend the address family to IP tunnel traffic]) ++ AC_MSG_CHECKING(if we have the Security Authorization Session API) ++ AC_TRY_COMPILE([#include ], ++ [SessionCreate(0, 0);], ++ [ac_cv_use_security_session_api="yes" ++ AC_DEFINE(USE_SECURITY_SESSION_API, 1, ++ [platform has the Security Authorization Session API]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT(yes)], ++ [ac_cv_use_security_session_api="no" ++ AC_MSG_RESULT(no)]) ++ AC_MSG_CHECKING(if we have an in-memory credentials cache) ++ AC_TRY_COMPILE( ++ [#include ], ++ [cc_context_t c; ++ (void) cc_initialize (&c, 0, NULL, NULL);], ++ [AC_DEFINE(USE_CCAPI, 1, ++ [platform uses an in-memory credentials cache]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT(yes) ++ if test "x$ac_cv_use_security_session_api" = "xno"; then ++ AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***) ++ fi], ++ [AC_MSG_RESULT(no)] ++ ) + m4_pattern_allow([AU_IPv]) + AC_CHECK_DECL([AU_IPv4], [], + AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) +diff -up openssh-7.2p1/gss-genr.c.gsskex openssh-7.2p1/gss-genr.c +--- openssh-7.2p1/gss-genr.c.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/gss-genr.c 2016-02-19 10:01:04.867969323 +0100 +@@ -41,12 +41,167 @@ + #include "buffer.h" + #include "log.h" + #include "ssh2.h" ++#include "cipher.h" ++#include "key.h" ++#include "kex.h" ++#include + + #include "ssh-gss.h" + + extern u_char *session_id2; + extern u_int session_id2_len; + ++typedef struct { ++ char *encoded; ++ gss_OID oid; ++} ssh_gss_kex_mapping; ++ ++/* ++ * XXX - It would be nice to find a more elegant way of handling the ++ * XXX passing of the key exchange context to the userauth routines ++ */ ++ ++Gssctxt *gss_kex_context = NULL; ++ ++static ssh_gss_kex_mapping *gss_enc2oid = NULL; ++ ++int ++ssh_gssapi_oid_table_ok() { ++ return (gss_enc2oid != NULL); ++} ++ ++/* ++ * Return a list of the gss-group1-sha1 mechanisms supported by this program ++ * ++ * We test mechanisms to ensure that we can use them, to avoid starting ++ * a key exchange with a bad mechanism ++ */ ++ ++char * ++ssh_gssapi_client_mechanisms(const char *host, const char *client) { ++ gss_OID_set gss_supported; ++ OM_uint32 min_status; ++ ++ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) ++ return NULL; ++ ++ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, ++ host, client)); ++} ++ ++char * ++ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, ++ const char *host, const char *client) { ++ Buffer buf; ++ size_t i; ++ int oidpos, enclen; ++ char *mechs, *encoded; ++ u_char digest[EVP_MAX_MD_SIZE]; ++ char deroid[2]; ++ const EVP_MD *evp_md = EVP_md5(); ++ EVP_MD_CTX md; ++ ++ if (gss_enc2oid != NULL) { ++ for (i = 0; gss_enc2oid[i].encoded != NULL; i++) ++ free(gss_enc2oid[i].encoded); ++ free(gss_enc2oid); ++ } ++ ++ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * ++ (gss_supported->count + 1)); ++ ++ buffer_init(&buf); ++ ++ oidpos = 0; ++ for (i = 0; i < gss_supported->count; i++) { ++ if (gss_supported->elements[i].length < 128 && ++ (*check)(NULL, &(gss_supported->elements[i]), host, client)) { ++ ++ deroid[0] = SSH_GSS_OIDTYPE; ++ deroid[1] = gss_supported->elements[i].length; ++ ++ EVP_DigestInit(&md, evp_md); ++ EVP_DigestUpdate(&md, deroid, 2); ++ EVP_DigestUpdate(&md, ++ gss_supported->elements[i].elements, ++ gss_supported->elements[i].length); ++ EVP_DigestFinal(&md, digest, NULL); ++ ++ encoded = xmalloc(EVP_MD_size(evp_md) * 2); ++ enclen = __b64_ntop(digest, EVP_MD_size(evp_md), ++ encoded, EVP_MD_size(evp_md) * 2); ++ ++ if (oidpos != 0) ++ buffer_put_char(&buf, ','); ++ ++ buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, ++ sizeof(KEX_GSS_GEX_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ buffer_put_char(&buf, ','); ++ buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, ++ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ buffer_put_char(&buf, ','); ++ buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, ++ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ ++ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); ++ gss_enc2oid[oidpos].encoded = encoded; ++ oidpos++; ++ } ++ } ++ gss_enc2oid[oidpos].oid = NULL; ++ gss_enc2oid[oidpos].encoded = NULL; ++ ++ buffer_put_char(&buf, '\0'); ++ ++ mechs = xmalloc(buffer_len(&buf)); ++ buffer_get(&buf, mechs, buffer_len(&buf)); ++ buffer_free(&buf); ++ ++ if (strlen(mechs) == 0) { ++ free(mechs); ++ mechs = NULL; ++ } ++ ++ return (mechs); ++} ++ ++gss_OID ++ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { ++ int i = 0; ++ ++ switch (kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; ++ break; ++ case KEX_GSS_GEX_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; ++ break; ++ default: ++ return GSS_C_NO_OID; ++ } ++ ++ while (gss_enc2oid[i].encoded != NULL && ++ strcmp(name, gss_enc2oid[i].encoded) != 0) ++ i++; ++ ++ if (gss_enc2oid[i].oid != NULL && ctx != NULL) ++ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); ++ ++ return gss_enc2oid[i].oid; ++} ++ + /* Check that the OID in a data stream matches that in the context */ + int + ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) +@@ -199,7 +354,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int de + } + + ctx->major = gss_init_sec_context(&ctx->minor, +- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, ++ ctx->client_creds, &ctx->context, ctx->name, ctx->oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, + 0, NULL, recv_tok, NULL, send_tok, flags, NULL); + +@@ -229,8 +384,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, con + } + + OM_uint32 ++ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) ++{ ++ gss_buffer_desc gssbuf; ++ gss_name_t gssname; ++ OM_uint32 status; ++ gss_OID_set oidset; ++ ++ gssbuf.value = (void *) name; ++ gssbuf.length = strlen(gssbuf.value); ++ ++ gss_create_empty_oid_set(&status, &oidset); ++ gss_add_oid_set_member(&status, ctx->oid, &oidset); ++ ++ ctx->major = gss_import_name(&ctx->minor, &gssbuf, ++ GSS_C_NT_USER_NAME, &gssname); ++ ++ if (!ctx->major) ++ ctx->major = gss_acquire_cred(&ctx->minor, ++ gssname, 0, oidset, GSS_C_INITIATE, ++ &ctx->client_creds, NULL, NULL); ++ ++ gss_release_name(&status, &gssname); ++ gss_release_oid_set(&status, &oidset); ++ ++ if (ctx->major) ++ ssh_gssapi_error(ctx); ++ ++ return(ctx->major); ++} ++ ++OM_uint32 + ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) + { ++ if (ctx == NULL) ++ return -1; ++ + if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, + GSS_C_QOP_DEFAULT, buffer, hash))) + ssh_gssapi_error(ctx); +@@ -238,6 +427,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer + return (ctx->major); + } + ++/* Priviledged when used by server */ ++OM_uint32 ++ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++{ ++ if (ctx == NULL) ++ return -1; ++ ++ ctx->major = gss_verify_mic(&ctx->minor, ctx->context, ++ gssbuf, gssmic, NULL); ++ ++ return (ctx->major); ++} ++ + void + ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, + const char *context) +@@ -251,11 +453,16 @@ ssh_gssapi_buildmic(Buffer *b, const cha + } + + int +-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) ++ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, ++ const char *client) + { + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; ++ Gssctxt *intctx = NULL; ++ ++ if (ctx == NULL) ++ ctx = &intctx; + + /* RFC 4462 says we MUST NOT do SPNEGO */ + if (oid->length == spnego_oid.length && +@@ -265,6 +472,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + major = ssh_gssapi_import_name(*ctx, host); ++ ++ if (!GSS_ERROR(major) && client) ++ major = ssh_gssapi_client_identity(*ctx, client); ++ + if (!GSS_ERROR(major)) { + major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, + NULL); +@@ -274,10 +485,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx + GSS_C_NO_BUFFER); + } + +- if (GSS_ERROR(major)) ++ if (GSS_ERROR(major) || intctx != NULL) + ssh_gssapi_delete_ctx(ctx); + + return (!GSS_ERROR(major)); + } + ++int ++ssh_gssapi_credentials_updated(Gssctxt *ctxt) { ++ static gss_name_t saved_name = GSS_C_NO_NAME; ++ static OM_uint32 saved_lifetime = 0; ++ static gss_OID saved_mech = GSS_C_NO_OID; ++ static gss_name_t name; ++ static OM_uint32 last_call = 0; ++ OM_uint32 lifetime, now, major, minor; ++ int equal; ++ ++ now = time(NULL); ++ ++ if (ctxt) { ++ debug("Rekey has happened - updating saved versions"); ++ ++ if (saved_name != GSS_C_NO_NAME) ++ gss_release_name(&minor, &saved_name); ++ ++ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, ++ &saved_name, &saved_lifetime, NULL, NULL); ++ ++ if (!GSS_ERROR(major)) { ++ saved_mech = ctxt->oid; ++ saved_lifetime+= now; ++ } else { ++ /* Handle the error */ ++ } ++ return 0; ++ } ++ ++ if (now - last_call < 10) ++ return 0; ++ ++ last_call = now; ++ ++ if (saved_mech == GSS_C_NO_OID) ++ return 0; ++ ++ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, ++ &name, &lifetime, NULL, NULL); ++ if (major == GSS_S_CREDENTIALS_EXPIRED) ++ return 0; ++ else if (GSS_ERROR(major)) ++ return 0; ++ ++ major = gss_compare_name(&minor, saved_name, name, &equal); ++ gss_release_name(&minor, &name); ++ if (GSS_ERROR(major)) ++ return 0; ++ ++ if (equal && (saved_lifetime < lifetime + now - 10)) ++ return 1; ++ ++ return 0; ++} ++ + #endif /* GSSAPI */ +diff -up openssh-7.2p1/gss-serv.c.gsskex openssh-7.2p1/gss-serv.c +--- openssh-7.2p1/gss-serv.c.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/gss-serv.c 2016-02-19 10:01:04.867969323 +0100 +@@ -45,17 +45,19 @@ + #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, NULL, {NULL, NULL, NULL, NULL}}; ++ GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, {NULL, NULL, NULL}, 0, 0}; + + ssh_gssapi_mech gssapi_null_mech = +- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; ++ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; + + #ifdef KRB5 + extern ssh_gssapi_mech gssapi_kerberos_mech; +@@ -142,6 +144,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss + } + + /* 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) + { +@@ -151,7 +176,9 @@ ssh_gssapi_supported_oids(gss_OID_set *o + gss_OID_set supported; + + gss_create_empty_oid_set(&min_status, oidset); +- gss_indicate_mechs(&min_status, &supported); ++ ++ 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, +@@ -277,8 +304,48 @@ 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); ++ } + +- gss_buffer_desc ename; ++ 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; + +@@ -293,6 +360,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g + 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); +@@ -310,6 +384,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g + 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; +@@ -320,11 +396,20 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g + 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); ++ krb5_ccache ccache = NULL; ++ krb5_error_code problem; ++ ++ if (gssapi_client.store.data != NULL) { ++ if ((problem = krb5_cc_resolve(gssapi_client.store.data, gssapi_client.store.envval, &ccache))) { ++ debug("%s: krb5_cc_resolve(): %.100s", __func__, ++ krb5_get_err_text(gssapi_client.store.data, problem)); ++ } else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) { ++ debug("%s: krb5_cc_resolve(): %.100s", __func__, ++ krb5_get_err_text(gssapi_client.store.data, problem)); ++ } else { ++ krb5_free_context(gssapi_client.store.data); ++ gssapi_client.store.data = NULL; ++ } + } + } + +@@ -357,7 +442,7 @@ ssh_gssapi_do_child(char ***envp, u_int + + /* Privileged */ + int +-ssh_gssapi_userok(char *user) ++ssh_gssapi_userok(char *user, struct passwd *pw) + { + OM_uint32 lmin; + +@@ -367,9 +452,11 @@ ssh_gssapi_userok(char *user) + return 0; + } + if (gssapi_client.mech && gssapi_client.mech->userok) +- if ((*gssapi_client.mech->userok)(&gssapi_client, user)) ++ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { ++ gssapi_client.used = 1; ++ gssapi_client.store.owner = pw; + return 1; +- else { ++ } else { + /* Destroy delegated credentials if userok fails */ + gss_release_buffer(&lmin, &gssapi_client.displayname); + gss_release_buffer(&lmin, &gssapi_client.exportedname); +@@ -383,14 +470,90 @@ ssh_gssapi_userok(char *user) + return (0); + } + +-/* Privileged */ +-OM_uint32 +-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++/* 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) + { +- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, +- gssbuf, gssmic, NULL); ++ return (PAM_CONV_ERR); ++} ++#endif + +- return (ctx->major); ++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 -up openssh-7.2p1/gss-serv-krb5.c.gsskex openssh-7.2p1/gss-serv-krb5.c +--- openssh-7.2p1/gss-serv-krb5.c.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/gss-serv-krb5.c 2016-02-19 10:01:04.867969323 +0100 +@@ -121,7 +121,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl + krb5_error_code problem; + krb5_principal princ; + OM_uint32 maj_status, min_status; +- int len; ++ const char *new_ccname, *new_cctype; + const char *errmsg; + + if (client->creds == NULL) { +@@ -181,11 +181,26 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl + return; + } + +- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); ++ new_cctype = krb5_cc_get_type(krb_context, ccache); ++ new_ccname = krb5_cc_get_name(krb_context, ccache); ++ + client->store.envvar = "KRB5CCNAME"; +- len = strlen(client->store.filename) + 6; +- client->store.envval = xmalloc(len); +- snprintf(client->store.envval, len, "FILE:%s", client->store.filename); ++#ifdef USE_CCAPI ++ xasprintf(&client->store.envval, "API:%s", new_ccname); ++ client->store.filename = NULL; ++#else ++ if (new_ccname[0] == ':') ++ new_ccname++; ++ xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname); ++ if (strcmp(new_cctype, "DIR") == 0) { ++ char *p; ++ p = strrchr(client->store.envval, '/'); ++ if (p) ++ *p = '\0'; ++ } ++ if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0)) ++ client->store.filename = xstrdup(new_ccname); ++#endif + + #ifdef USE_PAM + if (options.use_pam) +@@ -194,9 +209,76 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl + + krb5_cc_close(krb_context, ccache); + ++ client->store.data = krb_context; ++ + return; + } + ++int ++ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, ++ ssh_gssapi_client *client) ++{ ++ krb5_ccache ccache = NULL; ++ krb5_principal principal = NULL; ++ char *name = NULL; ++ krb5_error_code problem; ++ OM_uint32 maj_status, min_status; ++ ++ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) { ++ logit("krb5_cc_resolve(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ return 0; ++ } ++ ++ /* Find out who the principal in this cache is */ ++ if ((problem = krb5_cc_get_principal(krb_context, ccache, ++ &principal))) { ++ logit("krb5_cc_get_principal(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ if ((problem = krb5_unparse_name(krb_context, principal, &name))) { ++ logit("krb5_unparse_name(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ ++ if (strcmp(name,client->exportedname.value)!=0) { ++ debug("Name in local credentials cache differs. Not storing"); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ krb5_free_unparsed_name(krb_context, name); ++ return 0; ++ } ++ krb5_free_unparsed_name(krb_context, name); ++ ++ /* Name matches, so lets get on with it! */ ++ ++ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) { ++ logit("krb5_cc_initialize(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ krb5_free_principal(krb_context, principal); ++ ++ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds, ++ ccache))) { ++ logit("gss_krb5_copy_ccache() failed. Sorry!"); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ return 1; ++} ++ + ssh_gssapi_mech gssapi_kerberos_mech = { + "toWM5Slw5Ew8Mqkay+al2g==", + "Kerberos", +@@ -204,7 +286,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = { + NULL, + &ssh_gssapi_krb5_userok, + NULL, +- &ssh_gssapi_krb5_storecreds ++ &ssh_gssapi_krb5_storecreds, ++ &ssh_gssapi_krb5_updatecreds + }; + + #endif /* KRB5 */ +diff -up openssh-7.2p1/kex.c.gsskex openssh-7.2p1/kex.c +--- openssh-7.2p1/kex.c.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/kex.c 2016-02-19 10:01:04.868969323 +0100 +@@ -54,6 +54,10 @@ + #include "sshbuf.h" + #include "digest.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + #if OPENSSL_VERSION_NUMBER >= 0x00907000L + # if defined(HAVE_EVP_SHA256) + # define evp_ssh_sha256 EVP_sha256 +@@ -107,6 +111,11 @@ static const struct kexalg kexalgs[] = { + #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL) + { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, + #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ ++#ifdef GSSAPI ++ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, ++#endif + { NULL, -1, -1, -1}, + }; + +@@ -140,6 +149,12 @@ kex_alg_by_name(const char *name) + for (k = kexalgs; k->name != NULL; k++) { + if (strcmp(k->name, name) == 0) + return k; ++#ifdef GSSAPI ++ if (strncmp(name, "gss-", 4) == 0) { ++ if (strncmp(k->name, name, strlen(k->name)) == 0) ++ return k; ++ } ++#endif + } + return NULL; + } +diff -up openssh-7.2p1/kexgssc.c.gsskex openssh-7.2p1/kexgssc.c +--- openssh-7.2p1/kexgssc.c.gsskex 2016-02-19 10:01:04.868969323 +0100 ++++ openssh-7.2p1/kexgssc.c 2016-02-19 10:01:04.868969323 +0100 +@@ -0,0 +1,338 @@ ++/* ++ * 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 "includes.h" ++ ++#include ++#include ++ ++#include ++ ++#include "xmalloc.h" ++#include "buffer.h" ++#include "ssh2.h" ++#include "key.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "digest.h" ++ ++#include "ssh-gss.h" ++ ++int ++kexgss_client(struct ssh *ssh) { ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; ++ Gssctxt *ctxt; ++ OM_uint32 maj_status, min_status, ret_flags; ++ u_int klen, kout, slen = 0, strlen; ++ DH *dh; ++ BIGNUM *dh_server_pub = NULL; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *p = NULL; ++ BIGNUM *g = NULL; ++ u_char *kbuf; ++ u_char *serverhostkey = NULL; ++ u_char *empty = ""; ++ char *msg; ++ char *lang; ++ int type = 0; ++ int first = 1; ++ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&ctxt); ++ if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type) ++ == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ if (ssh->kex->gss_client && ++ ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client)) ++ fatal("Couldn't acquire client credentials"); ++ ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange\n"); ++ nbits = dh_estimate(ssh->kex->we_need * 8); ++ packet_start(SSH2_MSG_KEXGSS_GROUPREQ); ++ packet_put_int(min); ++ packet_put_int(nbits); ++ packet_put_int(max); ++ ++ packet_send(); ++ ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUP); ++ ++ if ((p = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(p); ++ if ((g = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(g); ++ packet_check_eom(); ++ ++ if (BN_num_bits(p) < min || BN_num_bits(p) > max) ++ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", ++ min, BN_num_bits(p), max); ++ ++ dh = dh_new_group(g, p); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ /* Step 1 - e is dh->pub_key */ ++ dh_gen_key(dh, ssh->kex->we_need * 8); ++ ++ /* This is f, we initialise it now to make life easier */ ++ dh_server_pub = BN_new(); ++ if (dh_server_pub == NULL) ++ fatal("dh_server_pub == NULL"); ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ ssh->kex->gss_deleg_creds, token_ptr, &send_tok, ++ &ret_flags); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length != 0) { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ fatal("gss_init_context failed"); ++ } ++ ++ /* If we've got an old receive buffer get rid of it */ ++ if (token_ptr != GSS_C_NO_BUFFER) ++ free(recv_tok.value); ++ ++ if (maj_status == GSS_S_COMPLETE) { ++ /* If mutual state flag is not true, kex fails */ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual authentication failed"); ++ ++ /* If integ avail flag is not true kex fails */ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity check failed"); ++ } ++ ++ /* ++ * If we have data to send, then the last message that we ++ * received cannot have been a 'complete'. ++ */ ++ if (send_tok.length != 0) { ++ if (first) { ++ packet_start(SSH2_MSG_KEXGSS_INIT); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ packet_put_bignum2(dh->pub_key); ++ first = 0; ++ } else { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ do { ++ type = packet_read(); ++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { ++ debug("Received KEXGSS_HOSTKEY"); ++ if (serverhostkey) ++ fatal("Server host key received more than once"); ++ serverhostkey = ++ packet_get_string(&slen); ++ } ++ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); ++ ++ switch (type) { ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ debug("Received GSSAPI_CONTINUE"); ++ if (maj_status == GSS_S_COMPLETE) ++ fatal("GSSAPI Continue received from server when complete"); ++ recv_tok.value = packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ packet_get_bignum2(dh_server_pub); ++ msg_tok.value = packet_get_string(&strlen); ++ msg_tok.length = strlen; ++ ++ /* Is there a token included? */ ++ if (packet_get_char()) { ++ recv_tok.value= ++ packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ /* If we're already complete - protocol error */ ++ if (maj_status == GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: received token when complete"); ++ } else { ++ /* No token included */ ++ if (maj_status != GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: did not receive final token"); ++ } ++ break; ++ case SSH2_MSG_KEXGSS_ERROR: ++ debug("Received Error"); ++ maj_status = packet_get_int(); ++ min_status = packet_get_int(); ++ msg = packet_get_string(NULL); ++ lang = packet_get_string(NULL); ++ fatal("GSSAPI Error: \n%.400s",msg); ++ default: ++ packet_disconnect("Protocol error: didn't expect packet type %d", ++ type); ++ } ++ token_ptr = &recv_tok; ++ } else { ++ /* No data, and not complete */ ++ if (maj_status != GSS_S_COMPLETE) ++ fatal("Not complete, and no token output"); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ /* ++ * We _must_ have received a COMPLETE message in reply from the ++ * server, which will have set dh_server_pub and msg_tok ++ */ ++ ++ if (type != SSH2_MSG_KEXGSS_COMPLETE) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ ++ /* Check f in range [1, p-1] */ ++ if (!dh_pub_is_valid(dh, dh_server_pub)) ++ packet_disconnect("bad server public DH value"); ++ ++ /* compute K=f^x mod p */ ++ klen = DH_size(dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_server_pub, dh); ++ if ((int)kout < 0) ++ fatal("DH_compute_key: failed"); ++ ++ shared_secret = BN_new(); ++ if (shared_secret == NULL) ++ fatal("kexgss_client: BN_new failed"); ++ ++ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) ++ fatal("kexdh_client: BN_bin2bn failed"); ++ ++ memset(kbuf, 0, klen); ++ free(kbuf); ++ ++ hashlen = sizeof(hash); ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( ssh->kex->client_version_string, ++ ssh->kex->server_version_string, ++ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), ++ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ dh->pub_key, /* e */ ++ dh_server_pub, /* f */ ++ shared_secret, /* K */ ++ hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ ssh->kex->hash_alg, ++ ssh->kex->client_version_string, ++ ssh->kex->server_version_string, ++ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), ++ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ min, nbits, max, ++ dh->p, dh->g, ++ dh->pub_key, ++ dh_server_pub, ++ shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ /* Verify that the hash matches the MIC we just got. */ ++ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) ++ packet_disconnect("Hash's MIC didn't verify"); ++ ++ free(msg_tok.value); ++ ++ DH_free(dh); ++ if (serverhostkey) ++ free(serverhostkey); ++ BN_clear_free(dh_server_pub); ++ ++ /* save session id */ ++ if (ssh->kex->session_id == NULL) { ++ ssh->kex->session_id_len = hashlen; ++ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); ++ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); ++ } ++ ++ if (ssh->kex->gss_deleg_creds) ++ ssh_gssapi_credentials_updated(ctxt); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ return kex_send_newkeys(ssh); ++} ++ ++#endif /* GSSAPI */ +diff -up openssh-7.2p1/kexgsss.c.gsskex openssh-7.2p1/kexgsss.c +--- openssh-7.2p1/kexgsss.c.gsskex 2016-02-19 10:01:04.868969323 +0100 ++++ openssh-7.2p1/kexgsss.c 2016-02-19 10:01:04.868969323 +0100 +@@ -0,0 +1,295 @@ ++/* ++ * 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 ++ ++#include ++#include ++ ++#include "xmalloc.h" ++#include "buffer.h" ++#include "ssh2.h" ++#include "key.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ ++#include "servconf.h" ++#include "ssh-gss.h" ++#include "digest.h" ++ ++extern ServerOptions options; ++ ++int ++kexgss_server(struct ssh *ssh) ++{ ++ OM_uint32 maj_status, min_status; ++ ++ /* ++ * Some GSSAPI implementations use the input value of ret_flags (an ++ * output variable) as a means of triggering mechanism specific ++ * features. Initializing it to zero avoids inadvertently ++ * activating this non-standard behaviour. ++ */ ++ ++ OM_uint32 ret_flags = 0; ++ gss_buffer_desc gssbuf, recv_tok, msg_tok; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ Gssctxt *ctxt = NULL; ++ u_int slen, klen, kout; ++ u_char *kbuf; ++ DH *dh; ++ int min = -1, max = -1, nbits = -1; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *dh_client_pub = NULL; ++ int type = 0; ++ gss_OID oid; ++ char *mechs; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ ++ /* Initialise GSSAPI */ ++ ++ /* If we're rekeying, privsep means that some of the private structures ++ * in the GSSAPI code are no longer available. This kludges them back ++ * into life ++ */ ++ if (!ssh_gssapi_oid_table_ok()) ++ if ((mechs = ssh_gssapi_server_mechanisms())) ++ free(mechs); ++ ++ debug2("%s: Identifying %s", __func__, ssh->kex->name); ++ oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type); ++ if (oid == GSS_C_NO_OID) ++ fatal("Unknown gssapi mechanism"); ++ ++ debug2("%s: Acquiring credentials", __func__); ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ fatal("Unable to acquire credentials for the server"); ++ ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange"); ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); ++ min = packet_get_int(); ++ nbits = packet_get_int(); ++ max = packet_get_int(); ++ min = MAX(DH_GRP_MIN, min); ++ max = MIN(DH_GRP_MAX, max); ++ packet_check_eom(); ++ if (max < min || nbits < min || max < nbits) ++ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", ++ min, nbits, max); ++ dh = PRIVSEP(choose_dh(min, nbits, max)); ++ if (dh == NULL) ++ packet_disconnect("Protocol error: no matching group found"); ++ ++ packet_start(SSH2_MSG_KEXGSS_GROUP); ++ packet_put_bignum2(dh->p); ++ packet_put_bignum2(dh->g); ++ packet_send(); ++ ++ packet_write_wait(); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ dh_gen_key(dh, ssh->kex->we_need * 8); ++ ++ do { ++ debug("Wait SSH2_MSG_GSSAPI_INIT"); ++ type = packet_read(); ++ switch(type) { ++ case SSH2_MSG_KEXGSS_INIT: ++ if (dh_client_pub != NULL) ++ fatal("Received KEXGSS_INIT after initialising"); ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ ++ if ((dh_client_pub = BN_new()) == NULL) ++ fatal("dh_client_pub == NULL"); ++ ++ packet_get_bignum2(dh_client_pub); ++ ++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ ++ break; ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ break; ++ default: ++ packet_disconnect( ++ "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ ++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags)); ++ ++ free(recv_tok.value); ++ ++ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) ++ fatal("Zero length token output when incomplete"); ++ ++ if (dh_client_pub == NULL) ++ fatal("No client public key"); ++ ++ if (maj_status & GSS_S_CONTINUE_NEEDED) { ++ debug("Sending GSSAPI_CONTINUE"); ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, send_tok.length); ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length > 0) { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, send_tok.length); ++ packet_send(); ++ } ++ fatal("accept_ctx died"); ++ } ++ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual Authentication flag wasn't set"); ++ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity flag wasn't set"); ++ ++ if (!dh_pub_is_valid(dh, dh_client_pub)) ++ packet_disconnect("bad client public DH value"); ++ ++ klen = DH_size(dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_client_pub, dh); ++ if ((int)kout < 0) ++ fatal("DH_compute_key: failed"); ++ ++ shared_secret = BN_new(); ++ if (shared_secret == NULL) ++ fatal("kexgss_server: BN_new failed"); ++ ++ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) ++ fatal("kexgss_server: BN_bin2bn failed"); ++ ++ memset(kbuf, 0, klen); ++ free(kbuf); ++ ++ hashlen = sizeof(hash); ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( ++ ssh->kex->client_version_string, ssh->kex->server_version_string, ++ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), ++ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), ++ NULL, 0, /* Change this if we start sending host keys */ ++ dh_client_pub, dh->pub_key, shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ ssh->kex->hash_alg, ++ ssh->kex->client_version_string, ssh->kex->server_version_string, ++ buffer_ptr(ssh->kex->peer), buffer_len(ssh->kex->peer), ++ buffer_ptr(ssh->kex->my), buffer_len(ssh->kex->my), ++ NULL, 0, ++ min, nbits, max, ++ dh->p, dh->g, ++ dh_client_pub, ++ dh->pub_key, ++ shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ BN_clear_free(dh_client_pub); ++ ++ if (ssh->kex->session_id == NULL) { ++ ssh->kex->session_id_len = hashlen; ++ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); ++ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); ++ } ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) ++ fatal("Couldn't get MIC"); ++ ++ packet_start(SSH2_MSG_KEXGSS_COMPLETE); ++ packet_put_bignum2(dh->pub_key); ++ packet_put_string(msg_tok.value,msg_tok.length); ++ ++ if (send_tok.length != 0) { ++ packet_put_char(1); /* true */ ++ packet_put_string(send_tok.value, send_tok.length); ++ } else { ++ packet_put_char(0); /* false */ ++ } ++ packet_send(); ++ ++ gss_release_buffer(&min_status, &send_tok); ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ DH_free(dh); ++ ++ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ kex_send_newkeys(ssh); ++ ++ /* If this was a rekey, then save out any delegated credentials we ++ * just exchanged. */ ++ if (options.gss_store_rekey) ++ ssh_gssapi_rekey_creds(); ++ return 0; ++} ++#endif /* GSSAPI */ +diff -up openssh-7.2p1/kex.h.gsskex openssh-7.2p1/kex.h +--- openssh-7.2p1/kex.h.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/kex.h 2016-02-19 10:01:04.868969323 +0100 +@@ -92,6 +92,11 @@ enum kex_exchange { + KEX_DH_GEX_SHA256, + KEX_ECDH_SHA2, + KEX_C25519_SHA256, ++#ifdef GSSAPI ++ KEX_GSS_GRP1_SHA1, ++ KEX_GSS_GRP14_SHA1, ++ KEX_GSS_GEX_SHA1, ++#endif + KEX_MAX + }; + +@@ -140,6 +145,12 @@ struct kex { + u_int flags; + int hash_alg; + int ec_nid; ++#ifdef GSSAPI ++ int gss_deleg_creds; ++ int gss_trust_dns; ++ char *gss_host; ++ char *gss_client; ++#endif + char *client_version_string; + char *server_version_string; + char *failed_choice; +@@ -189,6 +200,10 @@ int kexecdh_client(struct ssh *); + int kexecdh_server(struct ssh *); + int kexc25519_client(struct ssh *); + int kexc25519_server(struct ssh *); ++#ifdef GSSAPI ++int kexgss_client(struct ssh *); ++int kexgss_server(struct ssh *); ++#endif + + int kex_dh_hash(const char *, const char *, + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, +diff -up openssh-7.2p1/Makefile.in.gsskex openssh-7.2p1/Makefile.in +--- openssh-7.2p1/Makefile.in.gsskex 2016-02-19 10:01:04.864969325 +0100 ++++ openssh-7.2p1/Makefile.in 2016-02-19 10:01:04.868969323 +0100 +@@ -90,6 +90,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ + atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ ++ kexgssc.o \ + msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ + ssh-pkcs11.o smult_curve25519_ref.o \ + poly1305.o chacha.o cipher-chachapoly.o \ +@@ -111,7 +112,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw + auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ + auth2-none.o auth2-passwd.o auth2-pubkey.o \ + monitor_mm.o monitor.o monitor_wrap.o auth-krb5.o \ +- auth2-gss.o gss-serv.o gss-serv-krb5.o \ ++ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \ + loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ + sftp-server.o sftp-common.o \ + sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ +diff -up openssh-7.2p1/monitor.c.gsskex openssh-7.2p1/monitor.c +--- openssh-7.2p1/monitor.c.gsskex 2016-02-19 10:01:04.830969345 +0100 ++++ openssh-7.2p1/monitor.c 2016-02-19 10:01:04.869969322 +0100 +@@ -159,6 +159,8 @@ 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 +@@ -239,11 +241,18 @@ struct mon_table mon_dispatch_proto20[] + {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 + {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 + #ifdef WITH_OPENSSL + {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, + #endif +@@ -358,6 +367,10 @@ monitor_child_preauth(Authctxt *_authctx + /* 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; + +@@ -466,6 +479,10 @@ monitor_child_postauth(struct monitor *p + 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); +@@ -1893,6 +1910,13 @@ monitor_apply_keystate(struct monitor *p + # endif + #endif /* WITH_OPENSSL */ + kex->kex[KEX_C25519_SHA256] = kexc25519_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->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; +@@ -1992,6 +2016,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer + 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; + +@@ -2019,6 +2046,9 @@ mm_answer_gss_accept_ctx(int sock, Buffe + 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); +@@ -2036,6 +2066,7 @@ mm_answer_gss_accept_ctx(int sock, Buffe + 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); + } +@@ -2047,6 +2078,9 @@ mm_answer_gss_checkmic(int sock, Buffer + 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); +@@ -2073,7 +2107,11 @@ mm_answer_gss_userok(int sock, Buffer *m + { + int authenticated; + +- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); ++ 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); +@@ -2086,5 +2124,73 @@ mm_answer_gss_userok(int sock, Buffer *m + /* 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); ++ ++ free(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); ++ ++ free(store.filename); ++ free(store.envvar); ++ free(store.envval); ++ ++ buffer_clear(m); ++ buffer_put_int(m, ok); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); ++ ++ return(0); ++} ++ + #endif /* GSSAPI */ + +diff -up openssh-7.2p1/monitor.h.gsskex openssh-7.2p1/monitor.h +--- openssh-7.2p1/monitor.h.gsskex 2016-02-19 10:01:04.830969345 +0100 ++++ openssh-7.2p1/monitor.h 2016-02-19 10:01:04.869969322 +0100 +@@ -60,6 +60,8 @@ enum monitor_reqtype { + #ifdef WITH_SELINUX + MONITOR_REQ_AUTHROLE = 80, + #endif ++ MONITOR_REQ_GSSSIGN = 82, MONITOR_ANS_GSSSIGN = 83, ++ MONITOR_REQ_GSSUPCREDS = 84, MONITOR_ANS_GSSUPCREDS = 85, + + MONITOR_REQ_PAM_START = 100, + MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103, +diff -up openssh-7.2p1/monitor_wrap.c.gsskex openssh-7.2p1/monitor_wrap.c +--- openssh-7.2p1/monitor_wrap.c.gsskex 2016-02-19 10:01:04.830969345 +0100 ++++ openssh-7.2p1/monitor_wrap.c 2016-02-19 10:01:04.869969322 +0100 +@@ -1087,7 +1087,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss + } + + int +-mm_ssh_gssapi_userok(char *user) ++mm_ssh_gssapi_userok(char *user, struct passwd *pw) + { + Buffer m; + int authenticated = 0; +@@ -1104,5 +1104,50 @@ mm_ssh_gssapi_userok(char *user) + 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 */ + +diff -up openssh-7.2p1/monitor_wrap.h.gsskex openssh-7.2p1/monitor_wrap.h +--- openssh-7.2p1/monitor_wrap.h.gsskex 2016-02-19 10:01:04.830969345 +0100 ++++ openssh-7.2p1/monitor_wrap.h 2016-02-19 10:01:04.869969322 +0100 +@@ -61,8 +61,10 @@ BIGNUM *mm_auth_rsa_generate_challenge(K + 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); ++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 +diff -up openssh-7.2p1/readconf.c.gsskex openssh-7.2p1/readconf.c +--- openssh-7.2p1/readconf.c.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/readconf.c 2016-02-19 10:01:04.870969322 +0100 +@@ -148,6 +148,8 @@ typedef enum { + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, + oAddressFamily, oGssAuthentication, oGssDelegateCreds, ++ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, ++ oGssServerIdentity, + oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, + oSendEnv, oControlPath, oControlMaster, oControlPersist, + oHashKnownHosts, +@@ -193,10 +195,19 @@ static struct { + { "afstokenpassing", oUnsupported }, + #if defined(GSSAPI) + { "gssapiauthentication", oGssAuthentication }, ++ { "gssapikeyexchange", oGssKeyEx }, + { "gssapidelegatecredentials", oGssDelegateCreds }, ++ { "gssapitrustdns", oGssTrustDns }, ++ { "gssapiclientidentity", oGssClientIdentity }, ++ { "gssapiserveridentity", oGssServerIdentity }, ++ { "gssapirenewalforcesrekey", oGssRenewalRekey }, + #else + { "gssapiauthentication", oUnsupported }, ++ { "gssapikeyexchange", oUnsupported }, + { "gssapidelegatecredentials", oUnsupported }, ++ { "gssapitrustdns", oUnsupported }, ++ { "gssapiclientidentity", oUnsupported }, ++ { "gssapirenewalforcesrekey", oUnsupported }, + #endif + { "fallbacktorsh", oDeprecated }, + { "usersh", oDeprecated }, +@@ -926,10 +937,30 @@ parse_time: + intptr = &options->gss_authentication; + goto parse_flag; + ++ case oGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case oGssDelegateCreds: + intptr = &options->gss_deleg_creds; + goto parse_flag; + ++ case oGssTrustDns: ++ intptr = &options->gss_trust_dns; ++ goto parse_flag; ++ ++ case oGssClientIdentity: ++ charptr = &options->gss_client_identity; ++ goto parse_string; ++ ++ case oGssServerIdentity: ++ charptr = &options->gss_server_identity; ++ goto parse_string; ++ ++ case oGssRenewalRekey: ++ intptr = &options->gss_renewal_rekey; ++ goto parse_flag; ++ + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; +@@ -1648,7 +1679,12 @@ initialize_options(Options * options) + options->pubkey_authentication = -1; + options->challenge_response_authentication = -1; + options->gss_authentication = -1; ++ options->gss_keyex = -1; + options->gss_deleg_creds = -1; ++ options->gss_trust_dns = -1; ++ options->gss_renewal_rekey = -1; ++ options->gss_client_identity = NULL; ++ options->gss_server_identity = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->kbd_interactive_devices = NULL; +@@ -1777,8 +1813,14 @@ fill_default_options(Options * options) + options->challenge_response_authentication = 1; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_deleg_creds == -1) + options->gss_deleg_creds = 0; ++ if (options->gss_trust_dns == -1) ++ options->gss_trust_dns = 0; ++ if (options->gss_renewal_rekey == -1) ++ options->gss_renewal_rekey = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +diff -up openssh-7.2p1/readconf.h.gsskex openssh-7.2p1/readconf.h +--- openssh-7.2p1/readconf.h.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/readconf.h 2016-02-19 10:01:04.870969322 +0100 +@@ -45,7 +45,12 @@ typedef struct { + int challenge_response_authentication; + /* Try S/Key or TIS, authentication. */ + int gss_authentication; /* Try GSS authentication */ ++ int gss_keyex; /* Try GSS key exchange */ + int gss_deleg_creds; /* Delegate GSS credentials */ ++ int gss_trust_dns; /* Trust DNS for GSS canonicalization */ ++ int gss_renewal_rekey; /* Credential renewal forces rekey */ ++ char *gss_client_identity; /* Principal to initiate GSSAPI with */ ++ char *gss_server_identity; /* GSSAPI target principal */ + int password_authentication; /* Try password + * authentication. */ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ +diff -up openssh-7.2p1/regress/cert-hostkey.sh.gsskex openssh-7.2p1/regress/cert-hostkey.sh +--- openssh-7.2p1/regress/cert-hostkey.sh.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/regress/cert-hostkey.sh 2016-02-19 10:01:04.870969322 +0100 +@@ -46,7 +46,7 @@ touch $OBJ/host_revoked_plain + touch $OBJ/host_revoked_cert + cp $OBJ/host_ca_key.pub $OBJ/host_revoked_ca + +-PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` ++PLAIN_TYPES=`$SSH -Q key-plain | grep -v null | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` + + # Prepare certificate, plain key and CA KRLs + ${SSHKEYGEN} -kf $OBJ/host_krl_empty || fatal "KRL init failed" +diff -up openssh-7.2p1/regress/cert-userkey.sh.gsskex openssh-7.2p1/regress/cert-userkey.sh +--- openssh-7.2p1/regress/cert-userkey.sh.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/regress/cert-userkey.sh 2016-02-19 10:01:04.870969322 +0100 +@@ -7,7 +7,7 @@ rm -f $OBJ/authorized_keys_$USER $OBJ/us + cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak + +-PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` ++PLAIN_TYPES=`$SSH -Q key-plain | grep -v null | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` + + kname() { + n=`echo "$1" | sed 's/^dsa/ssh-dss/;s/^rsa/ssh-rsa/;s/^ed/ssh-ed/'` +diff -up openssh-7.2p1/regress/kextype.sh.gsskex openssh-7.2p1/regress/kextype.sh +--- openssh-7.2p1/regress/kextype.sh.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/regress/kextype.sh 2016-02-19 10:01:04.870969322 +0100 +@@ -14,6 +14,9 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/ssh + + tries="1 2 3 4" + for k in `${SSH} -Q kex`; do ++ if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o $k = "gss-group14-sha1-" ]; then ++ continue ++ fi + verbose "kex $k" + for i in $tries; do + ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true +diff -up openssh-7.2p1/regress/rekey.sh.gsskex openssh-7.2p1/regress/rekey.sh +--- openssh-7.2p1/regress/rekey.sh.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/regress/rekey.sh 2016-02-19 10:01:04.870969322 +0100 +@@ -38,6 +38,9 @@ increase_datafile_size 300 + + opts="" + for i in `${SSH} -Q kex`; do ++ if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o $i = "gss-group14-sha1-" ]; then ++ continue ++ fi + opts="$opts KexAlgorithms=$i" + done + for i in `${SSH} -Q cipher`; do +@@ -56,6 +59,9 @@ done + if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then + for c in `${SSH} -Q cipher-auth`; do + for kex in `${SSH} -Q kex`; do ++ if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o $kex = "gss-group14-sha1-" ]; then ++ continue ++ fi + verbose "client rekey $c $kex" + ssh_data_rekeying "KexAlgorithms=$kex" -oRekeyLimit=256k -oCiphers=$c + done +diff -up openssh-7.2p1/servconf.c.gsskex openssh-7.2p1/servconf.c +--- openssh-7.2p1/servconf.c.gsskex 2016-02-19 10:01:04.857969329 +0100 ++++ openssh-7.2p1/servconf.c 2016-02-19 10:01:04.870969322 +0100 +@@ -117,8 +117,10 @@ initialize_server_options(ServerOptions + options->kerberos_ticket_cleanup = -1; + options->kerberos_get_afs_token = -1; + options->gss_authentication=-1; ++ options->gss_keyex = -1; + options->gss_cleanup_creds = -1; + options->gss_strict_acceptor = -1; ++ options->gss_store_rekey = -1; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; +@@ -288,10 +290,14 @@ fill_default_server_options(ServerOption + options->kerberos_get_afs_token = 0; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_cleanup_creds == -1) + options->gss_cleanup_creds = 1; + if (options->gss_strict_acceptor == -1) + options->gss_strict_acceptor = 0; ++ if (options->gss_store_rekey == -1) ++ options->gss_store_rekey = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -422,7 +428,7 @@ typedef enum { + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, +- sAcceptEnv, sPermitTunnel, ++ sGssKeyEx, sGssStoreRekey, sAcceptEnv, sPermitTunnel, + sMatch, sPermitOpen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, + sHostCertificate, +@@ -496,11 +502,17 @@ static struct { + { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, + { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, ++ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, + #else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, + #endif ++ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, + { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, + { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, + { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, +@@ -1246,6 +1258,10 @@ process_server_config_line(ServerOptions + intptr = &options->gss_authentication; + goto parse_flag; + ++ case sGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case sGssCleanupCreds: + intptr = &options->gss_cleanup_creds; + goto parse_flag; +@@ -1254,6 +1270,10 @@ process_server_config_line(ServerOptions + intptr = &options->gss_strict_acceptor; + goto parse_flag; + ++ case sGssStoreRekey: ++ intptr = &options->gss_store_rekey; ++ goto parse_flag; ++ + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; +@@ -2274,6 +2294,9 @@ dump_config(ServerOptions *o) + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); ++ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); ++ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); ++ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); + #endif + dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(sKbdInteractiveAuthentication, +diff -up openssh-7.2p1/servconf.h.gsskex openssh-7.2p1/servconf.h +--- openssh-7.2p1/servconf.h.gsskex 2016-02-19 10:01:04.857969329 +0100 ++++ openssh-7.2p1/servconf.h 2016-02-19 10:01:04.871969321 +0100 +@@ -118,8 +118,10 @@ typedef struct { + int kerberos_get_afs_token; /* If true, try to get AFS token if + * authenticated with Kerberos. */ + int gss_authentication; /* If true, permit GSSAPI authentication */ ++ int gss_keyex; /* If true, permit GSSAPI key exchange */ + int gss_cleanup_creds; /* If true, destroy cred cache on logout */ + int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ ++ int gss_store_rekey; + int password_authentication; /* If true, permit password + * authentication. */ + int kbd_interactive_authentication; /* If true, permit */ +diff -up openssh-7.2p1/ssh_config.5.gsskex openssh-7.2p1/ssh_config.5 +--- openssh-7.2p1/ssh_config.5.gsskex 2016-02-19 10:01:04.871969321 +0100 ++++ openssh-7.2p1/ssh_config.5 2016-02-19 10:05:58.630146245 +0100 +@@ -824,10 +824,40 @@ The default is + Specifies whether user authentication based on GSSAPI is allowed. + The default is + .Dq no . ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI may be used. When using ++GSSAPI key exchange the server need not have a host key. ++The default is ++.Dq no . ++.It Cm GSSAPIClientIdentity ++If set, specifies the GSSAPI client identity that ssh should use when ++connecting to the server. The default is unset, which means that the default ++identity will be used. ++.It Cm GSSAPIServerIdentity ++If set, specifies the GSSAPI server identity that ssh should expect when ++connecting to the server. The default is unset, which means that the ++expected GSSAPI server identity will be determined from the target ++hostname. + .It Cm GSSAPIDelegateCredentials + Forward (delegate) credentials to the server. + The default is + .Dq no . ++.It Cm GSSAPIRenewalForcesRekey ++If set to ++.Dq yes ++then renewal of the client's GSSAPI credentials will force the rekeying of the ++ssh connection. With a compatible server, this can delegate the renewed ++credentials to a session on the server. ++The default is ++.Dq no . ++.It Cm GSSAPITrustDns ++Set to ++.Dq yes to indicate that the DNS is trusted to securely canonicalize ++the name of the host being connected to. If ++.Dq no, the hostname entered on the ++command line will be passed untouched to the GSSAPI library. ++The default is ++.Dq no . + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 +diff -up openssh-7.2p1/ssh_config.gsskex openssh-7.2p1/ssh_config +--- openssh-7.2p1/ssh_config.gsskex 2016-02-19 10:01:04.852969332 +0100 ++++ openssh-7.2p1/ssh_config 2016-02-19 10:01:04.871969321 +0100 +@@ -26,6 +26,8 @@ + # HostbasedAuthentication no + # GSSAPIAuthentication no + # GSSAPIDelegateCredentials no ++# GSSAPIKeyExchange no ++# GSSAPITrustDNS no + # BatchMode no + # CheckHostIP yes + # AddressFamily any +diff -up openssh-7.2p1/sshconnect2.c.gsskex openssh-7.2p1/sshconnect2.c +--- openssh-7.2p1/sshconnect2.c.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/sshconnect2.c 2016-02-19 10:01:04.872969321 +0100 +@@ -161,9 +161,34 @@ ssh_kex2(char *host, struct sockaddr *ho + struct kex *kex; + int r; + ++#ifdef GSSAPI ++ char *orig = NULL, *gss = NULL; ++ char *gss_host = NULL; ++#endif ++ + xxx_host = host; + xxx_hostaddr = hostaddr; + ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ /* Add the GSSAPI mechanisms currently supported on this ++ * client to the key exchange algorithm proposal */ ++ orig = options.kex_algorithms; ++ ++ if (options.gss_trust_dns) ++ gss_host = (char *)get_canonical_hostname(1); ++ else ++ gss_host = host; ++ ++ gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity); ++ if (gss) { ++ debug("Offering GSSAPI proposal: %s", gss); ++ xasprintf(&options.kex_algorithms, ++ "%s,%s", gss, orig); ++ } ++ } ++#endif ++ + if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) + fatal("%s: kex_names_cat", __func__); + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); +@@ -195,6 +220,17 @@ ssh_kex2(char *host, struct sockaddr *ho + order_hostkeyalgs(host, hostaddr, port)); + } + ++#ifdef GSSAPI ++ /* If we've got GSSAPI algorithms, then we also support the ++ * 'null' hostkey, as a last resort */ ++ if (options.gss_keyex && gss) { ++ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], ++ "%s,null", orig); ++ free(gss); ++ } ++#endif ++ + if (options.rekey_limit || options.rekey_interval) + packet_set_rekey_limits((u_int32_t)options.rekey_limit, + (time_t)options.rekey_interval); +@@ -212,11 +248,31 @@ ssh_kex2(char *host, struct sockaddr *ho + kex->kex[KEX_ECDH_SHA2] = kexecdh_client; + # endif + #endif ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; ++ } ++#endif + kex->kex[KEX_C25519_SHA256] = kexc25519_client; + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->verify_host_key=&verify_host_key_callback; + ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->gss_deleg_creds = options.gss_deleg_creds; ++ kex->gss_trust_dns = options.gss_trust_dns; ++ kex->gss_client = options.gss_client_identity; ++ if (options.gss_server_identity) { ++ kex->gss_host = options.gss_server_identity; ++ } else { ++ kex->gss_host = gss_host; ++ } ++ } ++#endif ++ + dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); + + /* remove ext-info from the KEX proposals for rekeying */ +@@ -311,6 +367,7 @@ int input_gssapi_token(int type, u_int32 + int input_gssapi_hash(int type, u_int32_t, void *); + int input_gssapi_error(int, u_int32_t, void *); + int input_gssapi_errtok(int, u_int32_t, void *); ++int userauth_gsskeyex(Authctxt *authctxt); + #endif + + void userauth(Authctxt *, char *); +@@ -326,6 +383,11 @@ static char *authmethods_get(void); + + Authmethod authmethods[] = { + #ifdef GSSAPI ++ {"gssapi-keyex", ++ userauth_gsskeyex, ++ NULL, ++ &options.gss_authentication, ++ NULL}, + {"gssapi-with-mic", + userauth_gssapi, + NULL, +@@ -656,19 +718,31 @@ userauth_gssapi(Authctxt *authctxt) + static u_int mech = 0; + OM_uint32 min; + int ok = 0; ++ const char *gss_host; ++ ++ if (options.gss_server_identity) ++ gss_host = options.gss_server_identity; ++ else if (options.gss_trust_dns) ++ gss_host = get_canonical_hostname(1); ++ else ++ gss_host = authctxt->host; + + /* Try one GSSAPI method at a time, rather than sending them all at + * once. */ + + if (gss_supported == NULL) +- gss_indicate_mechs(&min, &gss_supported); ++ if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) { ++ gss_supported = NULL; ++ return 0; ++ } + + /* Check to see if the mechanism is usable before we offer it */ + while (mech < gss_supported->count && !ok) { + /* My DER encoding requires length<128 */ + if (gss_supported->elements[mech].length < 128 && + ssh_gssapi_check_mechanism(&gssctxt, +- &gss_supported->elements[mech], authctxt->host)) { ++ &gss_supported->elements[mech], gss_host, ++ options.gss_client_identity)) { + ok = 1; /* Mechanism works */ + } else { + mech++; +@@ -765,8 +839,8 @@ input_gssapi_response(int type, u_int32_ + { + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; +- int oidlen; +- char *oidv; ++ u_int oidlen; ++ u_char *oidv; + + if (authctxt == NULL) + fatal("input_gssapi_response: no authentication context"); +@@ -879,6 +953,48 @@ input_gssapi_error(int type, u_int32_t p + free(lang); + return 0; + } ++ ++int ++userauth_gsskeyex(Authctxt *authctxt) ++{ ++ Buffer b; ++ gss_buffer_desc gssbuf; ++ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ms; ++ ++ static int attempt = 0; ++ if (attempt++ >= 1) ++ return (0); ++ ++ if (gss_kex_context == NULL) { ++ debug("No valid Key exchange context"); ++ return (0); ++ } ++ ++ ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = buffer_ptr(&b); ++ gssbuf.length = buffer_len(&b); ++ ++ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { ++ buffer_free(&b); ++ return (0); ++ } ++ ++ packet_start(SSH2_MSG_USERAUTH_REQUEST); ++ packet_put_cstring(authctxt->server_user); ++ packet_put_cstring(authctxt->service); ++ packet_put_cstring(authctxt->method->name); ++ packet_put_string(mic.value, mic.length); ++ packet_send(); ++ ++ buffer_free(&b); ++ gss_release_buffer(&ms, &mic); ++ ++ return (1); ++} ++ + #endif /* GSSAPI */ + + int +diff -up openssh-7.2p1/sshd.c.gsskex openssh-7.2p1/sshd.c +--- openssh-7.2p1/sshd.c.gsskex 2016-02-19 10:01:04.860969328 +0100 ++++ openssh-7.2p1/sshd.c 2016-02-19 10:01:04.872969321 +0100 +@@ -974,8 +974,9 @@ notify_hostkeys(struct ssh *ssh) + } + debug3("%s: sent %d hostkeys", __func__, nkeys); + if (nkeys == 0) +- fatal("%s: no hostkeys", __func__); +- packet_send(); ++ debug3("%s: no hostkeys", __func__); ++ else ++ packet_send(); + sshbuf_free(buf); + } + +@@ -1845,10 +1846,13 @@ main(int ac, char **av) + logit("Disabling protocol version 1. Could not load host key"); + options.protocol &= ~SSH_PROTO_1; + } ++#ifndef GSSAPI ++ /* The GSSAPI key exchange can run without a host key */ + if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { + logit("Disabling protocol version 2. Could not load host key"); + options.protocol &= ~SSH_PROTO_2; + } ++#endif + if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { + logit("sshd: no hostkeys available -- exiting."); + exit(1); +@@ -2586,6 +2590,48 @@ do_ssh2_kex(void) + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( + list_hostkey_types()); + ++#ifdef GSSAPI ++ { ++ char *orig; ++ char *gss = NULL; ++ char *newstr = NULL; ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ /* ++ * If we don't have a host key, then there's no point advertising ++ * the other key exchange algorithms ++ */ ++ ++ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) ++ orig = NULL; ++ ++ if (options.gss_keyex) ++ gss = ssh_gssapi_server_mechanisms(); ++ else ++ gss = NULL; ++ ++ if (gss && orig) ++ xasprintf(&newstr, "%s,%s", gss, orig); ++ else if (gss) ++ newstr = gss; ++ else if (orig) ++ newstr = orig; ++ ++ /* ++ * If we've got GSSAPI mechanisms, then we've got the 'null' host ++ * key alg, but we can't tell people about it unless its the only ++ * host key algorithm we support ++ */ ++ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) ++ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; ++ ++ if (newstr) ++ myproposal[PROPOSAL_KEX_ALGS] = newstr; ++ else ++ fatal("No supported key exchange algorithms"); ++ } ++#endif ++ + /* start key exchange */ + if ((r = kex_setup(active_state, myproposal)) != 0) + fatal("kex_setup: %s", ssh_err(r)); +@@ -2600,6 +2646,13 @@ do_ssh2_kex(void) + # endif + #endif + kex->kex[KEX_C25519_SHA256] = kexc25519_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->client_version_string=client_version_string; + kex->server_version_string=server_version_string; +diff -up openssh-7.2p1/sshd_config.5.gsskex openssh-7.2p1/sshd_config.5 +--- openssh-7.2p1/sshd_config.5.gsskex 2016-02-19 10:01:04.858969329 +0100 ++++ openssh-7.2p1/sshd_config.5 2016-02-19 10:06:26.651172355 +0100 +@@ -623,6 +623,11 @@ The default is + Specifies whether user authentication based on GSSAPI is allowed. + The default is + .Dq no . ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange ++doesn't rely on ssh keys to verify host identity. ++The default is ++.Dq no . + .It Cm GSSAPICleanupCredentials + Specifies whether to automatically destroy the user's credentials cache + on logout. +@@ -643,6 +648,11 @@ machine's default store. + This facility is provided to assist with operation on multi homed machines. + The default is + .Dq yes . ++.It Cm GSSAPIStoreCredentialsOnRekey ++Controls whether the user's GSSAPI credentials should be updated following a ++successful connection rekeying. This option can be used to accepted renewed ++or updated credentials from a compatible client. The default is ++.Dq no . + .It Cm HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased authentication + as a comma-separated pattern list. +diff -up openssh-7.2p1/sshd_config.gsskex openssh-7.2p1/sshd_config +--- openssh-7.2p1/sshd_config.gsskex 2016-02-19 10:01:04.860969328 +0100 ++++ openssh-7.2p1/sshd_config 2016-02-19 10:01:04.873969320 +0100 +@@ -91,6 +91,8 @@ ChallengeResponseAuthentication no + # GSSAPI options + GSSAPIAuthentication yes + GSSAPICleanupCredentials no ++#GSSAPIStrictAcceptorCheck yes ++#GSSAPIKeyExchange no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will +diff -up openssh-7.2p1/ssh-gss.h.gsskex openssh-7.2p1/ssh-gss.h +--- openssh-7.2p1/ssh-gss.h.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/ssh-gss.h 2016-02-19 10:01:04.873969320 +0100 +@@ -1,6 +1,6 @@ + /* $OpenBSD: ssh-gss.h,v 1.11 2014/02/26 20:28:44 djm Exp $ */ + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * 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 +@@ -61,10 +61,22 @@ + + #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; + +@@ -72,8 +84,11 @@ 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 { +@@ -84,6 +99,7 @@ typedef struct ssh_gssapi_mech_struct { + 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 { +@@ -94,10 +110,11 @@ typedef struct { + gss_OID oid; /* client */ + gss_cred_id_t creds; /* server */ + gss_name_t client; /* server */ +- gss_cred_id_t client_creds; /* 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); +@@ -119,16 +136,32 @@ 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 *); ++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); ++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); ++ ++void ssh_gssapi_rekey_creds(void); + #endif /* GSSAPI */ + + #endif /* _SSH_GSS_H */ +diff -up openssh-7.2p1/sshkey.c.gsskex openssh-7.2p1/sshkey.c +--- openssh-7.2p1/sshkey.c.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/sshkey.c 2016-02-19 10:01:04.874969320 +0100 +@@ -115,6 +115,7 @@ static const struct keytype keytypes[] = + # endif /* OPENSSL_HAS_NISTP521 */ + # endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ ++ { "null", "null", KEY_NULL, 0, 0, 1 }, + { NULL, NULL, -1, -1, 0, 0 } + }; + +diff -up openssh-7.2p1/sshkey.h.gsskex openssh-7.2p1/sshkey.h +--- openssh-7.2p1/sshkey.h.gsskex 2016-02-12 11:47:25.000000000 +0100 ++++ openssh-7.2p1/sshkey.h 2016-02-19 10:01:04.874969320 +0100 +@@ -62,6 +62,7 @@ enum sshkey_types { + KEY_DSA_CERT, + KEY_ECDSA_CERT, + KEY_ED25519_CERT, ++ KEY_NULL, + KEY_UNSPEC + }; + diff --git a/openssh.spec b/openssh.spec index 50c85a6..54ee536 100644 --- a/openssh.spec +++ b/openssh.spec @@ -65,10 +65,10 @@ %endif # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 -%global openssh_ver 7.1p2 -%global openssh_rel 4 +%global openssh_ver 7.2p1 +%global openssh_rel 1 %global pam_ssh_agent_ver 0.10.2 -%global pam_ssh_agent_rel 1 +%global pam_ssh_agent_rel 2 Summary: An open source implementation of SSH protocol versions 1 and 2 Name: openssh @@ -105,7 +105,7 @@ Patch103: openssh-5.8p1-packet.patch #https://bugzilla.mindrot.org/show_bug.cgi?id=1402 # https://bugzilla.redhat.com/show_bug.cgi?id=1171248 # record pfs= field in CRYPTO_SESSION audit event -Patch200: openssh-6.7p1-audit.patch +Patch200: openssh-7.2p1-audit.patch # Audit race condition in forked child (#1310684) Patch201: openssh-7.1p2-audit-race-condition.patch @@ -143,7 +143,7 @@ Patch607: openssh-5.8p2-sigpipe.patch Patch609: openssh-5.5p1-x11.patch #? -Patch700: openssh-6.7p1-fips.patch +Patch700: openssh-7.2p1-fips.patch #? Patch702: openssh-5.1p1-askpass-progress.patch #? @@ -168,7 +168,7 @@ Patch714: openssh-6.7p1-kdf-cavs.patch #http://www.sxw.org.uk/computing/patches/openssh.html #changed cache storage type - #848228 -Patch800: openssh-6.6p1-gsskex.patch +Patch800: openssh-7.2p1-gsskex.patch #http://www.mail-archive.com/kerberos@mit.edu/msg17591.html Patch801: openssh-6.6p1-force_krb.patch # add new option GSSAPIEnablek5users and disable using ~/.k5users by default (#1169843) @@ -225,17 +225,9 @@ Patch931: openssh-6.9p1-scp-progressmeter.patch Patch932: openssh-7.0p1-gssKexAlgorithms.patch # Possibility to validate legacy systems by more fingerprints (#1249626)(#2439) Patch933: openssh-7.0p1-show-more-fingerprints.patch -# Brokend HostKeyAlgorthms on server using + sign -# from http://lists.mindrot.org/pipermail/openssh-unix-dev/2015-August/034324.html -Patch934: openssh-7.1p1-hostkeyalgorithms.patch -# Updated version of ssh-copy-id -# http://git.hands.com/ssh-copy-id -Patch935: openssh-7.1p1-ssh-copy-id.patch # Preserve IUTF8 tty mode flag over ssh connections (#1270248) # https://bugzilla.mindrot.org/show_bug.cgi?id=2477 Patch936: openssh-7.1p1-iutf8.patch -# CVE-2016-1908: possible fallback from untrusted to trusted X11 forwarding -Patch937: openssh-7.1p2-fallback-x11-untrusted.patch License: BSD @@ -469,10 +461,7 @@ popd %patch931 -p1 -b .progressmeter %patch932 -p1 -b .gsskexalg %patch933 -p1 -b .fingerprint -%patch934 -p1 -b .hostkey -%patch935 -p1 -b .ssh-copy-id %patch936 -p1 -b .iutf8 -%patch937 -p1 -b .x11-fallback %patch200 -p1 -b .audit %patch201 -p1 -b .audit-race @@ -734,8 +723,6 @@ getent passwd sshd >/dev/null || \ %attr(0755,root,root) %{_bindir}/scp %attr(0644,root,root) %{_mandir}/man1/scp.1* %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config -%attr(0755,root,root) %{_bindir}/slogin -%attr(0644,root,root) %{_mandir}/man1/slogin.1* %attr(0644,root,root) %{_mandir}/man5/ssh_config.5* %if ! %{rescue} %attr(0755,root,root) %{_bindir}/ssh-agent @@ -813,6 +800,9 @@ getent passwd sshd >/dev/null || \ %endif %changelog +* Mon Feb 29 2016 Jakub Jelen 7.2p1-1 + 0.10.2-2 +- New upstream release (#1312870) + * Wed Feb 24 2016 Jakub Jelen 7.1p2-4.1 + 0.10.2-1 - Fix race condition in auditing events when using multiplexing (#1308295) - Fix X11 forwarding CVE according to upstream diff --git a/pam_ssh_agent_auth-0.9.3-agent_structure.patch b/pam_ssh_agent_auth-0.9.3-agent_structure.patch index b7d8d40..1cab72c 100644 --- a/pam_ssh_agent_auth-0.9.3-agent_structure.patch +++ b/pam_ssh_agent_auth-0.9.3-agent_structure.patch @@ -104,3 +104,16 @@ diff -up openssh-7.1p2/pam_ssh_agent_auth-0.10.2/userauth_pubkey_from_id.c.psaa- goto user_auth_clean_exit; /* test for correct signature */ +diff --git a/pam_ssh_agent_auth-0.10.2/userauth_pubkey_from_id.c b/pam_ssh_agent_auth-0.10.2/userauth_pubkey_from_id.c +--- a/pam_ssh_agent_auth-0.10.2/userauth_pubkey_from_id.c ++++ b/pam_ssh_agent_auth-0.10.2/userauth_pubkey_from_id.c +@@ -85,7 +85,7 @@ userauth_pubkey_from_id(const char *ruser, Identity * id, Buffer * session_id2) + buffer_put_cstring(&b, pkalg); + buffer_put_string(&b, pkblob, blen); + +- if(ssh_agent_sign(id->ac->fd, id->key, &sig, &slen, buffer_ptr(&b), buffer_len(&b), 0) != 0) ++ if(ssh_agent_sign(id->ac->fd, id->key, &sig, &slen, buffer_ptr(&b), buffer_len(&b), NULL, 0) != 0) + goto user_auth_clean_exit; + + /* test for correct signature */ + diff --git a/sources b/sources index 596b1c0..24cfbd5 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ a212baca7ce11d596bd8dcb222859ace pam_ssh_agent_auth-0.10.2.tar.bz2 -4d8547670e2a220d5ef805ad9e47acf2 openssh-7.1p2.tar.gz +b984775f0cfff1f7ff18b8797fce8a28 openssh-7.2p1.tar.gz