Incompatible API changes, tmp version: STARTTLS flags overhaul
authorRick van Rein <rick@openfortress.nl>
Tue, 10 Nov 2015 12:23:34 +0000 (12:23 +0000)
committerRick van Rein <rick@openfortress.nl>
Tue, 10 Nov 2015 12:23:34 +0000 (12:23 +0000)
* Policy settings are all removed from flags
* SNI sending is now the default, disable by flag
* Anonymising Precursor implemented for suitable protocols (but not activated)
* New flag permits domains to authenticate on behalf of users
* Added a renegotiation request flag to the STARTTLS command
* Supported automated renegotiation when initiated by remote
* SIGCONT renegotiates TLS handshake in testcli, testsrv
* Defined (but not implemented) flags for on-the-fly certificates and delaygen

20 files changed:
.gitignore
doc/anonymising-precursor.md [new file with mode: 0644]
doc/p2p-tls.md [new file with mode: 0644]
etc/tlspool.conf
include/tlspool/commands.h
include/tlspool/internal.h
lib/libtlspool.c
lib/tlspool.py
src/config.c
src/ctlkey.c
src/donai.c
src/service.c
src/starttls.c
tool/Makefile.am
tool/ircproxy-privmsg-starttls.py
tool/key-exchange-tlsmail.py
tool/testcli.c
tool/testpeer.c [new file with mode: 0644]
tool/testsrv.c
tool/tlstunnel.c

index eb01607..7143bc1 100644 (file)
@@ -2,3 +2,4 @@
 *.o
 lib*.so
 lib*.a
+*.db
diff --git a/doc/anonymising-precursor.md b/doc/anonymising-precursor.md
new file mode 100644 (file)
index 0000000..fa74f5b
--- /dev/null
@@ -0,0 +1,144 @@
+TLS Pool and Anoymising Precursors
+==================================
+
+>   *The “Anonymising Precursor” is a new idea introduced by the TLS Pool.  It
+>   works just as the rest though, encapsulating the knowledge into the pool and
+>   making decisions on rather straightforward information (namely, service
+>   name and the role that is being played).*
+
+What is an Anonymising Precursor?
+---------------------------------
+
+While setting up a TLS connection, a lot of information is exchanged without
+encryption.  This includes credentials such as certificates, containing not only
+names but also keys (which are excellent long-term identifiers) for their
+owners.  An anonymising precursor is a prior phase to the TLS handshake to setup
+a layer of encryption without authentication.
+
+Once established, the a secure renegotiation can be done to authenticate parties
+and return the TLS connection with the same security, but with less visibility
+of identifying information.
+
+What does this add to TLS security?
+-----------------------------------
+
+The first phase is an ANON-DH exchange.  This is known to be open to
+man-in-the-middle attacks, so a third party might be sitting in between the end
+points that setup the TLS connection.  The properties of secure renegiation
+however, ensure that such a situation will be detected in retrospect.  This
+means that an attacker will be noticed, and that no silent observation of
+identifying information is possible.
+
+Why can this not be applied in general?
+---------------------------------------
+
+The TLS Pool will always followup on an ANON-DH phase with whatever
+authenticating cipher suite it deems necessary, but the remote party may
+use another implementation, and return from the TLS handshake phase after a mere
+ANON-DH phase (but still support the renegotiation).
+
+Between such ANON-DH phase and a renegotiation, information may be passed around
+that would seem okay, but that is in fact not authenticated.  This happens in
+the above situation on remote parties, but it heavily depends on the service
+whether this could spell trouble.
+
+Consider SMTP; the server sends a banner to welcome the recipient, but that is
+all.  If this got modified by an intermediate it could not do harm.  After the
+banner, the other party must act.  This service is absolutely safe to run with
+ANON-DH.
+
+Reasoning similar to SMTP applies to POP3.
+
+IMAP is more interesting; it can still use ANON-DH, but requires some more
+care and attention and
+it does show that not everything can be automatically
+secured with ANON-DH prefixing.  The initial banner by the IMAP server includes
+features supported on the server, and an intermediary could modify this
+information to establish a service degredation.  Usually however, it is not
+considered a privacy violation if this information is shown to this
+intermediary.  This means that we can permit ANON-DH, provided that either
+(1) the banner is not sent before the second handshake is complete, or
+(2) the TLS stack detects intermediate changes after the handshake is complete,
+which requires the common RFC 7627 "extended master secret" and RFC 5746
+"secure renegotiation" extensions.
+
+For IMAP, the reasonable thing to do is to let the server refuse ANON-DH
+if it does not provide these facilities, or when the client does not offer
+them in its Client Hello.  Alternatively, it may offer ANON-DH in either
+case, but always hold back on sending the banner before the second handshake
+has completed (which is what the TLS Pool will do).  The server thereby
+protects the information that it sends.  The client should validate that
+both extensions are being used by the server *if* it received any data
+before the second handshake is complete.
+
+What this demonstrates, is that protocol-specific knowledge is needed to
+decide whether and how to handle Anonymous Precursors.  The TLS Pool is provided
+with a service name so it is able to decide on that.  This extends the view
+that technical detail is ideally integrated into the TLS Pool to relieve the
+application from such complications -- and the involved potential of being
+subjected to security attacks.
+
+Another interesting protocol is HTTP.  As soon as the client has connected
+to a server over TLS, it will send a request, holding potentially private
+information that also needs to be authenticated, so it needs better protection
+than the IMAP banner which only needed authentication (even in hindsight).
+This means that the reasoning from IMAP cannot be applied.  Still, as it
+turns out, HTTP can in practice make good use of ANON-DH.
+
+For an HTTP client, the reasoning is quite simple; it should not propose
+ANON-DH unless it is going to initiate renegotiation before sending the request.
+This is generally possible to enforce in the client code; the TLS Pool
+handles the Anonymous Precursor in the same command as the customary
+continuation into server authentication, so the HTTP application will not
+be hinted at sending the request before the second handshake is complete.
+
+The HTTP server is another matter; it may assume that the client handles
+its own privacy well enough by implementing the foregoing mechanism.  However
+when the server requires authentication, it should ensure the authenticity
+of any request data sent before the second handshake is complete.  It could
+reason as for IMAP, leaving the privacy matter to the client, but that would
+mean that extended master secrets are required for a protocol that is not
+known for its willingness to mature towards new developments; HTTP is part
+of many code bases and not all of them are easy to update.  So in the specific
+case of HTTP, we lean towards not supporting it on the server when the
+client identity is required.
+
+Note however, that any TLS endpoint that does not desire the identity of
+its remote peer may always accept ANON-DH without risk.  This happens to be
+true for the majority of HTTP implementations; even when client identity is
+requested or required, a common HTTP server would start without asking for
+it and then, based on the path that the client sends in a request, renegotiate
+TLS and asking for the client ID.  Since ANON-DH only precedes the first
+handshake, it will be common to find HTTP servers that can permit ANON-DH
+prefixes for this general reason.
+
+
+Registry for Protocols
+----------------------
+
+By default, the TLS Pool will not offer to initiate ANON-DH as an initial phase.
+Only when the service specified during the STARTTLS exchange is known to work,
+it may start in this vain.  And even then, it will distinguish the choice
+between client and server roles; and it will correctly handle connections that
+may be either.
+
+Flags that may influence this decision are those that indicate that the remote
+identity is not important to the application; if this is the case, then
+unauthenticated traffic from the remote does not matter either.  Note that the
+identity of clients is unknown in many TLS connections; the most common use
+cases call for an authenticated server and an unauthenticated client, to provide
+for situations with any clients coming from anywhere.
+
+The code for the TLS Pool contains a registry for service, leading to their
+support for client and/or server roles.  This registry is openly discussed on
+the mailing list, found on <http://lists.arpa2.org/mailman/listinfo/tls-pool> —
+please go there to propose changes to the list, and be open for a discussion
+before the change is accepted.
+
+An invalid service name (according to RFC 6335) has been created through the
+inclusion of underscores.  This name can be used to explicitly
+permit an Anonymous Precursor to a generic protocol.  The name is
+`generic_anonpre`.  Use this as a service name for generic protocols that
+have no formally registered service name but that do permit the
+Anonymous Precursor phase.
+
diff --git a/doc/p2p-tls.md b/doc/p2p-tls.md
new file mode 100644 (file)
index 0000000..610106a
--- /dev/null
@@ -0,0 +1,104 @@
+Peer-to-Peer TLS
+================
+
+>   *The TLS Pool is prepared for support for peer-to-peer TLS.  This is a new
+>   idea, so it needs some guidance.*
+
+Many network protocols know which side acts as a server and which as a client,
+in terms of setting up the connection.  TLS needs this information.  There are
+however protocols that have no such direct separation of roles; symmetric
+protocols such as MSRP or generally peer-to-peer protocols work perfectly fine
+without appointing a party as the client, and the other as a server.  The fact
+that TLS needs to know this information is not helpful in such circumstances.
+
+One might argue that TCP connections are client-server connections, but this
+would overlook two facts.  UDP connections are not necessarily client-server
+connections and in fact TCP has always supported two active sides getting
+together.  This principle is embedded in NAT routers, which are founded on the
+TCP state diagrams.  Finally, SCTP has also been designed to permit two active
+sides contacting each other at the same time.  Clearly, the stringent
+appointment of client and server roles is only a strict necessity for the TLS
+stack.  Peer-to-peer TLS resolves this matter.
+
+What does peer-to-peer TLS do?
+------------------------------
+
+The active participant in a TLS connection starts off by sending a ClientHello
+record.  This is normally answered by a ServerHello.  In case of peer-to-peer
+TLS connection, an additional form is allowed, namely one where both sides send
+a ClientHello, and one of these is discarded as if it was never sent.  The
+discarding recipient then responds with a ServerHello.
+
+The tie-breaker that helps to decide which party should continue in the role of
+a client and which should continue as a server is an extension that is included
+in the ClientHello.  This extension from each side introduces a random number of
+sufficient size to make it practically impossible to generate the same random
+number on both ends.  The sender of the lower value ends up as the client, and
+the sender of the higher value ends up as the server.  The case where random
+number is the same on both sides is considered a refusal of the attempted TLS
+setup and leads to connection breakdown.  The random numbers have a fixed
+minimum and maximum value; these values are used when a ClientHello wants to
+indicate that it only wants to be a client, or that it only wants to be a
+server.
+
+How does this impact flags during STARTTLS?
+-------------------------------------------
+
+When invoking the STARTTLS command on a TLS Pool, the possible roles for the
+local and remote end must be setup; each may be flagged suitable as a client
+and/or a server.  A protocol that is strictly client-server will set only the
+client role on one end and only the server role on the other end; a protocol
+that is symmetric would send both roles on both end points.
+
+Where traditional TLS stack design requires server flags in server certificates,
+this is turned into a policy option under the TLS Pool.  The application does
+not require such properties from the TLS Pool.
+
+One thing that is truly important however, is whether the remote identity was
+authenticated, and whether or not a local identity may be shared.  This
+information is mirrorred, and is therefore suitable for the more flexible setup
+of a p2p TLS protocol.  The requirements to this end are setup in flags during
+STARTTLS; this is done in terms of local and remote properties, rather than in
+terms of what the client and server responsibilities are.  In the end, it does
+not matter if the remote authenticates with a client certificate or with a
+server certificate, as long as it proves its identity to be authentic, is the
+idea.
+
+Details of implementation with GnuTLS
+-------------------------------------
+
+GnuTLS needs to be setup differently depending on whether it will be acting
+as a client, or as a server.  Think of such things as the priority string
+and the credentials.  The general structure uses a callback function for
+the reception of a ClientHello:
+
+       clienthello () {
+               ...
+               ...setup GnuTLS session as a server...
+               ...
+       }
+
+       starttls () {
+               ...
+               if (tlsmodes & CLIENT) {
+                       ...setup GnuTLS session as a client...
+               }
+               if (tlsmodes & SERVER) {
+                       gnutls_handshake_set_post_client_hello_function (
+                               session,
+                               clienthello);
+               }
+               ...
+               ...perform the handshake...
+               ...
+       }
+
+The reasoning is that the client setup should be available when it is
+*possible* that the TLS session will act as a client, and that there is
+time enough to overwrite this with server settings when a ClientHello is
+received.  Indeed, while in `clienthello()`, the soon-to-be server has
+not yet sent out anything and it is only at this point that it will
+always require the server setup to be established.  The clearance of
+previously made client settings is not a waste; it was actually needed
+if the session was willing to be a client (and only then).
+
index 6ef336a..07e5ee9 100644 (file)
@@ -179,6 +179,58 @@ pkcs11_token pkcs11:manufacturer=SoftHSM%20project;model=SoftHSM%20v2
 tls_dhparamfile ../testdata/tlspool-dh-params.pkcs3
 
 #
+# The TLS Pool may welcome an Anonymous Precursor for services whose
+# protocol would not be at any danger.  There is a theoretic chance of
+# bytes being sent between the anonymous encryption establishment and
+# the secure renegotiation with authentication.  Such unauthenticated
+# data is known to not be a problem to the TLS Pool, but its size should
+# be kept limited; it normally is a banner at best.  The parameter
+# tls_maxpreauth defines the maximum permissable number of bytes for any
+# such unauthenticated data.
+#
+
+tls_maxpreauth 32768
+
+#
+# The TLS Pool can sign connections with on-the-fly certificates.
+# These are signed by a configured cert and key pair.  The subject name
+# in the generated certificate will match the localid supplied by the
+# application and/or lidentry callback, and other certificate fields
+# (such as Extended Key Usage) may be supplied for the IANA-standardised
+# service name supplied along with it.  Certificates last from 2 mins
+# before the signing time to 3 minutes thereafter and have no CRL or OCSP.
+#
+# The use of this is to setup locally signed connections, possibly for
+# (captive) proxies for HTTPS or other TLS applications.  This can only be
+# done on controlled (local) networks, where it is feasible to install the
+# signing certificate as an actual signing certificate.
+#
+# There is no strict reason why the signing certificate pair must be a
+# root certificate, even though this is the most likely setup.  It is also
+# possible to setup an intermediate certificate, as long as clients can
+# locally find the root and intermediate certificates that they need to
+# validate the on-the-fly certificate.  This is because the chain sent
+# will not contain the certificate specified as _signcert below, nor any
+# certificate higher up in the hierarchy.
+#
+# The value of tls_onthefly_signcert is the file:   URI of its DER certificate;
+# the value of tls_onthefly_signkey  is the pkcs11: URI of its private key.
+#
+# These settings are considered static, that is they are not relaoded on
+# a regular basis, nor is there support for rollover, in light of the
+# investment needed to install the new root certificates.  This means that
+# a new setting requires a restart of the TLS Pool.
+#
+# TODO: The present version of the TLS Pool does not implement this
+# behaviour yet, except for the flags that an application can use to call
+# for it.  When using it under the present version however, the result
+# will be as if this configuration had not been supplied.
+#
+
+# tls_onthefly_signcert file:/etc/tls/certs/tlspool-onthefly-signing.der
+# tls_onthefly_signkey  pkcs11:model=MODEL;manufacturer=MANUF;serial=SERIAL;token=TOKEN;id=OBJID;object=OBJLABEL;object-type=private
+
+#
 # The TLS Pool uses a local LDAP proxy which resolves distinguishedNames
 # ending in dc ,dc to remote LDAP servers.  It should also store or find
 # local public information, such as OpenPGP keys and X.509 certificates.
index 2c985c4..adc0694 100644 (file)
@@ -8,14 +8,14 @@
 #include <stdint.h>
 
 
-#define TLSPOOL_IDENTITY_TMP   "20150527tlspool@tmp.vanrein.org"
+#define TLSPOOL_IDENTITY_TMP   "20150824tlspool@tmp.vanrein.org"
 
 
 /****************************** STRUCTURES *****************************/
 
 
 #define TLSPOOL_CTLKEYLEN 16
-#define TLSPOOL_SERVICELEN 32
+#define TLSPOOL_SERVICELEN 16
 #define TLSPOOL_PRNGBUFLEN 350
 #define TLSPOOL_TIMEOUT_DEFAULT 0
 #define TLSPOOL_TIMEOUT_INFINITE (~(uint32_t)0)
@@ -207,9 +207,6 @@ typedef struct pioc_starttls starttls_t;
 #define PIOC_STARTTLS_LOCALID_V2               0x00000028
 
 
