/* * eap-tls.c - EAP-TLS implementation for PPP * * Copyright (c) Beniamino Galvani 2005 All rights reserved. * Jan Just Keijser 2006-2019 All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name(s) of the authors of this software must not be used to * endorse or promote products derived from this software without * prior written permission. * * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #ifndef OPENSSL_NO_ENGINE #include #endif #include #include #include #include #include #include #include "pppd-private.h" #include "tls.h" #include "eap.h" #include "eap-tls.h" #include "fsm.h" #include "lcp.h" #include "chap_ms.h" #include "mppe.h" #include "pathnames.h" #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #define SSL3_RT_HEADER 0x100 #endif typedef struct pw_cb_data { const void *password; const char *prompt_info; } PW_CB_DATA; #ifndef OPENSSL_NO_ENGINE /* The openssl configuration file and engines can be loaded only once */ static CONF *ssl_config = NULL; static ENGINE *cert_engine = NULL; static ENGINE *pkey_engine = NULL; #endif /* TLSv1.3 do we have a session ticket ? */ static int have_session_ticket = 0; void ssl_msg_callback(int write_p, int version, int ct, const void *buf, size_t len, SSL * ssl, void *arg); int ssl_new_session_cb(SSL *s, SSL_SESSION *sess); #ifdef PPP_WITH_MPPE #define EAPTLS_MPPE_KEY_LEN 32 /* * Generate keys according to RFC 2716 and add to reply */ void eaptls_gen_mppe_keys(struct eaptls_session *ets, int client) { unsigned char out[4*EAPTLS_MPPE_KEY_LEN]; const char *prf_label; size_t prf_size; unsigned char eap_tls13_context[] = { EAPT_TLS }; unsigned char *context = NULL; size_t context_len = 0; dbglog("EAP-TLS generating MPPE keys"); if (ets->tls_v13) { prf_label = "EXPORTER_EAP_TLS_Key_Material"; context = eap_tls13_context; context_len = 1; } else { prf_label = "client EAP encryption"; } dbglog("EAP-TLS PRF label = %s", prf_label); prf_size = strlen(prf_label); if (SSL_export_keying_material(ets->ssl, out, sizeof(out), prf_label, prf_size, context, context_len, 0) != 1) { warn( "EAP-TLS: Failed generating keying material" ); return; } /* * We now have the master send and receive keys. * From these, generate the session send and receive keys. * (see RFC3079 / draft-ietf-pppext-mppe-keys-03.txt for details) */ if (client) { mppe_set_keys(out, out + EAPTLS_MPPE_KEY_LEN, EAPTLS_MPPE_KEY_LEN); } else { mppe_set_keys(out + EAPTLS_MPPE_KEY_LEN, out, EAPTLS_MPPE_KEY_LEN); } } #endif /* PPP_WITH_MPPE */ static int password_callback (char *buf, int size, int rwflag, void *u) { if (buf) { strlcpy (buf, passwd, size); return strlen (buf); } return 0; } static CONF *eaptls_ssl_load_config( void ) { CONF *config; int ret_code; long error_line = 33; config = NCONF_new( NULL ); dbglog( "Loading OpenSSL config file" ); ret_code = NCONF_load( config, PPP_PATH_OPENSSLCONFFILE, &error_line ); if (ret_code == 0) { warn( "EAP-TLS: Error in OpenSSL config file %s at line %d", PPP_PATH_OPENSSLCONFFILE, error_line ); NCONF_free( config ); config = NULL; ERR_clear_error(); } dbglog( "Loading OpenSSL built-ins" ); #ifndef OPENSSL_NO_ENGINE ENGINE_load_builtin_engines(); #endif #if !defined(LIBRESSL_VERSION_NUMBER) || (LIBRESSL_VERSION_NUMBER < 0x4000000fL) OPENSSL_load_builtin_modules(); #endif dbglog( "Loading OpenSSL configured modules" ); if (CONF_modules_load( config, NULL, 0 ) <= 0 ) { warn( "EAP-TLS: Error loading OpenSSL modules" ); tls_log_sslerr(); config = NULL; } return config; } #ifndef OPENSSL_NO_ENGINE static ENGINE *eaptls_ssl_load_engine( char *engine_name ) { ENGINE *e = NULL; dbglog( "Enabling OpenSSL auto engines" ); ENGINE_register_all_complete(); dbglog( "Loading OpenSSL '%s' engine support", engine_name ); e = ENGINE_by_id( engine_name ); if (!e) { dbglog( "EAP-TLS: Cannot load '%s' engine support, trying 'dynamic'", engine_name ); e = ENGINE_by_id( "dynamic" ); if (e) { if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine_name, 0) || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) { warn( "EAP-TLS: Error loading dynamic engine '%s'", engine_name ); tls_log_sslerr(); ENGINE_free(e); e = NULL; } } else { warn( "EAP-TLS: Cannot load dynamic engine support" ); } } if (e) { dbglog( "Initialising engine" ); if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { warn( "EAP-TLS: Cannot use that engine" ); tls_log_sslerr(); ENGINE_free(e); e = NULL; } } return e; } #endif #ifndef OPENSSL_NO_ENGINE static int eaptls_UI_writer(UI *ui, UI_STRING *uis) { PW_CB_DATA* cb_data = (PW_CB_DATA*)UI_get0_user_data(ui); UI_set_result(ui, uis, cb_data->password); return 1; } static int eaptls_UI_stub(UI* ui) { return 1; } static int eaptls_UI_reader(UI *ui, UI_STRING *uis) { return 1; } #endif /* * Initialize the SSL stacks and tests if certificates, key and crl * for client or server use can be loaded. */ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath, char *certfile, char *privkeyfile, char *pkcs12) { #ifndef OPENSSL_NO_ENGINE char *cert_engine_name = NULL; char *pkey_engine_name = NULL; char *idx; #endif SSL_CTX *ctx; SSL *ssl; X509 *tmp; X509 *cert = NULL; PKCS12 *p12 = NULL; EVP_PKEY *pkey = NULL; STACK_OF(X509) *chain = NULL; BIO *input; int ret; /* * Without these can't continue */ if (!pkcs12[0]) { if (!(cacertfile[0] || capath[0])) { error("EAP-TLS: CA certificate file or path missing"); return NULL; } if (!certfile[0]) { error("EAP-TLS: Certificate missing"); return NULL; } if (!privkeyfile[0]) { error("EAP-TLS: Private key missing"); return NULL; } } tls_init(); #ifndef OPENSSL_NO_ENGINE /* load the openssl config file only once and load it before triggering the loading of a global openssl config file via SSL_CTX_new() */ if (!ssl_config) ssl_config = eaptls_ssl_load_config(); #endif ctx = SSL_CTX_new(tls_method()); if (!ctx) { error("EAP-TLS: Cannot initialize SSL CTX context"); goto fail; } #ifndef OPENSSL_NO_ENGINE /* if the certificate filename is of the form engine:id. e.g. pkcs11:12345 then we try to load and use this engine. If the certificate filename starts with a / or . then we ALWAYS assume it is a file and not an engine/pkcs11 identifier */ if ( (idx = index( certfile, ':' )) != NULL ) { cert_engine_name = strdup( certfile ); cert_engine_name[idx - certfile] = 0; dbglog( "Using engine '%s' for certificate, URI: '%s'", cert_engine_name, certfile ); } /* if the privatekey filename is of the form engine:id. e.g. pkcs11:12345 then we try to load and use this engine. If the privatekey filename starts with a / or . then we ALWAYS assume it is a file and not an engine/pkcs11 identifier */ if ( (idx = index( privkeyfile, ':' )) != NULL ) { pkey_engine_name = strdup( privkeyfile ); pkey_engine_name[idx - privkeyfile] = 0; dbglog( "Using engine '%s' for private key, URI: '%s'", pkey_engine_name, privkeyfile ); } if (cert_engine_name && pkey_engine_name) { if (strlen( certfile ) - strlen( cert_engine_name ) == 1) { if (strlen( privkeyfile ) - strlen( pkey_engine_name ) == 1) error( "EAP-TLS: both the certificate and privatekey identifiers are missing!" ); else { dbglog( "Substituting privatekey identifier for certificate identifier" ); certfile = privkeyfile; } } else { if (strlen( privkeyfile ) - strlen( pkey_engine_name ) == 1) { dbglog( "Substituting certificate identifier for privatekey identifier" ); privkeyfile = certfile; } } } if (ssl_config && cert_engine_name) cert_engine = eaptls_ssl_load_engine( cert_engine_name ); if (ssl_config && pkey_engine_name) { /* don't load the same engine twice */ if ( cert_engine && strcmp( cert_engine_name, pkey_engine_name) == 0 ) pkey_engine = cert_engine; else pkey_engine = eaptls_ssl_load_engine( pkey_engine_name ); } if (cert_engine_name) free(cert_engine_name); if (pkey_engine_name) free(pkey_engine_name); #endif SSL_CTX_set_default_passwd_cb (ctx, password_callback); if (tls_set_ca(ctx, capath, cacertfile) != 0) { goto fail; } if (init_server) SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(cacertfile)); #ifndef OPENSSL_NO_ENGINE if (cert_engine) { struct { const char *s_slot_cert_id; X509 *cert; } cert_info; cert_info.s_slot_cert_id = certfile; cert_info.cert = NULL; if (!ENGINE_ctrl_cmd( cert_engine, "LOAD_CERT_CTRL", 0, &cert_info, NULL, 0 ) ) { error( "EAP-TLS: Error loading certificate with URI '%s' from engine", certfile ); goto fail; } if (cert_info.cert) { dbglog( "Got the certificate" ); dbglog( "subject = %s", X509_NAME_oneline( X509_get_subject_name( cert_info.cert ), NULL, 0 ) ); cert = cert_info.cert; } else { warn("EAP-TLS: Cannot load key with URI: '%s'", certfile ); tls_log_sslerr(); } } else #endif { if (pkcs12[0]) { input = BIO_new_file(pkcs12, "r"); if (input == NULL) { error("EAP-TLS: Cannot open `%s' PKCS12 for input", pkcs12); goto fail; } p12 = d2i_PKCS12_bio(input, NULL); BIO_free(input); if (!p12) { error("EAP-TLS: Cannot load PKCS12 certificate"); goto fail; } if (PKCS12_parse(p12, passwd, &pkey, &cert, &chain) != 1) { error("EAP-TLS: Cannot parse PKCS12 certificate, invalid password"); PKCS12_free(p12); goto fail; } PKCS12_free(p12); } else { if (!SSL_CTX_use_certificate_chain_file(ctx, certfile)) { error( "EAP-TLS: Cannot load certificate %s", certfile ); goto fail; } } } if (cert) { if (!SSL_CTX_use_certificate(ctx, cert)) { error("EAP-TLS: Cannot use load certificate"); goto fail; } if (chain) { int i; for (i = 0; i < sk_X509_num(chain); i++) { if (!SSL_CTX_add_extra_chain_cert(ctx, sk_X509_value(chain, i))) { error("EAP-TLS: Cannot add extra chain certificate"); goto fail; } } } } /* * Check the Before and After dates of the certificate */ ssl = SSL_new(ctx); tmp = SSL_get_certificate(ssl); ret = X509_cmp_time(X509_get_notBefore(tmp), NULL); if (ret == 0) { warn( "EAP-TLS: Failed to read certificate notBefore field."); } if (ret > 0) { warn( "EAP-TLS: Your certificate is not yet valid!"); } ret = X509_cmp_time(X509_get_notAfter(tmp), NULL); if (ret == 0) { warn( "EAP-TLS: Failed to read certificate notAfter field."); } if (ret < 0) { warn( "EAP-TLS: Your certificate has expired!"); } SSL_free(ssl); #ifndef OPENSSL_NO_ENGINE if (pkey_engine) { PW_CB_DATA cb_data; cb_data.password = passwd; cb_data.prompt_info = privkeyfile; if (passwd[0] != 0) { UI_METHOD* transfer_pin = UI_create_method("transfer_pin"); UI_method_set_writer(transfer_pin, eaptls_UI_writer); UI_method_set_opener(transfer_pin, eaptls_UI_stub); UI_method_set_closer(transfer_pin, eaptls_UI_stub); UI_method_set_flusher(transfer_pin, eaptls_UI_stub); UI_method_set_reader(transfer_pin, eaptls_UI_reader); dbglog( "Using our private key URI: '%s' in engine", privkeyfile ); pkey = ENGINE_load_private_key(pkey_engine, privkeyfile, transfer_pin, &cb_data); if (transfer_pin) UI_destroy_method(transfer_pin); } else { dbglog( "Loading private key URI: '%s' from engine", privkeyfile ); pkey = ENGINE_load_private_key(pkey_engine, privkeyfile, NULL, NULL); } } else #endif { if (!pkey) { input = BIO_new_file(privkeyfile, "r"); if (!input) { error("EAP-TLS: Could not open private key, %s", privkeyfile); goto fail; } pkey = PEM_read_bio_PrivateKey(input, NULL, password_callback, NULL); BIO_free(input); if (!pkey) { error("EAP-TLS: Cannot load private key, %s", privkeyfile); goto fail; } } } if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) { error("EAP-TLS: Cannot use private key"); goto fail; } if (SSL_CTX_check_private_key(ctx) != 1) { error("EAP-TLS: Private key fails security check"); goto fail; } /* Configure the default options */ tls_set_opts(ctx); /* Set up a SSL Session cache with a callback. This is needed for TLSv1.3+. * During the initial handshake the server signals to the client early on * that the handshake is finished, even before the client has sent its * credentials to the server. The actual connection (and moment that the * client sends its credentials) only starts after the arrival of the first * session ticket. The 'ssl_new_session_cb' catches this ticket. */ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE); SSL_CTX_sess_set_new_cb(ctx, ssl_new_session_cb); /* Configure the maximum SSL version */ tls_set_version(ctx, max_tls_version); /* Configure the callback */ if (tls_set_verify(ctx, 5)) { goto fail; } /* Configure CRL check (if any) */ if (tls_set_crl(ctx, crl_dir, crl_file)) { goto fail; } return ctx; fail: if (cert) X509_free(cert); if (pkey) EVP_PKEY_free(pkey); if (chain) sk_X509_pop_free(chain, X509_free); tls_log_sslerr(); SSL_CTX_free(ctx); return NULL; } /* * Determine the maximum packet size by looking at the LCP handshake */ static int eaptls_get_mtu(int unit) { int mtu, mru; lcp_options *wo = &lcp_wantoptions[unit]; lcp_options *go = &lcp_gotoptions[unit]; lcp_options *ho = &lcp_hisoptions[unit]; lcp_options *ao = &lcp_allowoptions[unit]; mtu = ho->neg_mru? ho->mru: PPP_MRU; mru = go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU; mtu = MIN(MIN(mtu, mru), ao->mru)- PPP_HDRLEN - 10; dbglog("MTU = %d", mtu); return mtu; } /* * Init the ssl handshake (server mode) */ int eaptls_init_ssl_server(eap_state * esp) { struct eaptls_session *ets; char servcertfile[MAXWORDLEN]; char clicertfile[MAXWORDLEN]; char cacertfile[MAXWORDLEN]; char capath[MAXWORDLEN]; char pkfile[MAXWORDLEN]; char pkcs12[MAXWORDLEN]; /* * Allocate new eaptls session */ esp->es_server.ea_session = malloc(sizeof(struct eaptls_session)); if (!esp->es_server.ea_session) fatal("Allocation error"); ets = esp->es_server.ea_session; if (!esp->es_server.ea_peer) { error("EAP-TLS: Error: client name not set (BUG)"); return 0; } dbglog( "getting eaptls secret" ); if (!get_eaptls_secret(esp->es_unit, esp->es_server.ea_peer, esp->es_server.ea_name, clicertfile, servcertfile, cacertfile, capath, pkfile, pkcs12, 1)) { error( "EAP-TLS: Cannot get secret/password for client \"%s\", server \"%s\"", esp->es_server.ea_peer, esp->es_server.ea_name ); return 0; } ets->mtu = eaptls_get_mtu(esp->es_unit); ets->ctx = eaptls_init_ssl(1, cacertfile, capath, servcertfile, pkfile, pkcs12); if (!ets->ctx) goto fail; if (!(ets->ssl = SSL_new(ets->ctx))) goto fail; if (tls_set_verify_info(ets->ssl, esp->es_server.ea_peer, clicertfile, 0, &ets->info)) goto fail; /* * Set auto-retry to avoid timeouts on BIO_read */ SSL_set_mode(ets->ssl, SSL_MODE_AUTO_RETRY); /* * Initialize the BIOs we use to read/write to ssl engine */ ets->into_ssl = BIO_new(BIO_s_mem()); ets->from_ssl = BIO_new(BIO_s_mem()); SSL_set_bio(ets->ssl, ets->into_ssl, ets->from_ssl); SSL_set_msg_callback(ets->ssl, ssl_msg_callback); SSL_set_msg_callback_arg(ets->ssl, ets); SSL_set_accept_state(ets->ssl); ets->tls_v13 = 0; ets->data = NULL; ets->datalen = 0; ets->alert_sent = 0; ets->alert_recv = 0; return 1; fail: SSL_CTX_free(ets->ctx); return 0; } /* * Init the ssl handshake (client mode) */ int eaptls_init_ssl_client(eap_state * esp) { struct eaptls_session *ets; char servcertfile[MAXWORDLEN]; char clicertfile[MAXWORDLEN]; char cacertfile[MAXWORDLEN]; char capath[MAXWORDLEN]; char pkfile[MAXWORDLEN]; char pkcs12[MAXWORDLEN]; /* * Allocate new eaptls session */ esp->es_client.ea_session = malloc(sizeof(struct eaptls_session)); if (!esp->es_client.ea_session) fatal("Allocation error"); ets = esp->es_client.ea_session; ets->mtu = eaptls_get_mtu(esp->es_unit); dbglog( "calling get_eaptls_secret" ); if (!get_eaptls_secret(esp->es_unit, esp->es_client.ea_name, esp->es_client.ea_peer, clicertfile, servcertfile, cacertfile, capath, pkfile, pkcs12, 0)) { error( "EAP-TLS: Cannot get secret/password for client \"%s\", server \"%s\"", esp->es_client.ea_name, esp->es_client.ea_peer); return 0; } dbglog( "calling eaptls_init_ssl" ); ets->ctx = eaptls_init_ssl(0, cacertfile, capath, clicertfile, pkfile, pkcs12); if (!ets->ctx) goto fail; ets->ssl = SSL_new(ets->ctx); if (!ets->ssl) goto fail; if (tls_set_verify_info(ets->ssl, esp->es_client.ea_peer, servcertfile, 0, &ets->info)) goto fail; /* * Initialize the BIOs we use to read/write to ssl engine */ dbglog( "Initializing SSL BIOs" ); ets->into_ssl = BIO_new(BIO_s_mem()); ets->from_ssl = BIO_new(BIO_s_mem()); SSL_set_bio(ets->ssl, ets->into_ssl, ets->from_ssl); SSL_set_msg_callback(ets->ssl, ssl_msg_callback); SSL_set_msg_callback_arg(ets->ssl, ets); SSL_set_connect_state(ets->ssl); ets->tls_v13 = 0; ets->data = NULL; ets->datalen = 0; ets->alert_sent = 0; ets->alert_recv = 0; return 1; fail: dbglog( "eaptls_init_ssl_client: fail" ); SSL_CTX_free(ets->ctx); return 0; } void eaptls_free_session(struct eaptls_session *ets) { if (ets->ssl) SSL_free(ets->ssl); if (ets->ctx) SSL_CTX_free(ets->ctx); if (ets->info) tls_free_verify_info(&ets->info); free(ets); } int eaptls_is_init_finished(struct eaptls_session *ets) { if (ets->ssl && SSL_is_init_finished(ets->ssl)) { if (ets->tls_v13) return have_session_ticket; else return 1; } return 0; } /* * Handle a received packet, reassembling fragmented messages and * passing them to the ssl engine */ int eaptls_receive(struct eaptls_session *ets, u_char * inp, int len) { u_char flags; u_int tlslen = 0; u_char dummy[65536]; if (len < 1) { warn("EAP-TLS: received no or invalid data"); return 1; } GETCHAR(flags, inp); len--; if (flags & EAP_TLS_FLAGS_LI && len > 4) { /* * LenghtIncluded flag set -> this is the first packet of a message */ /* * the first 4 octets are the length of the EAP-TLS message */ GETLONG(tlslen, inp); len -= 4; if (!ets->data) { if (tlslen > EAP_TLS_MAX_LEN) { error("EAP-TLS: TLS message length > %d, truncated", EAP_TLS_MAX_LEN); tlslen = EAP_TLS_MAX_LEN; } /* * Allocate memory for the whole message */ ets->data = malloc(tlslen); if (!ets->data) fatal("EAP-TLS: allocation error\n"); ets->datalen = 0; ets->tlslen = tlslen; } else warn("EAP-TLS: non-first LI packet? that's odd..."); } else if (!ets->data) { /* * A non fragmented message without LI flag */ ets->data = malloc(len); if (!ets->data) fatal("EAP-TLS: memory allocation error in eaptls_receive\n"); ets->datalen = 0; ets->tlslen = len; } if (flags & EAP_TLS_FLAGS_MF) ets->frag = 1; else ets->frag = 0; if (len < 0) { warn("EAP-TLS: received malformed data"); return 1; } if (len + ets->datalen > ets->tlslen) { warn("EAP-TLS: received data > TLS message length"); return 1; } BCOPY(inp, ets->data + ets->datalen, len); ets->datalen += len; if (!ets->frag) { /* * If we have the whole message, pass it to ssl */ if (ets->datalen != ets->tlslen) { warn("EAP-TLS: received data != TLS message length"); return 1; } if (BIO_write(ets->into_ssl, ets->data, ets->datalen) == -1) tls_log_sslerr(); SSL_read(ets->ssl, dummy, 65536); free(ets->data); ets->data = NULL; ets->datalen = 0; } return 0; } /* * Return an eap-tls packet in outp. * A TLS message read from the ssl engine is buffered in ets->data. * At each call we control if there is buffered data and send a * packet of mtu bytes. */ int eaptls_send(struct eaptls_session *ets, u_char ** outp) { bool first = 0; int size; u_char fromtls[65536]; int res; u_char *start; start = *outp; if (!ets->data) { if(!ets->alert_sent) { res = SSL_read(ets->ssl, fromtls, 65536); } /* * Read from ssl */ if ((res = BIO_read(ets->from_ssl, fromtls, 65536)) == -1) { warn("EAP-TLS send: No data from BIO_read"); return 1; } ets->datalen = res; ets->data = malloc(ets->datalen); if (!ets->data) fatal("EAP-TLS: memory allocation error in eaptls_send\n"); BCOPY(fromtls, ets->data, ets->datalen); ets->offset = 0; first = 1; } size = ets->datalen - ets->offset; if (size > ets->mtu) { size = ets->mtu; ets->frag = 1; } else ets->frag = 0; PUTCHAR(EAPT_TLS, *outp); /* * Set right flags and length if necessary */ if (ets->frag && first) { PUTCHAR(EAP_TLS_FLAGS_LI | EAP_TLS_FLAGS_MF, *outp); PUTLONG(ets->datalen, *outp); } else if (ets->frag) { PUTCHAR(EAP_TLS_FLAGS_MF, *outp); } else PUTCHAR(0, *outp); /* * Copy the data in outp */ BCOPY(ets->data + ets->offset, *outp, size); INCPTR(size, *outp); /* * Copy the packet in retransmission buffer */ BCOPY(start, &ets->rtx[0], *outp - start); ets->rtx_len = *outp - start; ets->offset += size; if (ets->offset >= ets->datalen) { /* * The whole message has been sent */ free(ets->data); ets->data = NULL; ets->datalen = 0; ets->offset = 0; } return 0; } /* * Get the sent packet from the retransmission buffer */ void eaptls_retransmit(struct eaptls_session *ets, u_char ** outp) { BCOPY(ets->rtx, *outp, ets->rtx_len); INCPTR(ets->rtx_len, *outp); } /* * Every sent & received message this callback function is invoked, * so we know when alert messages have arrived or are sent and * we can print debug information about TLS handshake. */ void ssl_msg_callback(int write_p, int version, int content_type, const void *buf, size_t len, SSL * ssl, void *arg) { char string[256]; struct eaptls_session *ets = (struct eaptls_session *)arg; unsigned char code; const unsigned char*msg = buf; int hvers = msg[1] << 8 | msg[2]; if(write_p) strcpy(string, " -> "); else strcpy(string, " <- "); switch(content_type) { case SSL3_RT_HEADER: strcat(string, "SSL/TLS Header: "); switch(hvers) { case SSL3_VERSION: strcat(string, "SSL 3.0"); break; case TLS1_VERSION: strcat(string, "TLS 1.0"); break; case TLS1_1_VERSION: strcat(string, "TLS 1.1"); break; case TLS1_2_VERSION: strcat(string, "TLS 1.2"); break; default: sprintf(string, "SSL/TLS Header: Unknown version (%d)", hvers); } break; case SSL3_RT_ALERT: strcat(string, "Alert: "); code = msg[1]; if (write_p) { ets->alert_sent = 1; ets->alert_sent_desc = code; } else { ets->alert_recv = 1; ets->alert_recv_desc = code; } strcat(string, SSL_alert_desc_string_long(code)); break; case SSL3_RT_CHANGE_CIPHER_SPEC: strcat(string, "ChangeCipherSpec"); break; #ifdef SSL3_RT_INNER_CONTENT_TYPE case SSL3_RT_INNER_CONTENT_TYPE: strcat(string, "InnerContentType (TLS1.3)"); break; #endif case SSL3_RT_HANDSHAKE: strcat(string, "Handshake: "); code = msg[0]; switch(code) { case SSL3_MT_HELLO_REQUEST: strcat(string,"Hello Request"); break; case SSL3_MT_CLIENT_HELLO: strcat(string,"Client Hello"); break; case SSL3_MT_SERVER_HELLO: strcat(string,"Server Hello"); break; #ifdef SSL3_MT_NEWSESSION_TICKET case SSL3_MT_NEWSESSION_TICKET: strcat(string,"New Session Ticket"); break; #endif #ifdef SSL3_MT_END_OF_EARLY_DATA case SSL3_MT_END_OF_EARLY_DATA: strcat(string,"End of Early Data"); break; #endif #ifdef SSL3_MT_ENCRYPTED_EXTENSIONS case SSL3_MT_ENCRYPTED_EXTENSIONS: strcat(string,"Encryped Extensions"); break; #endif case SSL3_MT_CERTIFICATE: strcat(string,"Certificate"); break; case SSL3_MT_SERVER_KEY_EXCHANGE: strcat(string,"Server Key Exchange"); break; case SSL3_MT_CERTIFICATE_REQUEST: strcat(string,"Certificate Request"); break; case SSL3_MT_SERVER_DONE: strcat(string,"Server Hello Done"); break; case SSL3_MT_CERTIFICATE_VERIFY: strcat(string,"Certificate Verify"); break; case SSL3_MT_CLIENT_KEY_EXCHANGE: strcat(string,"Client Key Exchange"); break; case SSL3_MT_FINISHED: strcat(string,"Finished: "); hvers = SSL_version(ssl); switch(hvers){ case SSL3_VERSION: strcat(string, "SSL 3.0"); break; case TLS1_VERSION: strcat(string, "TLS 1.0"); break; case TLS1_1_VERSION: strcat(string, "TLS 1.1"); break; case TLS1_2_VERSION: strcat(string, "TLS 1.2"); break; #ifdef TLS1_3_VERSION case TLS1_3_VERSION: strcat(string, "TLS 1.3 (experimental)"); ets->tls_v13 = 1; break; #endif default: strcat(string, "Unknown version"); } break; default: sprintf( string, "Handshake: Unknown SSL3 code received: %d", code ); } break; default: sprintf( string, "SSL message contains unknown content type: %d", content_type ); } /* Alert messages must always be displayed */ if(content_type == SSL3_RT_ALERT) error("%s", string); else dbglog("%s", string); } int ssl_new_session_cb(SSL *s, SSL_SESSION *sess) { dbglog("EAP-TLS: Post-Handshake New Session Ticket arrived:"); have_session_ticket = 1; /* always return success */ return 1; }