FreeBSD Bugzilla – Attachment 165728 Details for
Bug 206346
security/openssh-portable: fix GSSAPI key exchange for 7.x
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
GSSAPI patch without HPN
extra-patch-gssapi (text/plain), 92.09 KB, created by
Garrett Wollman
on 2016-01-17 22:37:07 UTC
(
hide
)
Description:
GSSAPI patch without HPN
Filename:
MIME Type:
Creator:
Garrett Wollman
Created:
2016-01-17 22:37:07 UTC
Size:
92.09 KB
patch
obsolete
>From wollman@isfahel.bostonradio.org Sun Jan 17 17:18:22 2016 >X-VM-v5-Data: ([t nil nil nil nil nil nil nil nil] > ["92201" "Sunday" "17" "January" "2016" "17:18:15" "-0500" "Garrett Wollman" "wollman@isfahel.bostonradio.org" nil "3030" "files/extra-patch-gssapi" "^From:" nil nil "1" nil nil nil nil nil nil nil nil nil] > nil) >Return-Path: <wollman@isfahel.bostonradio.org> >Received: from isfahel.bostonradio.org (isfahel.bostonradio.org [128.31.0.59]) > by hergotha.csail.mit.edu (8.14.9/8.14.9) with ESMTP id u0HMIJ98079018 > for <wollman@bimajority.org>; Sun, 17 Jan 2016 17:18:19 -0500 (EST) > (envelope-from wollman@isfahel.bostonradio.org) >Received: from isfahel.bostonradio.org (localhost [127.0.0.1]) > by isfahel.bostonradio.org (8.14.9/8.14.9) with ESMTP id u0HMIF0A031797 > (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) > for <wollman@isfahel.bostonradio.org>; Sun, 17 Jan 2016 17:18:15 -0500 (EST) > (envelope-from wollman@isfahel.bostonradio.org) >Received: (from root@localhost) > by isfahel.bostonradio.org (8.14.9/8.14.9/Submit) id u0HMIFJH031796 > for wollman; Sun, 17 Jan 2016 17:18:15 -0500 (EST) > (envelope-from wollman) >Message-Id: <201601172218.u0HMIFJH031796@isfahel.bostonradio.org> (sfid-20160117_17182_E5589A33) >X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.4.3 (hergotha.csail.mit.edu [207.180.169.34]); Sun, 17 Jan 2016 17:18:19 -0500 (EST) >X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.4.3 (isfahel.bostonradio.org [127.0.0.1]); Sun, 17 Jan 2016 17:18:15 -0500 (EST) >X-Spam-Status: No, score=-0.3 required=5.0 tests=RP_MATCHES_RCVD,SARE_WEOFFER, > SPF_HELO_PASS,SPF_PASS autolearn=disabled version=3.4.1 >X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on > hergotha.csail.mit.edu >X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-9B0B3C9E >X-CRM114-CacheID: sfid-20160117_17182_E5589A33 >X-CRM114-Status: GOOD ( 16.53 ) >From: Garrett Wollman <wollman@isfahel.bostonradio.org> >To: wollman@isfahel.bostonradio.org >Subject: files/extra-patch-gssapi >Date: Sun, 17 Jan 2016 17:18:15 -0500 (EST) > >>From 6a0a4b2f79889c9b0d5e2478a6ee5f51be38dcc9 Mon Sep 17 00:00:00 2001 >From: Simon Wilkinson <simon@sxw.org.uk> >Date: Sun, 9 Feb 2014 16:09:48 +0000 >Subject: GSSAPI key exchange support > >This patch has been rejected upstream: "None of the OpenSSH developers are >in favour of adding this, and this situation has not changed for several >years. This is not a slight on Simon's patch, which is of fine quality, but >just that a) we don't trust GSSAPI implementations that much and b) we don't >like adding new KEX since they are pre-auth attack surface. This one is >particularly scary, since it requires hooks out to typically root-owned >system resources." > >However, quite a lot of people rely on this in Debian, and it's better to >have it merged into the main openssh package rather than having separate >-krb5 packages (as we used to have). It seems to have a generally good >security history. > >Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 >Last-Updated: 2016-01-04 > >Patch-Name: gssapi.patch >--- > ChangeLog.gssapi | 113 +++++++++++++++++++ > Makefile.in | 5 +- > auth-krb5.c | 17 ++- > auth.c | 3 +- > auth2-gss.c | 48 +++++++- > auth2.c | 2 + > clientloop.c | 13 +++ > config.h.in | 6 + > configure.ac | 24 ++++ > gss-genr.c | 275 ++++++++++++++++++++++++++++++++++++++++++++- > gss-serv-krb5.c | 85 ++++++++++++-- > gss-serv.c | 185 +++++++++++++++++++++++++++--- > kex.c | 16 +++ > kex.h | 14 +++ > kexgssc.c | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > kexgsss.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++++ > monitor.c | 108 +++++++++++++++++- > monitor.h | 3 + > monitor_wrap.c | 47 +++++++- > monitor_wrap.h | 4 +- > readconf.c | 42 +++++++ > readconf.h | 5 + > servconf.c | 28 ++++- > servconf.h | 2 + > ssh-gss.h | 41 ++++++- > ssh_config | 2 + > ssh_config.5 | 36 +++++- > sshconnect2.c | 120 +++++++++++++++++++- > sshd.c | 110 ++++++++++++++++++ > sshd_config | 2 + > sshd_config.5 | 11 ++ > sshkey.c | 3 +- > sshkey.h | 1 + > 33 files changed, 1955 insertions(+), 47 deletions(-) > create mode 100644 ChangeLog.gssapi > create mode 100644 kexgssc.c > create mode 100644 kexgsss.c > >diff --git a/ChangeLog.gssapi b/ChangeLog.gssapi >new file mode 100644 >index 0000000..f117a33 >--- /dev/null >+++ b/ChangeLog.gssapi >@@ -0,0 +1,113 @@ >+20110101 >+ - Finally update for OpenSSH 5.6p1 >+ - Add GSSAPIServerIdentity option from Jim Basney >+ >+20100308 >+ - [ Makefile.in, key.c, key.h ] >+ Updates for OpenSSH 5.4p1 >+ - [ servconf.c ] >+ Include GSSAPI options in the sshd -T configuration dump, and flag >+ some older configuration options as being unsupported. Thanks to Colin >+ Watson. >+ - >+ >+20100124 >+ - [ sshconnect2.c ] >+ Adapt to deal with additional element in Authmethod structure. Thanks to >+ Colin Watson >+ >+20090615 >+ - [ gss-genr.c gss-serv.c kexgssc.c kexgsss.c monitor.c sshconnect2.c >+ sshd.c ] >+ Fix issues identified by Greg Hudson following a code review >+ Check return value of gss_indicate_mechs >+ Protect GSSAPI calls in monitor, so they can only be used if enabled >+ Check return values of bignum functions in key exchange >+ Use BN_clear_free to clear other side's DH value >+ Make ssh_gssapi_id_kex more robust >+ Only configure kex table pointers if GSSAPI is enabled >+ Don't leak mechanism list, or gss mechanism list >+ Cast data.length before printing >+ If serverkey isn't provided, use an empty string, rather than NULL >+ >+20090201 >+ - [ gss-genr.c gss-serv.c kex.h kexgssc.c readconf.c readconf.h ssh-gss.h >+ ssh_config.5 sshconnet2.c ] >+ Add support for the GSSAPIClientIdentity option, which allows the user >+ to specify which GSSAPI identity to use to contact a given server >+ >+20080404 >+ - [ gss-serv.c ] >+ Add code to actually implement GSSAPIStrictAcceptCheck, which had somehow >+ been omitted from a previous version of this patch. Reported by Borislav >+ Stoichkov >+ >+20070317 >+ - [ gss-serv-krb5.c ] >+ Remove C99ism, where new_ccname was being declared in the middle of a >+ function >+ >+20061220 >+ - [ servconf.c ] >+ Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and >+ documented, behaviour. Reported by Dan Watson. >+ >+20060910 >+ - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c >+ ssh-gss.h ] >+ add support for gss-group14-sha1 key exchange mechanisms >+ - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ] >+ Add GSSAPIStrictAcceptorCheck option to allow the disabling of >+ acceptor principal checking on multi-homed machines. >+ <Bugzilla #928> >+ - [ sshd_config ssh_config ] >+ Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample >+ configuration files >+ - [ kexgss.c kegsss.c sshconnect2.c sshd.c ] >+ Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf() >+ Limit length of error messages displayed by client >+ >+20060909 >+ - [ gss-genr.c gss-serv.c ] >+ move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server >+ only, where they belong >+ <Bugzilla #1225> >+ >+20060829 >+ - [ gss-serv-krb5.c ] >+ Fix CCAPI credentials cache name when creating KRB5CCNAME environment >+ variable >+ >+20060828 >+ - [ gss-genr.c ] >+ Avoid Heimdal context freeing problem >+ <Fixed upstream 20060829> >+ >+20060818 >+ - [ gss-genr.c ssh-gss.h sshconnect2.c ] >+ Make sure that SPENGO is disabled >+ <Bugzilla #1218 - Fixed upstream 20060818> >+ >+20060421 >+ - [ gssgenr.c, sshconnect2.c ] >+ a few type changes (signed versus unsigned, int versus size_t) to >+ fix compiler errors/warnings >+ (from jbasney AT ncsa.uiuc.edu) >+ - [ kexgssc.c, sshconnect2.c ] >+ fix uninitialized variable warnings >+ (from jbasney AT ncsa.uiuc.edu) >+ - [ gssgenr.c ] >+ pass oid to gss_display_status (helpful when using GSSAPI mechglue) >+ (from jbasney AT ncsa.uiuc.edu) >+ <Bugzilla #1220 > >+ - [ gss-serv-krb5.c ] >+ #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H >+ (from jbasney AT ncsa.uiuc.edu) >+ <Fixed upstream 20060304> >+ - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c >+ add client-side GssapiKeyExchange option >+ (from jbasney AT ncsa.uiuc.edu) >+ - [ sshconnect2.c ] >+ add support for GssapiTrustDns option for gssapi-with-mic >+ (from jbasney AT ncsa.uiuc.edu) >+ <gssapi-with-mic support is Bugzilla #1008> >diff --git a/Makefile.in b/Makefile.in >index 40cc7aa..3d2a328 100644 >--- a/Makefile.in >+++ b/Makefile.in >@@ -91,7 +91,8 @@ 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 \ >+ kexgssc.o > > SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ > sshconnect.o sshconnect1.o sshconnect2.o mux.o \ >@@ -105,7 +106,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ > 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 --git a/auth-krb5.c b/auth-krb5.c >index 0089b18..ec47869 100644 >--- a/auth-krb5.c >+++ b/auth-krb5.c >@@ -183,8 +183,13 @@ auth_krb5_password(Authctxt *authctxt, const char *password) > > len = strlen(authctxt->krb5_ticket_file) + 6; > authctxt->krb5_ccname = xmalloc(len); >+#ifdef USE_CCAPI >+ snprintf(authctxt->krb5_ccname, len, "API:%s", >+ authctxt->krb5_ticket_file); >+#else > snprintf(authctxt->krb5_ccname, len, "FILE:%s", > authctxt->krb5_ticket_file); >+#endif > > #ifdef USE_PAM > if (options.use_pam) >@@ -241,15 +246,22 @@ krb5_cleanup_proc(Authctxt *authctxt) > #ifndef HEIMDAL > krb5_error_code > ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { >- int tmpfd, ret, oerrno; >+ int ret, oerrno; > char ccname[40]; > mode_t old_umask; >+#ifdef USE_CCAPI >+ char cctemplate[] = "API:krb5cc_%d"; >+#else >+ char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX"; >+ int tmpfd; >+#endif > > ret = snprintf(ccname, sizeof(ccname), >- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); >+ cctemplate, geteuid()); > if (ret < 0 || (size_t)ret >= sizeof(ccname)) > return ENOMEM; > >+#ifndef USE_CCAPI > old_umask = umask(0177); > tmpfd = mkstemp(ccname + strlen("FILE:")); > oerrno = errno; >@@ -266,6 +278,7 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { > return oerrno; > } > close(tmpfd); >+#endif > > return (krb5_cc_resolve(ctx, ccname, ccache)); > } >diff --git a/auth.c b/auth.c >index 214c2c7..bd6a026 100644 >--- a/auth.c >+++ b/auth.c >@@ -354,7 +354,8 @@ auth_root_allowed(const char *method) > case PERMIT_NO_PASSWD: > if (strcmp(method, "publickey") == 0 || > strcmp(method, "hostbased") == 0 || >- strcmp(method, "gssapi-with-mic") == 0) >+ strcmp(method, "gssapi-with-mic") == 0 || >+ strcmp(method, "gssapi-keyex") == 0) > return 1; > break; > case PERMIT_FORCED_ONLY: >diff --git a/auth2-gss.c b/auth2-gss.c >index 1ca8357..3b5036d 100644 >--- a/auth2-gss.c >+++ b/auth2-gss.c >@@ -1,7 +1,7 @@ > /* $OpenBSD: auth2-gss.c,v 1.22 2015/01/19 20:07:45 markus Exp $ */ > > /* >- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. >+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -53,6 +53,40 @@ static int input_gssapi_mic(int type, u_int32_t plen, void *ctxt); > 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 +272,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) > > 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); >@@ -274,7 +309,8 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt) > 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"); > >@@ -290,6 +326,12 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt) > return 0; > } > >+Authmethod method_gsskeyex = { >+ "gssapi-keyex", >+ userauth_gsskeyex, >+ &options.gss_authentication >+}; >+ > Authmethod method_gssapi = { > "gssapi-with-mic", > userauth_gssapi, >diff --git a/auth2.c b/auth2.c >index 7177962..3f49bdc 100644 >--- a/auth2.c >+++ b/auth2.c >@@ -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 --git a/clientloop.c b/clientloop.c >index 87ceb3d..fba1b54 100644 >--- a/clientloop.c >+++ b/clientloop.c >@@ -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_char_arg, int ssh2_chan_id) > /* 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(NULL)) { >+ debug("credentials updated - forcing rekey"); >+ need_rekeying = 1; >+ } >+#endif >+ > if (need_rekeying || packet_need_rekeying()) { > debug("need rekeying"); > active_state->kex->done = 0; >diff --git a/config.h.in b/config.h.in >index 7500df5..97accd8 100644 >--- a/config.h.in >+++ b/config.h.in >@@ -1623,6 +1623,9 @@ > /* Use btmp to log bad logins */ > #undef USE_BTMP > >+/* platform uses an in-memory credentials cache */ >+#undef USE_CCAPI >+ > /* Use libedit for sftp */ > #undef USE_LIBEDIT > >@@ -1638,6 +1641,9 @@ > /* Use PIPES instead of a socketpair() */ > #undef USE_PIPES > >+/* platform has the Security Authorization Session API */ >+#undef USE_SECURITY_SESSION_API >+ > /* Define if you have Solaris process contracts */ > #undef USE_SOLARIS_PROCESS_CONTRACTS > >diff --git a/configure.ac b/configure.ac >index 9b05c30..7a25603 100644 >--- a/configure.ac >+++ b/configure.ac >@@ -625,6 +625,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) > [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 <Security/AuthSession.h>], >+ [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 <Kerberos/Kerberos.h>], >+ [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 --git a/gss-genr.c b/gss-genr.c >index d617d60..b4eca3f 100644 >--- a/gss-genr.c >+++ b/gss-genr.c >@@ -1,7 +1,7 @@ > /* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt Exp $ */ > > /* >- * Copyright (c) 2001-2007 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 >@@ -41,12 +41,167 @@ > #include "buffer.h" > #include "log.h" > #include "ssh2.h" >+#include "cipher.h" >+#include "key.h" >+#include "kex.h" >+#include <openssl/evp.h> > > #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(void) { >+ 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 deleg_creds, gss_buffer_desc *recv_tok, > } > > 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, const char *host) > } > > 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_t buffer, gss_buffer_t hash) > 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 char *user, const char *service, > } > > 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, gss_OID oid, const char *host) > 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_OID oid, const char *host) > 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 --git a/gss-serv-krb5.c b/gss-serv-krb5.c >index 795992d..fd8b371 100644 >--- a/gss-serv-krb5.c >+++ b/gss-serv-krb5.c >@@ -1,7 +1,7 @@ > /* $OpenBSD: gss-serv-krb5.c,v 1.8 2013/07/20 01:55:13 djm Exp $ */ > > /* >- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. >+ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -121,8 +121,8 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) > krb5_error_code problem; > krb5_principal princ; > OM_uint32 maj_status, min_status; >- int len; > const char *errmsg; >+ const char *new_ccname; > > if (client->creds == NULL) { > debug("No credentials stored"); >@@ -181,11 +181,16 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) > return; > } > >- client->store.filename = xstrdup(krb5_cc_get_name(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 >+ xasprintf(&client->store.envval, "FILE:%s", new_ccname); >+ client->store.filename = xstrdup(new_ccname); >+#endif > > #ifdef USE_PAM > if (options.use_pam) >@@ -197,6 +202,71 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) > 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 +274,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 --git a/gss-serv.c b/gss-serv.c >index 53993d6..2f6baf7 100644 >--- a/gss-serv.c >+++ b/gss-serv.c >@@ -1,7 +1,7 @@ > /* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 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 >@@ -45,17 +45,22 @@ > #include "session.h" > #include "misc.h" > #include "servconf.h" >+#include "uidswap.h" > > #include "ssh-gss.h" >+#include "monitor_wrap.h" >+ >+extern ServerOptions options; > > 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, 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 +147,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) > } > > /* Unprivileged */ >+char * >+ssh_gssapi_server_mechanisms(void) { >+ 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 +179,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset) > 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 +307,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); >+ } >+ >+ 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; >+ } > >- gss_buffer_desc ename; >+ 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 +363,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) > 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 +387,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) > 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; >@@ -357,7 +436,7 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep) > > /* Privileged */ > int >-ssh_gssapi_userok(char *user) >+ssh_gssapi_userok(char *user, struct passwd *pw) > { > OM_uint32 lmin; > >@@ -367,9 +446,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 +464,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(void) { >+ int ok; >+ int ret; >+#ifdef USE_PAM >+ pam_handle_t *pamh = NULL; >+ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; >+ char *envstr; >+#endif >+ >+ if (gssapi_client.store.filename == NULL && >+ gssapi_client.store.envval == NULL && >+ gssapi_client.store.envvar == NULL) >+ return; >+ >+ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); >+ >+ if (!ok) >+ return; >+ >+ debug("Rekeyed credentials stored successfully"); >+ >+ /* Actually managing to play with the ssh pam stack from here will >+ * be next to impossible. In any case, we may want different options >+ * for rekeying. So, use our own :) >+ */ >+#ifdef USE_PAM >+ if (!use_privsep) { >+ debug("Not even going to try and do PAM with privsep disabled"); >+ return; >+ } >+ >+ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, >+ &pamconv, &pamh); >+ if (ret) >+ return; >+ >+ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, >+ gssapi_client.store.envval); >+ >+ ret = pam_putenv(pamh, envstr); >+ if (!ret) >+ pam_setcred(pamh, PAM_REINITIALIZE_CRED); >+ pam_end(pamh, PAM_SUCCESS); >+#endif >+} >+ >+int >+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { >+ int ok = 0; >+ >+ /* Check we've got credentials to store */ >+ if (!gssapi_client.updated) >+ return 0; >+ >+ gssapi_client.updated = 0; >+ >+ temporarily_use_uid(gssapi_client.store.owner); >+ if (gssapi_client.mech && gssapi_client.mech->updatecreds) >+ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); >+ else >+ debug("No update function for this mechanism"); >+ >+ restore_uid(); >+ >+ return ok; > } > > #endif >diff --git a/kex.c b/kex.c >index b777b7d..390bb69 100644 >--- a/kex.c >+++ b/kex.c >@@ -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 >@@ -97,6 +101,14 @@ static const struct kexalg kexalgs[] = { > #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ > { NULL, -1, -1, -1}, > }; >+static const struct kexalg kexalg_prefixes[] = { >+#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 }, >+}; > > char * > kex_alg_list(char sep) >@@ -129,6 +141,10 @@ kex_alg_by_name(const char *name) > if (strcmp(k->name, name) == 0) > return k; > } >+ for (k = kexalg_prefixes; k->name != NULL; k++) { >+ if (strncmp(k->name, name, strlen(k->name)) == 0) >+ return k; >+ } > return NULL; > } > >diff --git a/kex.h b/kex.h >index d71b532..ee46815 100644 >--- a/kex.h >+++ b/kex.h >@@ -93,6 +93,9 @@ enum kex_exchange { > KEX_DH_GEX_SHA256, > KEX_ECDH_SHA2, > KEX_C25519_SHA256, >+ KEX_GSS_GRP1_SHA1, >+ KEX_GSS_GRP14_SHA1, >+ KEX_GSS_GEX_SHA1, > KEX_MAX > }; > >@@ -139,6 +142,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; >@@ -187,6 +196,11 @@ 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, > const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); >diff --git a/kexgssc.c b/kexgssc.c >new file mode 100644 >index 0000000..a49bac2 >--- /dev/null >+++ b/kexgssc.c >@@ -0,0 +1,336 @@ >+/* >+ * 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 <openssl/crypto.h> >+#include <openssl/bn.h> >+ >+#include <string.h> >+ >+#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; >+ 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); >+ (void) packet_get_string_ptr(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 (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); >+ 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 --git a/kexgsss.c b/kexgsss.c >new file mode 100644 >index 0000000..0847469 >--- /dev/null >+++ b/kexgsss.c >@@ -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 <string.h> >+ >+#include <openssl/crypto.h> >+#include <openssl/bn.h> >+ >+#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" >+#include "servconf.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()) { >+ 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 (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 --git a/monitor.c b/monitor.c >index a914209..2658aaa 100644 >--- a/monitor.c >+++ b/monitor.c >@@ -157,6 +157,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 >@@ -234,11 +236,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 >@@ -353,6 +362,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) > /* Permit requests for moduli and signatures */ > monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); > monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); >+#ifdef GSSAPI >+ /* and for the GSSAPI key exchange */ >+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); >+#endif > } else { > mon_dispatch = mon_dispatch_proto15; > >@@ -461,6 +474,10 @@ monitor_child_postauth(struct monitor *pmonitor) > monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); > monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); > monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); >+#ifdef GSSAPI >+ /* and for the GSSAPI key exchange */ >+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); >+#endif > } else { > mon_dispatch = mon_dispatch_postauth15; > monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); >@@ -1864,6 +1881,13 @@ monitor_apply_keystate(struct monitor *pmonitor) > # 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; >@@ -1963,6 +1987,9 @@ mm_answer_gss_setup_ctx(int sock, Buffer *m) > 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; > >@@ -1990,6 +2017,9 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m) > 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); >@@ -2007,6 +2037,7 @@ mm_answer_gss_accept_ctx(int sock, Buffer *m) > 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); > } >@@ -2018,6 +2049,9 @@ mm_answer_gss_checkmic(int sock, Buffer *m) > 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); >@@ -2044,7 +2078,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); >@@ -2057,5 +2095,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 --git a/monitor.h b/monitor.h >index 93b8b66..bc50ade 100644 >--- a/monitor.h >+++ b/monitor.h >@@ -65,6 +65,9 @@ enum monitor_reqtype { > MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, > MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, > >+ MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151, >+ MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153, >+ > }; > > struct mm_master; >diff --git a/monitor_wrap.c b/monitor_wrap.c >index eac421b..81ceddb 100644 >--- a/monitor_wrap.c >+++ b/monitor_wrap.c >@@ -1068,7 +1068,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) > } > > int >-mm_ssh_gssapi_userok(char *user) >+mm_ssh_gssapi_userok(char *user, struct passwd *pw) > { > Buffer m; > int authenticated = 0; >@@ -1085,5 +1085,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 --git a/monitor_wrap.h b/monitor_wrap.h >index de4a08f..9758290 100644 >--- a/monitor_wrap.h >+++ b/monitor_wrap.h >@@ -58,8 +58,10 @@ BIGNUM *mm_auth_rsa_generate_challenge(Key *); > 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 --git a/readconf.c b/readconf.c >index cd01482..56e0f44 100644 >--- a/readconf.c >+++ b/readconf.c >@@ -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 --git a/readconf.h b/readconf.h >index bb2d552..e7e80c3 100644 >--- a/readconf.h >+++ b/readconf.h >@@ -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 --git a/servconf.c b/servconf.c >index 6c7a91e..cfe7029 100644 >--- a/servconf.c >+++ b/servconf.c >@@ -117,8 +117,10 @@ initialize_server_options(ServerOptions *options) > 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; >@@ -275,10 +277,14 @@ fill_default_server_options(ServerOptions *options) > 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; >+ options->gss_strict_acceptor = 1; >+ 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) >@@ -412,6 +418,7 @@ typedef enum { > sHostKeyAlgorithms, > sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, > sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, >+ sGssKeyEx, sGssStoreRekey, > sAcceptEnv, sPermitTunnel, > sMatch, sPermitOpen, sForceCommand, sChrootDirectory, > sUsePrivilegeSeparation, sAllowAgentForwarding, >@@ -485,12 +492,20 @@ static struct { > #ifdef GSSAPI > { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, > { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, >+ { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL }, > { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, >+ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, >+ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, > #else > { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, > { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, >+ { "gssapicleanupcreds", 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 }, >@@ -1231,6 +1246,10 @@ process_server_config_line(ServerOptions *options, char *line, > 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; >@@ -1239,6 +1258,10 @@ process_server_config_line(ServerOptions *options, char *line, > 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; >@@ -2246,7 +2269,10 @@ dump_config(ServerOptions *o) > #endif > #ifdef GSSAPI > dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); >+ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); > dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); >+ 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 --git a/servconf.h b/servconf.h >index f4137af..778ba17 100644 >--- a/servconf.h >+++ b/servconf.h >@@ -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 --git a/ssh-gss.h b/ssh-gss.h >index a99d7f0..914701b 100644 >--- a/ssh-gss.h >+++ b/ssh-gss.h >@@ -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(void); >+ >+int ssh_gssapi_update_creds(ssh_gssapi_ccache *store); >+void ssh_gssapi_rekey_creds(void); >+ > #endif /* GSSAPI */ > > #endif /* _SSH_GSS_H */ >diff --git a/ssh_config b/ssh_config >index 03a228f..228e5ab 100644 >--- a/ssh_config >+++ b/ssh_config >@@ -26,6 +26,8 @@ > # HostbasedAuthentication no > # GSSAPIAuthentication no > # GSSAPIDelegateCredentials no >+# GSSAPIKeyExchange no >+# GSSAPITrustDNS no > # BatchMode no > # CheckHostIP yes > # AddressFamily any >diff --git a/ssh_config.5 b/ssh_config.5 >index a47f3ca..cac8cda 100644 >--- a/ssh_config.5 >+++ b/ssh_config.5 >@@ -749,11 +749,45 @@ Specifies whether user authentication based on GSSAPI is allowed. > 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 --git a/sshconnect2.c b/sshconnect2.c >index 7751031..32e9b0d 100644 >--- a/sshconnect2.c >+++ b/sshconnect2.c >@@ -160,6 +160,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) > struct kex *kex; > int r; > >+#ifdef GSSAPI >+ char *orig = NULL, *gss = NULL; >+ char *gss_host = NULL; >+#endif >+ > xxx_host = host; > xxx_hostaddr = hostaddr; > >@@ -193,6 +198,33 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) > order_hostkeyalgs(host, hostaddr, port)); > } > >+#ifdef GSSAPI >+ if (options.gss_keyex) { >+ /* Add the GSSAPI mechanisms currently supported on this >+ * client to the key exchange algorithm proposal */ >+ orig = myproposal[PROPOSAL_KEX_ALGS]; >+ >+ 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(&myproposal[PROPOSAL_KEX_ALGS], >+ "%s,%s", gss, orig); >+ >+ /* If we've got GSSAPI algorithms, then we also >+ * support the 'null' hostkey, as a last resort */ >+ 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); >@@ -211,10 +243,30 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) > # endif > #endif > kex->kex[KEX_C25519_SHA256] = kexc25519_client; >+#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->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 +358,7 @@ int input_gssapi_token(int type, u_int32_t, void *); > 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 +374,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 +685,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 +806,8 @@ input_gssapi_response(int type, u_int32_t plen, void *ctxt) > { > 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 +920,48 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt) > 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 --git a/sshd.c b/sshd.c >index 43d4650..d659a68 100644 >--- a/sshd.c >+++ b/sshd.c >@@ -126,6 +126,10 @@ > #include "version.h" > #include "ssherr.h" > >+#ifdef USE_SECURITY_SESSION_API >+#include <Security/AuthSession.h> >+#endif >+ > #ifndef O_NOCTTY > #define O_NOCTTY 0 > #endif >@@ -1833,10 +1837,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); >@@ -2151,6 +2158,60 @@ main(int ac, char **av) > remote_ip, remote_port, laddr, get_local_port()); > free(laddr); > >+#ifdef USE_SECURITY_SESSION_API >+ /* >+ * Create a new security session for use by the new user login if >+ * the current session is the root session or we are not launched >+ * by inetd (eg: debugging mode or server mode). We do not >+ * necessarily need to create a session if we are launched from >+ * inetd because Panther xinetd will create a session for us. >+ * >+ * The only case where this logic will fail is if there is an >+ * inetd running in a non-root session which is not creating >+ * new sessions for us. Then all the users will end up in the >+ * same session (bad). >+ * >+ * When the client exits, the session will be destroyed for us >+ * automatically. >+ * >+ * We must create the session before any credentials are stored >+ * (including AFS pags, which happens a few lines below). >+ */ >+ { >+ OSStatus err = 0; >+ SecuritySessionId sid = 0; >+ SessionAttributeBits sattrs = 0; >+ >+ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); >+ if (err) >+ error("SessionGetInfo() failed with error %.8X", >+ (unsigned) err); >+ else >+ debug("Current Session ID is %.8X / Session Attributes are %.8X", >+ (unsigned) sid, (unsigned) sattrs); >+ >+ if (inetd_flag && !(sattrs & sessionIsRoot)) >+ debug("Running in inetd mode in a non-root session... " >+ "assuming inetd created the session for us."); >+ else { >+ debug("Creating new security session..."); >+ err = SessionCreate(0, sessionHasTTY | sessionIsRemote); >+ if (err) >+ error("SessionCreate() failed with error %.8X", >+ (unsigned) err); >+ >+ err = SessionGetInfo(callerSecuritySession, &sid, >+ &sattrs); >+ if (err) >+ error("SessionGetInfo() failed with error %.8X", >+ (unsigned) err); >+ else >+ debug("New Session ID is %.8X / Session Attributes are %.8X", >+ (unsigned) sid, (unsigned) sattrs); >+ } >+ } >+#endif >+ > /* > * We don't want to listen forever unless the other side > * successfully authenticates itself. So we set up an alarm which is >@@ -2569,6 +2630,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)); >@@ -2583,6 +2686,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 --git a/sshd_config b/sshd_config >index 4d77f05..64786c9 100644 >--- a/sshd_config >+++ b/sshd_config >@@ -84,6 +84,8 @@ AuthorizedKeysFile .ssh/authorized_keys > # GSSAPI options > #GSSAPIAuthentication no > #GSSAPICleanupCredentials yes >+#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 --git a/sshd_config.5 b/sshd_config.5 >index b18d340..5491c89 100644 >--- a/sshd_config.5 >+++ b/sshd_config.5 >@@ -621,6 +621,12 @@ Specifies whether user authentication based on GSSAPI is allowed. > 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 --git a/sshkey.c b/sshkey.c >index 32dd8f2..5368e7c 100644 >--- a/sshkey.c >+++ b/sshkey.c >@@ -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 } > }; > >@@ -200,7 +201,7 @@ key_alg_list(int certs_only, int plain_only) > const struct keytype *kt; > > for (kt = keytypes; kt->type != -1; kt++) { >- if (kt->name == NULL) >+ if (kt->name == NULL || kt->type == KEY_NULL) > continue; > if ((certs_only && !kt->cert) || (plain_only && kt->cert)) > continue; >diff --git a/sshkey.h b/sshkey.h >index c8d3cdd..5cf4e5d 100644 >--- a/sshkey.h >+++ b/sshkey.h >@@ -62,6 +62,7 @@ enum sshkey_types { > KEY_DSA_CERT, > KEY_ECDSA_CERT, > KEY_ED25519_CERT, >+ KEY_NULL, > KEY_UNSPEC > }; > >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 206346
:
165727
| 165728 |
165729
|
165730