diff --git a/src/charon/plugins/eap_tls/eap_tls.c b/src/charon/plugins/eap_tls/eap_tls.c index fddd5073e..cf4294970 100644 --- a/src/charon/plugins/eap_tls/eap_tls.c +++ b/src/charon/plugins/eap_tls/eap_tls.c @@ -335,9 +335,17 @@ METHOD(eap_method_t, process, status_t, if (!(pkt->flags & EAP_TLS_START)) { if (data.len == sizeof(eap_tls_packet_t)) - { /* ACK to our fragment, send next */ - *out = read_buf(this, pkt->identifier); - return NEED_MORE; + { + if (this->output.len) + { /* ACK to our fragment, send next */ + *out = read_buf(this, pkt->identifier); + return NEED_MORE; + } + if (this->tls->is_complete(this->tls)) + { + return SUCCESS; + } + return FAILED; } if (!write_buf(this, pkt)) { diff --git a/src/charon/plugins/eap_tls/tls/tls.c b/src/charon/plugins/eap_tls/tls/tls.c index 39a46e2ce..ab03037fa 100644 --- a/src/charon/plugins/eap_tls/tls/tls.c +++ b/src/charon/plugins/eap_tls/tls/tls.c @@ -144,6 +144,12 @@ METHOD(tls_t, set_version, void, this->version = version; } +METHOD(tls_t, is_complete, bool, + private_tls_t *this) +{ + return this->crypto->get_eap_msk(this->crypto).len != 0; +} + METHOD(tls_t, get_eap_msk, chunk_t, private_tls_t *this) { @@ -179,6 +185,7 @@ tls_t *tls_create(bool is_server, identification_t *server, .is_server = _is_server, .get_version = _get_version, .set_version = _set_version, + .is_complete = _is_complete, .get_eap_msk = _get_eap_msk, .destroy = _destroy, }, diff --git a/src/charon/plugins/eap_tls/tls/tls.h b/src/charon/plugins/eap_tls/tls/tls.h index f40a59a02..283f591e7 100644 --- a/src/charon/plugins/eap_tls/tls/tls.h +++ b/src/charon/plugins/eap_tls/tls/tls.h @@ -134,6 +134,13 @@ struct tls_t { */ void (*set_version)(tls_t *this, tls_version_t version); + /** + * Check if TLS negotiation completed successfully. + * + * @return TRUE if TLS negotation and authentication complete + */ + bool (*is_complete)(tls_t *this); + /** * Get the MSK for EAP-TLS. * diff --git a/src/charon/plugins/eap_tls/tls/tls_crypto.c b/src/charon/plugins/eap_tls/tls/tls_crypto.c index 789b94289..f8894629f 100644 --- a/src/charon/plugins/eap_tls/tls/tls_crypto.c +++ b/src/charon/plugins/eap_tls/tls/tls_crypto.c @@ -425,42 +425,81 @@ static bool hash_handshake(private_tls_crypto_t *this, chunk_t *hash) } METHOD(tls_crypto_t, sign_handshake, bool, - private_tls_crypto_t *this, private_key_t *key, chunk_t *sig) + private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer) { + chunk_t sig, hash; + if (this->tls->get_version(this->tls) >= TLS_1_2) { - u_int16_t length; - u_int8_t hash_alg; - u_int8_t sig_alg; - - if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, sig)) + /* TODO: use supported algorithms instead of fixed SHA1/RSA */ + if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, &sig)) { return FALSE; } - /* TODO: signature scheme to hashsign algorithm mapping */ - hash_alg = 2; /* sha1 */ - sig_alg = 1; /* RSA */ - length = htons(sig->len); - *sig = chunk_cat("cccm", chunk_from_thing(hash_alg), - chunk_from_thing(sig_alg), chunk_from_thing(length), *sig); + writer->write_uint8(writer, 2); + writer->write_uint8(writer, 1); + writer->write_data16(writer, sig); + free(sig.ptr); } else { - u_int16_t length; - chunk_t hash; - if (!hash_handshake(this, &hash)) { return FALSE; } - if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig)) + if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig)) + { + free(hash.ptr); + return FALSE; + } + writer->write_data16(writer, sig); + free(hash.ptr); + free(sig.ptr); + } + return TRUE; +} + +METHOD(tls_crypto_t, verify_handshake, bool, + private_tls_crypto_t *this, public_key_t *key, tls_reader_t *reader) +{ + if (this->tls->get_version(this->tls) >= TLS_1_2) + { + u_int8_t hash, alg; + chunk_t sig; + + if (!reader->read_uint8(reader, &hash) || + !reader->read_uint8(reader, &alg) || + !reader->read_data16(reader, &sig)) + { + DBG1(DBG_IKE, "received invalid Certificate Verify"); + return FALSE; + } + /* TODO: map received hash/sig alg to signature scheme */ + if (hash != 2 || alg != 1 || + !key->verify(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, sig)) + { + return FALSE; + } + } + else + { + chunk_t sig, hash; + + if (!reader->read_data16(reader, &sig)) + { + DBG1(DBG_IKE, "received invalid Certificate Verify"); + return FALSE; + } + if (!hash_handshake(this, &hash)) + { + return FALSE; + } + if (!key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig)) { free(hash.ptr); return FALSE; } free(hash.ptr); - length = htons(sig->len); - *sig = chunk_cat("cm", chunk_from_thing(length), *sig); } return TRUE; } @@ -635,6 +674,7 @@ tls_crypto_t *tls_crypto_create(tls_t *tls) .set_protection = _set_protection, .append_handshake = _append_handshake, .sign_handshake = _sign_handshake, + .verify_handshake = _verify_handshake, .calculate_finished = _calculate_finished, .derive_secrets = _derive_secrets, .change_cipher = _change_cipher, diff --git a/src/charon/plugins/eap_tls/tls/tls_crypto.h b/src/charon/plugins/eap_tls/tls/tls_crypto.h index 49bc09c99..69b8da742 100644 --- a/src/charon/plugins/eap_tls/tls/tls_crypto.h +++ b/src/charon/plugins/eap_tls/tls/tls_crypto.h @@ -116,10 +116,21 @@ struct tls_crypto_t { * Create a signature of the handshake data using a given private key. * * @param key private key to use for signature - * @param sig allocated signature + * @param writer TLS writer to write signature to * @return TRUE if signature create successfully */ - bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key, chunk_t *sig); + bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key, + tls_writer_t *writer); + + /** + * Verify the signature over handshake data using a given public key. + * + * @param key public key to verify signature with + * @param reader TLS reader to read signature from + * @return TRUE if signature valid + */ + bool (*verify_handshake)(tls_crypto_t *this, public_key_t *key, + tls_reader_t *reader); /** * Calculate the data of a TLS finished message. diff --git a/src/charon/plugins/eap_tls/tls/tls_peer.c b/src/charon/plugins/eap_tls/tls/tls_peer.c index c52da6dfd..21bf77c36 100644 --- a/src/charon/plugins/eap_tls/tls/tls_peer.c +++ b/src/charon/plugins/eap_tls/tls/tls_peer.c @@ -172,7 +172,7 @@ static status_t process_certificate(private_tls_peer_t *this, if (first) { this->server_auth->add(this->server_auth, - AUTH_RULE_SUBJECT_CERT, cert); + AUTH_HELPER_SUBJECT_CERT, cert); DBG1(DBG_IKE, "received TLS server certificate '%Y'", cert->get_subject(cert)); first = FALSE; @@ -182,7 +182,7 @@ static status_t process_certificate(private_tls_peer_t *this, DBG1(DBG_IKE, "received TLS intermediate certificate '%Y'", cert->get_subject(cert)); this->server_auth->add(this->server_auth, - AUTH_RULE_IM_CERT, cert); + AUTH_HELPER_IM_CERT, cert); } } else @@ -486,6 +486,7 @@ static status_t send_key_exchange(private_tls_peer_t *this, DBG1(DBG_IKE, "encrypting TLS premaster secret failed"); return FAILED; } + public->destroy(public); writer->write_data16(writer, encrypted); @@ -503,16 +504,12 @@ static status_t send_key_exchange(private_tls_peer_t *this, static status_t send_certificate_verify(private_tls_peer_t *this, tls_handshake_type_t *type, tls_writer_t *writer) { - chunk_t signature; - if (!this->private || - !this->crypto->sign_handshake(this->crypto, this->private, &signature)) + !this->crypto->sign_handshake(this->crypto, this->private, writer)) { DBG1(DBG_IKE, "creating TLS Certificate Verify signature failed"); return FAILED; } - writer->write_data(writer, signature); - free(signature.ptr); *type = TLS_CERTIFICATE_VERIFY; this->state = STATE_VERIFY_SENT; diff --git a/src/charon/plugins/eap_tls/tls/tls_server.c b/src/charon/plugins/eap_tls/tls/tls_server.c index 8d5e016db..ba873c847 100644 --- a/src/charon/plugins/eap_tls/tls/tls_server.c +++ b/src/charon/plugins/eap_tls/tls/tls_server.c @@ -15,10 +15,29 @@ #include "tls_server.h" +#include + #include typedef struct private_tls_server_t private_tls_server_t; + +typedef enum { + STATE_INIT, + STATE_HELLO_RECEIVED, + STATE_HELLO_SENT, + STATE_CERT_SENT, + STATE_CERTREQ_SENT, + STATE_HELLO_DONE, + STATE_CERT_RECEIVED, + STATE_KEY_EXCHANGE_RECEIVED, + STATE_CERT_VERIFY_RECEIVED, + STATE_CIPHERSPEC_CHANGED_IN, + STATE_FINISHED_RECEIVED, + STATE_CIPHERSPEC_CHANGED_OUT, + STATE_FINISHED_SENT, +} server_state_t; + /** * Private data of an tls_server_t object. */ @@ -48,36 +67,510 @@ struct private_tls_server_t { * Peer identity */ identification_t *peer; + + /** + * State we are in + */ + server_state_t state; + + /** + * Hello random data selected by client + */ + char client_random[32]; + + /** + * Hello random data selected by server + */ + char server_random[32]; + + /** + * Auth helper for peer authentication + */ + auth_cfg_t *peer_auth; + + /** + * Auth helper for server authentication + */ + auth_cfg_t *server_auth; + + /** + * Peer private key + */ + private_key_t *private; + + /** + * Selected TLS cipher suite + */ + tls_cipher_suite_t suite; }; +/** + * Process client hello message + */ +static status_t process_client_hello(private_tls_server_t *this, + tls_reader_t *reader) +{ + u_int16_t version; + chunk_t random, session, ciphers, compression, ext = chunk_empty; + tls_cipher_suite_t *suites; + int count, i; + + this->crypto->append_handshake(this->crypto, + TLS_CLIENT_HELLO, reader->peek(reader)); + + if (!reader->read_uint16(reader, &version) || + !reader->read_data(reader, sizeof(this->client_random), &random) || + !reader->read_data8(reader, &session) || + !reader->read_data16(reader, &ciphers) || + !reader->read_data8(reader, &compression) || + (reader->remaining(reader) && !reader->read_data16(reader, &ext))) + { + DBG1(DBG_IKE, "received invalid ClientHello"); + return FAILED; + } + + memcpy(this->client_random, random.ptr, sizeof(this->client_random)); + + if (version < this->tls->get_version(this->tls)) + { + this->tls->set_version(this->tls, version); + } + count = ciphers.len / sizeof(u_int16_t); + suites = alloca(count * sizeof(tls_cipher_suite_t)); + for (i = 0; i < count; i++) + { + suites[i] = untoh16(&ciphers.ptr[i * sizeof(u_int16_t)]); + } + this->suite = this->crypto->select_cipher_suite(this->crypto, suites, count); + if (!this->suite) + { + DBG1(DBG_IKE, "received cipher suite inacceptable"); + return FAILED; + } + this->state = STATE_HELLO_RECEIVED; + return NEED_MORE; +} + +/** + * Process certificate + */ +static status_t process_certificate(private_tls_server_t *this, + tls_reader_t *reader) +{ + certificate_t *cert; + tls_reader_t *certs; + chunk_t data; + bool first = TRUE; + + this->crypto->append_handshake(this->crypto, + TLS_CERTIFICATE, reader->peek(reader)); + + if (!reader->read_data24(reader, &data)) + { + return FAILED; + } + certs = tls_reader_create(data); + while (certs->remaining(certs)) + { + if (!certs->read_data24(certs, &data)) + { + certs->destroy(certs); + return FAILED; + } + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, data, BUILD_END); + if (cert) + { + if (first) + { + this->peer_auth->add(this->peer_auth, + AUTH_HELPER_SUBJECT_CERT, cert); + DBG1(DBG_IKE, "received TLS peer certificate '%Y'", + cert->get_subject(cert)); + first = FALSE; + } + else + { + DBG1(DBG_IKE, "received TLS intermediate certificate '%Y'", + cert->get_subject(cert)); + this->peer_auth->add(this->peer_auth, AUTH_HELPER_IM_CERT, cert); + } + } + else + { + DBG1(DBG_IKE, "parsing TLS certificate failed, skipped"); + } + } + certs->destroy(certs); + this->state = STATE_CERT_RECEIVED; + return NEED_MORE; +} + +/** + * Process Client Key Exchange + */ +static status_t process_key_exchange(private_tls_server_t *this, + tls_reader_t *reader) +{ + chunk_t encrypted, premaster; + + this->crypto->append_handshake(this->crypto, + TLS_CLIENT_KEY_EXCHANGE, reader->peek(reader)); + + if (!reader->read_data16(reader, &encrypted)) + { + DBG1(DBG_IKE, "received invalid Client Key Exchange"); + return FAILED; + } + + if (!this->private || + !this->private->decrypt(this->private, encrypted, &premaster)) + { + DBG1(DBG_IKE, "decrypting Client Key Exchange data failed"); + return FAILED; + } + this->crypto->derive_secrets(this->crypto, premaster, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + chunk_clear(&premaster); + + this->state = STATE_KEY_EXCHANGE_RECEIVED; + return NEED_MORE; +} + +/** + * Process Certificate verify + */ +static status_t process_cert_verify(private_tls_server_t *this, + tls_reader_t *reader) +{ + bool verified = FALSE; + enumerator_t *enumerator; + public_key_t *public; + auth_cfg_t *auth; + tls_reader_t *sig; + + enumerator = charon->credentials->create_public_enumerator( + charon->credentials, KEY_ANY, this->peer, this->peer_auth); + while (enumerator->enumerate(enumerator, &public, &auth)) + { + sig = tls_reader_create(reader->peek(reader)); + verified = this->crypto->verify_handshake(this->crypto, public, sig); + sig->destroy(sig); + if (verified) + { + break; + } + DBG1(DBG_IKE, "signature verification failed, trying another key"); + } + enumerator->destroy(enumerator); + + if (!verified) + { + DBG1(DBG_IKE, "no trusted certificate found for '%Y' to verify TLS peer", + this->peer); + return FAILED; + } + + this->crypto->append_handshake(this->crypto, + TLS_CERTIFICATE_VERIFY, reader->peek(reader)); + this->state = STATE_CERT_VERIFY_RECEIVED; + return NEED_MORE; +} + +/** + * Process finished message + */ +static status_t process_finished(private_tls_server_t *this, + tls_reader_t *reader) +{ + chunk_t received; + char buf[12]; + + if (!reader->read_data(reader, sizeof(buf), &received)) + { + DBG1(DBG_IKE, "received client finished too short"); + return FAILED; + } + if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) + { + DBG1(DBG_IKE, "calculating client finished failed"); + return FAILED; + } + if (!chunk_equals(received, chunk_from_thing(buf))) + { + DBG1(DBG_IKE, "received client finished invalid"); + return FAILED; + } + + this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); + this->state = STATE_FINISHED_RECEIVED; + return NEED_MORE; +} METHOD(tls_handshake_t, process, status_t, private_tls_server_t *this, tls_handshake_type_t type, tls_reader_t *reader) { + tls_handshake_type_t expected; + + switch (this->state) + { + case STATE_INIT: + if (type == TLS_CLIENT_HELLO) + { + return process_client_hello(this, reader); + } + expected = TLS_CLIENT_HELLO; + break; + case STATE_HELLO_DONE: + if (type == TLS_CERTIFICATE) + { + return process_certificate(this, reader); + } + expected = TLS_CERTIFICATE; + break; + case STATE_CERT_RECEIVED: + if (type == TLS_CLIENT_KEY_EXCHANGE) + { + return process_key_exchange(this, reader); + } + expected = TLS_CLIENT_KEY_EXCHANGE; + break; + case STATE_KEY_EXCHANGE_RECEIVED: + if (type == TLS_CERTIFICATE_VERIFY) + { + return process_cert_verify(this, reader); + } + expected = TLS_CERTIFICATE_VERIFY; + break; + case STATE_CIPHERSPEC_CHANGED_IN: + if (type == TLS_FINISHED) + { + return process_finished(this, reader); + } + expected = TLS_FINISHED; + break; + default: + DBG1(DBG_IKE, "TLS %N not expected in current state", + tls_handshake_type_names, type); + return FAILED; + } + DBG1(DBG_IKE, "TLS %N expected, but received %N", + tls_handshake_type_names, expected, tls_handshake_type_names, type); + return FAILED; +} + +/** + * Send ServerHello message + */ +static status_t send_server_hello(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + rng_t *rng; + + htoun32(&this->server_random, time(NULL)); + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + return FAILED; + } + rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4); + rng->destroy(rng); + + writer->write_uint16(writer, this->tls->get_version(this->tls)); + writer->write_data(writer, chunk_from_thing(this->server_random)); + /* session identifier => none, we don't support session resumption */ + writer->write_data8(writer, chunk_empty); + /* add selected suite */ + writer->write_uint16(writer, this->suite); + /* NULL compression only */ + writer->write_uint8(writer, 0); + + *type = TLS_SERVER_HELLO; + this->state = STATE_HELLO_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send Certificate + */ +static status_t send_certificate(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + enumerator_t *enumerator; + certificate_t *cert; + auth_rule_t rule; + tls_writer_t *certs; + chunk_t data; + + this->private = charon->credentials->get_private(charon->credentials, + KEY_ANY, this->server, this->server_auth); + if (!this->private) + { + DBG1(DBG_IKE, "no TLS server certificate found for '%Y'", this->server); + return FAILED; + } + + /* generate certificate payload */ + certs = tls_writer_create(256); + cert = this->server_auth->get(this->server_auth, AUTH_RULE_SUBJECT_CERT); + if (cert) + { + DBG1(DBG_IKE, "sending TLS server certificate '%Y'", + cert->get_subject(cert)); + data = cert->get_encoding(cert); + certs->write_data24(certs, data); + free(data.ptr); + } + enumerator = this->server_auth->create_enumerator(this->server_auth); + while (enumerator->enumerate(enumerator, &rule, &cert)) + { + if (rule == AUTH_RULE_IM_CERT) + { + DBG1(DBG_IKE, "sending TLS intermediate certificate '%Y'", + cert->get_subject(cert)); + data = cert->get_encoding(cert); + certs->write_data24(certs, data); + free(data.ptr); + } + } + enumerator->destroy(enumerator); + + writer->write_data24(writer, certs->get_buf(certs)); + certs->destroy(certs); + + *type = TLS_CERTIFICATE; + this->state = STATE_CERT_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send Certificate Request + */ +static status_t send_certificate_request(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + tls_writer_t *authorities; + enumerator_t *enumerator; + certificate_t *cert; + identification_t *id; + + /* currently only RSA signatures are supported */ + writer->write_data8(writer, chunk_from_chars(1)); + if (this->tls->get_version(this->tls) >= TLS_1_2) + { + /* enforce RSA with SHA1 signatures */ + writer->write_data16(writer, chunk_from_chars(2, 1)); + } + + authorities = tls_writer_create(64); + enumerator = charon->credentials->create_cert_enumerator( + charon->credentials, CERT_X509, KEY_RSA, NULL, TRUE); + while (enumerator->enumerate(enumerator, &cert)) + { + id = cert->get_subject(cert); + authorities->write_data16(authorities, id->get_encoding(id)); + } + enumerator->destroy(enumerator); + writer->write_data16(writer, authorities->get_buf(authorities)); + authorities->destroy(authorities); + + *type = TLS_CERTIFICATE_REQUEST; + this->state = STATE_CERTREQ_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send Hello Done + */ +static status_t send_hello_done(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + *type = TLS_SERVER_HELLO_DONE; + this->state = STATE_HELLO_DONE; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send Finished + */ +static status_t send_finished(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + char buf[12]; + + if (!this->crypto->calculate_finished(this->crypto, "server finished", buf)) + { + DBG1(DBG_IKE, "calculating server finished data failed"); + return FAILED; + } + + writer->write_data(writer, chunk_from_thing(buf)); + + *type = TLS_FINISHED; + this->state = STATE_FINISHED_SENT; + this->crypto->derive_eap_msk(this->crypto, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); return NEED_MORE; } METHOD(tls_handshake_t, build, status_t, private_tls_server_t *this, tls_handshake_type_t *type, tls_writer_t *writer) { - return INVALID_STATE; + switch (this->state) + { + case STATE_HELLO_RECEIVED: + return send_server_hello(this, type, writer); + case STATE_HELLO_SENT: + return send_certificate(this, type, writer); + case STATE_CERT_SENT: + return send_certificate_request(this, type, writer); + case STATE_CERTREQ_SENT: + return send_hello_done(this, type, writer); + case STATE_CIPHERSPEC_CHANGED_OUT: + return send_finished(this, type, writer); + case STATE_FINISHED_SENT: + return INVALID_STATE; + default: + return INVALID_STATE; + } } METHOD(tls_handshake_t, cipherspec_changed, bool, private_tls_server_t *this) { + if (this->state == STATE_FINISHED_RECEIVED) + { + this->crypto->change_cipher(this->crypto, FALSE); + this->state = STATE_CIPHERSPEC_CHANGED_OUT; + return TRUE; + } return FALSE; } METHOD(tls_handshake_t, change_cipherspec, bool, private_tls_server_t *this) { + if (this->state == STATE_CERT_VERIFY_RECEIVED) + { + this->crypto->change_cipher(this->crypto, TRUE); + this->state = STATE_CIPHERSPEC_CHANGED_IN; + return TRUE; + } return FALSE; } METHOD(tls_handshake_t, destroy, void, private_tls_server_t *this) { + DESTROY_IF(this->private); + this->peer_auth->destroy(this->peer_auth); + this->server_auth->destroy(this->server_auth); free(this); } @@ -101,6 +594,9 @@ tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto, .crypto = crypto, .server = server, .peer = peer, + .state = STATE_INIT, + .peer_auth = auth_cfg_create(), + .server_auth = auth_cfg_create(), ); return &this->public;