/* * 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.
*
*/
/*
* 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;
/*
* 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;
}
#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
#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;
/* 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;
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;
/* 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;
}
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);
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;
/*
* 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");
/*
* 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");
/*
* 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];