Assignment clobbers value in if
[tlspool] / src / starttls.c
index d830b45..8821960 100644 (file)
 #include <syslog.h>
 #include <errno.h>
 
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
 #include <gnutls/gnutls.h>
 #include <gnutls/pkcs11.h>
 #include <gnutls/abstract.h>
 #include <libtasn1.h>
 
 #include <krb5.h>
+/* Plus, from k5-int.h: */
+krb5_error_code KRB5_CALLCONV krb5_decrypt_tkt_part(krb5_context,
+                                                    const krb5_keyblock *,
+                                                    krb5_ticket * );
+
 
 #include <quick-der/api.h>
 #include <quick-der/rfc4120.h>
@@ -47,6 +46,7 @@ typedef DER_OVLY_rfc4120_EncryptedData encrypted_data_t;
 
 #ifdef WINDOWS_PORT
 #include <winsock2.h>
+#include <ws2tcpip.h>
 #else
 #include <poll.h>
 #include <sys/types.h>
@@ -57,6 +57,7 @@ typedef DER_OVLY_rfc4120_EncryptedData encrypted_data_t;
 #ifndef __MINGW64__
 #include <arpa/inet.h>
 #endif
+#include <netinet/in.h>
 #endif
 
 #ifdef WINDOWS_PORT
@@ -130,7 +131,7 @@ static struct credinfo srv_creds [EXPECTED_SRV_CREDCOUNT];
 static struct credinfo cli_creds [EXPECTED_CLI_CREDCOUNT];
 static int srv_credcount = 0;
 static int cli_credcount = 0;
-static const char const *onthefly_p11uri = "pkcs11:manufacturer=ARPA2.net;token=TLS+Pool+internal;object=on-the-fly+signer;type=private;serial=1";
+static const char onthefly_p11uri[] = "pkcs11:manufacturer=ARPA2.net;token=TLS+Pool+internal;object=on-the-fly+signer;type=private;serial=1";
 static unsigned long long onthefly_serial;  //TODO: Fill with now * 1000
 static gnutls_x509_crt_t onthefly_issuercrt = NULL;
 static gnutls_privkey_t onthefly_issuerkey = NULL;
@@ -141,7 +142,7 @@ static pthread_mutex_t onthefly_signer_lock = PTHREAD_MUTEX_INITIALIZER;
 static krb5_context krbctx_cli, krbctx_srv;
 static krb5_keytab  krb_kt_cli, krb_kt_srv;
 static bool         got_cc_cli, got_cc_srv;
-static int have_key_tgt (
+static int have_key_tgt_cc (
                                struct command *cmd, // in, session context
                                krb5_context ctx,    // in, kerberos context
                                bool use_cc,         // in, whether to use cc
@@ -150,13 +151,14 @@ static int have_key_tgt (
                                char *p11uri,        // in/opt, PKCS #11 pwd URI
                                krb5_keytab kt,      // in/opt, keytab
                                krb5_keyblock *key,  // opt/opt session key
-                               krb5_creds *tgt);    // out/opt, tkt granting tkt
+                               krb5_creds **tgt,    // out/opt, tkt granting tkt
+                               krb5_ccache *cc);    // out/opt, cred cache
 static int have_service_ticket (
                                struct command *cmd, // in, session context
                                krb5_context ctx,    // in, kerberos context
                                krb5_ccache cc_opt,  // in/opt, credcache
                                krb5_principal cli,  // in, client principal
-                               krb5_creds *ticket); // out/opt, tkt granting tkt
+                               krb5_creds **ticket);// out/opt, tkt granting tkt
 #endif
 
 
@@ -704,7 +706,7 @@ int gnutls_token_callback (void *const userdata,
                return GNUTLS_E_PKCS11_TOKEN_ERROR;
        }
 }
+
 
 /*
  * Implement the GnuTLS function for PIN callback.  This function calls
@@ -803,13 +805,33 @@ void setup_starttls (void) {
                setup_starttls_credentials ());
        //
        // Parse the default priority string
-       E_g2e ("Failed to setup NORMAL priority cache",
-               gnutls_priority_init (&priority_normal, "NONE:+VERS-TLS-ALL:+VERS-DTLS-ALL:+COMP-NULL:"
 #ifdef HAVE_TLS_KDH
-               "%%ASYM_CERT_TYPES:"
+       E_g2e ("Failed to setup NORMAL priority cache",
+               gnutls_priority_init (&priority_normal,
+                       "NONE:"
+                       "%ASYM_CERT_TYPES:"
+                       "+VERS-TLS-ALL:+VERS-DTLS-ALL:"
+                       "+COMP-NULL:"
+                       "+CIPHER-ALL:+CURVE-ALL:+SIGN-ALL:+MAC-ALL:"
+                       "+ANON-ECDH:"
+                       "+ECDHE-KRB:" // +ECDHE-KRB-RSA:+ECDHE-KRB-ECDSA:"
+                       "+ECDHE-RSA:+DHE-RSA:+ECDHE-ECDSA:+DHE-DSS:+RSA:"
+                       "+CTYPE-SRV-KRB:+CTYPE-SRV-X.509:+CTYPE-SRV-OPENPGP:"
+                       "+CTYPE-CLI-KRB:+CTYPE-CLI-X.509:+CTYPE-CLI-OPENPGP:"
+                       "+SRP:+SRP-RSA:+SRP-DSS",
+                       NULL));
+#else
+       E_g2e ("Failed to setup NORMAL priority cache",
+               gnutls_priority_init (&priority_normal,
+                       "NONE:"
+                       "+VERS-TLS-ALL:+VERS-DTLS-ALL:"
+                       "+COMP-NULL:+CIPHER-ALL:+CURVE-ALL:+SIGN-ALL:+MAC-ALL:"
+                       "+ANON-ECDH:"
+                       "+ECDHE-RSA:+DHE-RSA:+ECDHE-ECDSA:+DHE-DSS:+RSA:"
+                       "+CTYPE-X.509:+CTYPE-OPENPGP:"
+                       "+SRP:+SRP-RSA:+SRP-DSS",
+                       NULL));
 #endif
-               "+CIPHER-ALL:+CURVE-ALL:+SIGN-ALL:+MAC-ALL:+ANON-ECDH:+ECDHE-RSA:+DHE-RSA:+ECDHE-ECDSA:+DHE-DSS:+RSA:+CTYPE-X.509:+CTYPE-OPENPGP:+SRP:+SRP-RSA:+SRP-DSS", NULL));
-               // gnutls_priority_init (&priority_normal, "NORMAL:-RSA:+ANON-ECDH:+RSA:+CTYPE-X.509:+CTYPE-OPENPGP:+SRP:+SRP-RSA:+SRP-DSS", NULL));
        //
        // Try to setup on-the-fly signing key / certificate and gen a certkey
        otfsigcrt = cfg_tls_onthefly_signcert ();
@@ -840,7 +862,7 @@ fprintf (stderr, "DEBUG: gtls_errno==%d after failing to open file for onthefly_
                                        GNUTLS_E_FILE_ERROR);
                        } else {
                                gnutls_datum_t cd = {
-                                       .data = crt,
+                                       .data = (unsigned char *)(&crt[0]),
                                        .size = len
                                };
 fprintf (stderr, "DEBUG: gtls_errno==%d before importing onthefly_issuercrt\n", gtls_errno);
@@ -1114,7 +1136,7 @@ gtls_error clisrv_cert_retrieve (gnutls_session_t session,
        char *rolestr;
        char sni [sizeof (cmd->cmd.pio_data.pioc_starttls.localid)];
        size_t snilen = sizeof (sni);
-       int snitype;
+       unsigned int snitype;
        int ok;
        uint32_t flags;
        char *p11priv;
@@ -1125,6 +1147,7 @@ gtls_error clisrv_cert_retrieve (gnutls_session_t session,
 
        //
        // Setup a number of common references and structures
+       errno = 0;
        *pcert = NULL;
        cmd = (struct command *) gnutls_session_get_ptr (session);
        if (cmd == NULL) {
@@ -1194,7 +1217,7 @@ gtls_error clisrv_cert_retrieve (gnutls_session_t session,
 #endif
        } else {
                // GNUTLS_CRT_RAW, GNUTLS_CRT_UNKNOWN, or other
-               tlog (TLOG_TLS, LOG_ERR, "Funny sort of certificate retrieval attempted as a %s", rolestr);
+               tlog (TLOG_TLS, LOG_ERR, "Funny sort of certificate %d retrieval attempted as a %s", certtp, rolestr);
                E_g2e ("Requested certtype is neither X.509 nor OpenPGP",
                        GNUTLS_E_CERTIFICATE_ERROR);
                return gtls_errno;
@@ -1328,16 +1351,17 @@ fprintf (stderr, "DEBUG: Missing certificate for local ID %s and remote ID %s\n"
                        //
                        // First, try to obtain a TGT and key, in various ways
                        krb5_keyblock key;
-                       krb5_creds tgt;
-                       int status;
-                       memset (&key, 0, sizeof (key));
-                       memset (&tgt, 0, sizeof (tgt));
-                       status = have_key_tgt (
+                       krb5_creds *tgt = NULL;
+                       krb5_creds *ticket = NULL;
+                       krb5_ccache cc = NULL;
+                       int status = 0;
+                       memset (&key,    0, sizeof (key   ));
+                       status = have_key_tgt_cc (
                                cmd, krbctx_cli,
                                1, 0, 0,
                                p11priv,
                                krb_kt_cli,
-                               &key, &tgt);
+                               &key, &tgt, &cc);
                        if (status >= 1) {
                                // We never use this key ourselves
                                krb5_free_keyblock_contents (krbctx_cli, &key);
@@ -1348,38 +1372,83 @@ fprintf (stderr, "DEBUG: Missing certificate for local ID %s and remote ID %s\n"
                                break;
                        }
                        //
-                       // Now find a service ticket to talk to
+                       // Store client identity in session object
+                       if (0 != krb5_copy_principal (
+                                       krbctx_cli,
+                                       tgt->client,
+                                       &cmd->krbid_cli)) {
+                               krb5_free_creds (krbctx_cli, tgt);
+                               tgt = NULL;
+                               if (cc != NULL) {
+                                       krb5_cc_close (krbctx_cli, cc);
+                                       cc = NULL;
+                               }
+                               gtls_errno = GNUTLS_E_NO_CERTIFICATE_FOUND;
+                               break;
+                       }
+                       //
+                       // Now find a service ticket to talk to, and its key
                        //TODO// Pass credcache instead?
                        status = have_service_ticket (
                                cmd, krbctx_cli,
-                               NULL,   //TODO// Should get cc from have_key_tgt()
-                               tgt.client,
-                               &cmd->krb_tkt);
-                       krb5_free_cred_contents (krbctx_cli, &tgt);
+                               cc,
+                               cmd->krbid_cli,
+                               &ticket);
+                       if (cc != NULL) {
+                               // We don't need cc anymore below
+                               krb5_cc_close (krbctx_cli, cc);
+                       }
                        if (status < 1) {
                                // Stop processing when no ticket was found
+                               krb5_free_creds (krbctx_cli, tgt);
+                               tgt = NULL;
                                gtls_errno = GNUTLS_E_NO_CERTIFICATE_FOUND;
                                break;
                        }
-                       certdatum.data = cmd->krb_tkt.ticket.data;
-                       certdatum.size = cmd->krb_tkt.ticket.length;
+                       //
+                       // Only for KDH-Only mode can the client rely on a
+                       // server principal taken from the ticket;
+                       // So only store krbid_srv for KDH-Only mode.
+                       if ((gnutls_certificate_type_get_peers (cmd->session)
+                                               == GNUTLS_CRT_KRB) &&
+                                       (0 != krb5_copy_principal (
+                                               krbctx_cli,
+                                               tgt->server,
+                                               &cmd->krbid_srv))) {
+                               krb5_free_creds (krbctx_cli, ticket);
+                               gtls_errno = GNUTLS_E_NO_CERTIFICATE_FOUND;
+                               break;
+                       }
+                       krb5_free_creds (krbctx_cli, tgt);
+                       tgt = NULL;
+                       if (0 != krb5_copy_keyblock_contents (
+                                       krbctx_cli,
+                                       &ticket->keyblock,
+                                       &cmd->krb_key)) {
+                               gtls_errno = GNUTLS_E_NO_CERTIFICATE_FOUND;
+                               // continue, with E_g2e() skipping import
+                       }
+                       certdatum.data = ticket->ticket.data;
+                       certdatum.size = ticket->ticket.length;
                        E_g2e ("MOVED: Failed to import Kerberos ticket",
                                gnutls_pcert_import_krb_raw (
                                        *pcert,
                                        &certdatum,
                                        0));
+                       krb5_free_creds (krbctx_cli, ticket);
                } else {
                        //
                        // For KDH-Only, the server supplies one of:
-                       //  - an empty ticket (0 bytes long)
-                       //  - a TGT for user-to-user mode (where considered useful)
+                       //  - a TGT for user-to-user mode (for p2p exchanges)
+                       //  - an DER NULL to waive u2u mode
                        //TODO// E_g2e ("MOVED: Failed to import Kerberos ticket",
                        //TODO//        gnutls_pcert_import_krb_raw (
                        //TODO//                *pcert,
                        //TODO//                &certdatum,     //TODO:WHATSFOUND//
                        //TODO//                0));
                        int u2u = 0;
-                       int status;
+                       int status = 0;
+                       krb5_creds *tgt = NULL;
                        //
                        // Determine whether we want to run in user-to-user mode
                        // for which we should supply a TGT to the TLS client
@@ -1388,10 +1457,11 @@ fprintf (stderr, "DEBUG: Missing certificate for local ID %s and remote ID %s\n"
                        // u2u = u2u || "shaken hands on TLS symmetry extension"
                        u2u = u2u && got_cc_srv;  // We may simply not be able!
                        //
-                       // When not in user-to-user mode, deliver 0 bytes
+                       // When not in user-to-user mode, deliver DER NULL
                        if (!u2u) {
-                               certdatum.data = "";
-                               certdatum.size = 0;
+                               static unsigned char der_null_data[] = "\x05\x00";
+                               certdatum.data = der_null_data;
+                               certdatum.size = 2;
                                E_g2e ("Failed to withhold Kerberos server ticket",
                                        gnutls_pcert_import_krb_raw (
                                                *pcert,
@@ -1401,29 +1471,37 @@ fprintf (stderr, "DEBUG: Missing certificate for local ID %s and remote ID %s\n"
                        }
                        //
                        // Continue specifically for user-to-user mode.
+                       //TODO// Setup server principal identity
                        //
                        // Fetch the service's key
-                       status = have_key_tgt (
+                       status = have_key_tgt_cc (
                                cmd, krbctx_srv,
                                1, 0, 0,        // Hmm... later we know kvno/etype
                                p11priv,
                                krb_kt_srv,
-                               &cmd->krb_key, &cmd->krb_tkt);
+                               &cmd->krb_key, &tgt, NULL);
                        if (status == 1) {
                                // There's no use in having just the key
                                krb5_free_keyblock_contents (krbctx_srv, &cmd->krb_key);
+                               memset (&cmd->krb_key, 0, sizeof (cmd->krb_key));
                        }
                        if (status < 2) {
                                gtls_errno = GNUTLS_E_NO_CERTIFICATE_FOUND;
-                               break;
+                       } else if (0 != krb5_copy_principal (
+                                               krbctx_srv,
+                                               tgt->server,
+                                               &cmd->krbid_srv)) {
+                               gtls_errno = GNUTLS_E_NO_CERTIFICATE_FOUND;
                        }
-                       certdatum.data = cmd->krb_tkt.ticket.data;
-                       certdatum.size = cmd->krb_tkt.ticket.length;
+                       certdatum.data = tgt->ticket.data;
+                       certdatum.size = tgt->ticket.length;
                        E_g2e ("Failed to withhold Kerberos server ticket",
                                gnutls_pcert_import_krb_raw (
                                        *pcert,
                                        &certdatum,
                                        0));
+                       krb5_free_creds (krbctx_cli, tgt);
+                       tgt = NULL;
                }
                break;
 #endif
@@ -1434,6 +1512,7 @@ fprintf (stderr, "DEBUG: Missing certificate for local ID %s and remote ID %s\n"
 
 //TODO// Moved out (end)
 
+#ifdef ANCIENT_CODE_WHEN_DBERRNO_RAN_IN_PARALLEL
        //
        // Lap up any overseen POSIX error codes in errno
        if (errno) {
@@ -1441,11 +1520,12 @@ fprintf (stderr, "DEBUG: Missing certificate for local ID %s and remote ID %s\n"
                cmd->session_errno = errno;
                gtls_errno = GNUTLS_E_NO_CIPHER_SUITES; /* Vaguely matching */
        }