-/* TODO: Possibly support renegotiation, like for explicit authn */
-
-
 /* The PIN entry command.  The data stored in tlscmd_pinentry determines
  * what happens exactly.  When sent to the TLS Pool it can provide a
  * non-empty PIN, which only makes sense in response to a PIOC_PINENTRY_V1
@@ -366,66 +363,24 @@ typedef struct pioc_starttls starttls_t;
 #define PIOF_STARTTLS_REMOTEROLE_PEER          0x0000000c
 
 
-#if 0 /* WILL NOT IMPLEMENT -- WILL REMOVE SOON */
-
-/* PIOF_STARTTLS_REQUIRE_DNSSEC tells the TLS Pool that DNSSEC must be
- * used for all information in DNS; so, a self-acclaimed I-can-do-without
- * domain is no longer permitted to connect over TLS.  The TLS Pool may
- * rely on an external resolver to properly set the AD bits.
- */
-#define PIOF_STARTTLS_REQUIRE_DNSSEC           0x00000010
-
-
-/* PIOF_STARTTLS_TIGHTEN_LDAP tells the TLS Pool that LDAP connections must
- * be secured through TLS.  In addition, the certificate used by LDAP will
- * be verified as is normally done for domain certificates.  Normally, that
- * means it must be acknowledged in a TLSA record.  Users and domains from
- * LDAP servers that do not live up to this are no longer trusted as peers
- * on grounds of their occurrence in LDAP alone.
- */
-#define PIOF_STARTTLS_REQUIRE_LDAP_CERT                0x00000020
-#define PIOF_STARTTLS_REQUIRE_LDAP_DANE                0x00000040
-#define PIOF_STARTTLS_REQUIRE_LDAP_SECURITY    0x00000060
-
-
-/* PIOF_STARTTLS_SKIP_EXT_AUTHN tells the TLS Pool that no external
- * authentication is needed on top of the normal operations of the
- * TLS Pool.  Usually, if an external authentication source is configured,
- * it will be RADIUS.  If it is not even configured, then this flag is of
- * no consequence.
- */
-#define PIOF_STARTTLS_SKIP_EXT_AUTHN           0x00000080
-
-
-/* PIOF_STARTTLS_SKIP_EXT_AUTHZ tells the TLS Pool that no external
- * authorization is needed on top of the normal operations of the
- * TLS Pool.  Usually, if an external authorization source is configured,
- * it will be RADIUS.  If it is not even configured, then this flag is of
- * no consequence.
- */
-#define PIOF_STARTTLS_BYPASS_EXT_AUTHZ         0x00000100
-
-#endif
-
-
 /* PIOF_STARTTLS_DTLS requests to setup DTLS instead of TLS.
  */
 #define PIOF_STARTTLS_DTLS                     0x00000100
 
 
-/* PIOF_STARTTLS_SEND_SNI can be used for client-side STARTTLS as an
- * indication that the remotid is present and its domain should be
- * passed over to the other side as a Server Name Indication.  This
- * is not a common structure for all protocols, but it is harmless
+/* PIOF_STARTTLS_WITHOUT_SNI can be used for client-side STARTTLS as an
+ * indication that if the remotid is present then its domain should not
+ * be passed over to the other side as a Server Name Indication.  This
+ * is not a common structure for all protocols, but is sent by default
  * because it is an indicative TLS option.  Note that it is useful
- * of xxxxs: protocols, which immediately start TLS, but usually not
+ * for xxxxs: protocols, which immediately start TLS, but usually not
  * needed for protocols that issue a STARTTLS command during a normal
  * exchange.  Anyhow, this is application-determined. 
  * If the remoteid contains a user@ part, it is not sent as part of
- * the SNI information, because that would violate the format.  It
+ * the SNI information, because that would violate the format.  That
  * is a missed opportunity though.
  */
-#define PIOF_STARTTLS_SEND_SNI                 0x00000200
+#define PIOF_STARTTLS_WITHOUT_SNI              0x00000200
 
 
 /* PIOF_STARTTLS_IGNORE_CACHES requires the TLS Pool to perform the
@@ -447,9 +402,12 @@ typedef struct pioc_starttls starttls_t;
  * to authenticate with, and should still be able to access the service
  * over TLS.  It is also useful to permit anonymous TLS connections to
  * remote clients or servers if both sides agree to that.
+ *
  * Note that a bidirectionally unauthenticated TLS connection is not
- * protected from man in the middle attacks, although it does warrant
- * against passive observers.
+ * protected from man in the middle attacks, although its encryption
+ * may protect against passive observers.
+ *
+ * This flag is overridden by PIOF_STARTTLS_IGNORE_REMOTEID.
  */
 #define PIOF_STARTTLS_REQUEST_REMOTEID         0x00000800
 
@@ -460,9 +418,12 @@ typedef struct pioc_starttls starttls_t;
  * remote identity in any useful way.  It is also useful to permit
  * anonymous TLS connections to remote clients or servers if both sides
  * agree to that.
+ *
  * Note that a bidirectionally unauthenticated TLS connection is not
  * protected from man in the middle attacks, although it does warrant
  * against passive observers.
+ *
+ * This flag overrides PIOF_STARTTLS_REQUEST_REMOTEID.
  */
 #define PIOF_STARTTLS_IGNORE_REMOTEID          0x00001000
 
@@ -506,6 +467,19 @@ typedef struct pioc_starttls starttls_t;
 #define PIOF_STARTTLS_FORK                     0x00004000
 
 
+/* PIOF_STARTTLS_DOMAIN_REPRESENTS_USER indicates that the remote identity
+ * need not be the expected user@domain, but that the domain is acceptable
+ * as well.  This is a common flag on protocols such as SMTP, where a
+ * server represents all users under its domain, and authenticates as the
+ * domain instead of as the user.  Note that the flag applies equally well
+ * to clients as it does to servers.  If an application does not supply
+ * this flag, it must supply any remote_id field for a STARTTLS exchange in
+ * the exact format as it is supported by the server.  The returned remote_id
+ * will always be the exact identity as provided by the server.
+ */
+#define PIOF_STARTTLS_DOMAIN_REPRESENTS_USER   0x00008000
+
+
 /* PIOF_STARTTLS_LOCALID_CHECK requests that a local identity is provided
  * to the application before it is accepted; this mechanism allows the
  * application to check such things as its list of virtual host names, and
@@ -517,6 +491,16 @@ typedef struct pioc_starttls starttls_t;
 #define PIOF_STARTTLS_LOCALID_CHECK            0x00010000
 
 
+/* PIOF_STARTTLS_RENEGOTIATE takes a previously agreed TLS connection and
+ * renegotiates identities as specified in this STARTTLS request.  The
+ * ctlkey field indicates an attached TLS connection that is to be
+ * renegotiated; this field will not be modified in the course of this
+ * run of the STARTTLS command.
+ */
+#define PIOF_STARTTLS_RENEGOTIATE              0x00020000
+
+
+
 /*************************** PIOF_LIDENTRY_xxx FLAGS **************************/
 
 
@@ -639,8 +623,9 @@ typedef struct pioc_starttls starttls_t;
  * identities embedded in an infrastructure.
  *
  * TODO: This is unimplemented behaviour; the flag is merely allocated.
+ * The result of using this is currently immediate return of DB_NOTFOUND.
  */
-// #define PIOF_LIDENTRY_NEW                   0x00100000
+#define PIOF_LIDENTRY_NEW                      0x00100000
 
 
 /* PIOF_LIDENTRY_ONTHEFLY indicates in a response to callback that the selected
@@ -663,8 +648,10 @@ typedef struct pioc_starttls starttls_t;
  * such as off-the-shelve browsers that require a HTTPS proxy.
  *
  * TODO: This is unimplemented behaviour; the flag is merely allocated.
+ * For now, the response is the same as in lieu of configuration of a
+ * root key and cert, namely to return DB_NOTFOUND.
  */
-// #define PIOF_LIDENTRY_ONTHEFLY              0x00300000
+#define PIOF_LIDENTRY_ONTHEFLY                 0x00200000
 
 
 #endif //TLSPOOL_COMMANDS_H
index 2875029..6027ad2 100644 (file)
@@ -32,12 +32,13 @@ struct command {
        pthread_t handler;
        struct tlspool_command cmd;
        //TODO// TLS-agnostic data would be a (void *) to a driver stack item:
-       struct pioc_starttls *orig_piocdata;
+       struct pioc_starttls *orig_starttls;
        DB_TXN *txn;
        pool_datum_t lids [EXPECTED_LID_TYPE_COUNT];
        int session_errno;
        intptr_t session_certificate;
        intptr_t session_privatekey;
+       int anonpre;
 };
 
 
@@ -120,6 +121,7 @@ char *cfg_dbenv_dir (void);
 char *cfg_db_localid (void);
 char *cfg_db_disclose (void);
 char *cfg_tls_dhparamfile (void);
+unsigned int cfg_tls_maxpreauth (void);
 
 
 
@@ -229,6 +231,7 @@ struct ctlkeynode {
        uint8_t ctlkey [TLSPOOL_CTLKEYLEN];
        struct ctlkeynode *lessnode, *morenode;
        int ctlfd;
+       int forked;
        enum security_layer security;
 };
 
@@ -245,9 +248,10 @@ struct ctlkeynode {
  * only reason for failure would be that the ctlkey is already registered,
  * which signifies an extremely unlikely clash -- or a program error by
  * not using properly scattered random sources.  The provided *ctlfdp may
- * be -1 to signal it is not valid.
+ * be -1 to signal it is detached.  The forked flag should be non-zero
+ * to indicate that this is a forked connection.
  */
-int ctlkey_register (uint8_t *ctlkey, struct ctlkeynode *ckn, enum security_layer sec, int ctlfd);
+int ctlkey_register (uint8_t *ctlkey, struct ctlkeynode *ckn, enum security_layer sec, int ctlfd, int forked);
 
 /* Remove a registered cltkey value from th registry.  This is the most
  * complex operation, as it needs to merge the subtrees.
@@ -270,7 +274,7 @@ int ctlkey_unregister (uint8_t *ctlkey);
  * which means that a non-NULL return value must later be passed to a function
  * that unlocks the resource, ctlkey_unfind().
  */
-struct ctlkeynode *ctlkey_find (uint8_t *ctlkey, enum security_layer sec, int clientfd);
+struct ctlkeynode *ctlkey_find (uint8_t *ctlkey, enum security_layer sec, int ctlfd);
 
 /* Free a ctlkeynode that was returned by ctlkey_find().  This function also
  * accepts a NULL argument, though those need not be passed through this
index 04920be..6a72d5a 100644 (file)
@@ -308,6 +308,9 @@ static void *master_thread (void *path) {
                        //NOT-USED// mh.msg_control = anc;
                        //NOT-USED// mh.msg_controllen = sizeof (anc);
                        retval = recvmsg (poolfd, &mh, MSG_NOSIGNAL);
+                       if ((retval == -1) && (errno = EINTR)) {
+                               continue;       // Badly masked user signal
+                       }
                        if (retval == 0) {
                                errno = EPIPE;
                                retval = -1;
@@ -431,6 +434,7 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
        struct cmsghdr *cmsg;
        struct msghdr mh = { 0 };
        int processing;
+       int renegotiate = 0 != (tlsdata->flags & PIOF_STARTTLS_RENEGOTIATE);
 
        /* Prepare command structure */
        poolfd = tlspool_socket (NULL);
@@ -462,16 +466,18 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
 #  error "Failure on assumption of 16 bytes per ctlkey"
 #endif
 
-       assert (pthread_mutex_lock (&prng_lock) == 0);
-       * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 0] = random ();
-       * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 2] = random ();
-       * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 4] = random ();
-       * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 6] = random ();
-       * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 8] = random ();
-       * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [10] = random ();
-       * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [12] = random ();
-       * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [14] = random ();
-       pthread_mutex_unlock (&prng_lock);
+       if (!renegotiate) {
+               assert (pthread_mutex_lock (&prng_lock) == 0);
+               * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 0] = random ();
+               * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 2] = random ();
+               * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 4] = random ();
+               * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 6] = random ();
+               * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [ 8] = random ();
+               * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [10] = random ();
+               * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [12] = random ();
+               * (uint16_t *) &cmd.pio_data.pioc_starttls.ctlkey [14] = random ();
+               pthread_mutex_unlock (&prng_lock);
+       }
 // printf ("DEBUG: ctlkey =");
 // {int i; for (i=0;i<16;i++) printf (" %02x", cmd.pio_data.pioc_starttls.ctlkey [i]);}
 // printf ("\n");
