AS_HELP_STRING([--disable-eaptls], [Disable EAP-TLS authentication support]))
AS_IF([test "x$enable_eaptls" != "xno"],
AC_DEFINE([USE_EAPTLS], 1, ["Have EAP-TLS authentication support"]))
-AM_CONDITIONAL(WITH_EAPTLS, test "${enable_eaptls}" != "no")
+AM_CONDITIONAL(WITH_EAPTLS, test "x${enable_eaptls}" != "xno")
+
+#
+# Disable PEAP support
+AC_ARG_ENABLE([peap],
+ AS_HELP_STRING([--disable-peap], [Disable PEAP authentication support]))
+AS_IF([test "x${enable_peap}" != "xno"],
+ AC_DEFINE([USE_PEAP], 1, ["Have PEAP authentication support"]))
+AM_CONDITIONAL([WITH_PEAP], test "x${enable_peap}" != "xno")
#
# Disable OpenSSL engine support
#
# Check if OpenSSL has compiled in support for various ciphers
-AS_IF([test "${with_openssl}" != "no" ], [
+AS_IF([test "x${with_openssl}" != "xno" ], [
AX_CHECK_OPENSSL_DEFINE([OPENSSL_NO_MD4], [md4])
AX_CHECK_OPENSSL_DEFINE([OPENSSL_NO_MD5], [md5])
AX_CHECK_OPENSSL_DEFINE([OPENSSL_NO_DES], [des])
AX_CHECK_OPENSSL_DEFINE([OPENSSL_NO_SHA], [sha])
], [
- AS_IF([test "x$enable_eaptls" != "xno"],
- [AC_MSG_ERROR([OpenSSL not found, and if this is your intention then run configure --disable-eaptls])])
+ AS_IF([test "x${enable_eaptls}" != "xno" || test "x${enable_peap}" != "xno"],
+ [AC_MSG_ERROR([OpenSSL not found, and if this is your intention then run configure --disable-eaptls and --disable-peap])])
])
AM_CONDITIONAL([OPENSSL_HAVE_MD4], test "x${ac_cv_openssl_md4}" = "xyes")
CBCP.................: ${enable_cbcp:-no}
IPXCP................: ${enable_ipxcp:-no}
EAP-TLS..............: ${enable_eaptls:-yes}
+ PEAP.................: ${enable_peap:-yes}
"
sbin_PROGRAMS = pppd
dist_man8_MANS = pppd.8
-check_PROGRAMS = \
- utest_chap
+check_PROGRAMS =
utest_chap_SOURCES = chap_ms.c pppcrypt.c utils.c
utest_chap_CPPFLAGS = -DUNIT_TEST
utest_chap_LDFLAGS =
-TESTS = $(check_PROGRAMS)
+utest_peap_SOURCES = peap.c utils.c mppe.c
+utest_peap_CPPFLAGS = -DUNIT_TEST -I${top_srcdir}/include
+utest_peap_LDFLAGS =
if WITH_SRP
sbin_PROGRAMS += srp-entry
md4.h \
md5.h \
mppe.h \
- pppdconf.h \
patchlevel.h \
pathnames.h \
+ peap.h \
pppcrypt.h \
pppd.h \
+ pppdconf.h \
session.h \
sha1.h \
spinlock.h \
+ tls.h \
tdb.h \
upap.h
if WITH_CHAPMS
pppd_SOURCES += chap_ms.c
pppd_SOURCES += pppcrypt.c
+check_PROGRAMS += utest_chap
else
if WITH_SRP
pppd_SOURCES += pppcrypt.c
endif
if WITH_EAPTLS
-pppd_SOURCES += eap-tls.c
+pppd_SOURCES += eap-tls.c tls.c
+else
+if WITH_PEAP
+pppd_SOURCES += tls.c
+endif
endif
-if !WITH_OPENSSL
-pppd_SOURCES += md5.c md4.c sha1.c
-utest_chap_SOURCES += md5.c md4.c sha1.c
-else
-pppd_CPPFLAGS += $(OPENSSL_INCLUDES)
-pppd_LDFLAGS += $(OPENSSL_LDFLAGS)
+if WITH_PEAP
+pppd_SOURCES += peap.c
+check_PROGRAMS += utest_peap
+endif
-utest_chap_CPPFLAGS += $(OPENSSL_INCLUDES)
-utest_chap_LDFLAGS += $(OPENSSL_LDFLAGS)
-utest_chap_LDADD = $(OPENSSL_LIBS)
+noinst_LTLIBRARIES = libppp_crypt.la
+libppp_crypt_la_SOURCES=
-pppd_LIBS += $(OPENSSL_LIBS)
+if !WITH_OPENSSL
+libppp_crypt_la_SOURCES += md4.c md5.c sha1.c
+else
+libppp_crypt_la_CPPFLAGS=$(OPENSSL_INCLUDES)
+libppp_crypt_la_LDFLAGS=$(OPENSSL_LDFLAGS)
+libppp_crypt_la_LIBADD=$(OPENSSL_LIBS)
if !OPENSSL_HAVE_SHA
-pppd_SOURCES += sha1.c
-utest_chap_SOURCES += sha1.c
-endif
-if !OPENSSL_HAVE_MD4
-pppd_SOURCES += md4.c
-utest_chap_SOURCES += md4.c
+libppp_crypt_la_SOURCES += sha1.c
endif
if !OPENSSL_HAVE_MD5
-pppd_SOURCES += md5.c
-utest_chap_SOURCES += md5.c
+libppp_crypt_la_SOURCES += md5.c
+endif
+if !OPENSSL_HAVE_MD4
+libppp_crypt_la_SOURCES += md4.c
endif
endif
+utest_peap_LDADD = libppp_crypt.la
+utest_chap_LDADD = libppp_crypt.la
+pppd_LIBS += libppp_crypt.la
+
if WITH_SYSTEMD
pppd_LIBS += -lsystemd
endif
EXTRA_DIST = \
ppp.pam
+
+TESTS = $(check_PROGRAMS)
+
bool explicit_user = 0; /* Set if "user" option supplied */
bool explicit_passwd = 0; /* Set if "password" option supplied */
char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
-#ifdef USE_EAPTLS
-char *cacert_file = NULL; /* CA certificate file (pem format) */
-char *ca_path = NULL; /* directory with CA certificates */
-char *cert_file = NULL; /* client certificate file (pem format) */
-char *privkey_file = NULL; /* client private key file (pem format) */
-char *pkcs12_file = NULL; /* client private key envelope file (pkcs12 format) */
-char *crl_dir = NULL; /* directory containing CRL files */
-char *crl_file = NULL; /* Certificate Revocation List (CRL) file (pem format) */
-char *max_tls_version = NULL; /* Maximum TLS protocol version (default=1.2) */
-char *tls_verify_method = NULL;
-bool tls_verify_key_usage = 0;
-bool need_peer_eap = 0; /* Require peer to authenticate us */
+
+#if defined(USE_EAPTLS) || defined(USE_PEAP)
+char *cacert_file = NULL; /* CA certificate file (pem format) */
+char *ca_path = NULL; /* Directory with CA certificates */
+char *crl_dir = NULL; /* Directory containing CRL files */
+char *crl_file = NULL; /* Certificate Revocation List (CRL) file (pem format) */
+char *max_tls_version = NULL; /* Maximum TLS protocol version (default=1.2) */
+char *tls_verify_method = NULL; /* Verify certificate method */
+bool tls_verify_key_usage = 0; /* Verify peer certificate key usage */
+#endif
+
+#if defined(USE_EAPTLS)
+char *cert_file = NULL; /* Client certificate file (pem format) */
+char *privkey_file = NULL; /* Client private key file (pem format) */
+char *pkcs12_file = NULL; /* Client private key envelope file (pkcs12 format) */
+bool need_peer_eap = 0; /* Require peer to authenticate us */
#endif
static char *uafname; /* name of most recent +ua file */
"Set telephone number(s) which are allowed to connect",
OPT_PRIV | OPT_A2LIST },
-#ifdef USE_EAPTLS
- { "ca", o_string, &cacert_file, "EAP-TLS CA certificate in PEM format" },
- { "capath", o_string, &ca_path, "EAP-TLS CA certificate directory" },
- { "cert", o_string, &cert_file, "EAP-TLS client certificate in PEM format" },
- { "key", o_string, &privkey_file, "EAP-TLS client private key in PEM format" },
- { "crl-dir", o_string, &crl_dir, "Use CRLs in directory" },
- { "crl", o_string, &crl_file, "Use specific CRL file" },
- { "pkcs12", o_string, &pkcs12_file, "EAP-TLS client credentials in PKCS12 format" },
+#if defined(USE_EAPTLS) || defined(USE_PEAP)
+ { "ca", o_string, &cacert_file, "CA certificate in PEM format" },
+ { "capath", o_string, &ca_path, "TLS CA certificate directory" },
+ { "crl-dir", o_string, &crl_dir, "Use CRLs in directory" },
+ { "crl", o_string, &crl_file, "Use specific CRL file" },
{ "max-tls-version", o_string, &max_tls_version,
"Maximum TLS version (1.0/1.1/1.2 (default)/1.3)" },
{ "tls-verify-key-usage", o_bool, &tls_verify_key_usage,
"Verify certificate type and extended key usage" },
{ "tls-verify-method", o_string, &tls_verify_method,
"Verify peer by method (none|subject|name|suffix)" },
+#endif
+
+#if defined(USE_EAPTLS)
+ { "cert", o_string, &cert_file, "client certificate in PEM format" },
+ { "key", o_string, &privkey_file, "client private key in PEM format" },
+ { "pkcs12", o_string, &pkcs12_file, "EAP-TLS client credentials in PKCS12 format" },
{ "need-peer-eap", o_bool, &need_peer_eap,
"Require the peer to authenticate us", 1 },
-#endif /* USE_EAPTLS */
+#endif
{ NULL }
};
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif
+#include <openssl/ssl.h>
#include <openssl/hmac.h>
#include <openssl/err.h>
#include <openssl/ui.h>
#include <openssl/pkcs12.h>
#include "pppd.h"
+#include "tls.h"
#include "eap.h"
#include "eap-tls.h"
#include "fsm.h"
#include "mppe.h"
#include "pathnames.h"
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#define SSL3_RT_HEADER 0x100
+#endif
+
typedef struct pw_cb_data
{
const void *password;
/* TLSv1.3 do we have a session ticket ? */
static int have_session_ticket = 0;
-int ssl_verify_callback(int, X509_STORE_CTX *);
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);
-X509 *get_X509_from_file(char *filename);
-int ssl_cmp_certs(char *filename, X509 * a);
-
-/*
- * OpenSSL 1.1+ introduced a generic TLS_method()
- * For older releases we substitute the appropriate method
- */
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-
-#define TLS_method SSLv23_method
-
-#define SSL3_RT_HEADER 0x100
-
-#ifndef SSL_CTX_set_max_proto_version
-/** Mimics SSL_CTX_set_max_proto_version for OpenSSL < 1.1 */
-static inline int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, long tls_ver_max)
-{
- long sslopt = 0;
-
- if (tls_ver_max < TLS1_VERSION)
- {
- sslopt |= SSL_OP_NO_TLSv1;
- }
-#ifdef SSL_OP_NO_TLSv1_1
- if (tls_ver_max < TLS1_1_VERSION)
- {
- sslopt |= SSL_OP_NO_TLSv1_1;
- }
-#endif
-#ifdef SSL_OP_NO_TLSv1_2
- if (tls_ver_max < TLS1_2_VERSION)
- {
- sslopt |= SSL_OP_NO_TLSv1_2;
- }
-#endif
- SSL_CTX_set_options(ctx, sslopt);
-
- return 1;
-}
-#endif /* SSL_CTX_set_max_proto_version */
-
-#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
-
#ifdef MPPE
#define EAPTLS_MPPE_KEY_LEN 32
#endif /* MPPE */
-
-void log_ssl_errors( void )
-{
- unsigned long ssl_err = ERR_get_error();
-
- if (ssl_err != 0)
- dbglog("EAP-TLS SSL error stack:");
- while (ssl_err != 0) {
- dbglog( ERR_error_string( ssl_err, NULL ) );
- ssl_err = ERR_get_error();
- }
-}
-
-
int password_callback (char *buf, int size, int rwflag, void *u)
{
if (buf)
if (CONF_modules_load( config, NULL, 0 ) <= 0 )
{
warn( "EAP-TLS: Error loading OpenSSL modules" );
- log_ssl_errors();
+ tls_log_sslerr();
config = NULL;
}
|| !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0))
{
warn( "EAP-TLS: Error loading dynamic engine '%s'", engine_name );
- log_ssl_errors();
+ tls_log_sslerr();
ENGINE_free(e);
e = NULL;
}
if(!ENGINE_set_default(e, ENGINE_METHOD_ALL))
{
warn( "EAP-TLS: Cannot use that engine" );
- log_ssl_errors();
+ tls_log_sslerr();
ENGINE_free(e);
e = NULL;
}
* for client or server use can be loaded.
*/
SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
- char *certfile, char *peer_certfile, char *privkeyfile, char *pkcs12)
+ char *certfile, char *privkeyfile, char *pkcs12)
{
#ifndef OPENSSL_NO_ENGINE
char *cert_engine_name = NULL;
#endif
SSL_CTX *ctx;
SSL *ssl;
- X509_STORE *certstore;
- X509_LOOKUP *lookup;
X509 *tmp;
X509 *cert = NULL;
PKCS12 *p12 = NULL;
BIO *input;
int ret;
int reason;
-#if defined(TLS1_2_VERSION)
- long tls_version = TLS1_2_VERSION;
-#elif defined(TLS1_1_VERSION)
- long tls_version = TLS1_1_VERSION;
-#else
- long tls_version = TLS1_VERSION;
-#endif
/*
* Without these can't continue
}
}
- SSL_library_init();
- SSL_load_error_strings();
+ tls_init();
#ifndef OPENSSL_NO_ENGINE
/* load the openssl config file only once and load it before triggering
ssl_config = eaptls_ssl_load_config();
#endif
- ctx = SSL_CTX_new(TLS_method());
-
+ ctx = SSL_CTX_new(tls_method());
if (!ctx) {
error("EAP-TLS: Cannot initialize SSL CTX context");
goto fail;
SSL_CTX_set_default_passwd_cb (ctx, password_callback);
- if (strlen(cacertfile) == 0) cacertfile = NULL;
- if (strlen(capath) == 0) capath = NULL;
-
- if (!SSL_CTX_load_verify_locations(ctx, cacertfile, capath))
- {
- error("EAP-TLS: Cannot load verify locations");
- if (cacertfile) dbglog("CA certificate file = [%s]", cacertfile);
- if (capath) dbglog("CA certificate path = [%s]", capath);
+ if (tls_set_ca(ctx, capath, cacertfile) != 0) {
goto fail;
}
else
{
warn("EAP-TLS: Cannot load key with URI: '%s'", certfile );
- log_ssl_errors();
+ tls_log_sslerr();
}
}
else
goto fail;
}
- /* Explicitly set the NO_TICKETS flag to support Win7/Win8 clients */
- SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3
-#ifdef SSL_OP_NO_TICKET
- | SSL_OP_NO_TICKET
-#endif
- );
-
- /* OpenSSL 1.1.1+ does not include RC4 ciphers by default.
- * This causes totally obsolete WinXP clients to fail. If you really
- * need ppp+EAP-TLS+openssl 1.1.1+WinXP then enable RC4 cipers and
- * make sure that you use an OpenSSL that supports them
-
- SSL_CTX_set_cipher_list(ctx, "RC4");
- */
-
+ /* 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
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);
- /* As EAP-TLS+TLSv1.3 is highly experimental we offer the user a chance to override */
- if (max_tls_version)
- {
- if (strncmp(max_tls_version, "1.0", 3) == 0)
- tls_version = TLS1_VERSION;
- else if (strncmp(max_tls_version, "1.1", 3) == 0)
- tls_version = TLS1_1_VERSION;
- else if (strncmp(max_tls_version, "1.2", 3) == 0)
-#ifdef TLS1_2_VERSION
- tls_version = TLS1_2_VERSION;
-#else
- {
- warn("TLSv1.2 not available. Defaulting to TLSv1.1");
- tls_version = TLS_1_1_VERSION;
- }
-#endif
- else if (strncmp(max_tls_version, "1.3", 3) == 0)
-#ifdef TLS1_3_VERSION
- tls_version = TLS1_3_VERSION;
-#else
- warn("TLSv1.3 not available.");
-#endif
- }
-
- dbglog("EAP-TLS: Setting max protocol version to 0x%X", tls_version);
- SSL_CTX_set_max_proto_version(ctx, tls_version);
-
- SSL_CTX_set_verify_depth(ctx, 5);
- SSL_CTX_set_verify(ctx,
- SSL_VERIFY_PEER |
- SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
- &ssl_verify_callback);
-
- if (crl_dir) {
- if (!(certstore = SSL_CTX_get_cert_store(ctx))) {
- error("EAP-TLS: Failed to get certificate store");
- goto fail;
- }
-
- if (!(lookup =
- X509_STORE_add_lookup(certstore, X509_LOOKUP_hash_dir()))) {
- error("EAP-TLS: Store lookup for CRL failed");
-
- goto fail;
- }
-
- X509_LOOKUP_add_dir(lookup, crl_dir, X509_FILETYPE_PEM);
- X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK);
- }
-
- if (crl_file) {
- FILE *fp = NULL;
- X509_CRL *crl = NULL;
-
- fp = fopen(crl_file, "r");
- if (!fp) {
- error("EAP-TLS: Cannot open CRL file '%s'", crl_file);
- goto fail;
- }
-
- crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL);
- if (!crl) {
- error("EAP-TLS: Cannot read CRL file '%s'", crl_file);
- goto fail;
- }
-
- if (!(certstore = SSL_CTX_get_cert_store(ctx))) {
- error("EAP-TLS: Failed to get certificate store");
- goto fail;
- }
- if (!X509_STORE_add_crl(certstore, crl)) {
- error("EAP-TLS: Cannot add CRL to certificate store");
- goto fail;
- }
- X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK);
+ /* Configure the maximum SSL version */
+ tls_set_version(ctx, max_tls_version);
+ /* Configure the callback */
+ if (tls_set_verify(ctx, 5)) {
+ goto fail;
}
- /*
- * If a peer certificate file was specified, it must be valid, else fail
- */
- if (peer_certfile[0]) {
- if (!(tmp = get_X509_from_file(peer_certfile))) {
- error("EAP-TLS: Error loading client certificate from file %s",
- peer_certfile);
- goto fail;
- }
- X509_free(tmp);
+ /* Configure CRL check (if any) */
+ if (tls_set_crl(ctx, crl_dir, crl_file)) {
+ goto fail;
}
return ctx;
if (chain)
sk_X509_pop_free(chain, X509_free);
- log_ssl_errors();
+ tls_log_sslerr();
SSL_CTX_free(ctx);
return NULL;
}
if (!esp->es_server.ea_session)
fatal("Allocation error");
ets = esp->es_server.ea_session;
- ets->client = 0;
if (!esp->es_server.ea_peer) {
error("EAP-TLS: Error: client name not set (BUG)");
return 0;
}
- strlcpy(ets->peer, esp->es_server.ea_peer, MAXWORDLEN-1);
-
dbglog( "getting eaptls secret" );
if (!get_eaptls_secret(esp->es_unit, esp->es_server.ea_peer,
esp->es_server.ea_name, clicertfile,
ets->mtu = eaptls_get_mtu(esp->es_unit);
- ets->ctx = eaptls_init_ssl(1, cacertfile, capath, servcertfile, clicertfile, pkfile, pkcs12);
+ 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_msg_callback(ets->ssl, ssl_msg_callback);
SSL_set_msg_callback_arg(ets->ssl, ets);
- /*
- * Attach the session struct to the connection, so we can later
- * retrieve it when doing certificate verification
- */
- SSL_set_ex_data(ets->ssl, 0, ets);
-
SSL_set_accept_state(ets->ssl);
ets->tls_v13 = 0;
ets->datalen = 0;
ets->alert_sent = 0;
ets->alert_recv = 0;
-
- /*
- * If we specified the client certificate file, store it in ets->peercertfile,
- * so we can check it later in ssl_verify_callback()
- */
- if (clicertfile[0])
- strlcpy(&ets->peercertfile[0], clicertfile, MAXWORDLEN);
- else
- ets->peercertfile[0] = 0;
-
return 1;
fail:
if (!esp->es_client.ea_session)
fatal("Allocation error");
ets = esp->es_client.ea_session;
- ets->client = 1;
-
- /*
- * If available, copy server name in ets; it will be used in cert
- * verify
- */
- if (esp->es_client.ea_peer)
- strlcpy(ets->peer, esp->es_client.ea_peer, MAXWORDLEN-1);
- else
- ets->peer[0] = 0;
-
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,
- ets->peer, clicertfile,
+ 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, ets->peer );
+ 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, servcertfile, pkfile, pkcs12);
+ 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
*/
SSL_set_msg_callback(ets->ssl, ssl_msg_callback);
SSL_set_msg_callback_arg(ets->ssl, ets);
-
- /*
- * Attach the session struct to the connection, so we can later
- * retrieve it when doing certificate verification
- */
- SSL_set_ex_data(ets->ssl, 0, ets);
-
SSL_set_connect_state(ets->ssl);
ets->tls_v13 = 0;
ets->datalen = 0;
ets->alert_sent = 0;
ets->alert_recv = 0;
-
- /*
- * If we specified the server certificate file, store it in
- * ets->peercertfile, so we can check it later in
- * ssl_verify_callback()
- */
- if (servcertfile[0])
- strlcpy(ets->peercertfile, servcertfile, MAXWORDLEN);
- else
- ets->peercertfile[0] = 0;
-
return 1;
fail:
if (ets->ctx)
SSL_CTX_free(ets->ctx);
+ if (ets->info)
+ tls_free_verify_info(&ets->info);
+
free(ets);
}
}
if (BIO_write(ets->into_ssl, ets->data, ets->datalen) == -1)
- log_ssl_errors();
+ tls_log_sslerr();
SSL_read(ets->ssl, dummy, 65536);
INCPTR(ets->rtx_len, *outp);
}
-/*
- * Verify a certificate.
- * Most of the work (signatures and issuer attributes checking)
- * is done by ssl; we check the CN in the peer certificate
- * against the peer name.
- */
-int ssl_verify_callback(int ok, X509_STORE_CTX * ctx)
-{
- char subject[256];
- char cn_str[256];
- X509 *peer_cert;
- int err, depth;
- SSL *ssl;
- struct eaptls_session *ets;
- char *ptr1 = NULL, *ptr2 = NULL;
-
- peer_cert = X509_STORE_CTX_get_current_cert(ctx);
- err = X509_STORE_CTX_get_error(ctx);
- depth = X509_STORE_CTX_get_error_depth(ctx);
-
- dbglog("certificate verify depth: %d", depth);
-
- if (auth_required && !ok) {
- X509_NAME_oneline(X509_get_subject_name(peer_cert),
- subject, 256);
-
- X509_NAME_get_text_by_NID(X509_get_subject_name(peer_cert),
- NID_commonName, cn_str, 256);
-
- dbglog("Certificate verification error:\n depth: %d CN: %s"
- "\n err: %d (%s)\n", depth, cn_str, err,
- X509_verify_cert_error_string(err));
-
- return 0;
- }
-
- ssl = X509_STORE_CTX_get_ex_data(ctx,
- SSL_get_ex_data_X509_STORE_CTX_idx());
-
- ets = (struct eaptls_session *)SSL_get_ex_data(ssl, 0);
-
- if (ets == NULL) {
- error("Error: SSL_get_ex_data returned NULL");
- return 0;
- }
-
- log_ssl_errors();
-
- if (!depth)
- {
- /* Verify certificate based on certificate type and extended key usage */
- if (tls_verify_key_usage) {
- int purpose = ets->client ? X509_PURPOSE_SSL_SERVER : X509_PURPOSE_SSL_CLIENT ;
- if (X509_check_purpose(peer_cert, purpose, 0) == 0) {
- error("Certificate verification error: nsCertType mismatch");
- return 0;
- }
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
- int flags = ets->client ? XKU_SSL_SERVER : XKU_SSL_CLIENT;
- if (!(X509_get_extended_key_usage(peer_cert) & flags)) {
- error("Certificate verification error: invalid extended key usage");
- return 0;
- }
-#endif
- info("Certificate key usage: OK");
- }
-
- /*
- * If acting as client and the name of the server wasn't specified
- * explicitely, we can't verify the server authenticity
- */
- if (!tls_verify_method)
- tls_verify_method = TLS_VERIFY_NONE;
-
- if (!ets->peer[0] || !strcmp(TLS_VERIFY_NONE, tls_verify_method)) {
- warn("Certificate verication disabled or no peer name was specified");
- return ok;
- }
-
- /* This is the peer certificate */
- X509_NAME_oneline(X509_get_subject_name(peer_cert),
- subject, 256);
-
- X509_NAME_get_text_by_NID(X509_get_subject_name(peer_cert),
- NID_commonName, cn_str, 256);
-
- /* Verify based on subject name */
- ptr1 = ets->peer;
- if (!strcmp(TLS_VERIFY_SUBJECT, tls_verify_method)) {
- ptr2 = subject;
- }
-
- /* Verify based on common name (default) */
- if (strlen(tls_verify_method) == 0 ||
- !strcmp(TLS_VERIFY_NAME, tls_verify_method)) {
- ptr2 = cn_str;
- }
-
- /* Match the suffix of common name */
- if (!strcmp(TLS_VERIFY_SUFFIX, tls_verify_method)) {
- int len = strlen(ptr1);
- int off = strlen(cn_str) - len;
- ptr2 = cn_str;
- if (off > 0) {
- ptr2 = cn_str + off;
- }
- }
-
- if (strcmp(ptr1, ptr2)) {
- error("Certificate verification error: CN (%s) != %s", ptr1, ptr2);
- return 0;
- }
-
- info("Certificate CN: %s, peer name %s", cn_str, ets->peer);
-
- /*
- * If a peer certificate file was specified, here we check it
- */
- if (ets->peercertfile[0]) {
- if (ssl_cmp_certs(&ets->peercertfile[0], peer_cert)
- != 0) {
- error
- ("Peer certificate doesn't match stored certificate");
- return 0;
- }
- }
- }
-
- return ok;
-}
-
-/*
- * Compare a certificate with the one stored in a file
- */
-int ssl_cmp_certs(char *filename, X509 * a)
-{
- X509 *b;
- int ret;
-
- if (!(b = get_X509_from_file(filename)))
- return 1;
-
- ret = X509_cmp(a, b);
- X509_free(b);
-
- return ret;
-
-}
-
-X509 *get_X509_from_file(char *filename)
-{
- FILE *fp;
- X509 *ret;
-
- if (!(fp = fopen(filename, "r")))
- return NULL;
-
- ret = PEM_read_X509(fp, NULL, NULL, NULL);
-
- fclose(fp);
-
- return ret;
-}
-
/*
* Every sent & received message this callback function is invoked,
* so we know when alert messages have arrived or are sent and
#define EAP_TLS_MAX_LEN 65536 /* max eap tls packet size */
+struct tls_info;
+
struct eaptls_session
{
u_char *data; /* buffered data */
SSL *ssl; /* ssl connection */
BIO *from_ssl;
BIO *into_ssl;
- char peer[MAXWORDLEN]; /* peer name */
char peercertfile[MAXWORDLEN];
bool alert_sent;
u_char alert_sent_desc;
char rtx[EAP_TLS_MAX_LEN]; /* retransmission buffer */
int rtx_len;
int mtu; /* unit mtu */
- bool client;
+ struct tls_info *info;
};
SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
- char *certfile, char *peer_certfile, char *privkeyfile, char *pkcs12);
+ char *certfile, char *privkeyfile, char *pkcs12);
int eaptls_init_ssl_server(eap_state * esp);
int eaptls_init_ssl_client(eap_state * esp);
void eaptls_free_session(struct eaptls_session *ets);
#include "pathnames.h"
#include "md5.h"
#include "eap.h"
+#ifdef USE_PEAP
+#include "peap.h"
+#endif /* USE_PEAP */
#ifdef USE_SRP
#ifdef HAVE_TIME_H
break;
#endif /* CHAPMS */
+#ifdef USE_PEAP
+ case EAPT_PEAP:
+
+ /* Initialize the PEAP context (if not already initialized) */
+ if (!esp->ea_peap) {
+ rhostname[0] = '\0';
+ if (explicit_remote || (remote_name[0] != '\0')) {
+ strlcpy(rhostname, remote_name, sizeof (rhostname));
+ }
+ if (peap_init(&esp->ea_peap, rhostname)) {
+ eap_send_nak(esp, id, EAPT_TLS);
+ break;
+ }
+ }
+
+ /* Process the PEAP packet */
+ if (peap_process(esp, id, inp, len)) {
+ eap_send_nak(esp, id, EAPT_TLS);
+ }
+
+ break;
+#endif /* USE_PEAP */
default:
info("EAP: unknown authentication type %d; Naking", typenum);
PRINTMSG(inp, len);
}
+#ifdef USE_PEAP
+ peap_finish(&esp->ea_peap);
+#endif
+
esp->es_client.ea_state = eapOpen;
auth_withpeer_success(esp->es_unit, PPP_EAP, 0);
}
esp->es_client.ea_state = eapBadAuth;
error("EAP: peer reports authentication failure");
+
+#ifdef USE_PEAP
+ peap_finish(&esp->ea_peap);
+#endif
+
auth_withpeer_fail(esp->es_unit, PPP_EAP);
}
int es_unit; /* Interface unit number */
struct eap_auth es_client; /* Client (authenticatee) data */
struct eap_auth es_server; /* Server (authenticator) data */
+#ifdef USE_PEAP
+ struct peap_state *ea_peap; /* Client PEAP (authenticator) data */
+#endif
int es_savedtime; /* Saved timeout */
int es_rechallenge; /* EAP rechallenge interval */
int es_lwrechallenge; /* SRP lightweight rechallenge inter */
mppe_set_keys(SendKey, RecvKey, SHA1_SIGNATURE_SIZE);
}
+#ifndef UNIT_TEST
+
/*
* Set MPPE options from plugins.
*/
}
}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2011 Rustam Kovhaev. All rights reserved.
+ * Copyright (c) 2021 Eivind Næss. 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.
+ *
+ * NOTES:
+ *
+ * PEAP has 2 phases,
+ * 1 - Outer EAP, where TLS session gets established
+ * 2 - Inner EAP, where inside TLS session with EAP MSCHAPV2 auth, or any other auth
+ *
+ * And so protocols encapsulation looks like this:
+ * Outer EAP -> TLS -> Inner EAP -> MSCHAPV2
+ * PEAP can compress an inner EAP packet prior to encapsulating it within
+ * the Data field of a PEAP packet by removing its Code, Identifier,
+ * and Length fields, and Microsoft PEAP server/client always does that
+ *
+ * Current implementation does not support:
+ * a) Fast reconnect
+ * b) Inner EAP fragmentation
+ * c) Any other auth other than MSCHAPV2
+ *
+ * For details on the PEAP protocol, look to Microsoft:
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-peap
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <openssl/opensslv.h>
+#include <openssl/ssl.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <net/ppp_defs.h>
+
+#include "pppd.h"
+#include "eap.h"
+#include "tls.h"
+#include "chap-new.h"
+#include "chap_ms.h"
+#include "mppe.h"
+#include "peap.h"
+
+#ifdef UNIT_TEST
+#define novm(x)
+#endif
+
+struct peap_state {
+ SSL_CTX *ctx;
+ SSL *ssl;
+ BIO *in_bio;
+ BIO *out_bio;
+
+ int phase;
+ int written, read;
+ u_char *in_buf;
+ u_char *out_buf;
+
+ u_char ipmk[PEAP_TLV_IPMK_LEN];
+ u_char tk[PEAP_TLV_TK_LEN];
+ u_char nonce[PEAP_TLV_NONCE_LEN];
+ struct tls_info *info;
+#ifdef CHAPMS
+ struct chap_digest_type *chap;
+#endif
+};
+
+/*
+ * K = Key, S = Seed, LEN = output length
+ * PRF+(K, S, LEN) = T1 | T2 | ... |Tn
+ * Where:
+ * T1 = HMAC-SHA1 (K, S | 0x01 | 0x00 | 0x00)
+ * T2 = HMAC-SHA1 (K, T1 | S | 0x02 | 0x00 | 0x00)
+ * ...
+ * Tn = HMAC-SHA1 (K, Tn-1 | S | n | 0x00 | 0x00)
+ * As shown, PRF+ is computed in iterations. The number of iterations (n)
+ * depends on the output length (LEN).
+ */
+static void peap_prfplus(u_char *seed, size_t seed_len, u_char *key, size_t key_len, u_char *out_buf, size_t pfr_len)
+{
+ int pos;
+ u_char *buf, *hash;
+ size_t max_iter, i, j, k;
+ u_int len;
+
+ max_iter = (pfr_len + SHA_DIGEST_LENGTH - 1) / SHA_DIGEST_LENGTH;
+ buf = malloc(seed_len + max_iter * SHA_DIGEST_LENGTH);
+ if (!buf)
+ novm("pfr buffer");
+ hash = malloc(pfr_len + SHA_DIGEST_LENGTH);
+ if (!hash)
+ novm("hash buffer");
+
+ for (i = 0; i < max_iter; i++) {
+ j = 0;
+ k = 0;
+
+ if (i > 0)
+ j = SHA_DIGEST_LENGTH;
+ for (k = 0; k < seed_len; k++)
+ buf[j + k] = seed[k];
+ pos = j + k;
+ buf[pos] = i + 1;
+ pos++;
+ buf[pos] = 0x00;
+ pos++;
+ buf[pos] = 0x00;
+ pos++;
+ if (!HMAC(EVP_sha1(), key, key_len, buf, pos, (hash + i * SHA_DIGEST_LENGTH), &len))
+ fatal("HMAC() failed");
+ for (j = 0; j < SHA_DIGEST_LENGTH; j++)
+ buf[j] = hash[i * SHA_DIGEST_LENGTH + j];
+ }
+ BCOPY(hash, out_buf, pfr_len);
+ free(hash);
+ free(buf);
+}
+
+static void generate_cmk(u_char *ipmk, u_char *tempkey, u_char *nonce, u_char *tlv_response_out, int client)
+{
+ const char *label = PEAP_TLV_IPMK_SEED_LABEL;
+ u_char data_tlv[PEAP_TLV_DATA_LEN] = {0};
+ u_char isk[PEAP_TLV_ISK_LEN] = {0};
+ u_char ipmkseed[PEAP_TLV_IPMKSEED_LEN] = {0};
+ u_char cmk[PEAP_TLV_CMK_LEN] = {0};
+ u_char buf[PEAP_TLV_CMK_LEN + PEAP_TLV_IPMK_LEN] = {0};
+ u_char compound_mac[PEAP_TLV_COMP_MAC_LEN] = {0};
+ u_int len;
+
+ /* format outgoing CB TLV response packet */
+ data_tlv[1] = PEAP_TLV_TYPE;
+ data_tlv[3] = PEAP_TLV_LENGTH_FIELD;
+ if (client)
+ data_tlv[7] = PEAP_TLV_SUBTYPE_RESPONSE;
+ else
+ data_tlv[7] = PEAP_TLV_SUBTYPE_REQUEST;
+ BCOPY(nonce, (data_tlv + PEAP_TLV_HEADERLEN), PEAP_TLV_NONCE_LEN);
+ data_tlv[60] = EAPT_PEAP;
+
+#ifdef MPPE
+ mppe_get_send_key(isk, MPPE_MAX_KEY_LEN);
+ mppe_get_recv_key(isk + MPPE_MAX_KEY_LEN, MPPE_MAX_KEY_LEN);
+#endif
+
+ BCOPY(label, ipmkseed, strlen(label));
+ BCOPY(isk, ipmkseed + strlen(label), PEAP_TLV_ISK_LEN);
+ peap_prfplus(ipmkseed, PEAP_TLV_IPMKSEED_LEN,
+ tempkey, PEAP_TLV_TEMPKEY_LEN, buf, PEAP_TLV_CMK_LEN + PEAP_TLV_IPMK_LEN);
+
+ BCOPY(buf, ipmk, PEAP_TLV_IPMK_LEN);
+ BCOPY(buf + PEAP_TLV_IPMK_LEN, cmk, PEAP_TLV_CMK_LEN);
+ if (!HMAC(EVP_sha1(), cmk, PEAP_TLV_CMK_LEN, data_tlv, PEAP_TLV_DATA_LEN, compound_mac, &len))
+ fatal("HMAC() failed");
+ BCOPY(compound_mac, data_tlv + PEAP_TLV_HEADERLEN + PEAP_TLV_NONCE_LEN, PEAP_TLV_COMP_MAC_LEN);
+ /* do not copy last byte to response packet */
+ BCOPY(data_tlv, tlv_response_out, PEAP_TLV_DATA_LEN - 1);
+}
+
+static void verify_compound_mac(struct peap_state *psm, u_char *in_buf)
+{
+ u_char nonce[PEAP_TLV_NONCE_LEN] = {0};
+ u_char out_buf[PEAP_TLV_LEN] = {0};
+
+ BCOPY(in_buf, nonce, PEAP_TLV_NONCE_LEN);
+ generate_cmk(psm->ipmk, psm->tk, nonce, out_buf, 0);
+ if (memcmp((in_buf + PEAP_TLV_NONCE_LEN), (out_buf + PEAP_TLV_HEADERLEN + PEAP_TLV_NONCE_LEN), PEAP_TLV_CMK_LEN))
+ fatal("server's CMK does not match client's CMK, potential MiTM");
+}
+
+#ifdef MPPE
+#define PEAP_MPPE_KEY_LEN 32
+
+static void generate_mppe_keys(u_char *ipmk, int client)
+{
+ const char *label = PEAP_TLV_CSK_SEED_LABEL;
+ u_char csk[PEAP_TLV_CSK_LEN] = {0};
+ size_t len;
+
+ dbglog("PEAP CB: generate mppe keys");
+ len = strlen(label);
+ len++; /* CSK requires NULL byte in seed */
+ peap_prfplus((u_char *)label, len, ipmk, PEAP_TLV_IPMK_LEN, csk, PEAP_TLV_CSK_LEN);
+
+ /*
+ * The first 64 bytes of the CSK are split into two MPPE keys, as follows.
+ *
+ * +-----------------------+------------------------+
+ * | First 32 bytes of CSK | Second 32 bytes of CSK |
+ * +-----------------------+------------------------+
+ * | MS-MPPE-Send-Key | MS-MPPE-Recv-Key |
+ * +-----------------------+------------------------+
+ */
+ if (client) {
+ mppe_set_keys(csk, csk + PEAP_MPPE_KEY_LEN, PEAP_MPPE_KEY_LEN);
+ } else {
+ mppe_set_keys(csk + PEAP_MPPE_KEY_LEN, csk, PEAP_MPPE_KEY_LEN);
+ }
+}
+
+#endif
+
+#ifndef UNIT_TEST
+
+static void peap_ack(eap_state *esp, u_char id)
+{
+ u_char *outp;
+
+ outp = outpacket_buf;
+ MAKEHEADER(outp, PPP_EAP);
+ PUTCHAR(EAP_RESPONSE, outp);
+ PUTCHAR(id, outp);
+ esp->es_client.ea_id = id;
+ PUTSHORT(PEAP_HEADERLEN, outp);
+ PUTCHAR(EAPT_PEAP, outp);
+ PUTCHAR(PEAP_FLAGS_ACK, outp);
+ output(esp->es_unit, outpacket_buf, PPP_HDRLEN + PEAP_HEADERLEN);
+}
+
+static void peap_response(eap_state *esp, u_char id, u_char *buf, int len)
+{
+ struct peap_state *psm = esp->ea_peap;
+ u_char *outp;
+ int peap_len;
+
+ outp = outpacket_buf;
+ MAKEHEADER(outp, PPP_EAP);
+ PUTCHAR(EAP_RESPONSE, outp);
+ PUTCHAR(id, outp);
+ esp->es_client.ea_id = id;
+
+ if (psm->phase == PEAP_PHASE_1)
+ peap_len = PEAP_HEADERLEN + PEAP_FRAGMENT_LENGTH_FIELD + len;
+ else
+ peap_len = PEAP_HEADERLEN + len;
+
+ PUTSHORT(peap_len, outp);
+ PUTCHAR(EAPT_PEAP, outp);
+
+ if (psm->phase == PEAP_PHASE_1) {
+ PUTCHAR(PEAP_L_FLAG_SET, outp);
+ PUTLONG(len, outp);
+ } else
+ PUTCHAR(PEAP_NO_FLAGS, outp);
+
+ BCOPY(buf, outp, len);
+ output(esp->es_unit, outpacket_buf, PPP_HDRLEN + peap_len);
+}
+
+void peap_do_inner_eap(u_char *in_buf, int in_len, eap_state *esp, int id,
+ u_char *out_buf, int *out_len)
+{
+ struct peap_state *psm = esp->ea_peap;
+ int used = 0;
+ int typenum;
+ int secret_len;
+ char secret[MAXSECRETLEN + 1];
+ char rhostname[MAXWORDLEN];
+ u_char *outp = out_buf;
+
+ dbglog("PEAP: EAP (in): %.*B", in_len, in_buf);
+
+ if (*(in_buf + EAP_HEADERLEN) == PEAP_CAPABILITIES_TYPE &&
+ in_len == (EAP_HEADERLEN + PEAP_CAPABILITIES_LEN)) {
+ /* use original packet as template for response */
+ BCOPY(in_buf, outp, EAP_HEADERLEN + PEAP_CAPABILITIES_LEN);
+ PUTCHAR(EAP_RESPONSE, outp);
+ PUTCHAR(id, outp);
+ /* change last byte to 0 to disable fragmentation */
+ *(outp + PEAP_CAPABILITIES_LEN + 1) = 0x00;
+ used = EAP_HEADERLEN + PEAP_CAPABILITIES_LEN;
+ goto done;
+ }
+ if (*(in_buf + EAP_HEADERLEN + PEAP_TLV_HEADERLEN) == PEAP_TLV_TYPE &&
+ in_len == PEAP_TLV_LEN) {
+ /* PEAP TLV message, do cryptobinding */
+ SSL_export_keying_material(psm->ssl, psm->tk, PEAP_TLV_TK_LEN,
+ PEAP_TLV_TK_SEED_LABEL, strlen(PEAP_TLV_TK_SEED_LABEL), NULL, 0, 0);
+ /* verify server's CMK */
+ verify_compound_mac(psm, in_buf + EAP_HEADERLEN + PEAP_TLV_RESULT_LEN + PEAP_TLV_HEADERLEN);
+ /* generate client's CMK with new nonce */
+ PUTCHAR(EAP_RESPONSE, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(PEAP_TLV_LEN, outp);
+ BCOPY(in_buf + EAP_HEADERLEN, outp, PEAP_TLV_RESULT_LEN);
+ outp = outp + PEAP_TLV_RESULT_LEN;
+ RAND_bytes(psm->nonce, PEAP_TLV_NONCE_LEN);
+ generate_cmk(psm->ipmk, psm->tk, psm->nonce, outp, 1);
+#ifdef MPPE
+ /* set mppe keys */
+ generate_mppe_keys(psm->ipmk, 1);
+#endif
+ used = PEAP_TLV_LEN;
+ goto done;
+ }
+
+ GETCHAR(typenum, in_buf);
+ in_len--;
+
+ switch (typenum) {
+ case EAPT_IDENTITY:
+ /* Respond with our identity to the peer */
+ PUTCHAR(EAPT_IDENTITY, outp);
+ BCOPY(esp->es_client.ea_name, outp,
+ esp->es_client.ea_namelen);
+ used += (esp->es_client.ea_namelen + 1);
+ break;
+
+ case EAPT_TLS:
+ /* Send NAK to EAP_TLS request */
+ PUTCHAR(EAPT_NAK, outp);
+ PUTCHAR(EAPT_MSCHAPV2, outp);
+ used += 2;
+ break;
+
+#if CHAPMS
+ case EAPT_MSCHAPV2: {
+
+ // Must have at least 4 more bytes to process CHAP header
+ if (in_len < 4) {
+ error("PEAP: received invalid MSCHAPv2 packet, too short");
+ break;
+ }
+
+ u_char opcode;
+ GETCHAR(opcode, in_buf);
+
+ u_char chap_id;
+ GETCHAR(chap_id, in_buf);
+
+ short mssize;
+ GETSHORT(mssize, in_buf);
+
+ // Validate the CHAP packet (including header)
+ if (in_len != mssize) {
+ error("PEAP: received invalid MSCHAPv2 packet, invalid length");
+ break;
+ }
+ in_len -= 4;
+
+ switch (opcode) {
+ case CHAP_CHALLENGE: {
+
+ u_char *challenge = in_buf; // VLEN + VALUE
+ u_char vsize;
+
+ GETCHAR(vsize, in_buf);
+ in_len -= 1;
+
+ if (vsize != MS_CHAP2_PEER_CHAL_LEN || in_len < MS_CHAP2_PEER_CHAL_LEN) {
+ error("PEAP: received invalid MSCHAPv2 packet, invalid value-length: %d", vsize);
+ goto done;
+ }
+
+ INCPTR(MS_CHAP2_PEER_CHAL_LEN, in_buf);
+ in_len -= MS_CHAP2_PEER_CHAL_LEN;
+
+ // Copy the provided remote host name
+ rhostname[0] = '\0';
+ if (in_len > 0) {
+ if (in_len >= sizeof(rhostname)) {
+ dbglog("PEAP: trimming really long peer name down");
+ in_len = sizeof(rhostname) - 1;
+ }
+ BCOPY(in_buf, rhostname, in_len);
+ rhostname[in_len] = '\0';
+ }
+
+ // In case the remote doesn't give us his name, or user explictly specified remotename is config
+ if (explicit_remote || (remote_name[0] != '\0' && in_len == 0))
+ strlcpy(rhostname, remote_name, sizeof(rhostname));
+
+ // Get the scrert for authenticating ourselves with the specified host
+ if (get_secret(esp->es_unit, esp->es_client.ea_name,
+ rhostname, secret, &secret_len, 0)) {
+
+ u_char response[MS_CHAP2_RESPONSE_LEN+1];
+ u_char user_len = esp->es_client.ea_namelen;
+ char *user = esp->es_client.ea_name;
+
+ psm->chap->make_response(response, chap_id, user,
+ challenge, secret, secret_len, NULL);
+
+ PUTCHAR(EAPT_MSCHAPV2, outp);
+ PUTCHAR(CHAP_RESPONSE, outp);
+ PUTCHAR(chap_id, outp);
+ PUTCHAR(0, outp);
+ PUTCHAR(5 + user_len + MS_CHAP2_RESPONSE_LEN, outp);
+ BCOPY(response, outp, MS_CHAP2_RESPONSE_LEN+1); // VLEN + VALUE
+ INCPTR(MS_CHAP2_RESPONSE_LEN+1, outp);
+ BCOPY(user, outp, user_len);
+ used = 5 + user_len + MS_CHAP2_RESPONSE_LEN + 1;
+
+ } else {
+ dbglog("PEAP: no CHAP secret for auth to %q", rhostname);
+ PUTCHAR(EAPT_NAK, outp);
+ ++used;
+ }
+ break;
+ }
+ case CHAP_SUCCESS: {
+
+ u_char status = CHAP_FAILURE;
+ if (psm->chap->check_success(chap_id, in_buf, in_len)) {
+ info("Chap authentication succeeded! %.*v", in_len, in_buf);
+ status = CHAP_SUCCESS;
+ }
+
+ PUTCHAR(EAPT_MSCHAPV2, outp);
+ PUTCHAR(status, outp);
+ used += 2;
+ break;
+ }
+ case CHAP_FAILURE: {
+
+ psm->chap->handle_failure(in_buf, in_len);
+ PUTCHAR(EAPT_MSCHAPV2, outp);
+ PUTCHAR(status, outp);
+ used += 2;
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ } // EAPT_MSCHAPv2
+#endif
+ default:
+
+ /* send compressed EAP NAK for any unknown packet */
+ PUTCHAR(EAPT_NAK, outp);
+ ++used;
+ }
+
+done:
+
+ dbglog("PEAP: EAP (out): %.*B", used, psm->out_buf);
+ *out_len = used;
+}
+
+int peap_init(struct peap_state **ctx, const char *rhostname)
+{
+ const SSL_METHOD *method;
+
+ if (!ctx)
+ return -1;
+
+ tls_init();
+
+ struct peap_state *psm = malloc(sizeof(*psm));
+ if (!psm)
+ novm("peap psm struct");
+ psm->in_buf = malloc(TLS_RECORD_MAX_SIZE);
+ if (!psm->in_buf)
+ novm("peap tls buffer");
+ psm->out_buf = malloc(TLS_RECORD_MAX_SIZE);
+ if (!psm->out_buf)
+ novm("peap tls buffer");
+ method = tls_method();
+ if (!method)
+ novm("TLS_method() failed");
+ psm->ctx = SSL_CTX_new(method);
+ if (!psm->ctx)
+ novm("SSL_CTX_new() failed");
+
+ /* Configure the default options */
+ tls_set_opts(psm->ctx);
+
+ /* Configure the max TLS version */
+ tls_set_version(psm->ctx, max_tls_version);
+
+ /* Configure the peer certificate callback */
+ tls_set_verify(psm->ctx, 5);
+
+ /* Configure CA locations */
+ if (tls_set_ca(psm->ctx, ca_path, cacert_file)) {
+ fatal("Could not set CA verify locations");
+ }
+
+ /* Configure CRL check (if any) */
+ if (tls_set_crl(psm->ctx, crl_dir, crl_file)) {
+ fatal("Could not set CRL verify locations");
+ }
+
+ psm->out_bio = BIO_new(BIO_s_mem());
+ psm->in_bio = BIO_new(BIO_s_mem());
+ BIO_set_mem_eof_return(psm->out_bio, -1);
+ BIO_set_mem_eof_return(psm->in_bio, -1);
+ psm->ssl = SSL_new(psm->ctx);
+ SSL_set_bio(psm->ssl, psm->in_bio, psm->out_bio);
+ SSL_set_connect_state(psm->ssl);
+ psm->phase = PEAP_PHASE_1;
+ tls_set_verify_info(psm->ssl, explicit_remote ? rhostname : NULL, NULL, 1, &psm->info);
+ psm->chap = chap_find_digest(CHAP_MICROSOFT_V2);
+ *ctx = psm;
+ return 0;
+}
+
+void peap_finish(struct peap_state **psm) {
+
+ if (psm && *psm) {
+ struct peap_state *tmp = *psm;
+
+ if (tmp->ssl)
+ SSL_free(tmp->ssl);
+
+ if (tmp->ctx)
+ SSL_CTX_free(tmp->ctx);
+
+ if (tmp->info)
+ tls_free_verify_info(&tmp->info);
+
+ // NOTE: BIO and memory is freed as a part of SSL_free()
+
+ free(*psm);
+ *psm = NULL;
+ }
+}
+
+int peap_process(eap_state *esp, u_char id, u_char *inp, int len)
+{
+ int ret;
+ int out_len;
+
+ struct peap_state *psm = esp->ea_peap;
+
+ if (esp->es_client.ea_id == id) {
+ info("PEAP: retransmits are not supported..");
+ return -1;
+ }
+
+ switch (*inp) {
+ case PEAP_S_FLAG_SET:
+ dbglog("PEAP: S bit is set, starting PEAP phase 1");
+ ret = SSL_do_handshake(psm->ssl);
+ if (ret != 1) {
+ ret = SSL_get_error(psm->ssl, ret);
+ if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_WRITE)
+ fatal("SSL_do_handshake(): %s", ERR_error_string(ret, NULL));
+
+ }
+ psm->read = BIO_read(psm->out_bio, psm->out_buf, TLS_RECORD_MAX_SIZE);
+ peap_response(esp, id, psm->out_buf, psm->read);
+ break;
+
+ case PEAP_LM_FLAG_SET:
+ dbglog("PEAP TLS: LM bits are set, need to get more TLS fragments");
+ inp = inp + PEAP_FRAGMENT_LENGTH_FIELD + PEAP_FLAGS_FIELD;
+ psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FRAGMENT_LENGTH_FIELD - PEAP_FLAGS_FIELD);
+ peap_ack(esp, id);
+ break;
+
+ case PEAP_M_FLAG_SET:
+ dbglog("PEAP TLS: M bit is set, need to get more TLS fragments");
+ inp = inp + PEAP_FLAGS_FIELD;
+ psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
+ peap_ack(esp, id);
+ break;
+
+ case PEAP_L_FLAG_SET:
+ case PEAP_NO_FLAGS:
+ if (*inp == PEAP_L_FLAG_SET) {
+ dbglog("PEAP TLS: L bit is set");
+ inp = inp + PEAP_FRAGMENT_LENGTH_FIELD + PEAP_FLAGS_FIELD;
+ psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FRAGMENT_LENGTH_FIELD - PEAP_FLAGS_FIELD);
+ } else {
+ dbglog("PEAP TLS: all bits are off");
+ inp = inp + PEAP_FLAGS_FIELD;
+ psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
+ }
+
+ if (psm->phase == PEAP_PHASE_1) {
+ dbglog("PEAP TLS: continue handshake");
+ ret = SSL_do_handshake(psm->ssl);
+ if (ret != 1) {
+ ret = SSL_get_error(psm->ssl, ret);
+ if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_WRITE)
+ fatal("SSL_do_handshake(): %s", ERR_error_string(ret, NULL));
+ }
+ if (SSL_is_init_finished(psm->ssl))
+ psm->phase = PEAP_PHASE_2;
+ if (BIO_ctrl_pending(psm->out_bio) == 0) {
+ peap_ack(esp, id);
+ break;
+ }
+ psm->read = 0;
+ psm->read = BIO_read(psm->out_bio, psm->out_buf,
+ TLS_RECORD_MAX_SIZE);
+ peap_response(esp, id, psm->out_buf, psm->read);
+ break;
+ }
+ psm->read = SSL_read(psm->ssl, psm->in_buf,
+ TLS_RECORD_MAX_SIZE);
+ out_len = TLS_RECORD_MAX_SIZE;
+ peap_do_inner_eap(psm->in_buf, psm->read, esp, id,
+ psm->out_buf, &out_len);
+ if (out_len > 0) {
+ psm->written = SSL_write(psm->ssl, psm->out_buf, out_len);
+ psm->read = BIO_read(psm->out_bio, psm->out_buf,
+ TLS_RECORD_MAX_SIZE);
+ peap_response(esp, id, psm->out_buf, psm->read);
+ }
+ break;
+ }
+ return 0;
+}
+
+#else
+
+u_char outpacket_buf[255];
+int debug = 1;
+int error_count = 0;
+int unsuccess = 0;
+
+/**
+ * Using the example in MS-PEAP, section 4.4.1.
+ * see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-peap/5308642b-90c9-4cc4-beec-fb367325c0f9
+ */
+int test_cmk(u_char *ipmk) {
+ u_char nonce[PEAP_TLV_NONCE_LEN] = {
+ 0x6C, 0x6B, 0xA3, 0x87, 0x84, 0x23, 0x74, 0x57,
+ 0xCC, 0xC9, 0x0B, 0x1A, 0x90, 0x8C, 0xBD, 0xF4,
+ 0x71, 0x1B, 0x69, 0x99, 0x4D, 0x0C, 0xFE, 0x8D,
+ 0x3D, 0xB4, 0x4E, 0xCB, 0xCD, 0xAD, 0x37, 0xE9
+ };
+
+ u_char tmpkey[PEAP_TLV_TEMPKEY_LEN] = {
+ 0x73, 0x8B, 0xB5, 0xF4, 0x62, 0xD5, 0x8E, 0x7E,
+ 0xD8, 0x44, 0xE1, 0xF0, 0x0D, 0x0E, 0xBE, 0x50,
+ 0xC5, 0x0A, 0x20, 0x50, 0xDE, 0x11, 0x99, 0x77,
+ 0x10, 0xD6, 0x5F, 0x45, 0xFB, 0x5F, 0xBA, 0xB7,
+ 0xE3, 0x18, 0x1E, 0x92, 0x4F, 0x42, 0x97, 0x38,
+ // 0xDE, 0x40, 0xC8, 0x46, 0xCD, 0xF5, 0x0B, 0xCB,
+ // 0xF9, 0xCE, 0xDB, 0x1E, 0x85, 0x1D, 0x22, 0x52,
+ // 0x45, 0x3B, 0xDF, 0x63
+ };
+
+ u_char expected[60] = {
+ 0x00, 0x0C, 0x00, 0x38, 0x00, 0x00, 0x00, 0x01,
+ 0x6C, 0x6B, 0xA3, 0x87, 0x84, 0x23, 0x74, 0x57,
+ 0xCC, 0xC9, 0x0B, 0x1A, 0x90, 0x8C, 0xBD, 0xF4,
+ 0x71, 0x1B, 0x69, 0x99, 0x4D, 0x0C, 0xFE, 0x8D,
+ 0x3D, 0xB4, 0x4E, 0xCB, 0xCD, 0xAD, 0x37, 0xE9,
+ 0x42, 0xE0, 0x86, 0x07, 0x1D, 0x1C, 0x8B, 0x8C,
+ 0x8E, 0x45, 0x8F, 0x70, 0x21, 0xF0, 0x6A, 0x6E,
+ 0xAB, 0x16, 0xB6, 0x46
+ };
+
+ u_char inner_mppe_keys[32] = {
+ 0x67, 0x3E, 0x96, 0x14, 0x01, 0xBE, 0xFB, 0xA5,
+ 0x60, 0x71, 0x7B, 0x3B, 0x5D, 0xDD, 0x40, 0x38,
+ 0x65, 0x67, 0xF9, 0xF4, 0x16, 0xFD, 0x3E, 0x9D,
+ 0xFC, 0x71, 0x16, 0x3B, 0xDF, 0xF2, 0xFA, 0x95
+ };
+
+ u_char response[60] = {};
+
+ // Set the inner MPPE keys (e.g. from CHAPv2)
+ mppe_set_keys(inner_mppe_keys, inner_mppe_keys + 16, 16);
+
+ // Generate and compare the response
+ generate_cmk(ipmk, tmpkey, nonce, response, 1);
+ if (memcmp(expected, response, sizeof(response)) != 0) {
+ dbglog("Failed CMK key generation\n");
+ dbglog("%.*B", sizeof(response), response);
+ dbglog("%.*B", sizeof(expected), expected);
+ return -1;
+ }
+
+ return 0;
+}
+
+int test_mppe(u_char *ipmk) {
+ u_char outer_mppe_send_key[MPPE_MAX_KEY_SIZE] = {
+ 0x6A, 0x02, 0xD7, 0x82, 0x20, 0x1B, 0xC7, 0x13,
+ 0x8B, 0xF8, 0xEF, 0xF7, 0x33, 0xB4, 0x96, 0x97,
+ 0x0D, 0x7C, 0xAB, 0x30, 0x0A, 0xC9, 0x57, 0x72,
+ 0x78, 0xE1, 0xDD, 0xD5, 0xAE, 0xF7, 0x66, 0x97
+ };
+
+ u_char outer_mppe_recv_key[MPPE_MAX_KEY_SIZE] = {
+ 0x17, 0x52, 0xD4, 0xE5, 0x84, 0xA1, 0xC8, 0x95,
+ 0x03, 0x9B, 0x4D, 0x05, 0xE3, 0xBC, 0x9A, 0x84,
+ 0x84, 0xDD, 0xC2, 0xAA, 0x6E, 0x2C, 0xE1, 0x62,
+ 0x76, 0x5C, 0x40, 0x68, 0xBF, 0xF6, 0x5A, 0x45
+ };
+
+ u_char result[MPPE_MAX_KEY_SIZE];
+ int len;
+
+ mppe_clear_keys();
+
+ generate_mppe_keys(ipmk, 1);
+
+ len = mppe_get_recv_key(result, sizeof(result));
+ if (len != sizeof(result)) {
+ dbglog("Invalid length of resulting MPPE recv key");
+ return -1;
+ }
+
+ if (memcmp(result, outer_mppe_recv_key, len) != 0) {
+ dbglog("Invalid result for outer mppe recv key");
+ return -1;
+ }
+
+ len = mppe_get_send_key(result, sizeof(result));
+ if (len != sizeof(result)) {
+ dbglog("Invalid length of resulting MPPE send key");
+ return -1;
+ }
+
+ if (memcmp(result, outer_mppe_send_key, len) != 0) {
+ dbglog("Invalid result for outer mppe send key");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ u_char ipmk[PEAP_TLV_IPMK_LEN] = {
+ 0x3A, 0x91, 0x1C, 0x25, 0x54, 0x73, 0xE8, 0x3E,
+ 0x9A, 0x0C, 0xC3, 0x33, 0xAE, 0x1F, 0x8A, 0x35,
+ 0xCD, 0xC7, 0x41, 0x63, 0xE7, 0xF6, 0x0F, 0x6C,
+ 0x65, 0xEF, 0x71, 0xC2, 0x64, 0x42, 0xAA, 0xAC,
+ 0xA2, 0xB6, 0xF1, 0xEB, 0x4F, 0x25, 0xEC, 0xA3,
+ };
+ int ret = -1;
+
+ ret = test_cmk(ipmk);
+ if (ret != 0) {
+ return -1;
+ }
+
+ ret = test_mppe(ipmk);
+ if (ret != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2011 Rustam Kovhaev. All rights reserved.
+ * Copyright (c) 2021 Eivind Næss. 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.
+ */
+
+#ifndef PPP_PEAP_H
+#define PPP_PEAP_H
+
+#define PEAP_PHASE_1 1
+#define PEAP_PHASE_2 2
+
+#define PEAP_HEADERLEN 6
+#define PEAP_FRAGMENT_LENGTH_FIELD 4
+#define PEAP_FLAGS_FIELD 1
+#define PEAP_FLAGS_ACK 0
+
+#define PEAP_CAPABILITIES_TYPE 254
+#define PEAP_CAPABILITIES_LEN 12
+
+#define PEAP_TLV_TYPE 12
+#define PEAP_TLV_LENGTH_FIELD 56
+#define PEAP_TLV_SUBTYPE_REQUEST 0
+#define PEAP_TLV_SUBTYPE_RESPONSE 1
+#define PEAP_TLV_HEADERLEN 8
+#define PEAP_TLV_RESULT_LEN 7
+#define PEAP_TLV_LEN 71
+
+/*
+ * Microsoft PEAP client/server never exchange
+ * outer TLVs during PEAP authentication
+ */
+#define PEAP_TLV_DATA_LEN 61
+
+#define PEAP_TLV_TK_LEN 60
+#define PEAP_TLV_ISK_LEN 32
+#define PEAP_TLV_IPMKSEED_LEN 59
+#define PEAP_TLV_TEMPKEY_LEN 40
+#define PEAP_TLV_IPMK_LEN 40
+#define PEAP_TLV_CMK_LEN 20
+#define PEAP_TLV_NONCE_LEN 32
+#define PEAP_TLV_COMP_MAC_LEN 20
+#define PEAP_TLV_CSK_LEN 128
+#define PEAP_TLV_TK_SEED_LABEL "client EAP encryption"
+#define PEAP_TLV_IPMK_SEED_LABEL "Inner Methods Compound Keys"
+#define PEAP_TLV_CSK_SEED_LABEL "Session Key Generating Function"
+
+#define PEAP_S_FLAG_SET 0x20
+#define PEAP_L_FLAG_SET 0x80
+#define PEAP_LM_FLAG_SET 0xC0
+#define PEAP_M_FLAG_SET 0x40
+#define PEAP_NO_FLAGS 0x00
+
+#define EAP_TLS_KEY_LEN 0x40
+#define TLS_RECORD_MAX_SIZE 0x4000
+
+struct peap_state;
+
+/**
+ * Initialize the PEAP structure
+ */
+int peap_init(struct peap_state** psm, const char *remote_name);
+
+/**
+ * Process a PEAP packet
+ */
+int peap_process(eap_state *esp, u_char id, u_char *inp, int len);
+
+/**
+ * Clean up the PEAP structure
+ */
+void peap_finish(struct peap_state **psm);
+
+#endif /* PPP_PEAP_H */
\fIbsdcomp 0\fR to disable BSD-Compress compression entirely.
.TP
.B ca \fIca-file
-(EAP-TLS) Use the file \fIca-file\fR as the X.509 Certificate Authority
+(EAP-TLS, or PEAP) Use the file \fIca-file\fR as the X.509 Certificate Authority
(CA) file (in PEM format), needed for setting up an EAP-TLS connection.
This option is used on the client-side in conjunction with the \fBcert\fR
-and \fBkey\fR options.
+and \fBkey\fR options. Either \fIca\fR, or \fIcapath\fR options are required
+for PEAP. EAP-TLS may also use the entry in eaptls-client or eaptls-server
+for a CA certificate associated with a particular peer.
+.TP
+.B capath \fIpath
+(EAP-TLS, or PEAP) Specify a location that contains public CA certificates.
+Either \fIca\fR, or \fIcapath\fR options are required for PEAP.
.TP
.B cdtrcts
Use a non-standard hardware flow control (i.e. DTR/CTS) to control
or \fBpty\fR option is used.
.TP
.B crl \fIfilename
-(EAP-TLS) Use the file \fIfilename\fR as the Certificate Revocation List
+(EAP-TLS, or PEAP) Use the file \fIfilename\fR as the Certificate Revocation List
to check for the validity of the peer's certificate. This option is not
-mandatory for setting up an EAP-TLS connection. Also see the \fBcrl-dir\fR
+mandatory for setting up a TLS connection. Also see the \fBcrl-dir\fR
option.
.TP
.B crl-dir \fIdirectory
-(EAP-TLS) Use the directory \fIdirectory\fR to scan for CRL files in
+(EAP-TLS, or PEAP) Use the directory \fIdirectory\fR to scan for CRL files in
has format ($hash.r0) to check for the validity of the peer's certificate.
-This option is not mandatory for setting up an EAP-TLS connection.
+This option is not mandatory for setting up a TLS connection.
Also see the \fBcrl\fR option.
.TP
.B debug
Terminate after \fIn\fR consecutive failed connection attempts. A
value of 0 means no limit. The default value is 10.
.TP
+.B max-tls-version \fIstring
+(EAP-TLS, or PEAP) Configures the max allowed TLS version used during
+negotiation with a peer. The default value for this is \fI1.2\fR. Values
+allowed for this option is \fI1.0.\fR, \fI1.1\fR, \fI1.2\fR, \fI1.3\fR.
+.TP
.B modem
Use the modem control lines. This option is the default. With this
option, pppd will wait for the CD (Carrier Detect) signal from the
Currently supports Microgate SyncLink adapters
under Linux and FreeBSD 2.2.8 and later.
.TP
+.B tls-verify-method \fIstring
+(EAP-TLS, or PEAP) Match the value specified for \fIremotename\fR to that that
+of the X509 certificates subject name, common name, or suffix of the common
+name. Respective values allowed for this option is: \fInone\fR, \fIsubject\fR,
+\fIname\fR, or \fIsuffix\fR. The default value for this option is \fIname\fR.
+.TP
+.B tls-verify-key-usage
+(EAP-TLS, or PEAP) Enables examination of peer certificate's purpose, and
+extended key usage attributes.
+.TP
.B unit \fInum
Sets the ppp unit number (for a ppp0 or ppp1 etc interface name) for outbound
connections. If the unit is already in use a dynamically allocated number will
extern bool dryrun; /* check everything, print options, exit */
extern int child_wait; /* # seconds to wait for children at end */
-#ifdef USE_EAPTLS
+#if defined(USE_EAPTLS) || defined(USE_PEAP)
#define TLS_VERIFY_NONE "none"
#define TLS_VERIFY_NAME "name"
#define TLS_VERIFY_SUBJECT "subject"
#define TLS_VERIFY_SUFFIX "suffix"
-extern char *crl_dir;
-extern char *crl_file;
-extern char *pkcs12_file;
+extern char *crl_dir;
+extern char *crl_file;
+extern char *ca_path;
+extern char *cacert_file;
+
extern char *max_tls_version;
extern bool tls_verify_key_usage;
extern char *tls_verify_method;
+#endif /* USE_EAPTLS || USE_PEAP */
+
+#ifdef USE_EAPTLS
+extern char *pkcs12_file;
#endif /* USE_EAPTLS */
#ifdef MAXOCTETS
--- /dev/null
+/*
+ * Copyright (c) 2021 Eivind Næss. 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.
+ */
+
+#include <string.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+
+#include "pppd.h"
+#include "tls.h"
+
+/**
+ * Structure used in verifying the peer certificate
+ */
+struct tls_info
+{
+ char *peer_name;
+ X509 *peer_cert;
+ bool client;
+};
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+/*
+ * OpenSSL 1.1+ introduced a generic TLS_method()
+ * For older releases we substitute the appropriate method
+ */
+#define TLS_method SSLv23_method
+
+#ifndef SSL_CTX_set_max_proto_version
+/** Mimics SSL_CTX_set_max_proto_version for OpenSSL < 1.1 */
+static inline int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, long tls_ver_max)
+{
+ long sslopt = 0;
+
+ if (tls_ver_max < TLS1_VERSION)
+ {
+ sslopt |= SSL_OP_NO_TLSv1;
+ }
+#ifdef SSL_OP_NO_TLSv1_1
+ if (tls_ver_max < TLS1_1_VERSION)
+ {
+ sslopt |= SSL_OP_NO_TLSv1_1;
+ }
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+ if (tls_ver_max < TLS1_2_VERSION)
+ {
+ sslopt |= SSL_OP_NO_TLSv1_2;
+ }
+#endif
+ SSL_CTX_set_options(ctx, sslopt);
+
+ return 1;
+}
+#endif /* SSL_CTX_set_max_proto_version */
+
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+
+/*
+ * Verify a certificate. Most of the work (signatures and issuer attributes checking)
+ * is done by ssl; we check the CN in the peer certificate against the peer name.
+ */
+static int tls_verify_callback(int ok, X509_STORE_CTX *ctx)
+{
+ char subject[256];
+ char cn_str[256];
+ X509 *peer_cert;
+ int err, depth;
+ SSL *ssl;
+ struct tls_info *inf;
+ char *ptr1 = NULL, *ptr2 = NULL;
+
+ peer_cert = X509_STORE_CTX_get_current_cert(ctx);
+ err = X509_STORE_CTX_get_error(ctx);
+ depth = X509_STORE_CTX_get_error_depth(ctx);
+
+ dbglog("certificate verify depth: %d", depth);
+
+ if (auth_required && !ok) {
+ X509_NAME_oneline(X509_get_subject_name(peer_cert),
+ subject, 256);
+
+ X509_NAME_get_text_by_NID(X509_get_subject_name(peer_cert),
+ NID_commonName, cn_str, 256);
+
+ dbglog("Certificate verification error:\n depth: %d CN: %s"
+ "\n err: %d (%s)\n", depth, cn_str, err,
+ X509_verify_cert_error_string(err));
+
+ return 0;
+ }
+
+ ssl = X509_STORE_CTX_get_ex_data(ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+
+ inf = (struct tls_info*) SSL_get_ex_data(ssl, 0);
+ if (inf == NULL) {
+ error("Error: SSL_get_ex_data returned NULL");
+ return 0;
+ }
+
+ tls_log_sslerr();
+
+ if (!depth)
+ {
+ /* Verify certificate based on certificate type and extended key usage */
+ if (tls_verify_key_usage) {
+ int purpose = inf->client ? X509_PURPOSE_SSL_SERVER : X509_PURPOSE_SSL_CLIENT ;
+ if (X509_check_purpose(peer_cert, purpose, 0) == 0) {
+ error("Certificate verification error: nsCertType mismatch");
+ return 0;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ int flags = inf->client ? XKU_SSL_SERVER : XKU_SSL_CLIENT;
+ if (!(X509_get_extended_key_usage(peer_cert) & flags)) {
+ error("Certificate verification error: invalid extended key usage");
+ return 0;
+ }
+#endif
+ info("Certificate key usage: OK");
+ }
+
+ /*
+ * If acting as client and the name of the server wasn't specified
+ * explicitely, we can't verify the server authenticity
+ */
+ if (!tls_verify_method)
+ tls_verify_method = TLS_VERIFY_NONE;
+
+ if (!inf->peer_name || !strcmp(TLS_VERIFY_NONE, tls_verify_method)) {
+ warn("Certificate verication disabled or no peer name was specified");
+ return ok;
+ }
+
+ /* This is the peer certificate */
+ X509_NAME_oneline(X509_get_subject_name(peer_cert),
+ subject, 256);
+
+ X509_NAME_get_text_by_NID(X509_get_subject_name(peer_cert),
+ NID_commonName, cn_str, 256);
+
+ /* Verify based on subject name */
+ ptr1 = inf->peer_name;
+ if (!strcmp(TLS_VERIFY_SUBJECT, tls_verify_method)) {
+ ptr2 = subject;
+ }
+
+ /* Verify based on common name (default) */
+ if (strlen(tls_verify_method) == 0 ||
+ !strcmp(TLS_VERIFY_NAME, tls_verify_method)) {
+ ptr2 = cn_str;
+ }
+
+ /* Match the suffix of common name */
+ if (!strcmp(TLS_VERIFY_SUFFIX, tls_verify_method)) {
+ int len = strlen(ptr1);
+ int off = strlen(cn_str) - len;
+ ptr2 = cn_str;
+ if (off > 0) {
+ ptr2 = cn_str + off;
+ }
+ }
+
+ if (strcmp(ptr1, ptr2)) {
+ error("Certificate verification error: CN (%s) != %s", ptr1, ptr2);
+ return 0;
+ }
+
+ if (inf->peer_cert) {
+ if (X509_cmp(inf->peer_cert, peer_cert) != 0) {
+ error("Peer certificate doesn't match stored certificate");
+ return 0;
+ }
+ }
+
+ info("Certificate CN: %s, peer name %s", cn_str, inf->peer_name);
+ }
+
+ return ok;
+}
+
+int tls_init()
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ SSL_library_init();
+ SSL_load_error_strings();
+#endif
+ return 0;
+}
+
+int tls_set_verify(SSL_CTX *ctx, int depth)
+{
+ SSL_CTX_set_verify_depth(ctx, depth);
+ SSL_CTX_set_verify(ctx,
+ SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ &tls_verify_callback);
+ return 0;
+}
+
+int tls_set_verify_info(SSL *ssl, const char *peer_name, const char *peer_cert,
+ bool client, struct tls_info **out)
+{
+ if (out != NULL) {
+ struct tls_info *tmp = calloc(sizeof(struct tls_info), 1);
+ if (!tmp) {
+ fatal("Allocation error");
+ }
+
+ tmp->client = client;
+ if (peer_name) {
+ tmp->peer_name = strdup(peer_name);
+ }
+
+ if (peer_cert && strlen(peer_cert) > 0) {
+ FILE *fp = fopen(peer_cert, "r");
+ if (fp) {
+ tmp->peer_cert = PEM_read_X509(fp, NULL, NULL, NULL);
+ fclose(fp);
+ }
+
+ if (!tmp->peer_cert) {
+ error("EAP-TLS: Error loading client certificate from file %s",
+ peer_cert);
+ tls_free_verify_info(&tmp);
+ return -1;
+ }
+ }
+
+ SSL_set_ex_data(ssl, 0, tmp);
+ *out = tmp;
+ return 0;
+ }
+
+ return -1;
+}
+
+void tls_free_verify_info(struct tls_info **in) {
+ if (in && *in) {
+ struct tls_info *tmp = *in;
+ if (tmp->peer_name) {
+ free(tmp->peer_name);
+ }
+ if (tmp->peer_cert) {
+ X509_free(tmp->peer_cert);
+ }
+ free(tmp);
+ *in = NULL;
+ }
+}
+
+const SSL_METHOD* tls_method() {
+ return TLS_method();
+}
+
+int tls_set_version(SSL_CTX *ctx, const char *max_version)
+{
+#if defined(TLS1_2_VERSION)
+ long tls_version = TLS1_2_VERSION;
+#elif defined(TLS1_1_VERSION)
+ long tls_version = TLS1_1_VERSION;
+#else
+ long tls_version = TLS1_VERSION;
+#endif
+
+ /* As EAP-TLS+TLSv1.3 is highly experimental we offer the user a chance to override */
+ if (max_version) {
+ if (strncmp(max_version, "1.0", 3) == 0) {
+ tls_version = TLS1_VERSION;
+ }
+ else if (strncmp(max_version, "1.1", 3) == 0) {
+ tls_version = TLS1_1_VERSION;
+ }
+ else if (strncmp(max_version, "1.2", 3) == 0) {
+#ifdef TLS1_2_VERSION
+ tls_version = TLS1_2_VERSION;
+#else
+ warn("TLSv1.2 not available. Defaulting to TLSv1.1");
+ tls_version = TLS_1_1_VERSION;
+#endif
+ }
+ else if (strncmp(max_version, "1.3", 3) == 0) {
+#ifdef TLS1_3_VERSION
+ tls_version = TLS1_3_VERSION;
+#else
+ warn("TLSv1.3 not available.");
+#endif
+ }
+ }
+
+ dbglog("Setting max protocol version to 0x%X", tls_version);
+ if (!SSL_CTX_set_max_proto_version(ctx, tls_version)) {
+ error("Could not set max protocol version");
+ return -1;
+ }
+
+ return 0;
+}
+
+int tls_set_opts(SSL_CTX *ctx) {
+
+ /* Explicitly set the NO_TICKETS flag to support Win7/Win8 clients */
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3
+#ifdef SSL_OP_NO_TICKET
+ | SSL_OP_NO_TICKET
+#endif
+ | SSL_OP_NO_COMPRESSION
+ );
+
+ /* OpenSSL 1.1.1+ does not include RC4 ciphers by default.
+ * This causes totally obsolete WinXP clients to fail. If you really
+ * need ppp+EAP-TLS+openssl 1.1.1+WinXP then enable RC4 cipers and
+ * make sure that you use an OpenSSL that supports them
+
+ SSL_CTX_set_cipher_list(ctx, "RC4");
+ */
+ return 0;
+}
+
+int tls_set_crl(SSL_CTX *ctx, const char *crl_dir, const char *crl_file)
+{
+ X509_STORE *certstore = NULL;
+ X509_LOOKUP *lookup = NULL;
+ FILE *fp = NULL;
+ int status = -1;
+
+ if (crl_dir) {
+ if (!(certstore = SSL_CTX_get_cert_store(ctx))) {
+ error("Failed to get certificate store");
+ goto done;
+ }
+
+ if (!(lookup =
+ X509_STORE_add_lookup(certstore, X509_LOOKUP_hash_dir()))) {
+ error("Store lookup for CRL failed");
+ goto done;
+ }
+
+ X509_LOOKUP_add_dir(lookup, crl_dir, X509_FILETYPE_PEM);
+ X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK);
+ }
+
+ if (crl_file) {
+ X509_CRL *crl = NULL;
+
+ fp = fopen(crl_file, "r");
+ if (!fp) {
+ error("Cannot open CRL file '%s'", crl_file);
+ goto done;
+ }
+
+ crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL);
+ if (!crl) {
+ error("Cannot read CRL file '%s'", crl_file);
+ goto done;
+ }
+
+ if (!(certstore = SSL_CTX_get_cert_store(ctx))) {
+ error("Failed to get certificate store");
+ goto done;
+ }
+ if (!X509_STORE_add_crl(certstore, crl)) {
+ error("Cannot add CRL to certificate store");
+ goto done;
+ }
+ X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK);
+ }
+
+ status = 0;
+
+done:
+
+ if (fp != NULL) {
+ fclose(fp);
+ }
+
+ return status;
+}
+
+int tls_set_ca(SSL_CTX *ctx, const char *ca_dir, const char *ca_file)
+{
+ if (ca_file && strlen(ca_file) == 0) {
+ ca_file = NULL;
+ }
+
+ if (ca_dir && strlen(ca_dir) == 0) {
+ ca_dir = NULL;
+ }
+
+ if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_dir)) {
+
+ error("Cannot load verify locations");
+ if (ca_file) {
+ dbglog("CA certificate file = [%s]", ca_file);
+ }
+
+ if (ca_dir) {
+ dbglog("CA certificate path = [%s]", ca_dir);
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
+void tls_log_sslerr( void )
+{
+ unsigned long ssl_err = ERR_get_error();
+
+ if (ssl_err != 0)
+ dbglog("EAP-TLS SSL error stack:");
+ while (ssl_err != 0) {
+ dbglog( ERR_error_string( ssl_err, NULL ) );
+ ssl_err = ERR_get_error();
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2021 Eivind Næss. 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.
+ */
+
+#ifndef TLS_H
+#define TLS_H
+
+/**
+ * Structure used in verifying the peer certificate
+ */
+struct tls_info;
+
+/**
+ * Initialize the SSL library
+ */
+int tls_init();
+
+/**
+ * Get the SSL_METHOD
+ */
+const SSL_METHOD* tls_method();
+
+/**
+ * Configure the SSL options
+ */
+int tls_set_opts(SSL_CTX *ctx);
+
+/**
+ * Configure the SSL context's max TLS version
+ */
+int tls_set_version(SSL_CTX *ctx, const char *max_version);
+
+/**
+ * Configure the SSL context's verify callback
+ */
+int tls_set_verify(SSL_CTX *ctx, int depth);
+
+/**
+ * Configure the SSL verify information
+ */
+int tls_set_verify_info(SSL *ssl, const char *peer_name, const char *peer_cert_file,
+ bool client, struct tls_info **out);
+
+/**
+ * Free the tls_info structure and it's members
+ */
+void tls_free_verify_info(struct tls_info **in);
+
+/**
+ * Configure the SSL context's CRL details
+ */
+int tls_set_crl(SSL_CTX *ctx, const char *crl_dir, const char *crl_file);
+
+/**
+ * Configure the SSL context's CA verify locations
+ */
+int tls_set_ca(SSL_CTX *ctx, const char *ca_dir, const char *ca_file);
+
+/**
+ * Log all errors from ssl library
+ */
+void tls_log_sslerr( void );
+
+#endif /* TLS_H */