+#endif
 
        //
        // Return the overral error code, hopefully GNUTLS_E_SUCCESS
        tlog (TLOG_TLS, LOG_DEBUG, "Returning %d / %s from clisrv_cert_retrieve()", gtls_errno, gnutls_strerror (gtls_errno));
-fprintf (stderr, "DEBUG: clisrv_cert_retrieve() sets *pcert to 0x%xl (length %d)... {pubkey = 0x%lx, cert= {data = 0x%lx, size=%ld}, type=%ld}\n", (long) *pcert, *pcert_length, (long) (*pcert)->pubkey, (long) (*pcert)->cert.data, (long) (*pcert)->cert.size, (long) (*pcert)->type);
+fprintf (stderr, "DEBUG: clisrv_cert_retrieve() sets *pcert to 0x%lx (length %d)... {pubkey = 0x%lx, cert= {data = 0x%lx, size=%ld}, type=%ld}\n", (long) *pcert, *pcert_length, (long) (*pcert)->pubkey, (long) (*pcert)->cert.data, (long) (*pcert)->cert.size, (long) (*pcert)->type);
        return gtls_errno;
 }
 
@@ -1475,6 +1555,9 @@ fprintf (stderr, "DEBUG: About to import %d bytes worth of X.509 certificate int
                                NULL,   /* use master key */
                                0));
                break;
+       case LID_TYPE_KRB5:
+               /* Binary information is currently moot, so do not load it */
+               break;
        default:
                /* Should not happen */
                break;