@@ -481,13 +487,15 @@ int tlspool_starttls (int cryptfd, starttls_t *tlsdata,
        iov.iov_len = sizeof (cmd);
        mh.msg_iov = &iov;
        mh.msg_iovlen = 1;
-       mh.msg_control = anc;
-       mh.msg_controllen = sizeof (anc);
-       cmsg = CMSG_FIRSTHDR (&mh);
-       cmsg->cmsg_level = SOL_SOCKET;
-       cmsg->cmsg_type = SCM_RIGHTS;
-       * (int *) CMSG_DATA (cmsg) = cryptfd;   /* cannot close it yet */
-       cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+       if (!renegotiate) {
+               mh.msg_control = anc;
+               mh.msg_controllen = sizeof (anc);
+               cmsg = CMSG_FIRSTHDR (&mh);
+               cmsg->cmsg_level = SOL_SOCKET;
+               cmsg->cmsg_type = SCM_RIGHTS;
+               * (int *) CMSG_DATA (cmsg) = cryptfd;   /* cannot close it yet */
+               cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+       }
        if (sendmsg (poolfd, &mh, MSG_NOSIGNAL) == -1) {
                // Let SIGPIPE be reported as EPIPE
                close (cryptfd);
index 18b047b..85961b2 100644 (file)
@@ -20,7 +20,7 @@ import random
 import threading
 
 
-TLSPOOL_IDENTITY_TMP   = '20150527tlspool@tmp.vanrein.org'
+TLSPOOL_IDENTITY_TMP   = '20150824tlspool@tmp.vanrein.org'
 
 
 PIOC_SUCCESS_V2           = 0x00000000
@@ -39,31 +39,32 @@ PIOC_LIDENTRY_CALLBACK_V2 = 0x00000201
 PIOC_LOCAL               = 0x80000000
 
 
-PIOF_STARTTLS_LOCALROLE_CLIENT = 0x00000001
-PIOF_STARTTLS_LOCALROLE_SERVER = 0x00000002
-PIOF_STARTTLS_LOCALROLE_PEER   = 0x00000003
+PIOF_STARTTLS_LOCALROLE_CLIENT         = 0x00000001
+PIOF_STARTTLS_LOCALROLE_SERVER         = 0x00000002
+PIOF_STARTTLS_LOCALROLE_PEER           = 0x00000003
 
-PIOF_STARTTLS_REMOTEROLE_CLIENT        = 0x00000004
-PIOF_STARTTLS_REMOTEROLE_SERVER        = 0x00000008
-PIOF_STARTTLS_REMOTEROLE_PEER  = 0x0000000c
+PIOF_STARTTLS_REMOTEROLE_CLIENT                = 0x00000004
+PIOF_STARTTLS_REMOTEROLE_SERVER                = 0x00000008
+PIOF_STARTTLS_REMOTEROLE_PEER          = 0x0000000c
 
-PIOF_STARTTLS_SEND_SNI         = 0x00000200
+PIOF_STARTTLS_WITHOUT_SNI              = 0x00000200
 
-PIOF_STARTTLS_DETACH           = 0x00002000
-PIOF_STARTTLS_FORK             = 0x00004000
-PIOF_STARTTLS_LOCALID_CHECK    = 0x00010000
+PIOF_STARTTLS_DETACH                   = 0x00002000
+# PIOF_STARTTLS_FORK                   = 0x00004000
+PIOF_STARTTLS_DOMAIN_REPRESENTS_USER   = 0x00008000
+PIOF_STARTTLS_LOCALID_CHECK            = 0x00010000
 
-PIOF_LIDENTRY_SKIP_DBENTRY     = 0x00000080    # in all _SKIP_
-PIOF_LIDENTRY_SKIP_USER                = 0x00000081
-PIOF_LIDENTRY_SKIP_DOMAIN_SAME = 0x00000082
-PIOF_LIDENTRY_SKIP_DOMAIN_ONEUP        = 0x00000084
-PIOF_LIDENTRY_SKIP_DOMAIN_SUB  = 0x00000086    # _SAME | _ONEUP
-PIOF_LIDENTRY_SKIP_NOTROOT     = 0x00000088
+PIOF_LIDENTRY_SKIP_DBENTRY             = 0x00000080    # in all _SKIP_
+PIOF_LIDENTRY_SKIP_USER                        = 0x00000081
+PIOF_LIDENTRY_SKIP_DOMAIN_SAME         = 0x00000082
+PIOF_LIDENTRY_SKIP_DOMAIN_ONEUP                = 0x00000084
+PIOF_LIDENTRY_SKIP_DOMAIN_SUB          = 0x00000086    # _SAME | _ONEUP
+PIOF_LIDENTRY_SKIP_NOTROOT             = 0x00000088
 
-PIOF_LIDENTRY_DBENTRY          = 0x00000100
-PIOF_LIDENTRY_DBINSERT         = 0x00000200
-PIOF_LIDENTRY_DBAPPEND         = 0x00000400
-PIOF_LIDENTRY_DBREORDER                = 0x00000800
+PIOF_LIDENTRY_DBENTRY                  = 0x00000100
+PIOF_LIDENTRY_DBINSERT                 = 0x00000200
+PIOF_LIDENTRY_DBAPPEND                 = 0x00000400
+PIOF_LIDENTRY_DBREORDER                        = 0x00000800
 # PIOF_LIDENTRY_NEW = 0x00010000
 # PIOF_LIDENTRY_ONTHEFLY = 0x00030000
 
index 92c48e2..997dbcc 100644 (file)
@@ -7,6 +7,7 @@
 #include <errno.h>
 #include <ctype.h>
 #include <string.h>
+#include <limits.h>
 
 #include <sys/socket.h>
 #include <sys/types.h>
@@ -60,6 +61,9 @@ enum VARS {
        CFGVAR_DB_LOCALID,
        CFGVAR_DB_DISCLOSE,
        CFGVAR_TLS_DHPARAMFILE,
+       CFGVAR_TLS_MAXPREAUTH,
+       CFGVAR_TLS_ONTHEFLY_SIGNCERT,
+       CFGVAR_TLS_ONTHEFLY_SIGNKEY,
        //
        CFGVAR_LENGTH,
        CFGVAR_NONE = -1
@@ -103,6 +107,9 @@ struct cfgopt config_options [] = {
        "db_localid",           cfg_setvar,     CFGVAR_DB_LOCALID,
        "db_disclose",          cfg_setvar,     CFGVAR_DB_DISCLOSE,
        "tls_dhparamfile",      cfg_setvar,     CFGVAR_TLS_DHPARAMFILE,
+       "tls_maxpreauth",       cfg_setvar,     CFGVAR_TLS_MAXPREAUTH,
+       "tls_onthefly_signcert",cfg_setvar,     CFGVAR_TLS_ONTHEFLY_SIGNCERT,
+       "tls_onthefly_signkey", cfg_setvar,     CFGVAR_TLS_ONTHEFLY_SIGNKEY,
        //
        NULL,                   NULL,           CFGVAR_NONE
 };
@@ -538,3 +545,31 @@ char *cfg_tls_dhparamfile (void) {
        return configvars [CFGVAR_TLS_DHPARAMFILE];
 }
 
+unsigned int cfg_tls_maxpreauth (void) {
+       char *mps = configvars [CFGVAR_TLS_MAXPREAUTH];
+       unsigned long mpi = 32768;
+       if (mps != NULL) {
+               mpi = strtoul (mps, &mps, 10);
+               if (mpi > UINT_MAX) {
+                       mpi = 32768;
+               }
+       }
+       return (unsigned int) mpi;
+}
+
+char *cfg_tls_onthefly_signcert (void) {
+       if (configvars [CFGVAR_TLS_ONTHEFLY_SIGNKEY]) {
+               return configvars [CFGVAR_TLS_ONTHEFLY_SIGNCERT];
+       } else {
+               return NULL;
+       }
+}
+
+char *cfg_tls_onthefly_signkey (void) {
+       if (configvars [CFGVAR_TLS_ONTHEFLY_SIGNCERT]) {
+               return configvars [CFGVAR_TLS_ONTHEFLY_SIGNKEY];
+       } else {
+               return NULL;
+       }
+}
+
index 21deb41..350b629 100644 (file)
@@ -91,9 +91,10 @@ static pthread_mutex_t ctlkey_registry_lock = PTHREAD_MUTEX_INITIALIZER;
  * only reason for failure would be that the ctlkey is already registered,
  * which signifies an extremely unlikely clash -- or a program error by
  * not using properly scattered random sources.  The provided ctlfd may
- * be -1 to signal it is not valid.
+ * be -1 to signal it is detached.  The forked flag should be non-zero
+ * to indicate that this is a forked connection.
  */
-int ctlkey_register (uint8_t *ctlkey, struct ctlkeynode *ckn, enum security_layer sec, int ctlfd) {
+int ctlkey_register (uint8_t *ctlkey, struct ctlkeynode *ckn, enum security_layer sec, int ctlfd, int forked) {
        int i;
        int todo;
        struct ctlkeynode **nodepp;
@@ -116,6 +117,7 @@ int ctlkey_register (uint8_t *ctlkey, struct ctlkeynode *ckn, enum security_laye
        memcpy (ckn->ctlkey, ctlkey, sizeof (ckn->ctlkey));
        ckn->security = sec;
        ckn->ctlfd = ctlfd;
+       ckn->forked = forked? 1: 0;
        *nodepp = ckn;
        pthread_mutex_unlock (&ctlkey_registry_lock);
        return 0;
@@ -181,7 +183,7 @@ int ctlkey_unregister (uint8_t *ctlkey) {
  * which means that a non-NULL return value must later be passed to a function
  * that unlocks the resource, ctlkey_unfind().
  */
-struct ctlkeynode *ctlkey_find (uint8_t *ctlkey, enum security_layer sec, int clientfd) {
+struct ctlkeynode *ctlkey_find (uint8_t *ctlkey, enum security_layer sec, int ctlfd) {
        struct ctlkeynode *ckn;
        //
        // Claim unique access; this lock survives until cltkey_unfind()
@@ -196,7 +198,7 @@ struct ctlkeynode *ctlkey_find (uint8_t *ctlkey, enum security_layer sec, int cl
                        /* Found the right node */
                        if (ckn->ctlfd < 0) {
                                ckn = NULL;     // Connection not under control
-                       } else if (ckn->ctlfd != clientfd) {
+                       } else if (ckn->ctlfd != ctlfd) {
                                ckn = NULL;     // Connection is not yours to find
                        } else if (ckn->security != sec) {
                                ckn = NULL;     // Connection is not of right type
@@ -332,29 +334,34 @@ void ctlkey_reattach (struct command *cmd) {
  * be cleaned up.  Note that detaching is not done before the TLS handshake
  * is complete.
  */
-static void _ctlkey_close_clientfd_recurse (int clisox, struct ctlkeynode **nodepp) {
+static void _ctlkey_close_ctlfd_recurse (int clisox, struct ctlkeynode **nodepp) {
        struct ctlkeynode *node = *nodepp;
        if (node == NULL) {
                return;
        }
-       _ctlkey_close_clientfd_recurse (clisox, &node->lessnode);
-       _ctlkey_close_clientfd_recurse (clisox, &node->morenode);
+       _ctlkey_close_ctlfd_recurse (clisox, &node->lessnode);
+       _ctlkey_close_ctlfd_recurse (clisox, &node->morenode);
        if (node->ctlfd == clisox) {
                // At this point, subnodes may be removed and juggled,
                // but we can still rely on unchanged (*nodepp) == node
                assert (*nodepp == node);
 //DEBUG// fprintf(stderr,"Unregistering control key (automatically, as controlling fd closes)\n");
-               _ctlkey_unregister_nodepp (nodepp);
-               // Now we know that *nodepp has changed, it is no longer
-               // pointing to node (so we may remove it).
-               // No changes have been made higher up though, so recursion
-               // assumptions are still valid; see _ctlkey_unregister_nodepp()
-               free (node);
+               if (node->forked) {
+                       node->ctlfd = -1;
+               } else {
+                       _ctlkey_unregister_nodepp (nodepp);
+                       // Now we know that *nodepp has changed, it is no longer
+                       // pointing to node (so we may remove it).
+                       // No changes have been made higher up though, so
+                       // recursion assumptions are still valid; see
+                       // _ctlkey_unregister_nodepp() for this assumption.
+                       free (node);
+               }
        }
 }
-void ctlkey_close_clientfd (int clisox) {
+void ctlkey_close_ctlfd (int clisox) {
        assert (pthread_mutex_lock (&ctlkey_registry_lock) == 0);
-       _ctlkey_close_clientfd_recurse (clisox, &rootnode);
+       _ctlkey_close_ctlfd_recurse (clisox, &rootnode);
        pthread_mutex_unlock (&ctlkey_registry_lock);
 }
 
index fe91504..1563381 100644 (file)
@@ -158,23 +158,20 @@ db_error dbcred_iterate_from_remoteid_selector (DBC *crs_disclose, DBC *crs_loca
                        // Got the selector pattern!
                        at_root = (remotesel->domlen == 1) && (*remotesel->domain == '.');
                        // Return immediately, IF no need for lidentry callbacks
-                       if (db_errno == 0) {
-                               if (lidentry_database_mayskip (levels_up, at_root)) {
-                                       // Simply set & return the first localid
-                                       db_errno = crs_localid->get (
-                                                       crs_localid,
-                                                       keydata,
-                                                       creddata,
-                                                       DB_SET);
-                               } else {
-                                       // Use LIDENTRY's DB callbacks, so fall through
-                                       lid_callback = 1;
-                               }
-                       }
                        if (db_errno != 0) {
                                return db_errno;
+                       }
+                       if (lidentry_database_mayskip (levels_up, at_root)) {
+                               // Simply set & return the first localid
+                               db_errno = crs_localid->get (
+                                               crs_localid,
+                                               keydata,
+                                               creddata,
+                                               DB_SET);
+                               return db_errno;
                        } else {
-                               break;
+                               // Use LIDENTRY's DB callbacks, so fall through
+                               lid_callback = 1;
                        }
                } else if (fnd != DB_NOTFOUND) {
                        E_d2e ("Failed while searching with remote ID selector", fnd);
@@ -235,6 +232,20 @@ printf ("DEBUG: Generated  LID-entry menu for %s.\n", remoteid);
                return DB_NOTFOUND;
        }
        //
+       // PIOF_LIDENTRY_NEW is unimplemented, and immediately reports
+       // what it may always fall back to -- DB_NOTFOUND
+       if (lidcbflags & PIOF_LIDENTRY_NEW) {
+               tlog (TLOG_UNIXSOCK, LOG_ERR, "Request to wait for new credential; not implemented so falling back to reporting DB_NOTFOUND");
+               return DB_NOTFOUND;
+       }
+       //
+       // PIOF_LIDENTRY_ONTHEFLY is unimplemented, and reports what it
+       // does in lieau of configured root key / cert -- DB_NOTFOUND
+       if (lidcbflags & PIOF_LIDENTRY_ONTHEFLY) {
+               tlog (TLOG_UNIXSOCK, LOG_ERR, "Request to generate certificate on the fly; not implemented so falling back to to reporting DB_NOTFOUND");
+               return DB_NOTFOUND;
+       }
+       //
        // Process the localid and lidcbflags by potential db updates:
        //  - PIOF_LIDENTRY_DBINSERT -> insert at the head (if new)
        //  - PIOF_LIDENTRY_DBAPPEND -> insert at the tail (if new)
index ae11974..dae0982 100644 (file)
@@ -118,6 +118,9 @@ static struct command *allocate_command_for_clientfd (int fd) {
  */
 static void free_commands_by_clientfd (int clientfd) {
        int i;
+       if (cmdpool == NULL) {
+               return;
+       }
        for (i=0; i<cmdpool_len; i++) {
                if (cmdpool [i].claimed) {
                        if (cmdpool [i].clientfd == clientfd) {
@@ -171,7 +174,7 @@ static void unregister_client_socket_byindex (int soxidx) {
        free_commands_by_clientfd (sox);
        pinentry_forget_clientfd (sox);
        lidentry_forget_clientfd (sox);
-       ctlkey_close_clientfd (sox);
+       ctlkey_close_ctlfd (sox);
        num_sox--;
        if (soxidx < num_sox) {
                memcpy (&soxinfo [soxidx], &soxinfo [num_sox], sizeof (*soxinfo));
@@ -187,6 +190,9 @@ int send_command (struct command *cmd, int passfd) {
        struct msghdr mh;
        struct cmsghdr *cmsg;
 
+       if (cmd == NULL) {
+               return 1;       // Success guaranteed when nobody is listening
+       }
        assert (passfd == -1);  // Working passfd code retained but not used
        bzero (anc, sizeof (anc));
        bzero (&iov, sizeof (iov));
@@ -221,8 +227,19 @@ int send_command (struct command *cmd, int passfd) {
 
 /* Report success to the user.  Note that this function does not terminate
  * actions, but it should be the last response to the client.
+ *
+ * We accept the situation where cmd==NULL to accommodate code that deals
+ * with re-run commands that were internally stored.  This saves massively
+ * in re-coding such code.
+ *
+ * We accept the situation where cmd==NULL to accommodate code that deals
+ * with re-run commands that were internally stored.  This saves massively
+ * in re-coding such code.
  */
 void send_success (struct command *cmd) {
+       if (cmd == NULL) {
+               return;
+       }
        cmd->cmd.pio_cmd = PIOC_SUCCESS_V1;
        cmd->cmd.pio_cbid = 0;
        if (!send_command (cmd, -1)) {
@@ -234,8 +251,15 @@ void send_success (struct command *cmd) {
 /* Report an error response to the user.  Report with the given errno and msg.
  * Note that this function does not terminate actions, but it should be the
  * last response to the client.
+ *
+ * We accept the situation where cmd==NULL to accommodate code that deals
+ * with re-run commands that were internally stored.  This saves massively
+ * in re-coding such code.
  */
 void send_error (struct command *cmd, int tlserrno, char *msg) {
+       if (cmd == NULL) {
+               return;
+       }
        if (tlserrno == 0) {
                send_success (cmd);
                return;
index cdc2584..ddd0455 100644 (file)
@@ -7,6 +7,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <memory.h>
+#include <string.h>
 #include <pthread.h>
 #include <assert.h>
 
@@ -81,7 +82,7 @@ struct credinfo {
 };
 
 #define EXPECTED_SRV_CREDCOUNT 3
-#define EXPECTED_CLI_CREDCOUNT 2
+#define EXPECTED_CLI_CREDCOUNT 3
 static struct credinfo srv_creds [EXPECTED_SRV_CREDCOUNT];
 static struct credinfo cli_creds [EXPECTED_CLI_CREDCOUNT];
 static int srv_credcount = 0;
@@ -92,6 +93,9 @@ static int cli_credcount = 0;
 struct ctlkeynode_tls {
        struct ctlkeynode regent;       // Structure for ctlkey_register()
        gnutls_session_t session;       // Additional data specifically for TLS
+       pthread_t owner;                // For interruption of copycat()
+       int plainfd;                    // Plain-side connection
+       int cryptfd;                    // Crypt-side connection
 };
 
 /* The list of accepted Exporter Label Prefixes for starttls_prng()
@@ -116,6 +120,73 @@ char *tlsprng_label_prefixes [] = {
        NULL
 };
 
+/* The registry with the service names that are deemed safe for an
+ * anonymous precursor phase; that is, the service names that may offer
+ * ANON-DH initially, and immediately renegotiate an authenticated
+ * connection.  See doc/anonymising-precursor.* for more information.
+ *
+ * The registry is ordered by case-independent service name, so it can
+ * be searched in 2log time.  Service names are as defined by IANA in the
+ * "Service Name and Transport Protocol Port Number Registry".
+ *
+ * The entries in the registry depend on the role played; either as a
+ * client or as a server.  This refers to the local node, and depends on
+ * uncertainty of the remote party's TLS implementation and whether or
+ * not the protocol could lead to the remote sending information that
+ * requires authentication before the secure renogiation into an
+ * authenticated connection has been completed by this side.  This is
+ * a protocol-dependent matter and the registry provided here serves to
+ * encapsulate this knowledge inside the TLS Pool instead of bothering
+ * application designers with it.  Entries that are not found in the
+ * registry are interpreted as not allowing an anonymising precursor.
+ *
+ * Note that ANONPRE_EXTEND_MASTER_SECRET cannot be verified before
+ * GnuTLS version 3.4.0; see "imap" below for the resulting impact.  This
+ * also impacts dynamic linking, because 3.4.0 introduces the new function
+ * gnutls_ext_get_data() that is used for this requirement.
+ */
+#define ANONPRE_FORBID 0x00
+#define ANONPRE_CLIENT 0x01
+#define ANONPRE_SERVER 0x02
+#define ANONPRE_EITHER (ANONPRE_CLIENT | ANONPRE_SERVER)
+#define ANONPRE_EXTEND_MASTER_SECRET 0x10
+struct anonpre_regentry {
+       char *service;
+       uint8_t flags;
+};
+struct anonpre_regentry anonpre_registry [] = {
+/* This registry is commented out for now, although the code to use it seems
+ * to work fine.  GnuTLS however, does not seem to support making the switch
+ * from ANON-ECDH to an authenticated handshake.  Details:
+ * http://lists.gnutls.org/pipermail/gnutls-help/2015-November/003998.html
+ *
+       { "generic_anonpre", ANONPRE_EITHER },  // Name invalid as per RFC 6335
+       { "http", ANONPRE_CLIENT },     // Server also if it ignores client ID
+#if GNUTLS_VERSION_NUMBER < 0x030400
+       { "imap", ANONPRE_SERVER },
+#else
+       { "imap", ANONPRE_EITHER | ANONPRE_EXTEND_MASTER_SECRET },
+#endif
+       { "pop3", ANONPRE_EITHER },
+       { "smtp", ANONPRE_EITHER },
+ *
+ * End of commenting out the registry
+ */
+};
+const int anonpre_registry_size = sizeof (anonpre_registry) / sizeof (struct anonpre_regentry);
+
+/* The maximum number of bytes that can be passed over a TLS connection before
+ * the authentication is complete in case of a anonymous precursor within a
+ * protocol that ensures that this cannot be a problem.
+ */
+int maxpreauth;
+
+/* The priorities cache for "NORMAL" -- used to preconfigure the server,
+ * actually to overcome its unwillingness to perform the handshake, and
+ * leave it to srv_clienthello() to setup the priority string.
+ */
+gnutls_priority_t priority_normal;
+
 
 /* Map a GnuTLS call (usually a function call) to a POSIX errno,
  * optionally reporting an errstr to avoid loosing information.
@@ -547,6 +618,9 @@ void setup_starttls (void) {
        const char *curver;
        int gtls_errno = GNUTLS_E_SUCCESS;
        //
+       // Setup configuration variables
+       maxpreauth = cfg_tls_maxpreauth ();
+       //
        // Basic library actions
        tlog (TLOG_TLS, LOG_DEBUG, "Compiled against GnuTLS version %s", GNUTLS_VERSION);
        curver = gnutls_check_version (GNUTLS_VERSION);
@@ -561,7 +635,7 @@ void setup_starttls (void) {
        // Setup logging / debugging
        if (cfg_log_level () == LOG_DEBUG) {
                gnutls_global_set_log_function (log_gnutls);
-               gnutls_global_set_log_level (2);
+               gnutls_global_set_log_level (9);
        }
        //
        // Setup callbacks for user communication
@@ -575,6 +649,13 @@ void setup_starttls (void) {
        // Setup shared credentials for all client server processes
        E_g2e ("Failed to setup GnuTLS callback credentials",
                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:+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));
+       //
+       // Finally, check whether there was any error setting up GnuTLS
        if (gtls_errno != GNUTLS_E_SUCCESS) {
                tlog (TLOG_TLS, LOG_CRIT, "FATAL: GnuTLS setup failed: %s", gnutls_strerror (gtls_errno));
                exit (1);
@@ -600,6 +681,7 @@ void cleanup_starttls (void) {
        gnutls_pkcs11_set_pin_function (NULL, NULL);
        gnutls_pkcs11_set_token_function (NULL, NULL);
        gnutls_pkcs11_deinit ();
+       gnutls_priority_deinit (priority_normal);
        gnutls_global_deinit ();
 }
 
@@ -628,13 +710,26 @@ void cleanup_starttls (void) {
  * page clearly stated yes.  However, these articles offer much more detail:
  * http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable
  * http://www.greenend.org.uk/rjk/tech/poll.html
+ *
+ * This function blocks during its call to poll(), in a state that can easily
+ * be restarted.  This is when thread cancellation is temporarily enabled.
+ * Other threads may use this to cancel the thread and have it joined with that
+ * thread which will subsume its tasks and restart the handshake.  We might
+ * later make this more advanced, by using a cancel stack push/pull mechanisms
+ * to ensure that recv() always results in send() in spite of cancellation.
+ *
+ * The return value of copycat is a GNUTLS_E_ code, usually GNUTLS_E_SUCCESS.
+ * For the moment, only one special value is of concern, namely
+ * GNUTLS_E_REHANDSHAKE which client or server side may receive when an
+ * attempt is made to renegotiate the security of the connection.
  */
-static void copycat (int local, int remote, gnutls_session_t wrapped, int client) {
+static int copycat (int local, int remote, gnutls_session_t wrapped, int client) {
        char buf [1024];
        struct pollfd inout [3];
        ssize_t sz;
        struct linger linger = { 1, 10 };
        int have_client;
+       int retval = GNUTLS_E_SUCCESS;
 
        inout [0].fd = local;
        inout [1].fd = remote;
@@ -649,7 +744,12 @@ static void copycat (int local, int remote, gnutls_session_t wrapped, int client
        inout [2].events = 0;   // error events only
        tlog (TLOG_COPYCAT, LOG_DEBUG, "Starting copycat cycle for local=%d, remote=%d, control=%d", local, remote, client);
        while (((inout [0].events | inout [1].events) & POLLIN) != 0) {
-               if (poll (inout, have_client? 3: 2, -1) == -1) {
+               int polled;
+               assert (pthread_setcancelstate (PTHREAD_CANCEL_ENABLE,  NULL) == 0);
+               pthread_testcancel ();  // Efficiency & Certainty
+               polled = poll (inout, have_client? 3: 2, -1);
+               assert (pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL) == 0);
+               if (polled == -1) {
                        tlog (TLOG_COPYCAT, LOG_DEBUG, "Copycat polling returned an error");
                        break;  // Polling sees an error
                }
@@ -677,7 +777,12 @@ static void copycat (int local, int remote, gnutls_session_t wrapped, int client
                        sz = gnutls_record_recv (wrapped, buf, sizeof (buf));
                        tlog (TLOG_COPYCAT, LOG_DEBUG, "Copycat received %d remote bytes from %d (or error if <0)", (int) sz, remote);
                        if (sz < 0) {
-                               if (gnutls_error_is_fatal (sz)) {
+                               //TODO// Process GNUTLS_E_REHANDSHAKE
+                               if (sz == GNUTLS_E_REHANDSHAKE) {
+                                       tlog (TLOG_TLS, LOG_INFO, "Received renegotiation request over TLS handle %d", remote);
+                                       retval = GNUTLS_E_REHANDSHAKE;
+                                       break;
+                               } else if (gnutls_error_is_fatal (sz)) {
                                        tlog (TLOG_TLS, LOG_ERR, "GnuTLS fatal error: %s", gnutls_strerror (sz));
                                        break;  // stream error
                                } else {
@@ -718,7 +823,7 @@ static void copycat (int local, int remote, gnutls_session_t wrapped, int client
                }
        }
        tlog (TLOG_COPYCAT, LOG_DEBUG, "Ending copycat cycle for local=%d, remote=%d", local, remote);
-       gnutls_deinit (wrapped);
+       return retval;
 }
 
 
@@ -791,7 +896,13 @@ gtls_error clisrv_cert_retrieve (gnutls_session_t session,
                        return gtls_errno;
                }
                if (*lid != '\0') {
-                       if (strncmp (sni, lid, snilen) != 0) {
+                       int atidx;
+                       for (atidx=128; atidx > 0; atidx--) {
+                               if (lid [atidx-1] == '@') {
+                                       break;
+                               }
+                       }
+                       if (strncmp (sni, lid + atidx, sizeof (sni)-atidx) != 0) {
                                tlog (TLOG_TLS, LOG_ERR, "SNI %s does not match preset local identity %s", sni, lid);
                                E_g2e ("Requested SNI does not match local identity",
                                        GNUTLS_E_NO_CERTIFICATE_FOUND);
@@ -843,7 +954,7 @@ gtls_error clisrv_cert_retrieve (gnutls_session_t session,
                // Note: This is only of interest for client operation
                if (lidrole == LID_ROLE_CLIENT) {
                        selector_t newrid = donai_from_stable_string (rid, strlen (rid));
-                       donai_t oldrid = donai_from_stable_string (cmd->orig_piocdata->remoteid, strlen (cmd->orig_piocdata->remoteid));
+                       donai_t oldrid = donai_from_stable_string (cmd->orig_starttls->remoteid, strlen (cmd->orig_starttls->remoteid));
                        if (!donai_matches_selector (&oldrid, &newrid)) {
                                return GNUTLS_E_NO_CERTIFICATE_FOUND;
                        }
@@ -854,6 +965,7 @@ gtls_error clisrv_cert_retrieve (gnutls_session_t session,
                        fetch_local_credentials (cmd));
        }
        if (cmd->lids [lidtype - LID_TYPE_MIN].data == NULL) {
+printf ("DEBUG: Missing certificate for local ID %s and remote ID %s\n", lid, rid);
                E_g2e ("Missing certificate for local ID",
                        GNUTLS_E_NO_CERTIFICATE_FOUND);
                return gtls_errno;
@@ -938,6 +1050,7 @@ gtls_error clisrv_cert_retrieve (gnutls_session_t session,
        //
        // 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));
+printf ("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);
        return gtls_errno;
 }
 
@@ -1242,6 +1355,83 @@ gtls_error fetch_local_credentials (struct command *cmd) {
 }
 
 
+/*
+ * Check if a given cmd has the given LID_TYPE setup.
+ * 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;
+}
+
+/* Configure the GnuTLS session with suitable credentials and priority string.
+ * The anonpre_ok flag should be non-zero to permit Anonymous Precursor.
+ *
+ * The credential setup is optional; when creds is NULL, no changes will
+ * be made.
+ */
+static int configure_session (struct command *cmd,
+                       gnutls_session_t session,
+                       struct credinfo *creds,
+                       int credcount,
+                       int anonpre_ok) {
+       int i;
+       int gtls_errno = GNUTLS_E_SUCCESS;
+       //
+       // Install the shared credentials for the client or server role
+       if (creds != NULL) {
+               gnutls_credentials_clear (session);
+               for (i=0; i<credcount; i++) {
+                       E_g2e ("Failed to install credentials into TLS session",
+                               gnutls_credentials_set (
+                                       session,
+                                       creds [i].credtp,
+                                       creds [i].cred  ));
+               }
+       }
+       //
+       // Setup the priority string for this session; this avoids future
+       // credential callbacks that ask for something impossible or
+       // undesired.
+       //
+       // Variation factors:
+       //  - starting configuration (can it be empty?)
+       //  - Configured security parameters (database? variable?)
+       //  - CTYPEs, SRP, ANON-or-not --> fill in as + or - characters
+       if (gtls_errno == GNUTLS_E_SUCCESS) {
+               char priostr [256];
+               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                              ?'+':'-',
+                       lidtpsup (cmd, LID_TYPE_X509)           ?'+':'-',
+                       lidtpsup (cmd, LID_TYPE_PGP)            ?'+':'-',
+                       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//
+               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",
+                       gnutls_priority_set_direct (
+                       session,
+                       priostr,
+                       NULL));
+       }
+       //
+       // Return the application GNUTLS_E_ code including _SUCCESS
+       return gtls_errno;
+}
+
 /* The callback functions retrieve various bits of information for the client
  * or server in the course of the handshake procedure.
  *
@@ -1258,6 +1448,9 @@ int srv_clienthello (gnutls_session_t session) {
        int gtls_errno = GNUTLS_E_SUCCESS;
        char *lid;
 
+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__);
        //
        // Setup a number of common references
        cmd = (struct command *) gnutls_session_get_ptr (session);
@@ -1267,9 +1460,45 @@ int srv_clienthello (gnutls_session_t session) {
        lid = cmd->cmd.pio_data.pioc_starttls.localid;
 
        //
+       // Setup server-specific credentials and priority string
+       //TODO// get anonpre value here
+fprintf (stderr, "DEBUG: Got gtls_errno = %d at %d\n", gtls_errno, __LINE__);
+fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
+       E_g2e ("Failed to reconfigure GnuTLS as a server",
+               configure_session (cmd,
+                       session,
+                       srv_creds, srv_credcount, 
+                       cmd->anonpre & ANONPRE_SERVER));
+fprintf (stderr, "DEBUG: Got gtls_errno = %d at %d\n", gtls_errno, __LINE__);
+
+       //
+       // Setup to ignore/request/require remote identity (from client)
+fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
+       if (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_IGNORE_REMOTEID) {
+               // Neither Request nor Require remoteid; ignore it
+               ;
+       } else if (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_REQUEST_REMOTEID) {
+               // Use Request instead of Require for remoteid
+               ( //RETURNS_VOID// E_g2e ("Failed to request remote identity",
+                       gnutls_certificate_server_set_request (
+                               session,
+                               GNUTLS_CERT_REQUEST));
+fprintf (stderr, "DEBUG: Got gtls_errno = %d at %d\n", gtls_errno, __LINE__);
+       } else {
+               // Require a remoteid from the client (default)
+               ( //RETURNS_VOID// E_g2e ("Failed to require remote identity (the default)",
+                       gnutls_certificate_server_set_request (
+                               session,
+                               GNUTLS_CERT_REQUIRE));
+fprintf (stderr, "DEBUG: Got gtls_errno = %d at %d\n", gtls_errno, __LINE__);
+       }
+
+       //
        // Find the client-helloed ServerNameIndication, or the service name
+fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
        sni [0] = '\0';
        if (gnutls_server_name_get (session, sni, &snilen, &snitype, 0) == 0) {
+fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
                switch (snitype) {
                case GNUTLS_NAME_DNS:
                        break;
@@ -1279,32 +1508,47 @@ int srv_clienthello (gnutls_session_t session) {
                default:
                        sni [0] = '\0';
                        tlog (TLOG_TLS, LOG_ERR, "Received an unexpected SNI type; that is possible but uncommon; skipping SNI");
+fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
                        break;
                }
        }
+fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
        if (sni [0] != '\0') {
                if (*lid != '\0') {
-                       if (strncmp (sni, lid, sizeof (sni)) != 0) {
+                       int atidx;
+                       for (atidx=128; atidx > 0; atidx--) {
+                               if (lid [atidx-1] == '@') {
+                                       break;
+                               }
+                       }
+                       if (strncmp (sni, lid + atidx, sizeof (sni)-atidx) != 0) {
                                tlog (TLOG_USER | TLOG_TLS, LOG_ERR, "Mismatch between client-sent SNI %s and local identity %s", sni, lid);
+fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
                                return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET;
                        }
                } else {
                        memcpy (lid, sni, sizeof (sni));
+fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
                }
        } else {
                memcpy (sni, lid, sizeof (sni)-1);
+fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
                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
        if (errno != 0) {
                cmd->session_errno = errno;
+fprintf (stderr, "DEBUG: Got errno = %d / %s at %d\n", errno, strerror (errno), __LINE__);
                gtls_errno = GNUTLS_E_NO_CIPHER_SUITES; /* Vaguely matching */
+fprintf (stderr, "DEBUG: Got gtls_errno = %d at %d\n", gtls_errno, __LINE__);
        }
 
        //
        // Round off with an overal judgement
+fprintf (stderr, "DEBUG: Returning glts_errno = %d or \"%s\" from srv_clihello()\n", gtls_errno, gnutls_strerror (gtls_errno));
        return gtls_errno;
 }
 
@@ -1313,7 +1557,7 @@ int cli_srpcreds_retrieve (gnutls_session_t session,
                                char **username,
                                char **password) {
        //TODO:FIXED//
-       tlog (TLOG_CRYPTO, LOG_DEBUG, "DEBUG: Picking up SRP credentials");
+       tlog (TLOG_CRYPTO, LOG_DEBUG, "Picking up SRP credentials");
        *username = strdup ("tester");
        *password = strdup ("test");
        return GNUTLS_E_SUCCESS;
@@ -1337,44 +1581,7 @@ int setup_starttls_credentials (void) {
        int gtls_errno_stack0;
 
        //
-       // Construct certificate credentials for X.509 and OpenPGP cli/srv
-       E_g2e ("Failed to allocate certificate credentials",
-               gnutls_certificate_allocate_credentials (
-                       &clisrv_certcred));
-       //TODO// What to do here when we add locking on DH params?
-       gnutls_certificate_set_dh_params (
-               clisrv_certcred,
-               dh_params);
-       gtls_errno_stack0 = gtls_errno;
-       /* TODO: Bad code.  GnuTLS 3.2.1 ignores retrieve_function2 when
-        * checking if it can handle the OpenPGP certificate type in
-        * _gnutls_session_cert_type_supported (gnutls_status.c:175) but
-        * it does see the "1" version field.  It does not callback the
-        * "1" version if "2" is present though.
-        */
-       if (!have_error_codes ()) /* TODO:GnuTLSversions E_g2e (...) */ gnutls_certificate_set_retrieve_function (
-               clisrv_certcred,
-               (void *) exit);
-       if (!have_error_codes ()) /* TODO:GnuTLSversions E_g2e (...) */ gnutls_certificate_set_retrieve_function2 (
-               clisrv_certcred,
-               clisrv_cert_retrieve);
-       if (gtls_errno == GNUTLS_E_SUCCESS) {
-               // Setup for certificates
-               tlog (TLOG_CERT, LOG_INFO, "Setting client and server certificate credentials");
-               cli_creds [cli_credcount].credtp = GNUTLS_CRD_CERTIFICATE;
-               cli_creds [cli_credcount].cred   = (void *) clisrv_certcred;
-               cli_credcount++;
-               srv_creds [srv_credcount].credtp = GNUTLS_CRD_CERTIFICATE;
-               srv_creds [srv_credcount].cred   = (void *) clisrv_certcred;
-               srv_credcount++;
-       } else if (clisrv_certcred != NULL) {
-               gnutls_certificate_free_credentials (clisrv_certcred);
-               clisrv_certcred = NULL;
-       }
-
-       //
        // Construct anonymous server credentials
-       gtls_errno = gtls_errno_stack0; // Don't pop, just forget last failures
        E_g2e ("Failed to allocate ANON-DH server credentials",
                gnutls_anon_allocate_server_credentials (
                        &srv_anoncred));
@@ -1391,16 +1598,18 @@ int setup_starttls_credentials (void) {
                srv_anoncred = NULL;
        }
 
-#ifdef MIRROR_IMAGE_OF_SERVER_ANONYMOUS_CREDENTIALS
        //
        // Construct anonymous client credentials
        gtls_errno = gtls_errno_stack0; // Don't pop, just forget last failures
        E_g2e ("Failed to allocate ANON-DH client credentials",
                gnutls_anon_allocate_client_credentials (
                        &cli_anoncred));
+#ifdef MIRROR_IMAGE_OF_SERVER_ANONYMOUS_CREDENTIALS
+       // NOTE: This is not done under TLS; server always provides DH params
        if (!have_error_codes ()) gnutls_anon_set_client_dh_params (
                cli_anoncred,
                dh_params);
+#endif
        if (gtls_errno == GNUTLS_E_SUCCESS) {
                tlog (TLOG_CRYPTO, LOG_INFO, "Setting client anonymous credentials");
                cli_creds [cli_credcount].credtp = GNUTLS_CRD_ANON;
@@ -1410,7 +1619,43 @@ int setup_starttls_credentials (void) {
                gnutls_anon_free_client_credentials (cli_anoncred);
                cli_anoncred = NULL;
        }
-#endif
+
+       //
+       // Construct certificate credentials for X.509 and OpenPGP cli/srv
+       gtls_errno = gtls_errno_stack0; // Don't pop, just forget last failures
+       E_g2e ("Failed to allocate certificate credentials",
+               gnutls_certificate_allocate_credentials (
+                       &clisrv_certcred));
+       //TODO// What to do here when we add locking on DH params?
+       gnutls_certificate_set_dh_params (
+               clisrv_certcred,
+               dh_params);
+       gtls_errno_stack0 = gtls_errno;
+       /* TODO: Bad code.  GnuTLS 3.2.1 ignores retrieve_function2 when
+        * checking if it can handle the OpenPGP certificate type in
+        * _gnutls_session_cert_type_supported (gnutls_status.c:175) but
+        * it does see the "1" version field.  It does not callback the
+        * "1" version if "2" is present though.
+        */
+       if (!have_error_codes ()) /* TODO:GnuTLSversions E_g2e (...) */ gnutls_certificate_set_retrieve_function (
+               clisrv_certcred,
+               (void *) exit);
+       if (!have_error_codes ()) /* TODO:GnuTLSversions E_g2e (...) */ gnutls_certificate_set_retrieve_function2 (
+               clisrv_certcred,
+               clisrv_cert_retrieve);
+       if (gtls_errno == GNUTLS_E_SUCCESS) {
+               // Setup for certificates
+               tlog (TLOG_CERT, LOG_INFO, "Setting client and server certificate credentials");
+               cli_creds [cli_credcount].credtp = GNUTLS_CRD_CERTIFICATE;
+               cli_creds [cli_credcount].cred   = (void *) clisrv_certcred;
+               cli_credcount++;
+               srv_creds [srv_credcount].credtp = GNUTLS_CRD_CERTIFICATE;
+               srv_creds [srv_credcount].cred   = (void *) clisrv_certcred;
+               srv_credcount++;
+       } else if (clisrv_certcred != NULL) {
+               gnutls_certificate_free_credentials (clisrv_certcred);
+               clisrv_certcred = NULL;
+       }
 
        //
        // Construct server credentials for SRP authentication
@@ -1552,14 +1797,6 @@ void cleanup_starttls_credentials (void) {
 
 
 /*
- * Check if a given cmd has the given LID_TYPE setup.
- * Return 1 for yes or 0 for no; this is used in priority strings.
- */
-static inline int lidtpsup (struct command *cmd, int lidtp) {
-       return cmd->lids [lidtp - LID_TYPE_MIN].data != NULL;
-}
-
-/*
  * The starttls_thread is a main program for the setup of a TLS connection,
  * either in client mode or server mode.  Note that the distinction between
  * client and server mode is only a TLS concern, but not of interest to the
@@ -1568,75 +1805,361 @@ static inline int lidtpsup (struct command *cmd, int lidtp) {
  * If the STARTTLS operation succeeds, this will be reported back to the
  * application, but the TLS pool will continue to be active in a copycat
  * procedure: encrypting outgoing traffic and decrypting incoming traffic.
- * TODO: Are client and server routines different?
+ *
+ * A new handshake may be initiated with a STARTTLS command with the special
+ * flag PIOF_STARTTLS_RENEGOTIATE and the ctlkey set to a previously setup
+ * TLS connection.  This command runs in a new thread, that cancels the old
+ * one (which it can only do while it is waiting in copycat) and then join
+ * that thread (and its data) with the current one.  This is based on the
+ * ctlkey, which serves to lookup the old thread's data.  When the
+ * connection ends for other reasons than a permitted cancel by another
+ * thread, will the thread cleanup its own resources.  In these situations,
+ * the new command determines the negotiation parameters, and returns identity
+ * information.
+ *
+ * In addition, the remote side may initiate renegotiation.  This is accepted
+ * without further ado (although future versions of the TLS Pool may add a
+ * callback mechanism to get it approved).  The renegotiation now runs under
+ * the originally supplied negotiation parameters.  In case it needs a new
+ * local identity, it may also perform callbacks.  Possibly repeating what
+ * happened before -- but most often, a server will start processing a
+ * protocol and determine that it requires more for the requested level of
+ * service, and then renegotiate.  This is common, for example, with HTTPS
+ * connections that decide they need a client certificate for certain URLs.
+ * The implementation of this facility is currently as unstructured as the
+ * facility itself, namely through a goto.  We may come to the conclusion
+ * that a loop is in fact a warranted alternative, but we're not yet
+ * convinced that this would match with other "structures" in TLS.
+ *
+ * In conclusion, there are three possible ways of running this code:
+ *  1. For a new connection.  Many variables are not known and build up
+ *     in the course of running the function.
+ *  2. After a command requesting renegotiation.  This overtakes the prior
+ *     connection's thread, and copies its data from the ctlkeynode_tls.
+ *     The resulting code has a number of variables filled in already at
+ *     an earlier stage.
+ *  3. After a remote request for renegotiation.  This loops back to an
+ *     earlier phase, but after the thread takeover and ctlkeynode_tls copy
+ *     of the explicit command for renegotation.  Its behaviour is subtly
+ *     different in that it has no command to act on, and so it cannot
+ *     send responses or error codes.  It will however log and shutdown
+ *     as the command-driven options would.  It will not perform callbacks
+ *     for PIOC_STARTTLS_LOCALID_V2 or PIOC_PLAINTEXT_CONNECT_V2.  It will
+ *     however trigger the PIOC_LIDENTRY_CALLBACK_V2 through the separate
+ *     callback command, if one is registered.
+ * Yeah, it's great fun, coding TLS and keeping it both flexible and secure.
  */
 static void *starttls_thread (void *cmd_void) {
-       struct command *cmd;
-       struct pioc_starttls orig_piocdata;
-       uint32_t orig_cmd;
+       struct command *cmd, *replycmd;
+       struct command cmd_copy; // for relooping during renegotiation
+       struct pioc_starttls orig_starttls;
+       uint32_t orig_cmdcode;
        int plainfd = -1;
        int cryptfd = -1;
-       int clientfd;
        gnutls_session_t session;
+       int got_session = 0;
        int gtls_errno = GNUTLS_E_SUCCESS;
        int i;
-       struct credinfo *clisrv_creds;
-       int clisrv_credcount;
-       struct ctlkeynode_tls *ckn;
+       struct ctlkeynode_tls *ckn = NULL;
        uint32_t tout;
+       int forked = 0;
+       int want_remoteid = 1;
+       int got_remoteid = 0;
+       int renegotiating = 0;
+       char *preauth = NULL;
+       unsigned int preauthlen = 0;
+       int taking_over = 0;
+       int my_maxpreauth = 0;
+       int anonpost = 0;
+
+       //
+       // Block thread cancellation -- and re-enable it in copycat()
+       assert (pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL) == 0);
 
        //
        // General thread setup
-       cmd = (struct command *) cmd_void;
+       replycmd = cmd = (struct command *) cmd_void;
        if (cmd == NULL) {
-               send_error (cmd, EINVAL, "Command structure not received");
+               send_error (replycmd, EINVAL, "Command structure not received");
+               assert (pthread_detach (pthread_self ()) == 0);
                return;
        }
        cmd->session_errno = 0;
-       orig_cmd = cmd->cmd.pio_cmd;
-       memcpy (&orig_piocdata, &cmd->cmd.pio_data.pioc_starttls, sizeof (orig_piocdata));
-       cmd->orig_piocdata = &orig_piocdata;
+       cmd->anonpre = 0;
+       orig_cmdcode = cmd->cmd.pio_cmd;
+       memcpy (&orig_starttls, &cmd->cmd.pio_data.pioc_starttls, sizeof (orig_starttls));
+       cmd->orig_starttls = &orig_starttls;
        cryptfd = cmd->passfd;
        cmd->passfd = -1;
+//TODO:TEST Removed here because it is tested below
+/*
        if (cryptfd < 0) {
                tlog (TLOG_UNIXSOCK, LOG_ERR, "No ciphertext file descriptor supplied to TLS Pool");
-               send_error (cmd, EINVAL, "No ciphertext file descriptor supplied to TLS Pool");
+               send_error (replycmd, EINVAL, "No ciphertext file descriptor supplied to TLS Pool");
+               assert (pthread_detach (pthread_self ()) == 0);
                return;
        }
-       clientfd = cmd->clientfd;
+*/
        cmd->session_certificate = (intptr_t) (void *) NULL;
-       cmd->session_privatekey = (intptr_t) (void *) NULL;
+       cmd->session_privatekey  = (intptr_t) (void *) NULL;
+
+       //
+       // In case of renegotiation, lookup the previous ctlkeynode by its
+       // ctlkey.  The fact that we have ckn != NULL indicates that we are
+       // renegotiating in the code below; it will supply information as
+       // we continue to run the TLS process.
+       if (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_RENEGOTIATE) {
+fprintf (stderr, "DEBUG: Got a request to renegotiate existing TLS connection\n");
+               //
+               // Check that no FD was passed (and ended up in cryptfd)
+               if (cryptfd >= 0) {
+                       tlog (TLOG_UNIXSOCK, LOG_ERR, "Renegotiation started with extraneous file descriptor");
+                       send_error (replycmd, EPROTO, "File handle supplied for renegotiation");
+                       close (cryptfd);
+                       assert (pthread_detach (pthread_self ()) == 0);
+                       return;
+               }
+               //
+               // 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);
+               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");
+                       assert (pthread_detach (pthread_self ()) == 0);
+                       return;
+               }
+               //
+               // Now cancel the pthread for this process
+               errno = pthread_cancel (ckn->owner);
+fprintf (stderr, "DEBUG: pthread_cancel returned %d\n", errno);
+               if (errno == 0) {
+                       void *retval;
+                       errno = pthread_join (ckn->owner, &retval);
+fprintf (stderr, "DEBUG: pthread_join returned %d\n", errno);
+               }
+               if (errno != 0) {
+                       tlog (TLOG_UNIXSOCK, LOG_ERR, "Failed to interrupt TLS connection for renegotiation");
+                       send_error (replycmd, errno, "Cannot interrupt TLS connection for renegotiation");
+                       ctlkey_unfind (&ckn->regent);
+                       assert (pthread_detach (pthread_self ()) == 0);
+                       // Do not free the ckn, as the other thread still runs
+                       return;
+               }
+               //
+               // We are in control!  Assimilate the TLS connection data.
+               renegotiating = 1;
+               plainfd = ckn->plainfd;
+               cryptfd = ckn->cryptfd;
+               session = ckn->session;
+               got_session = 1;
+               taking_over = 1;
+               ctlkey_unfind (&ckn->regent);
+       }
+
+       // Then follows the unstructured entry point for the unstructured
+       // request to a TLS connection to renegotiate its security parameters.
+       // Doing this in any other way than with goto would add a lot of
+       // make-belief structure that only existed to make this looping
+       // possible.  We'd rather be honest and admit the lack of structure
+       // 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:
+printf ("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);
+
+       //
+       // If this is server renegotiating, send a request to that end
+       //TODO// Only invoke gnutls_rehandshake() on the server
+       if (renegotiating && (taking_over || anonpost) && (gtls_errno == GNUTLS_E_SUCCESS)) {
+printf ("DEBUG: Invoking gnutls_rehandshake in renegotiation loop\n");
+               gtls_errno = gnutls_rehandshake (session);
+               if (gtls_errno == GNUTLS_E_INVALID_REQUEST) {
+                       // Clients should not do this; be forgiving
+                       gtls_errno = GNUTLS_E_SUCCESS;
+printf ("DEBUG: Client-side invocation flagged as wrong; compensated error\n");
+               }
+       }
+
+       //
+       // When renegotiating TLS security, ensure that it is done securely
+       if (renegotiating && (gnutls_safe_renegotiation_status (session) == 0)) {
+               send_error (replycmd, EPROTO, "Renegotiation requested while secure renegotiation is unavailable on remote");
+               if (cryptfd >= 0) {
+                       close (cryptfd);
+                       cryptfd = -1;
+               }
+               if (plainfd >= 0) {
+                       close (plainfd);
+                       plainfd = -1;
+               }
+               if (ckn != NULL) {
+                       if (ctlkey_unregister (ckn->regent.ctlkey)) {
+                               free (ckn);
+                               ckn = NULL;
+                       }
+               }
+               assert (pthread_detach (pthread_self ()) == 0);
+               return;
+       }
 
        //
-       // Potentially decouple the controlling fd (ctlkey is in orig_piocdata)
+       // Potentially decouple the controlling fd (ctlkey is in orig_starttls)
        if (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_FORK) {
                cmd->cmd.pio_data.pioc_starttls.flags &= ~PIOF_STARTTLS_FORK;
-               clientfd = -1;
+               forked = 1;
        }
 
        //
        // Setup BDB transactions and reset credential datum fields
-       bzero (&cmd->lids, sizeof (cmd->lids)); //TODO: Probably double work?
-       manage_txn_begin (&cmd->txn);
+       if (!anonpost) {
+               bzero (&cmd->lids, sizeof (cmd->lids));
+               manage_txn_begin (&cmd->txn);
+       }
 
        //
        // Permit cancellation of this thread -- TODO: Cleanup?
+//TODO:TEST// Defer setcancelstate untill copycat() activity
+/*
        errno = pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
        if (errno != 0) {
-               send_error (cmd, ESRCH, "STARTTLS handler thread cancellability refused");
+               send_error (replycmd, ESRCH, "STARTTLS handler thread cancellability refused");
+               if (cryptfd >= 0) {
+                       close (cryptfd);
+                       cryptfd = -1;
+               }
+               if (plainfd >= 0) {
+                       close (plainfd);
+                       plainfd = -1;
+               }
+               if (ckn != NULL) {
+                       if (ctlkey_unregister (ckn->regent.ctlkey)) {
+                               free (ckn);
+                               ckn = NULL;
+                       }
+               }
+               manage_txn_rollback (&cmd->txn);
+               assert (pthread_detach (pthread_self ()) == 0);
                return;
        }
+*/
        //
        // Check and setup the plaintext file handle
-       if (cryptfd == -1) {
-               send_error (cmd, EPROTO, "You must supply a TLS-protected socket");
+       if (cryptfd < 0) {
+               send_error (replycmd, EPROTO, "You must supply a TLS-protected socket");
+               if (plainfd >= 0) {
+                       close (plainfd);
+                       plainfd = -1;
+               }
+fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+               if (ckn != NULL) {      /* TODO: CHECK NEEDED? */
+                       if (ctlkey_unregister (ckn->regent.ctlkey)) {
+                               free (ckn);
+                               ckn = NULL;
+                       }
+               }
+               manage_txn_rollback (&cmd->txn);
+               assert (pthread_detach (pthread_self ()) == 0);
                return;
        }
 
        //
-       // Negotiate TLS; split client/server mode setup
-       // Note: GnuTLS cannot yet setup p2p connections
+       // Decide on support for the Anonymous Precursor, based on the
+       // service name and its appearance in the anonpre_registry.
+       // If the remoteid is not interesting to the client then also
+       // support an Anonymous Precursor; we have nothing to loose.
+       cmd->anonpre &= ~ANONPRE_EITHER;
+       if (renegotiating) {
+               ; // Indeed, during renegotiation we always disable ANON-DH
+       } else if (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_IGNORE_REMOTEID) {
+               cmd->anonpre = ANONPRE_EITHER;
+               want_remoteid = 0;
+       } else {
+               int anonpre_regidx =  anonpre_registry_size      >> 1;
+               int anonpre_regjmp = (anonpre_registry_size + 1) >> 1;
+               int cmp;
+               while (anonpre_regjmp > 0) {
+                       anonpre_regjmp = anonpre_regjmp >> 1;
+                       cmp = strncasecmp (anonpre_registry [anonpre_regidx].service,
+                               cmd->cmd.pio_data.pioc_starttls.service,
+                               TLSPOOL_SERVICELEN);
+printf ("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) {
+                               // anonpre_regent matches
+                               cmd->anonpre = anonpre_registry [anonpre_regidx].flags;
+                               break;
+                       } else if (cmp > 0) {
+                               // anonpre_regent too high
+                               anonpre_regidx -= 1 + anonpre_regjmp;
+                               if (anonpre_regidx < 0) {
+                                       anonpre_regidx = 0;
+                               }
+                       } else {
+                               // anonpre_regent too low
+                               anonpre_regidx += 1 + anonpre_regjmp;
+                               if (anonpre_regidx >= anonpre_registry_size) {
+                                       anonpre_regidx = anonpre_registry_size - 1;
+                               }
+                       }
+               }
+       }
+
+       //
+       // Setup flags for client and/or server roles (make sure there is one)
+       if ((!renegotiating) && ((cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_REMOTEROLE_CLIENT) == 0)) {
+               cmd->cmd.pio_data.pioc_starttls.flags &= ~PIOF_STARTTLS_LOCALROLE_SERVER;
+       }
+       if ((!renegotiating) && ((cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_REMOTEROLE_SERVER) == 0)) {
+               cmd->cmd.pio_data.pioc_starttls.flags &= ~PIOF_STARTTLS_LOCALROLE_CLIENT;
+       }
+       if ((cmd->cmd.pio_data.pioc_starttls.flags & (PIOF_STARTTLS_LOCALROLE_CLIENT | PIOF_STARTTLS_LOCALROLE_SERVER)) == 0) {
+               //
+               // Neither a TLS client nor a TLS server
+               //
+               send_error (replycmd, ENOTSUP, "Command not supported");
+               close (cryptfd);
+               if (plainfd >= 0) {
+                       close (plainfd);
+                       plainfd = -1;
+               }
+fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+               if (ckn != NULL) { /* TODO: CHECK NEEDED? */
+                       if (ctlkey_unregister (ckn->regent.ctlkey)) {
+                               free (ckn);
+                               ckn = NULL;
+                       }
+               }
+               manage_txn_rollback (&cmd->txn);
+               assert (pthread_detach (pthread_self ()) == 0);
+               return;
+       }
+
+       //
+       // Setup the TLS session.  Also see doc/p2p-tls.*
+       //
+       // TODO: GnuTLS cannot yet setup p2p connections
+       if (ckn != NULL) {
+               gnutls_session_set_ptr (
+                       session,
+                       cmd);
+               //TODO:DONE?// Clear various settings... creds, flags, modes? CLI/SRV?
+       } else {
+               E_g2e ("Failed to initialise GnuTLS peer session",
+                       gnutls_init (
+                               &session,
+                               (((cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_LOCALROLE_CLIENT)? GNUTLS_CLIENT: 0) |
+                                ((cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_LOCALROLE_SERVER)? GNUTLS_SERVER: 0))
+                               ));
+               if (gtls_errno == GNUTLS_E_SUCCESS) {
+                       got_session = 1;
+                       gnutls_session_set_ptr (
+                               session,
+                               cmd);
+               }
+       }
+       //
+       // Setup client-specific behaviour if needed
        if (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_LOCALROLE_CLIENT) {
+if (!renegotiating) {  //TODO:TEST//
                //
                // Setup as a TLS client
                //
@@ -1649,120 +2172,85 @@ static void *starttls_thread (void *cmd_void) {
                        //TODO:CRASH// srpbits);
                //
                // Setup as a TLS client
-               E_g2e ("Failed to initialise GnuTLS client session",
-                       gnutls_init (
-                               &session,
-                               GNUTLS_CLIENT));
-               if (gtls_errno == GNUTLS_E_SUCCESS) {
-                       gnutls_session_set_ptr (
-                               session,
-                               cmd);
-               }
                //
                // Setup for potential sending of SNI
-               if (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_SEND_SNI) {
+               if ((cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_WITHOUT_SNI) == 0) {
                        char *str = cmd->cmd.pio_data.pioc_starttls.remoteid;
+                       int ofs = 0;
                        int len = 0;
                        while (str [len] && (len < 128)) {
+                               if (str [len] == '@') {
+                                       ofs = len + 1;
+                               }
                                len++;
                        }
-                       if (len == 128) {
-                               send_error (cmd, EINVAL, "Remote ID is not set");
-                               close (cryptfd);
-                               return;
+                       // If no usable remoteid was setup, ignore it
+                       if ((len + ofs > 0) && (len < 128)) {
+                               cmd->cmd.pio_data.pioc_starttls.remoteid [sizeof (cmd->cmd.pio_data.pioc_starttls.remoteid)-1] = '\0';
+                               E_g2e ("Client failed to setup SNI",
+                                       gnutls_server_name_set (
+                                               session,
+                                               GNUTLS_NAME_DNS,
+                                               str + ofs,
+                                               len - ofs));
                        }
-                       cmd->cmd.pio_data.pioc_starttls.remoteid [sizeof (cmd->cmd.pio_data.pioc_starttls.remoteid)-1] = '\0';
-                       E_g2e ("Client failed to setup SNI",
-                               gnutls_server_name_set (
-                                       session,
-                                       GNUTLS_NAME_DNS,
-                                       str,
-                                       len));
                }
+} //TODO:TEST//
                //
                // Setup for client credential installation in this session
-               clisrv_creds     = cli_creds;
-               clisrv_credcount = cli_credcount;
-
-       } else if (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_LOCALROLE_SERVER) {
                //
-               // Setup as a TLS server
-               //
-               E_g2e ("Failed to initialise GnuTLS server session",
-                       gnutls_init (
-                               &session,
-                               GNUTLS_SERVER));
+               // Setup client-specific credentials and priority string
+printf ("DEBUG: Configuring client credentials\n");
+               E_g2e ("Failed to configure GnuTLS as a client",
+                       configure_session (cmd,
+                               session,
+                               anonpost? NULL: cli_creds,
+                               anonpost?    0: cli_credcount, 
+                               cmd->anonpre & ANONPRE_CLIENT));
+       }
+       //
+       // Setup callback to server-specific behaviour if needed
+       if (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_LOCALROLE_SERVER) {
+printf ("DEBUG: Configuring for server credentials callback if %d==0\n", gtls_errno);
+if (!renegotiating) {  //TODO:TEST//
                if (gtls_errno == GNUTLS_E_SUCCESS) {
-                       gnutls_session_set_ptr (session, cmd);
                        gnutls_handshake_set_post_client_hello_function (
                                session,
                                srv_clienthello);
                }
+} //TODO:TEST//
+               //TODO:TEST// configure_session _if_ not setup as a client (too)
                //
                // Setup for server credential installation in this session
-               clisrv_creds     = srv_creds;
-               clisrv_credcount = srv_credcount;
-
-       } else {
-               //
-               // Neither a TLS client nor a TLS server
                //
-               send_error (cmd, ENOTSUP, "Command not supported");
-               close (cryptfd);
-               return;
-       }
-
-       //
-       // Install the shared credentials for the client or server role
-       for (i=0; i<clisrv_credcount; i++) {
-               E_g2e ("Failed to install credentials into TLS session",
-                       gnutls_credentials_set (
-                               session,
-                               clisrv_creds [i].credtp,
-                               clisrv_creds [i].cred  ));
+               // Setup server-specific credentials and priority string
+#if 0
+               if (! (cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_LOCALROLE_CLIENT)) {
+printf ("DEBUG: Configuring server credentials (because it is not a client)\n");
+                       E_g2e ("Failed to configure GnuTLS as a server",
+                               configure_session (cmd,
+                                       session,
+                                       anonpost? NULL: srv_creds,
+                                       anonpost?    0: srv_credcount, 
+                                       cmd->anonpre & ANONPRE_SERVER));
+               }
+#endif
        }
 
        //
        // Prefetch local identities that might be used in this session
-       E_g2e ("Failed to fetch local credentials",
-               fetch_local_credentials (cmd));
+       if (!anonpost) {
+               E_g2e ("Failed to fetch local credentials",
+                       fetch_local_credentials (cmd));
+       }
 
        //
-       // Setup the priority string for this session; this avoids future
-       // credential callbacks that ask for something impossible or
-       // undesired.
-       //
-       // Variation factors:
-       //  - starting configuration (can it be empty?)
-       //  - Configured security parameters (database? variable?)
-       //  - CTYPEs, SRP, ANON-or-not --> fill in as + or - characters
-       //TODO// Support for ANON-DH where appropriate
-       if (gtls_errno == GNUTLS_E_SUCCESS) {
-               char priostr [256];
-               snprintf (priostr, sizeof (priostr)-1,
-                       "NORMAL:"
-                       "%cCTYPE-X.509:"
-                       "%cCTYPE-OPENPGP:"
-                       "%cSRP:%cSRP-RSA:%cSRP-DSS:"
-                       "%cANON-ECDH:%cANON-DH",
-                       lidtpsup (cmd, LID_TYPE_X509)           ?'+':'-',
-                       lidtpsup (cmd, LID_TYPE_PGP)            ?'+':'-',
-                       lidtpsup (cmd, LID_TYPE_SRP)            ?'+':'-',
-                       lidtpsup (cmd, LID_TYPE_SRP)            ?'+':'-',
-                       lidtpsup (cmd, LID_TYPE_SRP)            ?'+':'-',
-                       0 /* TODO: ANON-DH */                   ?'+':'-',
-                       0 /* TODO: ANON-DH */                   ?'+':'-');
-               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",
-                       gnutls_priority_set_direct (
-                       session,
-                       // "NORMAL:-KX-ALL:+SRP:+SRP-RSA:+SRP-DSS",
-                       // "NORMAL:+CTYPE-X.509:-CTYPE-OPENPGP:+CTYPE-X.509",
-                       // "NORMAL:-CTYPE-X.509:+CTYPE-OPENPGP:-CTYPE-X.509",
-                       // "NORMAL:+ANON-ECDH:+ANON-DH",
-                       priostr,
-                       NULL));
+       // Setup a temporary priority string so handshaking can start
+       if ((cmd->cmd.pio_data.pioc_starttls.flags & PIOF_STARTTLS_LOCALROLE_CLIENT) == 0) {
+               E_g2e ("Failed to preconfigure server token priority string",
+                               gnutls_priority_set (
+                                       session,
+                                       priority_normal));
        }
 
        //
@@ -1775,6 +2263,9 @@ static void *starttls_thread (void *cmd_void) {
        // Setup a timeout value as specified in the command, where TLS Pool
        // defines 0 as default and ~0 as infinite (GnuTLS has 0 as infinite).
        tout = cmd->cmd.pio_data.pioc_starttls.timeout;
+if (renegotiating) {
+; // Do not set timeout
+} else
        if (tout == TLSPOOL_TIMEOUT_DEFAULT) {
                gnutls_handshake_set_timeout (session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
        } else if (tout == TLSPOOL_TIMEOUT_INFINITE) {
@@ -1786,26 +2277,168 @@ static void *starttls_thread (void *cmd_void) {
        //
        // Now setup for the GnuTLS handshake
        //
+if (renegotiating) {
+; // Do not setup cryptfd
+} else
        if (gtls_errno == GNUTLS_E_SUCCESS) {
                gnutls_transport_set_int (session, cryptfd);
        }
        if (gtls_errno != GNUTLS_E_SUCCESS) {
                tlog (TLOG_TLS, LOG_ERR, "Failed to prepare for TLS: %s", gnutls_strerror (gtls_errno));
                if (cmd->session_errno) {
-                       send_error (cmd, cmd->session_errno, error_getstring ());
+                       send_error (replycmd, cmd->session_errno, error_getstring ());
                } else {
-                       send_error (cmd, EIO, "Failed to prepare for TLS");
+                       send_error (replycmd, EIO, "Failed to prepare for TLS");
+               }
+               if (got_session) {
+fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
+                       gnutls_deinit (session);
+                       got_session = 0;
                }
                close (cryptfd);
+               if (plainfd >= 0) {
+                       close (plainfd);
+                       plainfd = -1;
+               }
+fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+               if (ckn != NULL) {      /* TODO: CHECK NEEDED? */
+                       if (ctlkey_unregister (ckn->regent.ctlkey)) {
+                               free (ckn);
+                               ckn = NULL;
+                       }
+               }
+               manage_txn_rollback (&cmd->txn);
+               assert (pthread_detach (pthread_self ()) == 0);
                return;
        }
        tlog (TLOG_UNIXSOCK | TLOG_TLS, LOG_DEBUG, "TLS handshake started over %d", cryptfd);
        do {
+               //
+               // Take a rehandshaking step forward.
+               //
                gtls_errno = gnutls_handshake (session);
-        } while ((gtls_errno < 0) && (gnutls_error_is_fatal (gtls_errno) == 0));
+               //
+               // When data is sent before completing
+               // the rehandshake, then it's something
+               // harmless, given the criteria for the
+               // anonpre_registry.  We pass it on and
+               // don't worry about it.  We do report
+               // it though!
+               //
+               // Note: Applications should be willing
+               // to buffer or process such early data
+               // before the handshake is over or else
+               // the handshake will bail out in error.
+               //
+               if (gtls_errno == GNUTLS_E_GOT_APPLICATION_DATA) {
+                       if (my_maxpreauth <= 0) {
+                               tlog (TLOG_COPYCAT, LOG_ERR, "Received unwanted early data before authentication is complete");
+                               break; // Terminate the handshake
+                       } else if (preauth == NULL) {
+                               preauth = malloc (my_maxpreauth);
+                               if (preauth == NULL) {
+                                       gtls_errno = GNUTLS_E_MEMORY_ERROR;
+                                       break; // Terminate the handshake
+                               }
+                       }
+               }
+               if (gtls_errno == GNUTLS_E_GOT_APPLICATION_DATA) {
+                       if (preauthlen >= my_maxpreauth) {
+                               tlog (TLOG_COPYCAT, LOG_ERR, "Received more early data than willing to receive (%d bytes)", my_maxpreauth);
+                               break; // Terminate the handshake
+                       }
+               }
+               if (gtls_errno == GNUTLS_E_GOT_APPLICATION_DATA) {
+                       ssize_t sz;
+                       sz = gnutls_record_recv (session, preauth + preauthlen, my_maxpreauth - preauthlen);
+                       tlog (TLOG_COPYCAT, LOG_DEBUG, "Received %d remote bytes (or error if <0) from %d during anonymous precursor\n", (int) sz, cryptfd);
+                       if (sz > 0) {
+                               preauthlen += sz;
+                               gtls_errno = GNUTLS_E_SUCCESS;
+                       } else {
+                               gtls_errno = sz; // It's actually an error code
+                       }
+               }
+       } while ((gtls_errno < 0) &&
+               //DROPPED// (gtls_errno != GNUTLS_E_GOT_APPLICATION_DATA) &&
+               //DROPPED// (gtls_errno != GNUTLS_E_WARNING_ALERT_RECEIVED) &&
+               (gnutls_error_is_fatal (gtls_errno) == 0));
+       if (gtls_errno == 0) {
+               const gnutls_datum_t *certs;
+               unsigned int num_certs;
+               got_remoteid = 0;
+               switch (gnutls_auth_get_type (session)) { // Peer's cred type
+               case GNUTLS_CRD_CERTIFICATE:
+                       certs = gnutls_certificate_get_peers (session, &num_certs);
+                       if ((certs != NULL) && (num_certs >= 1)) {
+                               got_remoteid = 1;
+                       }
+                       // "certs" points into GnuTLS' internal data structures
+                       break;
+               case GNUTLS_CRD_PSK:
+                       // Difficult... what did the history say about this?
+                       got_remoteid = 0;
+                       break;
+               case GNUTLS_CRD_SRP:
+                       // Got a credential, validation follows later on
+                       //TODO// SRP does not really auth the server
+                       got_remoteid = 1;
+                       break;
+               case GNUTLS_CRD_ANON:
+                       // Did not get a credential, perhaps due to anonpre
+                       got_remoteid = 0;
+                       break;
+               case GNUTLS_CRD_IA:
+                       // Inner Application extension is no true credential
+                       // Should we compare the client-requested service?
+                       // Should we renegotiate into the ALPN protocol?
+                       got_remoteid = 0;
+                       break;
+               default:
+                       // Unknown creds cautiously considered unauthentitcated
+                       got_remoteid = 0;
+                       break;
+               }
+               //
+               // Now recognise and handle the Anonymous Precursor
+               if (((cmd->anonpre & ANONPRE_EITHER) != 0)
+                                       && want_remoteid && !got_remoteid) {
+                       assert (anonpost == 0);
+                       // Disable ANON-protocols but keep creds from before
+                       //TODO:ELSEWHERE// tlog (TLOG_TLS, LOG_DEBUG, "Reconfiguring TLS over %d without Anonymous Precursor\n", cryptfd);
+                       //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// 0));
+                       // We do not want to use ANON-DH if the flag
+                       // ANONPRE_EXTEND_MASTER_SECRET is set for the protocol
+                       // but the remote peer does not support it.  Only if
+                       // this problem cannot possibly occur, permit
+                       // my_maxpreauth > 0 for early data acceptance.
+                       my_maxpreauth = 0;
+                       if (cmd->anonpre & ANONPRE_EXTEND_MASTER_SECRET) {
+#if GNUTLS_VERSION_NUMBER >= 0x030400
+                               gnutls_ext_priv_data_t ext;
+                               if (!gnutls_ext_get_data (session, 23, &ext)) {
+                                       my_maxpreauth = maxpreauth;
+                               }
+#endif
+                       } else {
+                               my_maxpreauth = maxpreauth;
+                       }
+                       if (gtls_errno == 0) {
+                               tlog (TLOG_UNIXSOCK | TLOG_TLS, LOG_DEBUG, "TLS handshake continued over %d after anonymous precursor", cryptfd);
+                               renegotiating = 1; // (de)selects steps
+                               anonpost = 1;      // (de)selects steps
+                               goto renegotiate;
+                       }
+               }
+       }
        if ((gtls_errno == GNUTLS_E_SUCCESS) && cmd->session_errno) {
                gtls_errno = GNUTLS_E_USER_ERROR;
        }
+       taking_over = 0;
 
        //
        // Cleanup any prefetched identities
@@ -1815,6 +2448,15 @@ static void *starttls_thread (void *cmd_void) {
                }
        }
        bzero (cmd->lids, sizeof (cmd->lids));
+
+#if 0
+/* This is not proper.  gnutls_certificate_set_key() suggests that these are
+ * automatically cleaned up, and although this is not repeated in
+ * gnutls_certificate_set_retrieve_function2() it is likely to be related.
+ * Plus, renegotiation with this code in place bogged down on failed pcerts;
+ * they were detected in _gnutls_selected_cert_supported_kx() but their
+ * key exchange algorithm was never found.
+ */
        if (NULL != (void *) cmd->session_privatekey) {
                gnutls_privkey_deinit ((void *) cmd->session_privatekey);
                cmd->session_privatekey = (intptr_t) (void *) NULL;
@@ -1824,20 +2466,20 @@ static void *starttls_thread (void *cmd_void) {
                free ((void *) cmd->session_certificate);
                cmd->session_certificate = (intptr_t) (void *) NULL;
        }
+#endif
 
        //
-       // From here, assume nothing about the cmd structure; as part of the
-       // handshake, it may have passed through the client's control, as
+       // From here, assume nothing about the cmd->cmd structure; as part of
+       // the handshake, it may have passed through the client's control, as
        // part of a callback.  So, reinitialise the entire return structure.
        //TODO// Or backup the (struct pioc_starttls) before handshaking
-       cmd->cmd.pio_cmd = orig_cmd;
+       cmd->cmd.pio_cmd = orig_cmdcode;
        cmd->cmd.pio_data.pioc_starttls.localid  [0] =
        cmd->cmd.pio_data.pioc_starttls.remoteid [0] = 0;
 
        //
        // Respond to positive or negative outcome of the handshake
        if (gtls_errno != GNUTLS_E_SUCCESS) {
-               gnutls_deinit (session);
                tlog (TLOG_TLS, LOG_ERR, "TLS handshake failed: %s", gnutls_strerror (gtls_errno));
                if (cmd->session_errno) {
                        char *errstr;
@@ -1846,12 +2488,32 @@ static void *starttls_thread (void *cmd_void) {
                        if (errstr == NULL) {
                                errstr = "TLS handshake failed";
                        }
-                       send_error (cmd, cmd->session_errno, errstr);
+                       send_error (replycmd, cmd->session_errno, errstr);
                } else {
-                       send_error (cmd, EPERM, "TLS handshake failed");
+                       send_error (replycmd, EPERM, "TLS handshake failed");
+               }
+               if (preauth) {
+                       free (preauth);
+               }
+               if (got_session) {
+fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
+                       gnutls_deinit (session);
+                       got_session = 0;
                }
-               manage_txn_rollback (&cmd->txn);
                close (cryptfd);
+               if (plainfd >= 0) {
+                       close (plainfd);
+                       plainfd = -1;
+               }
+fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+               if (ckn != NULL) {      /* TODO: CHECK NEEDED? */
+                       if (ctlkey_unregister (ckn->regent.ctlkey)) {
+                               free (ckn);
+                               ckn = NULL;
+                       }
+               }
+               manage_txn_rollback (&cmd->txn);
+               assert (pthread_detach (pthread_self ()) == 0);
                return;
         } else {
                tlog (TLOG_UNIXSOCK | TLOG_TLS, LOG_INFO, "TLS handshake succeeded over %d", cryptfd);
@@ -1860,58 +2522,166 @@ static void *starttls_thread (void *cmd_void) {
 
        //
        // Request the plaintext file descriptor with a callback
-       uint32_t oldcmd = cmd->cmd.pio_cmd;
-       struct command *resp;
-       cmd->cmd.pio_cmd = PIOC_PLAINTEXT_CONNECT_V2;
-       tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Calling send_callback_and_await_response with PIOC_PLAINTEXT_CONNECT_V2");
-       resp = send_callback_and_await_response (cmd, 0);
-       assert (resp != NULL);  // No timeout, should be non-NULL
-       if (resp->cmd.pio_cmd != PIOC_PLAINTEXT_CONNECT_V2) {
-               tlog (TLOG_UNIXSOCK, LOG_ERR, "Callback response has unexpected command code");
-               send_error (cmd, EINVAL, "Callback response has bad command code");
-               manage_txn_rollback (&cmd->txn);
-               close (cryptfd);
-               close (plainfd);
-               return;
+       if (plainfd < 0) {
+               uint32_t oldcmd = cmd->cmd.pio_cmd;
+               struct command *resp;
+               cmd->cmd.pio_cmd = PIOC_PLAINTEXT_CONNECT_V2;
+               tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Calling send_callback_and_await_response with PIOC_PLAINTEXT_CONNECT_V2");
+               resp = send_callback_and_await_response (replycmd, 0);
+               assert (resp != NULL);  // No timeout, should be non-NULL
+               if (resp->cmd.pio_cmd != PIOC_PLAINTEXT_CONNECT_V2) {
+                       tlog (TLOG_UNIXSOCK, LOG_ERR, "Callback response has unexpected command code");
+                       send_error (replycmd, EINVAL, "Callback response has bad command code");
+                       if (preauth) {
+                               free (preauth);
+                       }
+                       if (got_session) {
+fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
+                               gnutls_deinit (session);
+                               got_session = 0;
+                       }
+                       close (cryptfd);
+fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+                       if (ckn) {      /* TODO: CHECK NEEDED? PRACTICE=>YES */
+                               if (ctlkey_unregister (ckn->regent.ctlkey)) {
+                                       free (ckn);
+                                       ckn = NULL;
+                               }
+                       }
+                       manage_txn_rollback (&cmd->txn);
+                       assert (pthread_detach (pthread_self ()) == 0);
+                       return;
+               }
+               cmd->cmd.pio_cmd = oldcmd;
+               tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Processing callback response that set plainfd:=%d for lid==\"%s\" and rid==\"%s\"", cmd->passfd, cmd->cmd.pio_data.pioc_starttls.localid, cmd->cmd.pio_data.pioc_starttls.remoteid);
+               plainfd = resp->passfd;
+               resp->passfd = -1;
        }
-       tlog (TLOG_UNIXSOCK, LOG_DEBUG, "Processing callback response that set plainfd:=%d for lid==\"%s\" and rid==\"%s\"", cmd->passfd, cmd->cmd.pio_data.pioc_starttls.localid, cmd->cmd.pio_data.pioc_starttls.remoteid);
-       plainfd = resp->passfd;
-       resp->passfd = -1;
        if (plainfd < 0) {
                tlog (TLOG_UNIXSOCK, LOG_ERR, "No plaintext file descriptor supplied to TLS Pool");
-               send_error (cmd, EINVAL, "No plaintext file descriptor supplied to TLS Pool");
-               manage_txn_rollback (&cmd->txn);
+               send_error (replycmd, EINVAL, "No plaintext file descriptor supplied to TLS Pool");
+               if (preauth) {
+                       free (preauth);
+               }
+               if (got_session) {
+fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
+                       gnutls_deinit (session);
+                       got_session = 0;
+               }
                close (cryptfd);
+fprintf (stderr, "ctlkey_unregister under ckn=0x%x at %d\n", ckn, __LINE__);
+               if (ckn != NULL) {      /* TODO: CHECK NEEDED? */
+                       if (ctlkey_unregister (ckn->regent.ctlkey)) {
+                               free (ckn);
+                               ckn = NULL;
+                       }
+               }
+               manage_txn_rollback (&cmd->txn);
+               assert (pthread_detach (pthread_self ()) == 0);
                return;
        }
-       cmd->cmd.pio_cmd = oldcmd;
-       //DEFERRED// send_command (cmd, -1);            // app sent plainfd to us
+       //DEFERRED// send_command (replycmd, -1);               // app sent plainfd to us
 
        //
        // Copy TLS records until the connection is closed
        manage_txn_commit (&cmd->txn);
-       ckn = (struct ctlkeynode_tls *) malloc (sizeof (struct ctlkeynode_tls));
+       if (!renegotiating) {
+               ckn = (struct ctlkeynode_tls *) malloc (sizeof (struct ctlkeynode_tls));
+       }
        if (ckn == NULL) {
-               send_error (cmd, ENOMEM, "Out of memory allocating control key structure");
+               send_error (replycmd, ENOMEM, "Out of memory allocating control key structure");
        } else {
-               int detach = (orig_piocdata.flags & PIOF_STARTTLS_DETACH) != 0;
+               int detach = (orig_starttls.flags & PIOF_STARTTLS_DETACH) != 0;
                ckn->session = session;
+               ckn->owner = pthread_self ();
+               ckn->cryptfd = cryptfd;
+               ckn->plainfd = plainfd;
 //DEBUG// fprintf (stderr, "Registering control key\n");
-               if (ctlkey_register (orig_piocdata.ctlkey, &ckn->regent, security_tls, detach? -1: cmd->clientfd) == 0) {
-                       send_command (cmd, -1);         // app sent plainfd to us
-                       copycat (plainfd, cryptfd, session, detach? -1: clientfd);
+               if (renegotiating || (ctlkey_register (orig_starttls.ctlkey, &ckn->regent, security_tls, detach? -1: cmd->clientfd, forked) == 0)) {
+                       int copied = GNUTLS_E_SUCCESS;
+                       send_command (replycmd, -1);            // app sent plainfd to us
+                       if (preauth) {
+
+                               //
+                               // Check on extended master secret if desired
+                               if (cmd->anonpre & ANONPRE_EXTEND_MASTER_SECRET) {
+#if GNUTLS_VERSION_NUMBER >= 0x030400
+                                       gnutls_ext_priv_data_t ext;
+                                       if (!gnutls_ext_get_data (session, 23, &ext)) {
+                                               cmd->anonpre &= ~ANONPRE_EXTEND_MASTER_SECRET;
+                                       }
+#endif
+                               }
+                               if (cmd->anonpre & ANONPRE_EXTEND_MASTER_SECRET) {
+                                       tlog (TLOG_COPYCAT, LOG_ERR, "Received %d remote bytes from anonymous precursor but lacking %s-required authentication through extended master secret", orig_starttls.service);
+                                       gtls_errno = GNUTLS_E_LARGE_PACKET;
+                                       copied = 0;
+
+                               } else if (write (plainfd, preauth, preauthlen) == preauthlen) {
+                                       tlog (TLOG_COPYCAT, LOG_DEBUG, "Passed on %d remote bytes from anonymous precursor to %d\n", preauthlen, plainfd);
+                                       free (preauth);
+                                       preauth = NULL;
+                                       copied = copycat (plainfd, cryptfd, session, detach? -1: cmd->clientfd);
+                               } else {
+                                       tlog (TLOG_COPYCAT, LOG_DEBUG, "Failed to pass on %d remote bytes from anonymous precursor to %d\n", preauthlen, plainfd);
+                               }
+                       } else {
+                               copied = copycat (plainfd, cryptfd, session, detach? -1: cmd->clientfd);
+                       }
+                       // Renegotiate if copycat asked us to
+                       if (copied == GNUTLS_E_REHANDSHAKE) {
+                               // Yes, goto is a dirty technique.  On the
+                               // other hand, so is forcing unstructured
+                               // code flows into a make-belief structure
+                               // that needs changing over and over again.
+                               // I fear goto is the most reasonable way
+                               // of handling this rather obtuse structure
+                               // of renegotiation of security in TLS :(
+                               //TODO// Ensure secure renegotiation!!!
+                               renegotiating = 1;
+                               replycmd = NULL; // Bypass all send_XXX()
+                               memcpy (&cmd_copy, cmd, sizeof (cmd_copy));
+                               cmd = &cmd_copy;
+                               memcpy (cmd->cmd.pio_data.pioc_starttls.localid, orig_starttls.localid, sizeof (cmd->cmd.pio_data.pioc_starttls.localid));
+                               memcpy (cmd->cmd.pio_data.pioc_starttls.remoteid, orig_starttls.remoteid, sizeof (cmd->cmd.pio_data.pioc_starttls.remoteid));
+                               cmd->cmd.pio_data.pioc_starttls.flags = orig_starttls.flags & ~PIOF_STARTTLS_LOCALID_CHECK;
+                               // Disabling the flag causing LOCALID_CHECK
+                               // ...and plainfd >= 0 so no PLAINTEXT_CONNECT
+                               // ...so there will be no callbacks to cmd
+printf ("DEBUG: Goto renegotiate with cmd.lid = \"%s\" and orig_cmd.lid = \"%s\" and cmd.rid = \"%s\" and orig_cmd.rid = \"%s\" and cmd.flags = 0x%x and orig_cmd.flags = 0x%x\n", cmd->cmd.pio_data.pioc_starttls.localid, orig_starttls.localid, cmd->cmd.pio_data.pioc_starttls.remoteid, orig_starttls.remoteid, cmd->cmd.pio_data.pioc_starttls.flags, orig_starttls.flags);
+                               goto renegotiate;
+                       }
 //DEBUG// fprintf (stderr, "Unregistering control key\n");
-                       if (ctlkey_unregister (orig_piocdata.ctlkey)) {
+                       // Unregister by ctlkey, which should always succeed
+                       // if the TLS connection hadn't been closed down yet;
+                       // and if it does, the memory can be freed.  Note that
+                       // the ctlkey is not taken from the ckn, which may
+                       // 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__);
+                       if (ctlkey_unregister (orig_starttls.ctlkey)) {
                                free (ckn);
                        }
                        ckn = NULL;
 //DEBUG// fprintf (stderr, "Unregistered  control key\n");
                } else {
-                       send_error (cmd, ENOENT, "Failed to register control key for TLS connection");
+                       send_error (replycmd, ENOENT, "Failed to register control key for TLS connection");
                }
        }
+       if (preauth) {
+               free (preauth);
+               preauth = NULL;
+       }
        close (plainfd);
        close (cryptfd);
+       if (got_session) {
+fprintf (stderr, "gnutls_deinit (0x%x) at %d\n", session, __LINE__);
+               gnutls_deinit (session);
+               got_session = 0;
+       }
+       assert (pthread_detach (pthread_self ()) == 0);
+       return;
 }
 
 
@@ -1928,12 +2698,15 @@ void starttls (struct command *cmd) {
                send_error (cmd, ESRCH, "STARTTLS thread refused");
                return;
        }
+//TODO:TEST// Thread detaches itself before terminating w/o followup
+/*
        errno = pthread_detach (cmd->handler);
        if (errno != 0) {
                pthread_cancel (cmd->handler);
                send_error (cmd, ESRCH, "STARTTLS thread detachment refused");
                return;
        }
+*/
 }
 
 
index 6b7943e..2a1ddd3 100644 (file)
@@ -1,6 +1,6 @@
 AM_CFLAGS = -I$(srcdir)/../include -pthread
 AM_LDFLAGS = -L$(srcdir)/../lib
-bin_PROGRAMS = tlstunnel testcli testsrv lidsel
+bin_PROGRAMS = tlstunnel testcli testsrv testpeer lidsel
 dist_bin_SCRIPTS = set_disclose set_localid get_localid
 tlstunnel_SOURCES = tlstunnel.c tlstunnel-chat.c
 tlstunnel_LDADD = ../lib/libtlspool.la
@@ -8,5 +8,7 @@ testcli_SOURCES = testcli.c
 testcli_LDADD = ../lib/libtlspool.la
 testsrv_SOURCES = testsrv.c
 testsrv_LDADD = ../lib/libtlspool.la
+testpeer_SOURCES = testpeer.c
+testpeer_LDADD = ../lib/libtlspool.la
 lidsel_SOURCES = lidsel.c
 lidsel_LDADD = ../lib/libtlspool.la
index ccd77fa..66bb9f6 100755 (executable)
@@ -317,7 +317,7 @@ class PrivateChannel ():
                if self.server:
                        roles = tlspool.PIOF_STARTTLS_LOCALROLE_SERVER | tlspool.PIOF_STARTTLS_REMOTEROLE_CLIENT | tlspool.PIOF_STARTTLS_DETACH
                else:
-                       roles = tlspool.PIOF_STARTTLS_LOCALROLE_CLIENT | tlspool.PIOF_STARTTLS_REMOTEROLE_SERVER | tlspool.PIOF_STARTTLS_SEND_SNI | tlspool.PIOF_STARTTLS_DETACH
+                       roles = tlspool.PIOF_STARTTLS_LOCALROLE_CLIENT | tlspool.PIOF_STARTTLS_REMOTEROLE_SERVER | tlspool.PIOF_STARTTLS_DETACH
                print 'Requesting STARTTLS'
                tlsdata = {
                        'flags': roles,
index 78533c5..21563af 100755 (executable)
@@ -68,7 +68,7 @@ class TLSconnection (threading.Thread):
                if self.server:
                        roles = tlspool.PIOF_STARTTLS_LOCALROLE_SERVER | tlspool.PIOF_STARTTLS_REMOTEROLE_CLIENT | tlspool.PIOF_STARTTLS_DETACH
                else:
-                       roles = tlspool.PIOF_STARTTLS_LOCALROLE_CLIENT | tlspool.PIOF_STARTTLS_REMOTEROLE_SERVER | tlspool.PIOF_STARTTLS_SEND_SNI | tlspool.PIOF_STARTTLS_DETACH
+                       roles = tlspool.PIOF_STARTTLS_LOCALROLE_CLIENT | tlspool.PIOF_STARTTLS_REMOTEROLE_SERVER | tlspool.PIOF_STARTTLS_DETACH
                print 'Requesting STARTTLS'
                tlsdata = {
                        'flags': roles,
index 0ba6fb6..5bf66ca 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <poll.h>
 #include <errno.h>
+#include <signal.h>
 
 #include <sys/socket.h>
 #include <netinet/in.h>
 
 
 static starttls_t tlsdata_cli = {
-       .flags = PIOF_STARTTLS_SEND_SNI
-               | PIOF_STARTTLS_LOCALROLE_CLIENT
+       .flags =  PIOF_STARTTLS_LOCALROLE_CLIENT
                | PIOF_STARTTLS_REMOTEROLE_SERVER,
        .local = 0,
        .ipproto = IPPROTO_TCP,
        .localid = "testcli@tlspool.arpa2.lab",
        .remoteid = "testsrv@tlspool.arpa2.lab",
+       .service = "generic_anonpre",
 };
 
+void sigcont_handler (int signum);
+static struct sigaction sigcont_action = {
+       .sa_handler = sigcont_handler,
+       .sa_mask = 0,
+       .sa_flags = SA_NODEFER
+};
+
+static int sigcont = 0;
 
 void runterminal (int chanio) {
        struct pollfd inout [2];
@@ -32,15 +41,33 @@ void runterminal (int chanio) {
        inout [1].fd = chanio;
        inout [0].events = inout [1].events = POLLIN;
        while (1) {
+               if (sigcont) {
+                       sigcont = 0;
+                       printf ("Received SIGCONT, will now initiate TLS handshake renegotiation\n");
+                       tlsdata_cli.flags =  PIOF_STARTTLS_LOCALROLE_CLIENT
+                                       | PIOF_STARTTLS_REMOTEROLE_SERVER
+                                       | PIOF_STARTTLS_RENEGOTIATE;
+                       strcpy (tlsdata_cli.localid, "testcli@tlspool.arpa2.lab");
+                       strcpy (tlsdata_cli.remoteid, "testsrv@tlspool.arpa2.lab");
+                       if (-1 == tlspool_starttls (-1, &tlsdata_cli, NULL, NULL)) {
+                               printf ("TLS handshake renegotiation failed, terminating\n");
+                               break;
+                       }
+                       printf ("TLS handshake renegotiation completed successfully\n");
+               }
                if (poll (inout, 2, -1) == -1) {
-                       break;
+                       if (sigcont) {
+                               continue;
+                       } else {
+                               break;
+                       }
                }
                if ((inout [0].revents | inout [1].revents) & ~POLLIN) {
                        break;
                }
                if (inout [0].revents & POLLIN) {
                        sz = read (0, buf, sizeof (buf), MSG_DONTWAIT);
-                       printf ("Read %d bytes\n", sz);
+                       printf ("Read %d bytes, sigcont==%d (should be 0 for proper operation)\n", sz, sigcont);
                        if (sz == -1) {
                                break;
                        } else if (sz == 0) {
@@ -54,7 +81,7 @@ void runterminal (int chanio) {
                }
                if (inout [1].revents & POLLIN) {
                        sz = read (chanio, buf, sizeof (buf), MSG_DONTWAIT);
-                       printf ("Received %d bytes\n", sz);
+                       printf ("Received %d bytes, sigcont==%d (should be 0 for proper operation)\n", sz, sigcont);
                        if (sz == -1) {
                                break;
                        } else if (sz == 0) {
@@ -70,10 +97,25 @@ void runterminal (int chanio) {
 }
 
 
+void sigcont_handler (int signum) {
+       sigcont = 1;
+}
+
+
 int main (int argc, char *argv) {
-       int sox;
        int plainfd;
+       int sox;
        struct sockaddr_in6 sin6;
+       sigset_t sigcontset;
+
+       if (sigemptyset (&sigcontset) ||
+           sigaddset (&sigcontset, SIGCONT) ||
+           pthread_sigmask (SIG_BLOCK, &sigcontset, NULL)) {
+               perror ("Failed to block SIGCONT in worker threads");
+               exit (1);
+       }
+
+reconnect:
        sox = socket (AF_INET6, SOCK_STREAM, 0);
        if (sox == -1) {
                perror ("Failed to create socket on testcli");
@@ -85,6 +127,11 @@ int main (int argc, char *argv) {
        memcpy (&sin6.sin6_addr, &in6addr_loopback, 16);
        if (connect (sox, (struct sockaddr *) &sin6, sizeof (sin6)) == -1) {
                perror ("Socket failed to connect on testcli");
+               if (errno == ECONNREFUSED) {
+                       close (sox);
+                       sleep (1);
+                       goto reconnect;
+               }
                exit (1);
        }
        plainfd = -1;
@@ -112,6 +159,17 @@ int main (int argc, char *argv) {
        if (tlspool_control_reattach (tlsdata_cli.ctlkey) != -1) {
                printf ("ERROR: Could reattach the control twice?!?\n");
        }
+       if (-1 == sigaction (SIGCONT, &sigcont_action, NULL)) {
+               perror ("Failed to install signal handler for SIGCONT");
+               close (plainfd);
+               exit (1);
+       } else if (pthread_sigmask (SIG_UNBLOCK, &sigcontset, NULL)) {
+               perror ("Failed to unblock SIGCONT on terminal handler");
+               close (plainfd);
+               exit (1);
+       } else {
+               printf ("SIGCONT will trigger renegotiation of the TLS handshake\n");
+       }
        printf ("DEBUG: Local plainfd = %d\n", plainfd);
        runterminal (plainfd);
        close (plainfd);
diff --git a/tool/testpeer.c b/tool/testpeer.c
new file mode 100644 (file)
index 0000000..a72e01d
--- /dev/null
@@ -0,0 +1,175 @@
+/* tlspool/testpeer.c -- Exchange plaintext stdio over the network */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <poll.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <tlspool/starttls.h>
+
+
+static starttls_t tlsdata_cli = {
+       .flags =  PIOF_STARTTLS_LOCALROLE_PEER
+               | PIOF_STARTTLS_REMOTEROLE_PEER,
+       .local = 0,
+       .ipproto = IPPROTO_TCP,
+       .localid = "",
+       .remoteid = "",
+       // .localid = "testcli@tlspool.arpa2.lab",
+       // .remoteid = "testsrv@tlspool.arpa2.lab",
+       .service = "generic_anonpre",
+};
+
+void sigcont_handler (int signum);
+static struct sigaction sigcont_action = {
+       .sa_handler = sigcont_handler,
+       .sa_mask = 0,
+       .sa_flags = SA_NODEFER
+};
+
+static int sigcont = 0;
+
+void runterminal (int chanio) {
+       struct pollfd inout [2];
+       ssize_t sz;
+       char buf [512];
+       inout [0].fd = 0;
+       inout [1].fd = chanio;
+       inout [0].events = inout [1].events = POLLIN;
+       while (1) {
+               if (sigcont) {
+                       sigcont = 0;
+                       printf ("Received SIGCONT, will now initiate TLS handshake renegotiation\n");
+                       tlsdata_cli.flags |= PIOF_STARTTLS_RENEGOTIATE;
+                       strcpy (tlsdata_cli.remoteid, "testsrv@tlspool.arpa2.lab");
+                       if (-1 == tlspool_starttls (-1, &tlsdata_cli, NULL, NULL)) {
+                               printf ("TLS handshake renegotiation failed, terminating\n");
+                               break;
+                       }
+                       printf ("TLS handshake renegotiation completed successfully\n");
+               }
+               if (poll (inout, 2, -1) == -1) {
+                       if (sigcont) {
+                               continue;
+                       } else {
+                               break;
+                       }
+               }
+               if ((inout [0].revents | inout [1].revents) & ~POLLIN) {
+                       break;
+               }
+               if (inout [0].revents & POLLIN) {
+                       sz = read (0, buf, sizeof (buf), MSG_DONTWAIT);
+                       printf ("Read %d bytes, sigcont==%d (should be 0 for proper operation)\n", sz, sigcont);
+                       if (sz == -1) {
+                               break;
+                       } else if (sz == 0) {
+                               errno = 0;
+                               break;
+                       } else if (write (chanio, buf, sz, MSG_DONTWAIT) != sz) {
+                               break;
+                       } else {
+                               printf ("Sent %d bytes\n", sz);
+                       }
+               }
+               if (inout [1].revents & POLLIN) {
+                       sz = read (chanio, buf, sizeof (buf), MSG_DONTWAIT);
+                       printf ("Received %d bytes, sigcont==%d (should be 0 for proper operation)\n", sz, sigcont);
+                       if (sz == -1) {
+                               break;
+                       } else if (sz == 0) {
+                               errno = 0;
+                               break;
+                       } else if (write (1, buf, sz, MSG_DONTWAIT) != sz) {
+                               break;
+                       } else {
+                               printf ("Printed %d bytes\n", sz);
+                       }
+               }
+       }
+}
+
+
+void sigcont_handler (int signum) {
+       sigcont = 1;
+}
+
+
+int main (int argc, char *argv) {
+       int plainfd;
+       int sox;
+       struct sockaddr_in6 sin6;
+       sigset_t sigcontset;
+
+       fprintf (stderr, "FATAL: This code is untested due to TLS Pool and GnuTLS readiness\n");
+       exit (1);
+
+       if (sigemptyset (&sigcontset) ||
+           sigaddset (&sigcontset, SIGCONT) ||
+           pthread_sigmask (SIG_BLOCK, &sigcontset, NULL)) {
+               perror ("Failed to block SIGCONT in worker threads");
+               exit (1);
+       }
+       sox = socket (AF_INET6, SOCK_STREAM, 0);
+       if (sox == -1) {
+               perror ("Failed to create socket on testcli");
+               exit (1);
+       }
+       bzero (&sin6, sizeof (sin6));
+       sin6.sin6_family = AF_INET6;
+       sin6.sin6_port = htons (12345);
+       memcpy (&sin6.sin6_addr, &in6addr_loopback, 16);
+       if (connect (sox, (struct sockaddr *) &sin6, sizeof (sin6)) == -1) {
+               perror ("Socket failed to connect on testcli");
+               exit (1);
+       }
+       plainfd = -1;
+       if (-1 == tlspool_starttls (sox, &tlsdata_cli, &plainfd, NULL)) {
+               perror ("Failed to STARTTLS on testcli");
+               if (plainfd >= 0) {
+                       close (plainfd);
+               }
+               exit (1);
+       }
+       printf ("DEBUG: STARTTLS succeeded on testcli\n");
+       // Play around, just for fun, with the control key
+       if (tlspool_control_reattach (tlsdata_cli.ctlkey) != -1) {
+               printf ("ERROR: Could reattach before detaching the control?!?\n");
+       }
+       if (tlspool_control_detach (tlsdata_cli.ctlkey) == -1) {
+               printf ("ERROR: Could not detach the control?!?\n");
+       }
+       if (tlspool_control_detach (tlsdata_cli.ctlkey) != -1) {
+               printf ("ERROR: Could detach the control twice?!?\n");
+       }
+       if (tlspool_control_reattach (tlsdata_cli.ctlkey) == -1) {
+               printf ("ERROR: Could not reattach the control?!?\n");
+       }
+       if (tlspool_control_reattach (tlsdata_cli.ctlkey) != -1) {
+               printf ("ERROR: Could reattach the control twice?!?\n");
+       }
+       if (-1 == sigaction (SIGCONT, &sigcont_action, NULL)) {
+               perror ("Failed to install signal handler for SIGCONT");
+               close (plainfd);
+               exit (1);
+       } else if (pthread_sigmask (SIG_UNBLOCK, &sigcontset, NULL)) {
+               perror ("Failed to unblock SIGCONT on terminal handler");
+               close (plainfd);
+               exit (1);
+       } else {
+               printf ("SIGCONT will trigger renegotiation of the TLS handshake\n");
+       }
+       printf ("DEBUG: Local plainfd = %d\n", plainfd);
+       runterminal (plainfd);
+       close (plainfd);
+       printf ("DEBUG: Closed connection.  Waiting 2s to improve testing.\n");
+       sleep (2);
+       return 0;
+}
+
index ced7a45..f58c852 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <poll.h>
 #include <errno.h>
+#include <signal.h>
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -20,7 +21,18 @@ static starttls_t tlsdata_srv = {
        .local = 0,
        .ipproto = IPPROTO_TCP,
        .localid = "testsrv@tlspool.arpa2.lab",
+       .service = "generic_anonpre",
 };
+static starttls_t tlsdata_now;
+
+void sigcont_handler (int signum);
+static struct sigaction sigcont_action = {
+       .sa_handler = sigcont_handler,
+       .sa_mask = 0,
+       .sa_flags = SA_NODEFER
+};
+
+static int sigcont = 0;
 
 void runterminal (int chanio) {
        struct pollfd inout [2];
@@ -30,15 +42,32 @@ void runterminal (int chanio) {
        inout [1].fd = chanio;
        inout [0].events = inout [1].events = POLLIN;
        while (1) {
+               if (sigcont) {
+                       sigcont = 0;
+                       printf ("Received SIGCONT, will now initiate TLS handshake renegotiation\n");
+                       tlsdata_now.flags = PIOF_STARTTLS_LOCALROLE_SERVER
+                                       | PIOF_STARTTLS_REMOTEROLE_CLIENT
+                                       | PIOF_STARTTLS_RENEGOTIATE;
+                       strcpy (tlsdata_now.localid, "testsrv@tlspool.arpa2.lab");
+                       if (-1 == tlspool_starttls (-1, &tlsdata_now, NULL, NULL)) {
+                               printf ("TLS handshake renegotiation failed, terminating\n");
+                               break;
+                       }
+                       printf ("TLS handshake renegotiation completed successfully\n");
+               }
                if (poll (inout, 2, -1) == -1) {
-                       break;
+                       if (sigcont) {
+                               continue;
+                       } else {
+                               break;
+                       }
                }
                if ((inout [0].revents | inout [1].revents) & ~POLLIN) {
                        break;
                }
                if (inout [0].revents & POLLIN) {
                        sz = read (0, buf, sizeof (buf), MSG_DONTWAIT);
-                       printf ("Read %d bytes\n", sz);
+                       printf ("Read %d bytes, sigcont==%d (should be 0 for proper operation)\n", sz, sigcont);
                        if (sz == -1) {
                                break;
                        } else if (sz == 0) {
@@ -52,7 +81,7 @@ void runterminal (int chanio) {
                }
                if (inout [1].revents & POLLIN) {
                        sz = read (chanio, buf, sizeof (buf), MSG_DONTWAIT);
-                       printf ("Received %d bytes\n", sz);
+                       printf ("Received %d bytes, sigcont==%d (should be 0 for proper operation)\n", sz, sigcont);
                        if (sz == -1) {
                                break;
                        } else if (sz == 0) {
@@ -67,10 +96,24 @@ void runterminal (int chanio) {
        }
 }
 
+void sigcont_handler (int signum) {
+       sigcont = 1;
+}
+
 int main (int argc, char *argv) {
        int sox, cnx;
        int plainfd;
        struct sockaddr_in6 sin6;
+       sigset_t sigcontset;
+
+       if (sigemptyset (&sigcontset) ||
+           sigaddset (&sigcontset, SIGCONT) ||
+           pthread_sigmask (SIG_BLOCK, &sigcontset, NULL)) {
+               perror ("Failed to block SIGCONT in worker thread");
+               exit (1);
+       }
+
+reconnect:
        sox = socket (AF_INET6, SOCK_STREAM, 0);
        if (sox == -1) {
                perror ("Failed to create socket on testsrv");
@@ -82,6 +125,11 @@ int main (int argc, char *argv) {
        memcpy (&sin6.sin6_addr, &in6addr_loopback, 16);
        if (bind (sox, (struct sockaddr *) &sin6, sizeof (sin6)) == -1) {
                perror ("Socket failed to bind on testsrv");
+               if (errno == EADDRINUSE) {
+                       close (sox);
+                       sleep (1);
+                       goto reconnect;
+               }
                exit (1);
        }
        if (listen (sox, 5) == -1) {
@@ -93,7 +141,7 @@ int main (int argc, char *argv) {
                        perror ("Failed to accept incoming connection");
                        continue;
                }
-               starttls_t tlsdata_now = tlsdata_srv;
+               tlsdata_now = tlsdata_srv;
                plainfd = -1;
                if (-1 == tlspool_starttls (cnx, &tlsdata_now, &plainfd, NULL)) {
                        perror ("Failed to STARTTLS on testsrv");
@@ -103,10 +151,32 @@ int main (int argc, char *argv) {
                        exit (1);
                }
                printf ("DEBUG: STARTTLS succeeded on testsrv\n");
+               if (sigcont) {
+                       printf ("Ignoring SIGCONT received prior to the new connection\n");
+                       sigcont = 0;
+               }
+               if (-1 == sigaction (SIGCONT, &sigcont_action, NULL)) {
+                       perror ("Failed to install signal handler for SIGCONT");
+                       close (plainfd);
+                       close (sox);
+                       exit (1);
+               } else if (pthread_sigmask (SIG_UNBLOCK, &sigcontset, NULL))  {
+                       perror ("Failed to unblock SIGCONT on terminal handler");
+                       close (plainfd);
+                       close (sox);
+                       exit (1);
+               } else {
+                       printf ("SIGCONT will trigger renegotiation of the TLS handshake during a connection\n");
+               }
                printf ("DEBUG: Local plainfd = %d\n", plainfd);
                runterminal (plainfd);
                printf ("DEBUG: Client connection terminated\n");
                close (plainfd);
+               if (pthread_sigmask (SIG_BLOCK, &sigcontset, NULL))  {
+                       perror ("Failed to block signal handler for SIGCONT");
+                       close (sox);
+                       exit (1);
+               }
        }
        return 0;
 }
index 33ac632..71791aa 100644 (file)
@@ -565,7 +565,7 @@ int main (int argc, char *argv []) {
        }
        tlsdata.flags |= (sctpdtls? PIOF_STARTTLS_DTLS: 0);     //TODO// Later
        if (role == 'c') {
-               tlsdata.flags |= PIOF_STARTTLS_SEND_SNI;
+               //DO-SEND-SNI// tlsdata.flags |= PIOF_STARTTLS_WITHOUT_SNI;
        }
        if (tlsfork) {
                tlsdata.flags |= PIOF_STARTTLS_FORK;