@@ -1540,7 +1623,7 @@ gnutls_pcert_st *load_certificate_chain (uint32_t flags, unsigned int *chainlen,
                long nextlen;
                // Note: Accept BER because the outside SEQUENCE is not signed
                certlen = asn1_get_length_ber (
-                       ((char *) certdatum->data) + 1,
+                       (certdatum->data) + 1,
                        certdatum->size,
                        &lenlen);
                certlen += 1 + lenlen;
@@ -1554,11 +1637,11 @@ gnutls_pcert_st *load_certificate_chain (uint32_t flags, unsigned int *chainlen,
                        *chainlen = 0;
                        return NULL;
                }
-               nextdatum.data = ((char *) certdatum->data) + certlen;
-               nextdatum.size =           certdatum->size  - certlen;
+               nextdatum.data = (certdatum->data) + certlen;
+               nextdatum.size =  certdatum->size  - certlen;
                certdatum->size = certlen;
                nextlen = asn1_get_length_ber (
-                       ((char *) nextdatum.data) + 1,
+                       nextdatum.data + 1,
                        nextdatum.size,
                        &lenlen);
                nextlen += 1 + lenlen;
@@ -1607,6 +1690,11 @@ gnutls_pcert_st *load_certificate_chain (uint32_t flags, unsigned int *chainlen,
 }
 
 
+
+/********** KERBEROS SUPPORT FUNCTIONS FOR TLS-KDH **********/
+
+
+
 /* Prepare the Kerberos resources for use by clients and/or servers.
  */
 #ifdef HAVE_TLS_KDH
@@ -1641,6 +1729,7 @@ static int setup_starttls_kerberos (void) {
                k5err = krb5_kt_resolve (krbctx_srv, cfg, &krb_kt_srv);
        }
        cfg = cfg_krb_client_credcache ();
+#if 0  /* Temporary bypass of cctype checks */
        if ((k5err == 0) && (cfg != NULL)) {
                k5err = krb5_cc_set_default_name (krbctx_cli, cfg);
                if (k5err == 0) {
@@ -1652,7 +1741,9 @@ static int setup_starttls_kerberos (void) {
                        krb5_cc_close (krbctx_cli, krb_cc_tmp);
                }
        }
+#endif
        cfg = cfg_krb_server_credcache ();
+#if 0  /* Temporary bypass of cctype checks */
        if ((k5err == 0) && (cfg != NULL)) {
                k5err = krb5_cc_set_default_name (krbctx_srv, cfg);
                if (k5err == 0) {
@@ -1664,6 +1755,7 @@ static int setup_starttls_kerberos (void) {
                        krb5_cc_close (krbctx_srv, krb_cc_tmp);
                }
        }
+#endif
        //
        // Check for consistency and log helpful messages for the sysop
        if (k5err != 0) {
@@ -1679,25 +1771,29 @@ static int setup_starttls_kerberos (void) {
                tlog (TLOG_DAEMON | TLOG_KERBEROS, LOG_ERR, "No kerberos_server_keytab configured, so Kerberos cannot work at all");
                retval = GNUTLS_E_UNWANTED_ALGORITHM;
 /* TODO: Only for MIT krb5 1.11 and up
-       } else if (0 == krb5_kt_have_content (krb_ctx, krb_kt_cli)) {
-               tlog (TLOG_DAEMON | TLOG_KERBEROS, LOG_ERR, "Keytab in kerberos_client_keytab is absent or empty");
+       } else if (0 == krb5_kt_have_content (krb_ctx, krb_kt_srv)) {
+               tlog (TLOG_DAEMON | TLOG_KERBEROS, LOG_ERR, "Keytab in kerberos_server_keytab is absent or empty");
                retval = GNUTLS_E_UNWANTED_ALGORITHM;
  */
        }
        if (krbctx_cli == NULL) {
                tlog (TLOG_DAEMON | TLOG_KERBEROS, LOG_ERR, "No kerberos_client_credcache configured, so Kerberos cannot work at all");
                retval = GNUTLS_E_UNWANTED_ALGORITHM;
+#if 0  /* Temporary bypass of cctype checks */
        } else if (!krb5_cc_support_switch (
                        krbctx_cli, cctype_cli)) {
                tlog (TLOG_DAEMON | TLOG_KERBEROS, LOG_ERR, "Your kerberos_client_credcache does not support multilpe identities");
                retval = GNUTLS_E_UNWANTED_ALGORITHM;
+#endif
        }
        if (krbctx_srv == NULL) {
                tlog (TLOG_DAEMON | TLOG_KERBEROS, LOG_WARNING, "No kerberos_server_credcache configured, so user-to-user Kerberos will not work");
+#if 0  /* Temporary bypass of cctype checks */
        } else if (!krb5_cc_support_switch (
                        krbctx_srv, cctype_srv)) {
                tlog (TLOG_DAEMON | TLOG_KERBEROS, LOG_ERR, "Your kerberos_server_credcache does not support multilpe identities");
                retval = GNUTLS_E_UNWANTED_ALGORITHM;
+#endif
        }
        if (retval != GNUTLS_E_SUCCESS) {
                cleanup_starttls_kerberos ();
@@ -1708,7 +1804,7 @@ static int setup_starttls_kerberos (void) {
 
 
 /* Cleanup Kerberos resources.  This must be an idempotent function, because
- * it is called when Kerberos panics as well as when 
+ * it is called when Kerberos panics as well as when
  */
 #ifdef HAVE_TLS_KDH
 static void cleanup_starttls_kerberos (void) {
@@ -1732,11 +1828,6 @@ static void cleanup_starttls_kerberos (void) {
 #endif
 
 
-
-/********** KERBEROS SUPPORT FUNCTIONS FOR TLS-KDH **********/
-
-
-
 /* Prompter callback function for PKCS #11.
  *
  * TODO: Use "struct pkcs11iter" as data, possibly interact with the user,
@@ -1823,7 +1914,7 @@ static krb5_error_code clisrv_p11krb_callback (krb5_context ctx,
  * so 0 means error, 1 means key only and 2 means key and tgt.
  */
 #ifdef HAVE_TLS_KDH
-static int have_key_tgt (struct command *cmd,       // in, session context
+static int have_key_tgt_cc (struct command *cmd,            // in, session context
                                krb5_context ctx,    // in, kerberos context
                                bool use_cc,         // in, whether to use cc
                                krb5_kvno kvno,      // in, kvno (0 for highest)
@@ -1831,10 +1922,12 @@ static int have_key_tgt (struct command *cmd,        // in, session context
                                char *p11uri,        // in/opt, PKCS #11 pwd URI
                                krb5_keytab kt,      // in/opt, keytab
                                krb5_keyblock *key,  // opt/opt session key
-                               krb5_creds *tgt) {   // out/opt, tkt granting tkt
+                               krb5_creds **tgt,    // out/opt, tkt granting tkt
+                               krb5_ccache *cc) {   // out/opt, cred cache
        int k5err = 0;
-       krb5_ccache cc = NULL;
+       krb5_ccache newcc = NULL;
        krb5_principal sought  = NULL;
+       krb5_principal sought1 = NULL;
        krb5_principal tgtname = NULL;
        krb5_keytab_entry ktentry;
        const char *svc = cmd->cmd.pio_data.pioc_starttls.service;
@@ -1847,12 +1940,14 @@ static int have_key_tgt (struct command *cmd,        // in, session context
        time_t now = 0;
        //
        // Assertions, and initialise variables
-       assert (cmd != NULL);
-       assert (ctx != NULL);
-       assert (key != NULL);
-       assert (tgt != NULL);
+       assert ( cmd != NULL);
+       assert ( ctx != NULL);
+       assert ( key != NULL);
+       assert (*tgt == NULL);
        krb5_free_keyblock_contents (ctx, key);
-       krb5_free_cred_contents (ctx, tgt);
+       if (cc != NULL) {
+               *cc = NULL;
+       }
        //
        // Construct the realm name
        liddom = strrchr (lid, '@');
@@ -1864,7 +1959,7 @@ static int have_key_tgt (struct command *cmd,          // in, session context
                lid1len = strnlen (lid, 128);
        }
        k5err = krb5_get_host_realm (ctx, liddom, &realms);
-       if ((k5err == 0) && (realms [0] != NULL) && (**realms != '\0')) {
+       if ((k5err == 0) && (realms [0] != NULL) && (*realms [0] != '\0')) {
                strncpy (realm, realms [0], sizeof (realm));
                realm [sizeof (realm)-1] = '\0';
        } else {
@@ -1894,6 +1989,7 @@ retry:
                                        strlen (realm), realm,
                                        strnlen (lid, 128), lid,
                                        0);
+               break;
        case KRB5_NT_SRV_HST:
                if (strcmp (svc, "http") == 0) {
                        svc = "HTTP";
@@ -1916,22 +2012,26 @@ retry:
        } else {
                sought = NULL;
        }
-       k5err = krb5_cc_cache_match (ctx, sought, &cc);
-       if (sought != NULL) {
-               krb5_free_principal (ctx, sought);
-               sought = NULL;
-       }
+       k5err = krb5_cc_cache_match (ctx, sought, &newcc);
        if (k5err != 0) {
-               if (nametype_alt != nametype) {
+               if ((nametype_alt != nametype) && (sought1 == NULL)) {
                        nametype = nametype_alt;
+                       sought1  = sought;
+                       sought   = NULL;
                        goto retry;
                }
-               // We utterly failed
-               goto cleanup;
+               //
+               // We failed to find an *existing* credentials cache
+               // for the local identity.
+               //
+               // Our new hope is to create a fresh credential, and add
+               // it to the current credcache.  To that end, we now try
+               // to overrule k5err by getting hold of our default cc.
+               goto from_scratch;
        }
        //
        // Construct the TGT name
-       k5err = krb5_build_principal (ctx, &tgtname,
+       k5err = krb5_build_principal_ext (ctx, &tgtname,
                                strlen (realm), realm,
                                6, "krbtgt",
                                strlen (realm), realm,
@@ -1944,46 +2044,56 @@ retry:
        //
        // Try to get the service ticket for the TGT name from the cache
        krb5_creds credreq;
+       memset (&credreq, 0, sizeof (credreq));
+       credreq.client = sought;
+       credreq.server = tgtname;
        k5err = krb5_get_credentials (ctx,
                                /* KRB5_GC_USER_USER ?|? */
                                        ( use_cc ? 0 : KRB5_GC_CACHED ),
-                               cc,
-                               &credreq, &tgt);
+                               newcc,
+                               &credreq, tgt);
        time (&now);
        if ((k5err == 0)
-                               && (now + 300 > tgt->times.endtime)
-                               && (now + 300 < tgt->times.renew_till)) {
-               krb5_free_creds (ctx, tgt);
+                               && (now + 300 > (*tgt)->times.endtime)
+                               && (now + 300 < (*tgt)->times.renew_till)) {
+               //TODO:NOTHERE// krb5_free_creds (ctx, *tgt);
+               //TODO:NOTHERE// *tgt = NULL;
                // Try to renew the ticket
                k5err = krb5_get_renewed_creds (ctx,
-                               tgt,
+                               *tgt,
                                sought,
-                               cc,
+                               newcc,
                                NULL);   /* krbtgt/REALM@REALM */
        }
        if ((k5err == 0)
-                               && (now + 300 > tgt->times.endtime)) {
+                               && (now + 300 > (*tgt)->times.endtime)) {
                // Thanks, but no thanks!
-               krb5_free_creds (ctx, tgt);
+               krb5_free_creds (ctx, *tgt);
+               *tgt = NULL;
                k5err = 666;
        }
        if (k5err == 0) {
                // First case worked -- return <key,tgt> from credout
                k5err = krb5_copy_keyblock_contents (ctx,
-                               &tgt->keyblock,
+                               &(*tgt)->keyblock,
                                key);
                // On failure, key shows failure
+               if (cc != NULL) {
+                       *cc = newcc;
+                       newcc = NULL;
+               }
                goto cleanup;
        }
+from_scratch:
        //
-       // Prior attempts failed.  Instead, look for keytab presence.
+       // Prior attempts failed.  Instead, look for keytab or p11uri presence.
        // This is skipped when the use_cc option below welcomes krb5_creds.
-       if ((key->contents == NULL) && (p11uri == NULL)) {
+       if ((key->contents == NULL) && (p11uri == NULL) && (kt == NULL)) {
                // We cannot obtain a new krbtgt
                // We simply return what we've got (which may be nothing)
                goto cleanup;
        }
-       if (!use_cc) {
+       if ((kt == NULL) && (!use_cc)) {
                // We have nowhere to store a new krbtgt if we got it
                // We simply return what we've got (which is at least a key)
                goto cleanup;
@@ -1992,48 +2102,83 @@ retry:
        // Either we have a keytab key, or we have a p11uri,
        // so we can attempt to create a new credcache with a new krbtgt
        if (use_cc) {
-               if (cc == NULL) {
-                       k5err = krb5_cc_default (ctx, &cc);
+               if (newcc == NULL) {
+                       k5err = krb5_cc_default (ctx, &newcc);
                        if (k5err != 0) {
                                // Utter failure to do even the simplest thing
                                goto cleanup;
                        }
                }
-               if (p11uri == NULL) {
-                       k5err = krb5_get_init_creds_keytab (
-                                       ctx,
-                                       tgt,
-                                       sought, //TODO:KEEP
-                                       kt,
-                                       0,    /* start now please */
-                                       NULL, /* get a TGT please */
-                                       NULL);  //TODO// opts needed?
-               } else {
-                       //TODO// Prepare PKCS #11 access
-                       k5err = krb5_get_init_creds_password (
-                                       ctx,
-                                       tgt,
-                                       sought, //TODO:KEEP
-                                       NULL,   // Use callbacks for password
-                                       clisrv_p11krb_callback,
-                                       cmd,  /* callback data pointer */
-                                       0,    /* start now please */
-                                       NULL, /* get a TGT please */
-                                       NULL);  //TODO// opts needed?
-                       //TODO// End PKCS #11 access
+               *tgt = malloc (sizeof (**tgt));
+               if (*tgt == NULL) {
+                       // Memory error
+                       goto cleanup;
                }
-               if (k5err == 0) {
-                       krb5_copy_keyblock_contents (ctx,
-                               &ktentry.key,
-                               key);
-                       // Failure will show up in key
+               memset (*tgt, 0, sizeof (**tgt));
+               if ((sought != NULL) && (sought1 == NULL)) {
+                       // We only tried one name
+                       sought1 = sought;
+                       sought = NULL;
                }
+               do {
+                       if (sought1 == NULL) {
+                               break;
+                       }
+                       if (p11uri == NULL) {
+                               k5err = krb5_get_init_creds_keytab (
+                                               ctx,
+                                               *tgt,
+                                               sought1,
+                                               kt,
+                                               0,    /* start now please */
+                                               NULL, /* get a TGT please */
+                                               NULL);  //TODO// opts needed?
+                       } else {
+                               //TODO// Prepare PKCS #11 access
+                               k5err = krb5_get_init_creds_password (
+                                               ctx,
+                                               *tgt,
+                                               sought1,
+#ifdef TOM_IS_WEG
+                                               NULL,   // Use callbacks for password
+                                               clisrv_p11krb_callback,
+#else
+                                               "1234",
+                                               NULL,
+#endif
+                                               cmd,  /* callback data pointer */
+                                               0,    /* start now please */
+                                               NULL, /* get a TGT please */
+                                               NULL);  //TODO// opts needed?
+                               //TODO// End PKCS #11 access
+                       }
+                       krb5_free_principal (ctx, sought1);
+                       sought1 = sought;
+                       sought = NULL;
+               } while (k5err != 0);
                if (k5err != 0) {
                        // Failed to initiate new credentials
+                       krb5_free_creds (ctx, *tgt);
+                       *tgt = NULL;
                        goto cleanup;
                }
+               // Try to store the credential, if it was found
+               if (sought1 != NULL) {
+                       k5err = krb5_cc_initialize (ctx, newcc, sought1);
+                       if (k5err == 0) {
+                               k5err = krb5_cc_store_cred (ctx, newcc, *tgt);
+                       }
+               }
+               // Copy the keyblock; any failure will show up in key
+               krb5_copy_keyblock_contents (ctx,
+                       &(*tgt)->keyblock, //TODO:UNINIT// &ktentry.key,
+                       key);
                //
                // We succeeded in setting up a new Ticket Granting Ticket!
+               if (cc != NULL) {
+                       *cc = newcc;
+                       newcc = NULL;
+               }
                goto cleanup;
        }
        //
@@ -2052,6 +2197,10 @@ retry:
                                key);
                        krb5_free_keytab_entry_contents (ctx, &ktentry);
                        // On failure, key shows failure.
+                       if (cc != NULL) {
+                               *cc = newcc;
+                               newcc = NULL;
+                       }
                        goto cleanup;
                }
        }
@@ -2060,19 +2209,47 @@ retry:
 cleanup:
        //
        // Cleanup and return the <key,tgt> values as they were delivered
+       if (sought1 != NULL) {
+               krb5_free_principal (ctx, sought1);
+               sought1 = NULL;
+       }
+       if (sought != NULL) {
+               krb5_free_principal (ctx, sought);
+               sought = NULL;
+       }
        if (tgtname != NULL) {
                krb5_free_principal (ctx, tgtname);
                tgtname = NULL;
        }
-       if (cc != NULL) {
-               krb5_cc_close (ctx, cc);
+       if (newcc != NULL) {
+               krb5_cc_close (ctx, newcc);
+               newcc = NULL;
        }
        if (key->contents == NULL) {
+               if (k5err != 0) {
+                       const char *errmsg = krb5_get_error_message (ctx, k5err);
+                       tlog (TLOG_DAEMON, LOG_ERR, "Kerberos error in have_key_tgt_cc: %s", errmsg);
+                       krb5_free_error_message (ctx, errmsg);
+               }
+               if (*tgt != NULL) {
+                       krb5_free_creds (ctx, *tgt);
+                       *tgt = NULL;
+               }
+               if ((cc != NULL) && (*cc != NULL)) {
+                       krb5_cc_close (ctx, *cc);
+                       *cc = NULL;
+               }
                return 0;
-       } else if (tgt->ticket.data == NULL) {
+       } else if (tgt == NULL) {
+               if ((cc != NULL) && (*cc != NULL)) {
+                       krb5_cc_close (ctx, *cc);
+                       *cc = NULL;
+               }
                return 1;
-       } else {
+       } else if ((cc == NULL) || (*cc == NULL)) {
                return 2;
+       } else {
+               return 3;
        }
 }
 #endif
@@ -2107,7 +2284,7 @@ static int have_service_ticket (
                                krb5_context ctx,     // in, kerberos context
                                krb5_ccache cc_opt,   // in/opt, credcache
                                krb5_principal cli,   // in, client principal
-                               krb5_creds *ticket) { // out, tkt granting tkt
+                               krb5_creds **ticket) {// out, tkt granting tkt
        int k5err = 0;
        krb5_ccache cc = cc_opt;
        krb5_flags u2u = 0;
@@ -2118,9 +2295,10 @@ static int have_service_ticket (
        // Sanity checks and initialisation
        memset (&tkt_srv, 0, sizeof (tkt_srv));
        memset (&credreq, 0, sizeof (credreq));
+       *ticket = NULL;
        //
        // Determine the optional cc parameter if it was not provided
-       //TODO// This can go if we always get it passed from have_key_tgt()
+       //TODO// This can go if we always get it passed from have_key_tgt_cc()
        if (cc == NULL) {
                k5err = krb5_cc_cache_match (ctx, cli, &cc);
                if (k5err != 0) {
@@ -2141,7 +2319,7 @@ static int have_service_ticket (
                riddom = rid;  // localid is a host
        }
        k5err = krb5_get_host_realm (ctx, riddom, &realms);
-       if ((k5err == 0) && (realms [0] != NULL) && (**realms != '\0')) {
+       if ((k5err == 0) && (realms [0] != NULL) && (*realms [0] != '\0')) {
                strncpy (realm, realms [0], sizeof (realm));
                realm [sizeof (realm)-1] = '\0';
        } else {
@@ -2178,26 +2356,26 @@ static int have_service_ticket (
        //  - we are sure of GNUTLS_CRD_CERTIFICATE because we implement it now
        //  - we must ensure that this is KDH-Only (remote GNUTLS_CRT_KRB)
        //  - we must ensure that the remote provided a non-empty ticket
-       if (gnutls_certificate_type_get (cmd->session) == GNUTLS_CRT_KRB) {
-               // This is KDH-Only
-               const gnutls_datum_t *peer_tkt;
-               unsigned int peer_tkt_count;
-               peer_tkt = gnutls_certificate_get_peers (cmd->session, &peer_tkt_count);
-               if ((peer_tkt != NULL) && (peer_tkt_count >= 1) && (peer_tkt [0].size > 5)) {
+       if (gnutls_certificate_type_get_peers (cmd->session) == GNUTLS_CRT_KRB) {
+               // This is KDH-Only -- and the server may present a TGT
+               const gnutls_datum_t *opt_srv_tkt;
+               unsigned int srv_tkt_count;
+               opt_srv_tkt = gnutls_certificate_get_peers (cmd->session, &srv_tkt_count);
+               if ((opt_srv_tkt != NULL) && (srv_tkt_count >= 1) && (opt_srv_tkt [0].size > 5)) {
                        // Looks good, we'll use only the first (normally only) one
-                       credreq.second_ticket.data   = peer_tkt [0].data;
-                       credreq.second_ticket.length = peer_tkt [0].size;
+                       credreq.second_ticket.data   = opt_srv_tkt [0].data;
+                       credreq.second_ticket.length = opt_srv_tkt [0].size;
                        u2u = KRB5_GC_USER_USER;
                }
        }
        //
        // Fetch the ticket for the service
-       k5err = krb5_get_credentials (ctx, u2u, cc, &credreq, &ticket);
+       k5err = krb5_get_credentials (ctx, u2u, cc, &credreq, ticket);
        //
        // Cleanup and return; the return value depends on k5err
 cleanup:
        if ((cc != NULL) && (cc_opt == NULL)) {
-               //TODO// This can go if we always get it passed from have_key_tgt()
+               //TODO// This can go if we always get it passed from have_key_tgt_cc()
                krb5_cc_close (ctx, cc);
                cc = NULL;
        }
@@ -2225,6 +2403,7 @@ dercursor qder2b_pack_int32 (uint8_t *target_4b, int32_t value) {
                        // Skip sign-extending initial bytes
                        uint32_t neutro = (value >> (shift - 1) ) & 0x000001ff;
                        if ((neutro == 0x000001ff) || (neutro == 0x00000000)) {
+                               shift -= 8;
                                continue;
                        }
                }
@@ -2293,12 +2472,31 @@ done:
 }
 
 
+#ifdef HAVE_TLS_KDH
+/* TODO: Debugging function for printing (descr,ptr,len) ranges */
+static inline void prange (char *descr, uint8_t *ptr, int len) {
+       fprintf (stderr, "%s #%04d: %02x %02x %02x %02x %02x %02x %02x %02x...%02x %02x %02x %02x\n",
+                       descr, len,
+                       ptr [0], ptr [1], ptr [2], ptr [3],
+                       ptr [4], ptr [5], ptr [6], ptr [7],
+                       ptr [len-4], ptr [len-3], ptr [len-2], ptr [len-1]);
+}
+static inline void prangefull (char *descr, uint8_t *ptr, int len) {
+       fprintf (stderr, "%s #%04d:", descr, len);
+       while (len-- > 0) {
+               fprintf (stderr, " %02x", *ptr++);
+       }
+       fprintf (stderr, "\n");
+}
+#endif
+
+
 /* The callback function that retrieves a TLS-KDH "signature", which is kept
  * outside of GnuTLS.  The callback computes an authenticator encrypted to
  * the session's Kerberos key.
  */
 #ifdef HAVE_TLS_KDH
-gtls_error cli_kdhsig_encode (gnutls_session_t session,
+static gtls_error cli_kdhsig_encode (gnutls_session_t session,
                        gnutls_datum_t *enc_authenticator,
                        gnutls_datum_t *dec_authenticator,
                        const gnutls_datum_t *hash,
@@ -2321,11 +2519,17 @@ gtls_error cli_kdhsig_encode (gnutls_session_t session,
        cmd = (struct command *) gnutls_session_get_ptr (session);
        memset (&auth, 0, sizeof (auth));
        memset (&subkey, 0, sizeof (subkey));
-       assert (cmd->krb_tkt.client != NULL);
+       assert (cmd->krbid_cli != NULL);
        assert (cmd->krb_key.contents != NULL);
+       static const uint8_t auth_packer [] = {
+                       DER_PACK_rfc4120_Authenticator, DER_PACK_END };
+       static const uint8_t encdata_packer [] = {
+                       DER_PACK_rfc4120_EncryptedData, DER_PACK_END };
        //
        // Setup secure hash in authenticator (never optional for TLS-KDH)
        auth.cksum.cksumtype = qder2b_pack_int32 (dercksumtype, checksum_type);
+       auth.cksum.checksum.derptr = hash->data;
+       auth.cksum.checksum.derlen = hash->size;
        //
        // Optionally include a subkey (namely, for KDH-Only)
        peercert = gnutls_certificate_type_get_peers (session);
@@ -2336,20 +2540,21 @@ gtls_error cli_kdhsig_encode (gnutls_session_t session,
                                ENCTYPE_AES256_CTS_HMAC_SHA1_96,
                                &subkey);
                if (k5err != 0) {
-                       return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+                       return GNUTLS_E_ENCRYPTION_FAILED;
                }
                auth.subkey.keytype = qder2b_pack_int32 (dersubkey, subkey.enctype);
                auth.subkey.keyvalue.derptr = subkey.contents;
                auth.subkey.keyvalue.derlen = subkey.length;
+prange ("cli_K", subkey.contents, subkey.length);
        }
        //
        // Setup the client realm and principal name
-       auth.crealm.derptr = cmd->krb_tkt.client->realm.data;
-       auth.crealm.derlen = cmd->krb_tkt.client->realm.length;
-       auth.cname.name_type = qder2b_pack_int32 (dernametype, cmd->krb_tkt.client->type);
+       auth.crealm.derptr = cmd->krbid_cli->realm.data;
+       auth.crealm.derlen = cmd->krbid_cli->realm.length;
+       auth.cname.name_type = qder2b_pack_int32 (dernametype, cmd->krbid_cli->type);
        // The SEQUENCE OF with just one component is trivial to prepack
-       auth.cname.name_string.derptr = cmd->krb_tkt.client->data [0].data;
-       auth.cname.name_string.derlen = cmd->krb_tkt.client->data [0].length;
+       auth.cname.name_string.derptr = cmd->krbid_cli->data [0].data;
+       auth.cname.name_string.derlen = cmd->krbid_cli->data [0].length;
        //
        // Setup the Kerberos version number (5)
        auth.authenticator_vno = qder2b_pack_int32 (derv5, 5);
@@ -2363,75 +2568,84 @@ gtls_error cli_kdhsig_encode (gnutls_session_t session,
        auth.cusec = qder2b_pack_int32 (dercusec, now_us);
        //
        // Pack the decoded result into dec_authenticator
-       uint8_t auth_packer [] = { DER_PACK_rfc4120_Authenticator };
        size_t declen = der_pack (      auth_packer,
                                        (const dercursor *) &auth,
                                        NULL    // Measure length, no output yet
                                        );
-       uint8_t *decptr = malloc (declen);
+       uint8_t *decptr = gnutls_malloc (declen);
        if (decptr == NULL) {
                return GNUTLS_E_MEMORY_ERROR;
        }
        der_pack (                      auth_packer,
                                        (const dercursor *) &auth,
-                                       decptr);
+                                       decptr + declen);
+       krb5_free_keyblock_contents (krbctx_cli, &subkey);
+prangefull ("cli_A", decptr, declen);
        size_t rawlen;
        if (0 != krb5_c_encrypt_length (krbctx_cli,
-                                       cmd->krb_tkt.keyblock.enctype,
+                                       cmd->krb_key.enctype,
                                        declen,
                                        &rawlen)) {
-               return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+               gnutls_free (decptr);
+               return GNUTLS_E_ENCRYPTION_FAILED;
        }
-       uint8_t *rawptr = malloc (rawlen);
+       uint8_t *rawptr = gnutls_malloc (rawlen);
        if (rawptr == NULL) {
-               free (decptr);
+               gnutls_free (decptr);
                return GNUTLS_E_MEMORY_ERROR;
        }
        krb5_data decdata;
        krb5_enc_data rawdata;
        memset (&decdata, 0, sizeof (decdata));
+       memset (&rawdata, 0, sizeof (rawdata));
        decdata.data   = decptr;
        decdata.length = declen;
        rawdata.ciphertext.data   = rawptr;
        rawdata.ciphertext.length = rawlen;
        if (0 != krb5_c_encrypt (       krbctx_cli,
-                                       &cmd->krb_tkt.keyblock,
+                                       &cmd->krb_key,
                                        11 /* stealing key usage from AP-REQ */,
                                        NULL,
                                        &decdata,
                                        &rawdata)) {
-               return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+               gnutls_free (rawptr);
+               gnutls_free (decptr);
+               return GNUTLS_E_ENCRYPTION_FAILED;
        }
        //
        // Prepare the header information
        QDERBUF_INT32_T deretype;
        QDERBUF_UINT32_T derkvno;
        encrypted_data_t encdata;
-       encdata.etype = qder2b_pack_int32 (deretype, cmd->krb_tkt.keyblock.enctype);
-       //NOT// encdata.kvno  = qder2b_pack_int32 (derkvno,  cmd->krb_tkt.keyblock.kvno);
+       memset (&encdata, 0, sizeof (encdata));
+       encdata.etype = qder2b_pack_int32 (deretype, cmd->krb_key.enctype);
+       //NOT// encdata.kvno  = qder2b_pack_int32 (derkvno,  cmd->krb_key.kvno);
        encdata.cipher.derptr = rawdata.ciphertext.data;
        encdata.cipher.derlen = rawdata.ciphertext.length;
        //
        // Prepare for packing the header and rawdata as EncryptedData
-       uint8_t encdata_packer [] = { DER_PACK_rfc4120_EncryptedData };
        size_t enclen = der_pack (      encdata_packer,
                                        (const dercursor *) &encdata,
                                        NULL    // Measure length, no output yet
                                        );
-       uint8_t *encptr = malloc (enclen);
+       uint8_t *encptr = gnutls_malloc (enclen);
        if (encptr == NULL) {
+               gnutls_free (rawptr);
+               gnutls_free (decptr);
                return GNUTLS_E_MEMORY_ERROR;
        }
        der_pack (                      encdata_packer,
                                        (const dercursor *) &encdata,
-                                       encptr);
-       free (rawptr);
+                                       encptr + enclen);
+       gnutls_free (rawptr);
        //
        // Return our final verdict on the generation of the Authenticator
        dec_authenticator->data = decptr;
        dec_authenticator->size = declen;
        enc_authenticator->data = encptr;
        enc_authenticator->size = enclen;
+prange ("cli_D", decptr, declen);
+prange ("cli_E", encptr, enclen);
        return 0;
 }
 #endif
@@ -2442,12 +2656,169 @@ gtls_error cli_kdhsig_encode (gnutls_session_t session,
  * provided session hash and returns the decrypted authenticator.
  */
 #ifdef HAVE_TLS_KDH
-int srv_kdhsig_decode (gnutls_session_t session,
+static int srv_kdhsig_decode (gnutls_session_t session,
                        const gnutls_datum_t *enc_authenticator,
                        gnutls_datum_t *dec_authenticator,
                        gnutls_datum_t *hash,
                        int32_t *checksum_type) {
-       return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+       //
+       // Variables, sanity checks and initialisation
+       int k5err = 0;
+       struct command *cmd;
+       static const uint8_t encdata_packer [] = {
+               DER_PACK_rfc4120_EncryptedData, DER_PACK_END };
+       static const uint8_t auth_packer [] = {
+               DER_PACK_rfc4120_Authenticator, DER_PACK_END };
+       encrypted_data_t encdata;
+       cmd = (struct command *) gnutls_session_get_ptr (session);
+prange ("srv_E", enc_authenticator->data, enc_authenticator->size);
+       //
+       // Retrieve the session key and store it in cmd->krb_key.
+       //
+       // Prior setting of cmd->krb_key would be due to user-to-user mode
+       // having been setup with this as the server-supplied TGT key, in
+       // which case cmd->krb_key would need to be overwritten by the
+       // session key.
+       //
+       // When no prior cmd->krb_key is available, use the keytab to
+       // decode the client's ticket.
+       assert (gnutls_certificate_type_get_peers (session) == GNUTLS_CRT_KRB);
+       const gnutls_datum_t *certs;
+       unsigned int num_certs;
+       certs = gnutls_certificate_get_peers (cmd->session, &num_certs);
+       if (num_certs != 1) {
+               return GNUTLS_E_NO_CERTIFICATE_FOUND;
+       }
+       krb5_data krbcert;
+       krb5_ticket *tkt;
+       krbcert.data   = certs [0].data;
+       krbcert.length = certs [0].size;
+prange ("srv_C", certs [0].data, certs [0].size);
+       if (0 != krb5_decode_ticket (&krbcert, &tkt)) {
+               return GNUTLS_E_NO_CERTIFICATE_FOUND;
+       }
+       if (cmd->krb_key.contents != NULL) {
+               // user-to-user mode
+               k5err = krb5_decrypt_tkt_part (
+                                       krbctx_srv,
+                                       &cmd->krb_key,
+                                       tkt);
+               krb5_free_keyblock_contents (
+                                       krbctx_srv,
+                                       &cmd->krb_key);
+       } else {
+               // client-to-server mode
+               k5err = krb5_server_decrypt_ticket_keytab (
+                                       krbctx_srv,
+                                       krb_kt_srv,
+                                       tkt);
+       }
+       if (k5err == 0) {
+               k5err = krb5_copy_keyblock_contents (
+                                       krbctx_srv,
+                                       tkt->enc_part2->session,
+                                       &cmd->krb_key);
+       }
+       if (k5err == 0) {
+               k5err = krb5_copy_principal (
+                                       krbctx_srv,
+                                       tkt->enc_part2->client,
+                                       &cmd->krbid_cli);
+       }
+       if (k5err == 0) {
+               if (cmd->krbid_srv != NULL) {
+                       k5err = krb5_principal_compare (
+                                                       krbctx_srv,
+                                                       tkt->server,
+                                                       cmd->krbid_srv);
+                               // Server name changed since u2u setup => k5err
+               } else {
+                       k5err = krb5_copy_principal (
+                                                       krbctx_srv,
+                                                       tkt->server,
+                                                       &cmd->krbid_srv);
+               }
+       }
+       krb5_free_ticket (krbctx_srv, tkt);
+       if (k5err != 0) {
+               const char *errmsg = krb5_get_error_message (krbctx_srv, k5err);
+               tlog (TLOG_DAEMON, LOG_ERR, "Kerberos error in srv_kdhsig_decode: %s", errmsg);
+               krb5_free_error_message (krbctx_srv, errmsg);
+               return GNUTLS_E_NO_CERTIFICATE_FOUND;
+       }
+       //
+       // Harvest the EncryptedData fields from the enc_authenticator
+       dercursor enctransport;
+       enctransport.derptr = enc_authenticator->data;
+       enctransport.derlen = enc_authenticator->size;
+prangefull ("EncData2unpack", enctransport.derptr, enctransport.derlen);
+       memset (&encdata, 0, sizeof (encdata));
+       if (0 != der_unpack (           &enctransport,
+                                       encdata_packer,
+                                       (dercursor *) &encdata,
+                                       1)) {
+               tlog (TLOG_DAEMON, LOG_ERR, "Failed to der_unpack(EncryptedData) in server: %s", strerror (errno));
+               return GNUTLS_E_DECRYPTION_FAILED;
+       }
+       if (encdata.kvno.derptr != NULL) {
+               //TODO//NOTYET//ANDWHY// return GNUTLS_E_DECRYPTION_FAILED;
+       }
+       int32_t etype = qder2b_unpack_int32 (encdata.etype);
+       //
+       // Decrypt the EncryptedData fields into the dec_authenticator
+       krb5_enc_data rawdata;
+       krb5_data decdata;
+       memset (&rawdata, 0, sizeof (rawdata));
+       memset (&decdata, 0, sizeof (decdata));
+       rawdata.enctype = etype;
+       rawdata.ciphertext.data   = encdata.cipher.derptr;
+       rawdata.ciphertext.length = encdata.cipher.derlen;
+prange ("srv_R", encdata.cipher.derptr, encdata.cipher.derlen);
+       decdata.data   = dec_authenticator->data;
+       decdata.length = dec_authenticator->size;
+       if (0 != krb5_c_decrypt (       krbctx_srv,
+                                       &cmd->krb_key,
+                                       11 /* stealing key usage from AP-REQ */,
+                                       NULL,
+                                       &rawdata,
+                                       &decdata)) {
+               return GNUTLS_E_DECRYPTION_FAILED;
+       }
+       dec_authenticator->size = decdata.length;
+prange ("srv_D", decdata.data, decdata.length);
+       //
+       // Unpack the decrypted Authenticator
+       dercursor decsyntax;
+       decsyntax.derptr = decdata.data;
+       decsyntax.derlen = decdata.length;
+prangefull ("srv_A", decdata.data, decdata.length);
+       authenticator_t auth;
+       memset (&auth, 0, sizeof (auth));
+       if (0 != der_unpack (           &decsyntax,
+                                       auth_packer,
+                                       (dercursor *) &auth,
+                                       1)) {
+               tlog (TLOG_DAEMON, LOG_ERR, "Failed to der_unpack(Authenticator) in server: %s", strerror (errno));
+               return GNUTLS_E_DECRYPTION_FAILED;
+       }
+       //
+       // Validate the contents of the Authenticator
+       if (qder2b_unpack_int32 (auth.authenticator_vno) != 5) {
+               return GNUTLS_E_DECRYPTION_FAILED;
+       }
+       if (auth.cksum.checksum.derptr == NULL) {
+               return GNUTLS_E_DECRYPTION_FAILED;
+       }
+       if (auth.cksum.checksum.derlen < 16) {
+               return GNUTLS_E_DECRYPTION_FAILED;
+       }
+       //TODO// Optionally, for KDH-Only, ensure presence and size of a subkey
+       //
+       // Produce the requested content from the Authenticator and return
+       *checksum_type = qder2b_unpack_int32 (auth.cksum.cksumtype);
+       hash->data = auth.cksum.checksum.derptr;
+       hash->size = auth.cksum.checksum.derlen;
+       return 0;
 }
 #endif
 
@@ -2511,7 +2882,7 @@ static void valexp_1_start (void *vcmd, struct valexp *ve, char pred) {
 /* valexp_I_start -- validation function for the GnuTLS backend.
  * This function ensures that the remote peer provides an identity.
  * TODO: We should compare the hostname as well, or compare if in remoteid
- * TODO: We may need to support more than just X509/PGP certificates 
+ * TODO: We may need to support more than just X509/PGP certificates
  */
 static void valexp_I_start (void *vcmd, struct valexp *ve, char pred) {
        struct command *cmd = (struct command *) vcmd;
@@ -2694,9 +3065,11 @@ static void valexp_Dd_start (void *vcmd, struct valexp *ve, char pred) {
        case IPPROTO_UDP:
                proto = "udp";
                break;
+#ifndef WINDOWS_PORT
        case IPPROTO_SCTP:
                proto = "sctp";
                break;
+#endif
        default:
                goto setflagval;
        }
@@ -2791,9 +3164,9 @@ static void valexp_Oo_start (void *vcmd, struct valexp *ve, char pred) {
                        valflag = o2vf (online_globaldir_x509 (
                                        rid,
                                        raw->data, raw->size));
-#ifdef GNUTLS_CRT_KRB
+#ifdef HAVE_TLS_KDH
                } else if (cmd->remote_cert_type == GNUTLS_CRT_KRB) {
-                       // Kerberos is sufficiently "live" to always pass O
+                       // Kerberos is sufficiently "live" to be pass O
                        valflag = 1;
                        goto setvalflag;
 #endif
@@ -3067,8 +3440,14 @@ static gtls_error fetch_remote_credentials (struct command *cmd) {
        }
        //
        // Continue loading the certificate information: X.509, PGP, ...
+#ifdef HAVE_TLS_KDH
+       cmd->remote_cert_type = gnutls_certificate_type_get_peers (cmd->session);
+       certs = gnutls_certificate_get_peers (cmd->session, &num_certs);
+       // Note: server's certs _may_ be DER NULL due to mutual auth in Kerberos
+#else
        cmd->remote_cert_type = gnutls_certificate_type_get (cmd->session);
        certs = gnutls_certificate_get_peers (cmd->session, &num_certs);
+#endif
        if (certs == NULL) {
                num_certs = 0;
        }
@@ -3279,7 +3658,7 @@ gtls_error fetch_local_credentials (struct command *cmd) {
 fprintf (stderr, "DEBUG: otfcert retrieval returned %d\n", gtls_errno);
                        return gtls_errno;
                } else {
-fprintf (stderr, "DEBUG: otfcert retrieval returned GNUTLS_E_AGAIN, so skip it\n", gtls_errno);
+fprintf (stderr, "DEBUG: otfcert retrieval returned GNUTLS_E_AGAIN, so skip it\n");
                        gtls_errno = GNUTLS_E_SUCCESS;  // Attempt failed, ignore
                }
        }
@@ -3387,9 +3766,13 @@ fprintf (stderr, "DEBUG: otfcert retrieval returned GNUTLS_E_AGAIN, so skip it\n
 #endif
                tlog (TLOG_DB, LOG_DEBUG, "BDB entry has flags=0x%08x, so we (%04x/%04x) %s it", flags, lidrole, LID_ROLE_MASK, ok? "store": "skip ");
                if (ok) {
+                       if (cmd->lids [lidtype - LID_TYPE_MIN].data != NULL) {
+                               free (cmd->lids [lidtype - LID_TYPE_MIN].data);
+                       }
                        // Move the credential into the command structure
                        dbt_store (&creddata,
                                &cmd->lids [lidtype - LID_TYPE_MIN]);
+fprintf (stderr, "DEBUG: Storing cmd->lids[%d].data %p\n", lidtype-LID_TYPE_MIN, cmd->lids [lidtype-LID_TYPE_MIN].data);
                        found = 1;
                } else {
                        // Skip the credential by freeing its data structure
@@ -3420,7 +3803,6 @@ fprintf (stderr, "DEBUG: otfcert retrieval returned GNUTLS_E_AGAIN, so skip it\n
  * Return 1 for yes or 0 for no; this is used in priority strings.
  */
 static inline int lidtpsup (struct command *cmd, int lidtp) {
-       return 1;       //TODO// Can we decide if we needn't authenticate?
        return cmd->lids [lidtp - LID_TYPE_MIN].data != NULL;
 }
 
@@ -3459,37 +3841,54 @@ static int configure_session (struct command *cmd,
        //  - Configured security parameters (database? variable?)
        //  - CTYPEs, SRP, ANON-or-not --> fill in as + or - characters
        if (gtls_errno == GNUTLS_E_SUCCESS) {
-               char priostr [256];
+               char priostr [512];
+#ifdef HAVE_TLS_KDH
                snprintf (priostr, sizeof (priostr)-1,
                        // "NORMAL:-RSA:" -- also ECDH-RSA, ECDHE-RSA, ...DSA...
                        "NONE:"
-#ifdef HAVE_TLS_KDH
                        "%%ASYM_CERT_TYPES:"
-#endif
                        "+VERS-TLS-ALL:+VERS-DTLS-ALL:"
                        "+COMP-NULL:"
                        "+CIPHER-ALL:+CURVE-ALL:+SIGN-ALL:+MAC-ALL:"
                        "%cANON-ECDH:"
+                       "+ECDHE-KRB:" // +ECDHE-KRB-RSA:+ECDHE-KRB-ECDHE:" // opt?
                        "+ECDHE-RSA:+DHE-RSA:+ECDHE-ECDSA:+DHE-DSS:+RSA:" //TODO//
-                       "%cCTYPE-X.509:"
-                       "%cCTYPE-OPENPGP:"
-#ifdef HAVE_TLS_KDH
-                       "%cCTYPE-CLI-KRB:%cCTYPE-SRV-X.509:"
-#endif
+                       "+CTYPE-SRV-KRB:+CTYPE-SRV-X.509:+CTYPE-SRV-OPENPGP:"
+                       "%cCTYPE-CLI-KRB:"
+                       "%cCTYPE-CLI-X.509:"
+                       "%cCTYPE-CLI-OPENPGP:"
                        "%cSRP:%cSRP-RSA:%cSRP-DSS",
                        anonpre_ok                              ?'+':'-',
-                       lidtpsup (cmd, LID_TYPE_X509)           ?'+':'-',
-                       lidtpsup (cmd, LID_TYPE_PGP)            ?'+':'-',
-#ifdef HAVE_TLS_KDH
-                       lidtpsup (cmd, LID_TYPE_KRB5)           ?'+':'-',
-                       lidtpsup (cmd, LID_TYPE_KRB5)           ?'+':'-',
-#endif
+                       1 /* lidtpsup (cmd, LID_TYPE_KRB5)*/            ?'+':'-',
+                       1 /*lidtpsup (cmd, LID_TYPE_X509)*/             ?'+':'-',
+                       1 /*lidtpsup (cmd, LID_TYPE_PGP)*/              ?'+':'-',
                        //TODO// Temporarily patched out SRP
                        lidtpsup (cmd, LID_TYPE_SRP)            ?'+':'-',
                        lidtpsup (cmd, LID_TYPE_SRP)            ?'+':'-',
                        lidtpsup (cmd, LID_TYPE_SRP)            ?'+':'-');
-// strcpy (priostr, "NONE:+VERS-TLS-ALL:+MAC-ALL:+RSA:+AES-128-CBC:+SIGN-ALL:+COMP-NULL");  //TODO:TEST//
-// strcpy (priostr, "NONE:+VERS-TLS-ALL:+VERS-DTLS-ALL:+MAC-ALL:+RSA:+AES-128-CBC:+SIGN-ALL:+COMP-NULL");  //TODO:TEST//
+#else
+               // It's not possible to make good decisions on certificate type
+               // for both sides based on knowledge of local authentication
+               // abilities.  So we permit all (but would like to be subtler).
+               snprintf (priostr, sizeof (priostr)-1,
+                       // "NORMAL:-RSA:" -- also ECDH-RSA, ECDHE-RSA, ...DSA...
+                       "NONE:"
+                       "+VERS-TLS-ALL:+VERS-DTLS-ALL:"
+                       "+COMP-NULL:"
+                       "+CIPHER-ALL:+CURVE-ALL:+SIGN-ALL:+MAC-ALL:"
+                       "%cANON-ECDH:"
+                       "+ECDHE-RSA:+DHE-RSA:+ECDHE-ECDSA:+DHE-DSS:+RSA:" //TODO//
+                       "%cCTYPE-X.509:"
+                       "%cCTYPE-OPENPGP:"
+                       "%cSRP:%cSRP-RSA:%cSRP-DSS",
+                       anonpre_ok                              ?'+':'-',
+                       1               ?'+':'-',
+                       1               ?'+':'-',
+                       //TODO// Temporarily patched out SRP
+                       1               ?'+':'-',
+                       1               ?'+':'-',
+                       1               ?'+':'-');
+#endif
                tlog (TLOG_TLS, LOG_DEBUG, "Constructed priority string %s for local ID %s",
                        priostr, cmd->cmd.pio_data.pioc_starttls.localid);
                E_g2e ("Failed to set GnuTLS priority string",
@@ -3511,24 +3910,30 @@ static int configure_session (struct command *cmd,
  *  - TLS hints -- Server Name Indication
  *  - User hints -- local and remote identities provided
  */
-int srv_clienthello (gnutls_session_t session) {
+static int srv_clienthello (gnutls_session_t session, unsigned int htype, unsigned int post, unsigned int incoming, const gnutls_datum_t *msg) {
        struct command *cmd;
+       int gtls_errno = GNUTLS_E_SUCCESS;
        char sni [sizeof (cmd->cmd.pio_data.pioc_starttls.remoteid)]; // static
        size_t snilen = sizeof (sni);
-       int snitype;
-       int gtls_errno = GNUTLS_E_SUCCESS;
+       unsigned int snitype;
        char *lid;
 
+tlog (LOG_DAEMON, LOG_INFO, "Invoked %sprocessor for Client Hello, htype=%d, incoming=%d\n",
+               post ? "post" : "pre",
+               htype,
+               incoming);
+
 fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
 errno = 0;
 fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
+
+if (!post) {
        //
        // Setup a number of common references
        cmd = (struct command *) gnutls_session_get_ptr (session);
        if (cmd == NULL) {
                return GNUTLS_E_INVALID_SESSION;
        }
-       lid = cmd->cmd.pio_data.pioc_starttls.localid;
 
        //
        // Setup server-specific credentials and priority string
@@ -3538,10 +3943,20 @@ fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno),
        E_g2e ("Failed to reconfigure GnuTLS as a server",
                configure_session (cmd,
                        session,
-                       srv_creds, srv_credcount, 
+                       srv_creds, srv_credcount,
                        cmd->anonpre & ANONPRE_SERVER));
 fprintf (stderr, "DEBUG: Got gtls_errno = %d at %d\n", gtls_errno, __LINE__);
 
+} else {
+
+       //
+       // Setup a number of common references
+       cmd = (struct command *) gnutls_session_get_ptr (session);
+       if (cmd == NULL) {
+               return GNUTLS_E_INVALID_SESSION;
+       }
+       lid = cmd->cmd.pio_data.pioc_starttls.localid;
+
        //
        // Setup to ignore/request/require remote identity (from client)
 fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
@@ -3607,6 +4022,7 @@ fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno),
                sni [sizeof (sni) - 1] = '\0';
        }
 fprintf (stderr, "DEBUG: Got gtls_errno = %d at %d\n", gtls_errno, __LINE__);
+}
 
        //
        // Lap up any unnoticed POSIX error messages
@@ -3851,6 +4267,10 @@ static void cleanup_starttls_credentials (void) {
                case GNUTLS_CRD_SRP:
                        gnutls_srp_free_server_credentials (crd->cred);
                        break;
+               case GNUTLS_CRD_PSK:
+               case GNUTLS_CRD_IA:
+                       //TODO: not handled
+                       break;
                //TODO// case GNUTLS_CRD_KDH:
                //TODO//        gnutls_kdh_free_server_credentials (crd->cred);
                //TODO//        break;
@@ -3869,6 +4289,10 @@ static void cleanup_starttls_credentials (void) {
                case GNUTLS_CRD_SRP:
                        gnutls_srp_free_client_credentials (crd->cred);
                        break;
+               case GNUTLS_CRD_PSK:
+               case GNUTLS_CRD_IA:
+                       //TODO: not handled
+                       break;
                //TODO// case GNUTLS_CRD_KDH:
                //TODO//        gnutls_kdh_free_client_credentials (crd->cred);
                //TODO//        break;
@@ -4004,7 +4428,7 @@ fprintf (stderr, "DEBUG: Got a request to renegotiate existing TLS connection\n"
                //
                // First find the ctlkeynode_tls
                ckn = (struct ctlkeynode_tls *) ctlkey_find (cmd->cmd.pio_data.pioc_starttls.ctlkey, security_tls, cmd->clientfd);
-fprintf (stderr, "DEBUG: Got ckn == 0x%0x\n", (intptr_t) ckn);
+fprintf (stderr, "DEBUG: Got ckn == %p\n", (void *) ckn);
                if (ckn == NULL) {
                        tlog (TLOG_UNIXSOCK, LOG_ERR, "Failed to find TLS connection for renegotiation by its ctlkey");
                        send_error (replycmd, ESRCH, "Cannot find TLS connection for renegotiation");
@@ -4054,7 +4478,7 @@ fprintf (stderr, "DEBUG: pthread_join returned %d\n", errno);
        // that TLS has in this respect.  Maybe we'll capture it one giant loop
        // at some point, but for now that does not seem to add any relief.
        renegotiate:
-fprintf (stderr, "DEBUG: Renegotiating = %d, anonpost = %d, plainfd = %d, cryptfd = %d, flags = 0x%x, session = 0x%x, got_session = %d, lid = \"%s\", rid = \"%s\"\n", renegotiating, anonpost, plainfd, cryptfd, cmd->cmd.pio_data.pioc_starttls.flags, session, got_session, cmd->cmd.pio_data.pioc_starttls.localid, cmd->cmd.pio_data.pioc_starttls.remoteid);
+fprintf (stderr, "DEBUG: Renegotiating = %d, anonpost = %d, plainfd = %d, cryptfd = %d, flags = 0x%x, session = %p, got_session = %d, lid = \"%s\", rid = \"%s\"\n", renegotiating, anonpost, plainfd, cryptfd, cmd->cmd.pio_data.pioc_starttls.flags, session, got_session, cmd->cmd.pio_data.pioc_starttls.localid, cmd->cmd.pio_data.pioc_starttls.remoteid);
 
        //
        // If this is server renegotiating, send a request to that end
@@ -4139,7 +4563,7 @@ fprintf (stderr, "DEBUG: Client-side invocation flagged as wrong; compensated er
                        close (plainfd);
                        plainfd = -1;
                }
-fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+fprintf (stderr, "ctlkey_unregister under ckn=%p at %d\n", (void *)ckn, __LINE__);
                if (ckn != NULL) {      /* TODO: CHECK NEEDED? */
                        if (ctlkey_unregister (ckn->regent.ctlkey)) {
                                free (ckn);
@@ -4169,7 +4593,7 @@ fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
                while (anonpre_regjmp > 0) {
                        anonpre_regjmp = anonpre_regjmp >> 1;
                        cmp = strncasecmp (anonpre_registry [anonpre_regidx].service,
-                               cmd->cmd.pio_data.pioc_starttls.service,
+                               (const char *)cmd->cmd.pio_data.pioc_starttls.service,
                                TLSPOOL_SERVICELEN);
 fprintf (stderr, "DEBUG: anonpre_determination, comparing [%d] %s to %s, found cmp==%d\n", anonpre_regidx, anonpre_registry [anonpre_regidx].service, cmd->cmd.pio_data.pioc_starttls.service, cmp);
                        if (cmp == 0) {
@@ -4210,7 +4634,7 @@ fprintf (stderr, "DEBUG: anonpre_determination, comparing [%d] %s to %s, found c
                        close (plainfd);
                        plainfd = -1;
                }
-fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+fprintf (stderr, "ctlkey_unregister under ckn=%p at %d\n", (void *)ckn, __LINE__);
                if (ckn != NULL) { /* TODO: CHECK NEEDED? */
                        if (ctlkey_unregister (ckn->regent.ctlkey)) {
                                free (ckn);
@@ -4275,8 +4699,9 @@ if (!renegotiating) {     //TODO:TEST//
                                len++;
                        }
                        // If no usable remoteid was setup, ignore it
-                       if ((len + ofs > 0) && (len < 128)) {
+                       if ((len > ofs) && (len < 128)) {
                                cmd->cmd.pio_data.pioc_starttls.remoteid [sizeof (cmd->cmd.pio_data.pioc_starttls.remoteid)-1] = '\0';
+                               tlog (TLOG_TLS, LOG_DEBUG, "Sending ServerNameIndication \"%.*s\"", len - ofs, str + ofs);
                                E_g2e ("Client failed to setup SNI",
                                        gnutls_server_name_set (
                                                session,
@@ -4295,7 +4720,7 @@ fprintf (stderr, "DEBUG: Configuring client credentials\n");
                        configure_session (cmd,
                                session,
                                anonpost? NULL: cli_creds,
-                               anonpost?    0: cli_credcount, 
+                               anonpost?    0: cli_credcount,
                                cmd->anonpre & ANONPRE_CLIENT));
        }
        //
@@ -4304,8 +4729,10 @@ fprintf (stderr, "DEBUG: Configuring client credentials\n");
 fprintf (stderr, "DEBUG: Configuring for server credentials callback if %d==0\n", gtls_errno);
 if (!renegotiating) {  //TODO:TEST//
                if (gtls_errno == GNUTLS_E_SUCCESS) {
-                       gnutls_handshake_set_post_client_hello_function (
+                       gnutls_handshake_set_hook_function (
                                session,
+                               GNUTLS_HANDSHAKE_CLIENT_HELLO,
+                               GNUTLS_HOOK_BOTH,
                                srv_clienthello);
                }
 } //TODO:TEST//
@@ -4321,7 +4748,7 @@ fprintf (stderr, "DEBUG: Configuring server credentials (because it is not a cli
                                configure_session (cmd,
                                        session,
                                        anonpost? NULL: srv_creds,
-                                       anonpost?    0: srv_credcount, 
+                                       anonpost?    0: srv_credcount,
                                        cmd->anonpre & ANONPRE_SERVER));
                }
 #endif
@@ -4381,7 +4808,7 @@ if (renegotiating) {
                        send_error (replycmd, EIO, "Failed to prepare for TLS");
                }
                if (got_session) {
-fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
+fprintf (stderr, "gnutls_deinit (%p) at %d\n", (void *)session, __LINE__);
                        gnutls_deinit (session);
                        got_session = 0;
                }
@@ -4390,7 +4817,7 @@ fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
                        close (plainfd);
                        plainfd = -1;
                }
-fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+fprintf (stderr, "ctlkey_unregister under ckn=%p at %d\n", (void *)ckn, __LINE__);
                if (ckn != NULL) {      /* TODO: CHECK NEEDED? */
                        if (ctlkey_unregister (ckn->regent.ctlkey)) {
                                free (ckn);
@@ -4455,8 +4882,17 @@ fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
                (gnutls_error_is_fatal (gtls_errno) == 0));
        //
        // Handshake done -- initialise remote_xxx, vfystatus, got_remoteid
-       E_g2e ("Failed to retrieve peer credentials",
-                       fetch_remote_credentials (cmd));
+       if ((gtls_errno == 0) && !(cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_IGNORE_REMOTEID)) {
+               // We want to try to authenticate the peer
+               E_g2e ("Failed to retrieve peer credentials",
+                               fetch_remote_credentials (cmd));
+               if (gtls_errno == GNUTLS_E_AUTH_ERROR) {
+                       if (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_REQUEST_REMOTEID) {
+                               // We do not _require_ authentication of the peer
+                               gtls_errno = 0;
+                       }
+               }
+       }
        if (gtls_errno == 0) {
                const gnutls_datum_t *certs;
                unsigned int num_certs;
@@ -4514,7 +4950,7 @@ fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
                        //TODO:ELSEWHERE// E_g2e ("Failed to reconfigure GnuTLS without anonymous precursor",
                                //TODO:ELSEWHERE// configure_session (cmd,
                                        //TODO:ELSEWHERE// session,
-                                       //TODO:ELSEWHERE// NULL, 0, 
+                                       //TODO:ELSEWHERE// NULL, 0,
                                        //TODO:ELSEWHERE// 0));
                        // We do not want to use ANON-DH if the flag
                        // ANONPRE_EXTEND_MASTER_SECRET is set for the protocol
@@ -4555,7 +4991,7 @@ fprintf (stderr, "DEBUG: Prior to valexp, gtls_errno = %d\n", gtls_errno);
                // Setup for validation expression runthrough
                cmd->valexp_result = -1;
                if ((cmd->trust_valexp != NULL) && (0 != strcmp (cmd->trust_valexp, "1"))) {
-fprintf (stderr, "DEBUG: Trust valexp \"%s\" @ 0x%016x\n", cmd->trust_valexp, (uint64_t) cmd->trust_valexp);
+fprintf (stderr, "DEBUG: Trust valexp \"%s\" @ %p\n", cmd->trust_valexp, (void *) cmd->trust_valexp);
                        valexp_conj [valexp_conj_count++] = cmd->trust_valexp;
                }
                if (cmd->lids [LID_TYPE_VALEXP - LID_TYPE_MIN].data != NULL) {
@@ -4570,7 +5006,7 @@ fprintf (stderr, "DEBUG: Trust valexp \"%s\" @ 0x%016x\n", cmd->trust_valexp, (u
                                &lid_valexp,
                                &ignored.data,
                                &ignored.size);
-fprintf (stderr, "DEBUG: LocalID valexp \"%s\" @ 0x%016x (ok=%d)\n", lid_valexp, (uint64_t) lid_valexp, ok);
+fprintf (stderr, "DEBUG: LocalID valexp \"%s\" @ %p (ok=%d)\n", lid_valexp, (void *) lid_valexp, ok);
                        if (ok && (lid_valexp != NULL)) {
                                valexp_conj [valexp_conj_count++] = lid_valexp;
                        } else {
@@ -4585,7 +5021,7 @@ fprintf (stderr, "DEBUG: Number of valexp is %d, gtls_errno=%d\n", valexp_conj_c
                                valexp_conj,
                                have_starttls_validation (),
                                (void *) cmd);
-fprintf (stderr, "DEBUG: Registered to verun = 0x%016x\n", (uint64_t) verun);
+fprintf (stderr, "DEBUG: Registered to verun = %p\n", (void *) verun);
                        if (verun == NULL) {
                                gtls_errno = GNUTLS_E_AUTH_ERROR;
                        }
@@ -4603,7 +5039,7 @@ fprintf (stderr, "DEBUG: valexp returns NEGATIVE result\n");
                        }
 else fprintf (stderr, "DEBUG: valexp returns POSITIVE result\n");
                        valexp_unregister (verun);
-fprintf (stderr, "DEBUG: Unregistered verun 0x%016x\n", (uint64_t) verun);
+fprintf (stderr, "DEBUG: Unregistered verun %p\n", (void *) verun);
                }
        }
 
@@ -4611,6 +5047,7 @@ fprintf (stderr, "DEBUG: Unregistered verun 0x%016x\n", (uint64_t) verun);
        // Cleanup any prefetched identities
        for (i=LID_TYPE_MIN; i<=LID_TYPE_MAX; i++) {
                if (cmd->lids [i - LID_TYPE_MIN].data != NULL) {
+fprintf (stderr, "DEBUG: Freeing cmd->lids[%d].data %p\n", i-LID_TYPE_MIN, (void *)(cmd->lids [i-LID_TYPE_MIN].data));
                        free (cmd->lids [i - LID_TYPE_MIN].data);
                }
        }
@@ -4628,10 +5065,15 @@ fprintf (stderr, "DEBUG: Unregistered verun 0x%016x\n", (uint64_t) verun);
                krb5_free_keyblock_contents (krbctx_srv, &cmd->krb_key);
                memset (&cmd->krb_key, 0, sizeof (cmd->krb_key));
        }
-       if (cmd->krb_tkt.client != NULL) {
+       if (cmd->krbid_srv != NULL) {
+               // RATHER BLUNT: It shouldn't matter which krbctx_ is used...
+               krb5_free_principal (krbctx_srv, cmd->krbid_srv);
+               cmd->krbid_srv = NULL;
+       }
+       if (cmd->krbid_cli != NULL) {
                // RATHER BLUNT: It shouldn't matter which krbctx_ is used...
-               krb5_free_cred_contents (krbctx_srv, &cmd->krb_tkt);
-               memset (&cmd->krb_tkt, 0, sizeof (cmd->krb_tkt));
+               krb5_free_principal (krbctx_srv, cmd->krbid_cli);
+               cmd->krbid_cli = NULL;
        }
 
 #if 0
@@ -4681,7 +5123,7 @@ fprintf (stderr, "DEBUG: Unregistered verun 0x%016x\n", (uint64_t) verun);
                        free (preauth);
                }
                if (got_session) {
-fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
+fprintf (stderr, "gnutls_deinit (%p) at %d\n", (void *)session, __LINE__);
                        gnutls_deinit (session);
                        got_session = 0;
                }
@@ -4690,7 +5132,7 @@ fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
                        close (plainfd);
                        plainfd = -1;
                }
-fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+fprintf (stderr, "ctlkey_unregister under ckn=%p at %d\n", (void *)ckn, __LINE__);
                if (ckn != NULL) {      /* TODO: CHECK NEEDED? */
                        if (ctlkey_unregister (ckn->regent.ctlkey)) {
                                free (ckn);
@@ -4721,12 +5163,12 @@ fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
                                free (preauth);
                        }
                        if (got_session) {
-fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
+fprintf (stderr, "gnutls_deinit (%p) at %d\n", (void *)session, __LINE__);
                                gnutls_deinit (session);
                                got_session = 0;
                        }
                        close (cryptfd);
-fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+fprintf (stderr, "ctlkey_unregister under ckn=%p at %d\n", (void *)ckn, __LINE__);
                        if (ckn) {      /* TODO: CHECK NEEDED? PRACTICE=>YES */
                                if (ctlkey_unregister (ckn->regent.ctlkey)) {
                                        free (ckn);
@@ -4749,12 +5191,12 @@ fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
                        free (preauth);
                }
                if (got_session) {
-fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
+fprintf (stderr, "gnutls_deinit (%p) at %d\n", (void *)session, __LINE__);
                        gnutls_deinit (session);
                        got_session = 0;
                }
                close (cryptfd);
-fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+fprintf (stderr, "ctlkey_unregister under ckn=%p at %d\n", (void *)ckn, __LINE__);
                if (ckn != NULL) {      /* TODO: CHECK NEEDED? */
                        if (ctlkey_unregister (ckn->regent.ctlkey)) {
                                free (ckn);
@@ -4844,7 +5286,7 @@ fprintf (stderr, "DEBUG: Goto renegotiate with cmd.lid = \"%s\" and orig_cmd.lid
                        // already have been freed if the ctlfd was closed
                        // and the connection could not continue detached
                        // (such as after forking it).
-fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+fprintf (stderr, "ctlkey_unregister under ckn=%p at %d\n", (void *)ckn, __LINE__);
                        if (ctlkey_unregister (orig_starttls.ctlkey)) {
                                free (ckn);
                        }
@@ -4862,7 +5304,7 @@ fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
        close (cryptfd);
        cleanup_any_remote_credentials (cmd);
        if (got_session) {
-fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
+fprintf (stderr, "gnutls_deinit (%p) at %d\n", (void *)session, __LINE__);
                gnutls_deinit (session);
                got_session = 0;
        }
@@ -4872,7 +5314,7 @@ fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
 
 
 /*
- * The starttls function responds to an application's request to 
+ * The starttls function responds to an application's request to
  * setup TLS for a given file descriptor, and return a file descriptor
  * with the unencrypted view when done.  The main thing done here is to
  * spark off a new thread that handles the operations.
@@ -4932,13 +5374,13 @@ void starttls_prng (struct command *cmd) {
                if (strlen (pf) != in1len) {
                        continue;
                }
-               if (strcmp (pf, in1) != 0) {
+               if (strcmp (pf, (const char *)in1) != 0) {
                        continue;
                }
        }
        if (*prefixes == NULL) {
                // RFC 5705 defines a private-use prefix "EXPERIMENTAL"
-               if ((in1len <= 12) || (strncmp (in1, "EXPERIMENTAL", 12) != 0)) {
+               if ((in1len <= 12) || (strncmp ((const char *)in1, "EXPERIMENTAL", 12) != 0)) {
                        err = 1;
                }
        }
@@ -4966,9 +5408,10 @@ void starttls_prng (struct command *cmd) {
        errno = 0;
        E_g2e ("GnuTLS PRNG based on session master key failed",
                gnutls_prf_rfc5705 (ckn->session,
-                       in1len, in1,
-                       (in2len >= 0)? in2len: 0, (in2len >= 0) ? in2: NULL,
-                       prnglen, prng->buffer));
+                       in1len, (const char *)in1,
+                       (in2len >= 0)? in2len: 0,
+                       (const char *)((in2len >= 0) ? in2: NULL),
+                       prnglen, (char *)prng->buffer));
        err = err || (errno != 0);
        //
        // Wipe temporary data / buffers for security reasons
@@ -5022,7 +5465,7 @@ gtls_error certificate_onthefly (struct command *cmd) {
                cmd->lids [LID_TYPE_X509 - LID_TYPE_MIN].data = NULL;
                cmd->lids [LID_TYPE_X509 - LID_TYPE_MIN].size = 0;
        }
-       
+
        //
        // Create an empty certificate
        E_g2e ("Failed to initialise on-the-fly certificate",
@@ -5065,7 +5508,7 @@ gtls_error certificate_onthefly (struct command *cmd) {
        //TODO:      gnutls_x509_crt_set_key_usage
        //TODO:SKIP? gnutls_x509_crt_set_ca_status
        for (i=0; i < svcusage_registry_size; i++) {
-               if (strcmp (svcusage_registry [i].service, cmd->cmd.pio_data.pioc_starttls.service) == 0) {
+               if (strcmp (svcusage_registry [i].service, (const char *)(cmd->cmd.pio_data.pioc_starttls.service)) == 0) {
                        const char **walker;
                        E_g2e ("Failed to setup basic key usage during on-the-fly certificate creation",
                                gnutls_x509_crt_set_key_usage (otfcert, svcusage_registry [i].usage));
@@ -5112,7 +5555,7 @@ gtls_error certificate_onthefly (struct command *cmd) {
                        // This is as expected, now .size will have been set
                        gtls_errno = GNUTLS_E_SUCCESS;
                } else {
-                       if (gtls_errno = GNUTLS_E_SUCCESS) {
+                       if (gtls_errno == GNUTLS_E_SUCCESS) {
                                // Something must be wrong if we receive OK
                                gtls_errno = GNUTLS_E_INVALID_REQUEST;
                        }
@@ -5135,12 +5578,12 @@ gtls_error certificate_onthefly (struct command *cmd) {
                cmd->lids [LID_TYPE_X509 - LID_TYPE_MIN].data = ptr;
                * (uint32_t *) ptr = htonl (LID_TYPE_X509 | LID_ROLE_BOTH);
                ptr += 4;
-               strcpy (ptr, onthefly_p11uri);
+               strcpy ((char *)ptr, onthefly_p11uri);
                ptr += strlen (onthefly_p11uri) + 1;
                restsz = cmd->lids [LID_TYPE_X509 - LID_TYPE_MIN].size - 4 - strlen (onthefly_p11uri) - 1;
                E_g2e ("Failed to export on-the-fly certificate as a credential",
                        gnutls_x509_crt_export (otfcert, GNUTLS_X509_FMT_DER, ptr, &restsz));
-char *pembuf [10000];
+char pembuf [10000];
 size_t pemlen = sizeof (pembuf) - 1;
 int exporterror = gnutls_x509_crt_export (otfcert, GNUTLS_X509_FMT_PEM, pembuf, &pemlen);
 if (exporterror == 0) {