]> git.ozlabs.org Git - ppp.git/commitdiff
Improve the PEAP contribution by Rustam Kovhaev
authorEivind Næss <eivnaes@yahoo.com>
Thu, 24 Jun 2021 23:06:11 +0000 (16:06 -0700)
committerEivind Næss <eivnaes@yahoo.com>
Mon, 27 Sep 2021 16:17:32 +0000 (09:17 -0700)
These changes adds to his contribution by

  * Adding options to perform CA/CRL checking and certificate validation
    consistent with what is already been done for EAP-TLS
  * Certificate validation is now in line with what is already been done
    for EAP-TLS. Users can now set "remotename" and "tls-verify-method" to
    control these.
  * Validation of certificate purpose and extended key usage is controlled
    by the option "tls-verify-key-usage".
  * Fixing up MPPE key generation to use the new API for handling MPPE keys
  * Man page is updated where appropriate for the new options.
  * Added unit-tests for the PEAP code in case of crypto or parameters would
    change in the future.
  * Added the peap feature to configure scripts. Users can now control the
    feature by specifying --enable-peap/--disable-peap.

To acheive feature parity with the EAP-TLS change, the EAP-TLS common code was
refactored into tls.c/.h such that it could be re-used in both instances.

Using PEAP/MSCHAPv2 is now supported in PPPD with this change.

Signed-off-by: Eivind Næss <eivnaes@yahoo.com>
14 files changed:
configure.ac
pppd/Makefile.am
pppd/auth.c
pppd/eap-tls.c
pppd/eap-tls.h
pppd/eap.c
pppd/eap.h
pppd/mppe.c
pppd/peap.c
pppd/peap.h
pppd/pppd.8
pppd/pppd.h
pppd/tls.c [new file with mode: 0644]
pppd/tls.h [new file with mode: 0644]

index d4db617ba0ec6416bebb0499ecac8b588d1d0284..283b666e6bc884017977215f0e06b0bff622adbb 100644 (file)
@@ -189,7 +189,15 @@ AC_ARG_ENABLE([eaptls],
     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"]))
     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
 
 #
 # Disable OpenSSL engine support
@@ -232,14 +240,14 @@ AM_CONDITIONAL(WITH_OPENSSL, test "${with_openssl}" != "no")
 
 #
 # Check if OpenSSL has compiled in support for various ciphers
 
 #
 # 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])
 ], [
     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")
 ])
 
 AM_CONDITIONAL([OPENSSL_HAVE_MD4], test "x${ac_cv_openssl_md4}" = "xyes")
@@ -414,4 +422,5 @@ Features enabled
     CBCP.................: ${enable_cbcp:-no}
     IPXCP................: ${enable_ipxcp:-no}
     EAP-TLS..............: ${enable_eaptls:-yes}
     CBCP.................: ${enable_cbcp:-no}
     IPXCP................: ${enable_ipxcp:-no}
     EAP-TLS..............: ${enable_eaptls:-yes}
+    PEAP.................: ${enable_peap:-yes}
 "
 "
index 22445072f78b8e3add93bd7aefb8dd6ed6b47e4e..1397088d1657d1a849d1e30a6fd2189eb5457b45 100644 (file)
@@ -1,13 +1,14 @@
 sbin_PROGRAMS = pppd
 dist_man8_MANS = pppd.8
 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 =
 
 
 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
 
 if WITH_SRP
 sbin_PROGRAMS += srp-entry
@@ -37,14 +38,16 @@ pppd_include_HEADERS = \
     md4.h \
     md5.h \
     mppe.h \
     md4.h \
     md5.h \
     mppe.h \
-    pppdconf.h \
     patchlevel.h \
     pathnames.h \
     patchlevel.h \
     pathnames.h \
+    peap.h \
     pppcrypt.h \
     pppd.h \
     pppcrypt.h \
     pppd.h \
+    pppdconf.h \
     session.h \
     sha1.h \
     spinlock.h \
     session.h \
     sha1.h \
     spinlock.h \
+    tls.h \
     tdb.h \
     upap.h 
 
     tdb.h \
     upap.h 
 
@@ -84,6 +87,7 @@ endif
 if WITH_CHAPMS
 pppd_SOURCES += chap_ms.c
 pppd_SOURCES += pppcrypt.c
 if WITH_CHAPMS
 pppd_SOURCES += chap_ms.c
 pppd_SOURCES += pppcrypt.c
+check_PROGRAMS += utest_chap
 else
 if WITH_SRP
 pppd_SOURCES += pppcrypt.c
 else
 if WITH_SRP
 pppd_SOURCES += pppcrypt.c
@@ -133,35 +137,42 @@ pppd_LIBS += -lpam -ldl
 endif
 
 if WITH_EAPTLS
 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
 
 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
 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
 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
 
 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
 if WITH_SYSTEMD
 pppd_LIBS += -lsystemd
 endif
@@ -181,3 +192,6 @@ pppd_LDADD = $(pppd_LIBS)
 
 EXTRA_DIST = \
     ppp.pam
 
 EXTRA_DIST = \
     ppp.pam
+
+TESTS = $(check_PROGRAMS)
+
index 75003523b1a7c47d887c414ec48200306f0f657f..01ece576ba2a7b5c57fffda29b4fb380d8b23e9a 100644 (file)
@@ -258,20 +258,23 @@ bool explicit_remote = 0; /* User specified explicit remote name */
 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 */
 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
 #endif
-bool tls_verify_cert = 0;      /* Do not verify server's SSL certificate */
 
 static char *uafname;          /* name of most recent +ua file */
 
 
 static char *uafname;          /* name of most recent +ua file */
 
@@ -446,26 +449,26 @@ option_t auth_options[] = {
       "Set telephone number(s) which are allowed to connect",
       OPT_PRIV | OPT_A2LIST },
 
       "Set telephone number(s) which are allowed to connect",
       OPT_PRIV | OPT_A2LIST },
 
-    { "tls-verify-certificate", o_bool, &tls_verify_cert,
-      "Enable server's SSL certificate validation", 1 },
-
-#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)" },
     { "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 },
     { "need-peer-eap", o_bool, &need_peer_eap,
       "Require the peer to authenticate us", 1 },
-#endif /* USE_EAPTLS */
+#endif
     { NULL }
 };
 
     { NULL }
 };
 
index b15d801565f54807165435a6a09a585f1c5cd665..b9bab842bbf2c290f7b635bfb38ad8e8e522cf36 100644 (file)
@@ -44,6 +44,7 @@
 #ifndef OPENSSL_NO_ENGINE
 #include <openssl/engine.h>
 #endif
 #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/hmac.h>
 #include <openssl/err.h>
 #include <openssl/ui.h>
@@ -51,6 +52,7 @@
 #include <openssl/pkcs12.h>
 
 #include "pppd.h"
 #include <openssl/pkcs12.h>
 
 #include "pppd.h"
+#include "tls.h"
 #include "eap.h"
 #include "eap-tls.h"
 #include "fsm.h"
 #include "eap.h"
 #include "eap-tls.h"
 #include "fsm.h"
 #include "mppe.h"
 #include "pathnames.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;
 typedef struct pw_cb_data
 {
     const void *password;
@@ -75,55 +81,10 @@ static ENGINE *pkey_engine = NULL;
 /* TLSv1.3 do we have a session ticket ? */
 static int have_session_ticket = 0;
 
 /* 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);
 
 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
 
 #ifdef MPPE
 #define EAPTLS_MPPE_KEY_LEN     32
 
@@ -178,20 +139,6 @@ void eaptls_gen_mppe_keys(struct eaptls_session *ets, int client)
 
 #endif /* MPPE */
 
 
 #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)
 int password_callback (char *buf, int size, int rwflag, void *u)
 {
     if (buf)
@@ -230,7 +177,7 @@ CONF *eaptls_ssl_load_config( void )
     if (CONF_modules_load( config, NULL, 0 ) <= 0 )
     {
         warn( "EAP-TLS: Error loading OpenSSL modules" );
     if (CONF_modules_load( config, NULL, 0 ) <= 0 )
     {
         warn( "EAP-TLS: Error loading OpenSSL modules" );
-        log_ssl_errors();
+        tls_log_sslerr();
         config = NULL;
     }
 
         config = NULL;
     }
 
@@ -257,7 +204,7 @@ ENGINE *eaptls_ssl_load_engine( char *engine_name )
              || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0))
             {
                 warn( "EAP-TLS: Error loading dynamic engine '%s'", engine_name );
              || !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;
             }
                 ENGINE_free(e);
                 e = NULL;
             }
@@ -274,7 +221,7 @@ ENGINE *eaptls_ssl_load_engine( char *engine_name )
         if(!ENGINE_set_default(e, ENGINE_METHOD_ALL))
         {
             warn( "EAP-TLS: Cannot use that engine" );
         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;
         }
             ENGINE_free(e);
             e = NULL;
         }
@@ -307,7 +254,7 @@ static int eaptls_UI_reader(UI *ui, UI_STRING *uis) {
  * for client or server use can be loaded.
  */
 SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
  * 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;
 {
 #ifndef OPENSSL_NO_ENGINE
     char        *cert_engine_name = NULL;
@@ -316,8 +263,6 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
 #endif
     SSL_CTX     *ctx;
     SSL         *ssl;
 #endif
     SSL_CTX     *ctx;
     SSL         *ssl;
-    X509_STORE  *certstore;
-    X509_LOOKUP *lookup;
     X509        *tmp;
     X509        *cert = NULL;
     PKCS12      *p12 = NULL;
     X509        *tmp;
     X509        *cert = NULL;
     PKCS12      *p12 = NULL;
@@ -326,13 +271,6 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
     BIO         *input;
     int          ret;
     int          reason;
     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 
 
     /*
      * Without these can't continue 
@@ -358,8 +296,7 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
         }
     }
 
         }
     }
 
-    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
 
 #ifndef OPENSSL_NO_ENGINE
     /* load the openssl config file only once and load it before triggering
@@ -369,8 +306,7 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
         ssl_config = eaptls_ssl_load_config();
 #endif
 
         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;
     if (!ctx) {
         error("EAP-TLS: Cannot initialize SSL CTX context");
         goto fail;
@@ -451,14 +387,7 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
 
     SSL_CTX_set_default_passwd_cb (ctx, password_callback);
 
 
     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;
     }
 
         goto fail;
     }
 
@@ -492,7 +421,7 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
         else
         {
             warn("EAP-TLS: Cannot load key with URI: '%s'", certfile );
         else
         {
             warn("EAP-TLS: Cannot load key with URI: '%s'", certfile );
-            log_ssl_errors();
+            tls_log_sslerr();
         }
     }
     else
         }
     }
     else
@@ -645,21 +574,8 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
         goto fail;
     }
 
         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
 
     /* 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
@@ -671,94 +587,17 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
     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);
 
     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;
     }
 
     return ctx;
@@ -774,7 +613,7 @@ fail:
     if (chain)
         sk_X509_pop_free(chain, X509_free);
 
     if (chain)
         sk_X509_pop_free(chain, X509_free);
 
-    log_ssl_errors();
+    tls_log_sslerr();
     SSL_CTX_free(ctx);
     return NULL;
 }
     SSL_CTX_free(ctx);
     return NULL;
 }
@@ -821,15 +660,12 @@ int eaptls_init_ssl_server(eap_state * esp)
     if (!esp->es_server.ea_session)
         fatal("Allocation error");
     ets = esp->es_server.ea_session;
     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;
     }
 
 
     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,
     dbglog( "getting eaptls secret" );
     if (!get_eaptls_secret(esp->es_unit, esp->es_server.ea_peer,
                    esp->es_server.ea_name, clicertfile,
@@ -841,13 +677,17 @@ int eaptls_init_ssl_server(eap_state * esp)
 
     ets->mtu = eaptls_get_mtu(esp->es_unit);
 
 
     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 (!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
      */
     /*
      * Set auto-retry to avoid timeouts on BIO_read
      */
@@ -863,12 +703,6 @@ int eaptls_init_ssl_server(eap_state * esp)
     SSL_set_msg_callback(ets->ssl, ssl_msg_callback);
     SSL_set_msg_callback_arg(ets->ssl, ets);
 
     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;
     SSL_set_accept_state(ets->ssl);
 
     ets->tls_v13 = 0;
@@ -877,16 +711,6 @@ int eaptls_init_ssl_server(eap_state * esp)
     ets->datalen = 0;
     ets->alert_sent = 0;
     ets->alert_recv = 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:
     return 1;
 
 fail:
@@ -914,38 +738,30 @@ int eaptls_init_ssl_client(eap_state * esp)
     if (!esp->es_client.ea_session)
         fatal("Allocation error");
     ets = esp->es_client.ea_session;
     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->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\"",
                    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" );
         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->ctx)
         goto fail;
 
     ets->ssl = SSL_new(ets->ctx);
-
     if (!ets->ssl)
         goto fail;
 
     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 
      */
     /*
      * Initialize the BIOs we use to read/write to ssl engine 
      */
@@ -956,13 +772,6 @@ int eaptls_init_ssl_client(eap_state * esp)
 
     SSL_set_msg_callback(ets->ssl, ssl_msg_callback);
     SSL_set_msg_callback_arg(ets->ssl, ets);
 
     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;
     SSL_set_connect_state(ets->ssl);
 
     ets->tls_v13 = 0;
@@ -971,17 +780,6 @@ int eaptls_init_ssl_client(eap_state * esp)
     ets->datalen = 0;
     ets->alert_sent = 0;
     ets->alert_recv = 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:
     return 1;
 
 fail:
@@ -999,6 +797,9 @@ void eaptls_free_session(struct eaptls_session *ets)
     if (ets->ctx)
         SSL_CTX_free(ets->ctx);
 
     if (ets->ctx)
         SSL_CTX_free(ets->ctx);
 
+    if (ets->info)
+        tls_free_verify_info(&ets->info);
+
     free(ets);
 }
 
     free(ets);
 }
 
@@ -1108,7 +909,7 @@ int eaptls_receive(struct eaptls_session *ets, u_char * inp, int len)
         }
 
         if (BIO_write(ets->into_ssl, ets->data, ets->datalen) == -1)
         }
 
         if (BIO_write(ets->into_ssl, ets->data, ets->datalen) == -1)
-            log_ssl_errors();
+            tls_log_sslerr();
 
         SSL_read(ets->ssl, dummy, 65536);
 
 
         SSL_read(ets->ssl, dummy, 65536);
 
@@ -1223,171 +1024,6 @@ void eaptls_retransmit(struct eaptls_session *ets, u_char ** outp)
     INCPTR(ets->rtx_len, *outp);
 }
 
     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
 /*
  * Every sent & received message this callback function is invoked,
  * so we know when alert messages have arrived or are sent and
index d0c80b6030ff240b82c1467b73795d2a5975393a..9c56687858aeece748575f2a96648483debb2ddd 100644 (file)
@@ -46,6 +46,8 @@
 
 #define EAP_TLS_MAX_LEN         65536  /* max eap tls packet size */
 
 
 #define EAP_TLS_MAX_LEN         65536  /* max eap tls packet size */
 
+struct tls_info;
+
 struct eaptls_session
 {
     u_char *data;               /* buffered data */
 struct eaptls_session
 {
     u_char *data;               /* buffered data */
@@ -58,7 +60,6 @@ struct eaptls_session
     SSL *ssl;                   /* ssl connection */
     BIO *from_ssl;
     BIO *into_ssl;
     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 peercertfile[MAXWORDLEN];
     bool alert_sent;
     u_char alert_sent_desc;
@@ -67,12 +68,12 @@ struct eaptls_session
     char rtx[EAP_TLS_MAX_LEN];  /* retransmission buffer */
     int rtx_len;
     int mtu;                    /* unit mtu */
     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,
 };
 
 
 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);
 int eaptls_init_ssl_server(eap_state * esp);
 int eaptls_init_ssl_client(eap_state * esp);
 void eaptls_free_session(struct eaptls_session *ets);
index b758711117948eb29c6b0a0ce6221f5c2e641e41..54c3d42b85fe82edce41c3571e3a6af937b8dbae 100644 (file)
@@ -2221,7 +2221,24 @@ eap_request(eap_state *esp, u_char *inp, int id, int len)
 #endif /* CHAPMS */
 #ifdef USE_PEAP
        case EAPT_PEAP:
 #endif /* CHAPMS */
 #ifdef USE_PEAP
        case EAPT_PEAP:
-               peap_process(esp, id, inp, len, rhostname);
+
+               /* 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 */
 
                break;
 #endif /* USE_PEAP */
 
@@ -2777,6 +2794,10 @@ eap_success(eap_state *esp, u_char *inp, int id, int len)
                PRINTMSG(inp, len);
        }
 
                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 = eapOpen;
        auth_withpeer_success(esp->es_unit, PPP_EAP, 0);
 }
@@ -2811,6 +2832,11 @@ eap_failure(eap_state *esp, u_char *inp, int id, int len)
        esp->es_client.ea_state = eapBadAuth;
 
        error("EAP: peer reports authentication failure");
        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);
 }
 
        auth_withpeer_fail(esp->es_unit, PPP_EAP);
 }
 
index 956b2ea895c850ab3667c20d955742a3db6a3809..5d582bc6e2db60ad61ec194ec55347674dcffa8e 100644 (file)
@@ -169,6 +169,9 @@ typedef struct eap_state {
        int es_unit;                    /* Interface unit number */
        struct eap_auth es_client;      /* Client (authenticatee) data */
        struct eap_auth es_server;      /* Server (authenticator) data */
        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 */
        int es_savedtime;               /* Saved timeout */
        int es_rechallenge;             /* EAP rechallenge interval */
        int es_lwrechallenge;           /* SRP lightweight rechallenge inter */
index d2ba0eba8fbd6969eacc13924b679130907e7d9f..f1b7abf0ab206b8f7fd31a78c2d23557c84d2245 100644 (file)
@@ -220,6 +220,8 @@ mppe_set_chapv2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
     mppe_set_keys(SendKey, RecvKey, SHA1_SIGNATURE_SIZE);
 }
 
     mppe_set_keys(SendKey, RecvKey, SHA1_SIGNATURE_SIZE);
 }
 
+#ifndef UNIT_TEST
+
 /*
  * Set MPPE options from plugins.
  */
 /*
  * Set MPPE options from plugins.
  */
@@ -251,3 +253,4 @@ mppe_set_enc_types(int policy, int types)
     }
 }
 
     }
 }
 
+#endif
index e8d1a19cd30b345c37d8462d3089f81fe7b26cc8..fb9af3e22ebcecd26ffcd102ebfed3294e57a51c 100644 (file)
@@ -1,14 +1,36 @@
 /*
 /*
- *      Copyright (c) 2011
+ * Copyright (c) 2011 Rustam Kovhaev. All rights reserved.
+ * Copyright (c) 2021 Eivind Næss. All rights reserved.
  *
  *
- * Authors:
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
  *
- *     Rustam Kovhaev <rkovhaev@gmail.com>
+ * 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
  *
  * 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
+ * 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
  *
  * And so protocols encapsulation looks like this:
  * Outer EAP -> TLS -> Inner EAP -> MSCHAPV2
@@ -21,6 +43,8 @@
  * b) Inner EAP fragmentation
  * c) Any other auth other than MSCHAPV2
  *
  * 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 <stdio.h>
 #include <openssl/hmac.h>
 #include <openssl/rand.h>
 #include <openssl/err.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 "pppd.h"
 #include "eap.h"
+#include "tls.h"
 #include "chap-new.h"
 #include "chap_ms.h"
 #include "chap-new.h"
 #include "chap_ms.h"
+#include "mppe.h"
 #include "peap.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;
 
 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;
        int written, read;
        u_char *in_buf;
        u_char *out_buf;
@@ -51,21 +84,11 @@ struct peap_state {
        u_char ipmk[PEAP_TLV_IPMK_LEN];
        u_char tk[PEAP_TLV_TK_LEN];
        u_char nonce[PEAP_TLV_NONCE_LEN];
        u_char ipmk[PEAP_TLV_IPMK_LEN];
        u_char tk[PEAP_TLV_TK_LEN];
        u_char nonce[PEAP_TLV_NONCE_LEN];
-};
-
-static struct peap_state *psm;
-static int peap_phase;
-extern bool tls_verify_cert;
-static bool init;
-
-static void ssl_init()
-{
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-       SSL_library_init();
-       SSL_load_error_strings();
+       struct tls_info *info;
+#ifdef CHAPMS
+       struct chap_digest_type *chap;
 #endif
 #endif
-       init = 1;
-}
+};
 
 /*
  * K = Key, S = Seed, LEN = output length
 
 /*
  * K = Key, S = Seed, LEN = output length
@@ -85,11 +108,11 @@ static void peap_prfplus(u_char *seed, size_t seed_len, u_char *key, size_t key_
        size_t max_iter, i, j, k;
        u_int len;
 
        size_t max_iter, i, j, k;
        u_int len;
 
-       max_iter = (pfr_len + SHA_HASH_LEN - 1) / SHA_HASH_LEN;
-       buf = malloc(seed_len + max_iter * SHA_HASH_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");
        if (!buf)
                novm("pfr buffer");
-       hash = malloc(pfr_len + SHA_HASH_LEN);
+       hash = malloc(pfr_len + SHA_DIGEST_LENGTH);
        if (!hash)
                novm("hash buffer");
 
        if (!hash)
                novm("hash buffer");
 
@@ -98,7 +121,7 @@ static void peap_prfplus(u_char *seed, size_t seed_len, u_char *key, size_t key_
                k = 0;
 
                if (i > 0)
                k = 0;
 
                if (i > 0)
-                       j = SHA_HASH_LEN;
+                       j = SHA_DIGEST_LENGTH;
                for (k = 0; k < seed_len; k++)
                        buf[j + k] = seed[k];
                pos = j + k;
                for (k = 0; k < seed_len; k++)
                        buf[j + k] = seed[k];
                pos = j + k;
@@ -108,17 +131,17 @@ static void peap_prfplus(u_char *seed, size_t seed_len, u_char *key, size_t key_
                pos++;
                buf[pos] = 0x00;
                pos++;
                pos++;
                buf[pos] = 0x00;
                pos++;
-               if (!HMAC(EVP_sha1(), key, key_len, buf, pos, (hash + i * SHA_HASH_LEN), &len))
+               if (!HMAC(EVP_sha1(), key, key_len, buf, pos, (hash + i * SHA_DIGEST_LENGTH), &len))
                        fatal("HMAC() failed");
                        fatal("HMAC() failed");
-               for (j = 0; j < SHA_HASH_LEN; j++)
-                       buf[j] = hash[i * SHA_HASH_LEN + j];
+               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);
 }
 
        }
        BCOPY(hash, out_buf, pfr_len);
        free(hash);
        free(buf);
 }
 
-static void generate_cmk(u_char *tempkey, u_char *nonce, u_char *tlv_response_out, int client)
+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};
 {
        const char *label = PEAP_TLV_IPMK_SEED_LABEL;
        u_char data_tlv[PEAP_TLV_DATA_LEN] = {0};
@@ -129,8 +152,6 @@ static void generate_cmk(u_char *tempkey, u_char *nonce, u_char *tlv_response_ou
        u_char compound_mac[PEAP_TLV_COMP_MAC_LEN] = {0};
        u_int len;
 
        u_char compound_mac[PEAP_TLV_COMP_MAC_LEN] = {0};
        u_int len;
 
-       if (debug)
-               info("PEAP CB: generate compound mac");
        /* format outgoing CB TLV response packet */
        data_tlv[1] = PEAP_TLV_TYPE;
        data_tlv[3] = PEAP_TLV_LENGTH_FIELD;
        /* format outgoing CB TLV response packet */
        data_tlv[1] = PEAP_TLV_TYPE;
        data_tlv[3] = PEAP_TLV_LENGTH_FIELD;
@@ -141,15 +162,17 @@ static void generate_cmk(u_char *tempkey, u_char *nonce, u_char *tlv_response_ou
        BCOPY(nonce, (data_tlv + PEAP_TLV_HEADERLEN), PEAP_TLV_NONCE_LEN);
        data_tlv[60] = EAPT_PEAP;
 
        BCOPY(nonce, (data_tlv + PEAP_TLV_HEADERLEN), PEAP_TLV_NONCE_LEN);
        data_tlv[60] = EAPT_PEAP;
 
-       BCOPY(mppe_send_key, isk, MPPE_MAX_KEY_LEN);
-       BCOPY(mppe_recv_key, isk + MPPE_MAX_KEY_LEN, MPPE_MAX_KEY_LEN);
+#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(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, psm->ipmk, 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(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");
@@ -158,28 +181,30 @@ static void generate_cmk(u_char *tempkey, u_char *nonce, u_char *tlv_response_ou
        BCOPY(data_tlv, tlv_response_out, PEAP_TLV_DATA_LEN - 1);
 }
 
        BCOPY(data_tlv, tlv_response_out, PEAP_TLV_DATA_LEN - 1);
 }
 
-static void verify_compound_mac(u_char *in_buf)
+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);
 {
        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->tk, nonce, out_buf, 0);
+       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");
 }
 
        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");
 }
 
-static void generate_mppe_keys(void)
+#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;
 
 {
        const char *label = PEAP_TLV_CSK_SEED_LABEL;
        u_char csk[PEAP_TLV_CSK_LEN] = {0};
        size_t len;
 
-       if (debug)
-               info("PEAP CB: generate mppe keys");
+       dbglog("PEAP CB: generate mppe keys");
        len = strlen(label);
        len++; /* CSK requires NULL byte in seed */
        len = strlen(label);
        len++; /* CSK requires NULL byte in seed */
-       peap_prfplus((u_char *)label, len, psm->ipmk, PEAP_TLV_IPMK_LEN, csk, PEAP_TLV_CSK_LEN);
+       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.
 
        /*
         * The first 64 bytes of the CSK are split into two MPPE keys, as follows.
@@ -190,19 +215,16 @@ static void generate_mppe_keys(void)
         * | MS-MPPE-Send-Key      | MS-MPPE-Recv-Key       |
         * +-----------------------+------------------------+
         */
         * | MS-MPPE-Send-Key      | MS-MPPE-Recv-Key       |
         * +-----------------------+------------------------+
         */
-       BCOPY(csk, mppe_send_key, MPPE_MAX_KEY_LEN);
-       BCOPY(csk + 32, mppe_recv_key, MPPE_MAX_KEY_LEN);
+       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);
+       }
 }
 
 }
 
-static void dump(u_char *buf, int len)
-{
-       int i = 0;
+#endif
 
 
-       dbglog("len: %d bytes", len);
-       for (i = 0; i < len; i++)
-               printf("%02x ", buf[i]);
-       printf("\n");
-}
+#ifndef UNIT_TEST
 
 static void peap_ack(eap_state *esp, u_char id)
 {
 
 static void peap_ack(eap_state *esp, u_char id)
 {
@@ -221,6 +243,7 @@ static void peap_ack(eap_state *esp, u_char id)
 
 static void peap_response(eap_state *esp, u_char id, u_char *buf, int len)
 {
 
 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;
 
        u_char *outp;
        int peap_len;
 
@@ -230,7 +253,7 @@ static void peap_response(eap_state *esp, u_char id, u_char *buf, int len)
        PUTCHAR(id, outp);
        esp->es_client.ea_id = id;
 
        PUTCHAR(id, outp);
        esp->es_client.ea_id = id;
 
-       if (peap_phase == PEAP_PHASE_1)
+       if (psm->phase == PEAP_PHASE_1)
                peap_len = PEAP_HEADERLEN + PEAP_FRAGMENT_LENGTH_FIELD + len;
        else
                peap_len = PEAP_HEADERLEN + len;
                peap_len = PEAP_HEADERLEN + PEAP_FRAGMENT_LENGTH_FIELD + len;
        else
                peap_len = PEAP_HEADERLEN + len;
@@ -238,7 +261,7 @@ static void peap_response(eap_state *esp, u_char id, u_char *buf, int len)
        PUTSHORT(peap_len, outp);
        PUTCHAR(EAPT_PEAP, outp);
 
        PUTSHORT(peap_len, outp);
        PUTCHAR(EAPT_PEAP, outp);
 
-       if (peap_phase == PEAP_PHASE_1) {
+       if (psm->phase == PEAP_PHASE_1) {
                PUTCHAR(PEAP_L_FLAG_SET, outp);
                PUTLONG(len, outp);
        } else
                PUTCHAR(PEAP_L_FLAG_SET, outp);
                PUTLONG(len, outp);
        } else
@@ -248,24 +271,20 @@ static void peap_response(eap_state *esp, u_char id, u_char *buf, int len)
        output(esp->es_unit, outpacket_buf, PPP_HDRLEN + peap_len);
 }
 
        output(esp->es_unit, outpacket_buf, PPP_HDRLEN + peap_len);
 }
 
-void do_inner_eap(u_char *in_buf, int in_len, eap_state *esp, int id,
-               char *rhostname, u_char *out_buf, int *out_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)
 {
 {
-       if (debug)
-               dump(in_buf, in_len);
-       int used;
-       u_char *outp;
+       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;
 
 
-       used = 0;
-       outp = out_buf;
+       dbglog("PEAP: EAP (in): %.*B", in_len, in_buf);
 
 
-       if (*in_buf == EAPT_IDENTITY && in_len == 1) {
-               PUTCHAR(EAPT_IDENTITY, outp);
-               used++;
-               BCOPY(esp->es_client.ea_name, outp,
-                               esp->es_client.ea_namelen);
-               used += esp->es_client.ea_namelen;
-       } else if (*(in_buf + EAP_HEADERLEN) == PEAP_CAPABILITIES_TYPE &&
+       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);
                        in_len  == (EAP_HEADERLEN + PEAP_CAPABILITIES_LEN)) {
                /* use original packet as template for response */
                BCOPY(in_buf, outp, EAP_HEADERLEN + PEAP_CAPABILITIES_LEN);
@@ -274,57 +293,15 @@ void do_inner_eap(u_char *in_buf, int in_len, eap_state *esp, int id,
                /* change last byte to 0 to disable fragmentation */
                *(outp + PEAP_CAPABILITIES_LEN + 1) = 0x00;
                used = EAP_HEADERLEN + PEAP_CAPABILITIES_LEN;
                /* change last byte to 0 to disable fragmentation */
                *(outp + PEAP_CAPABILITIES_LEN + 1) = 0x00;
                used = EAP_HEADERLEN + PEAP_CAPABILITIES_LEN;
-       } else if (*in_buf == EAPT_TLS && in_len  == 2) {
-               /* send NAK to EAP_TLS request */
-               PUTCHAR(EAPT_NAK, outp);
-               used++;
-               PUTCHAR(EAPT_MSCHAPV2, outp);
-               used++;
-       } else if (*in_buf == EAPT_MSCHAPV2 && *(in_buf + 1) == CHAP_CHALLENGE) {
-               /* MSCHAPV2 auth */
-               int secret_len;
-               char secret[MAXSECRETLEN + 1];
-               char *user;
-               u_char user_len;
-               u_char response[MS_CHAP2_RESPONSE_LEN];
-               u_char auth_response[MS_AUTH_RESPONSE_LENGTH + 1];
-               u_char chap_id;
-               u_char rchallenge[MS_CHAP2_PEER_CHAL_LEN];
-
-               user = esp->es_client.ea_name;
-               user_len = esp->es_client.ea_namelen;
-               chap_id = *(in_buf + 2);
-               BCOPY((in_buf + 6), rchallenge, MS_CHAP2_PEER_CHAL_LEN);
-               if (!get_secret(esp->es_unit, esp->es_client.ea_name,
-                                       rhostname, secret, &secret_len, 0))
-                       fatal("Can't read password file");
-               /* MSCHAPV2 response */
-               ChapMS2(rchallenge, NULL, esp->es_client.ea_name,
-                               secret, secret_len, response, auth_response, MS_CHAP2_AUTHENTICATEE);
-               PUTCHAR(EAPT_MSCHAPV2, outp);
-               PUTCHAR(CHAP_RESPONSE, outp);
-               PUTCHAR(chap_id, outp);
-               PUTCHAR(0, outp);
-               PUTCHAR(5 + user_len + MS_CHAP2_RESPONSE_LEN, outp);
-               PUTCHAR(MS_CHAP2_RESPONSE_LEN, outp)
-               BCOPY(response, outp, MS_CHAP2_RESPONSE_LEN);
-               outp = outp + MS_CHAP2_RESPONSE_LEN;
-               BCOPY(user, outp, user_len);
-               used = 5 + user_len + MS_CHAP2_RESPONSE_LEN + 1;
-       } else if (*in_buf == EAPT_MSCHAPV2 && *(in_buf + 1) == CHAP_SUCCESS) {
-               PUTCHAR(EAPT_MSCHAPV2, outp);
-               used++;
-               PUTCHAR(CHAP_SUCCESS, outp);
-               used++;
-               auth_peer_success(esp->es_unit, PPP_CHAP, CHAP_MICROSOFT_V2,
-                               esp->es_server.ea_peer, esp->es_server.ea_peerlen);
-       } else if (*(in_buf + EAP_HEADERLEN + PEAP_TLV_HEADERLEN) == PEAP_TLV_TYPE &&
+               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 */
                        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(in_buf + EAP_HEADERLEN + PEAP_TLV_RESULT_LEN + PEAP_TLV_HEADERLEN);
+               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);
                /* generate client's CMK with new nonce */
                PUTCHAR(EAP_RESPONSE, outp);
                PUTCHAR(id, outp);
@@ -332,26 +309,169 @@ void do_inner_eap(u_char *in_buf, int in_len, eap_state *esp, int id,
                BCOPY(in_buf + EAP_HEADERLEN, outp, PEAP_TLV_RESULT_LEN);
                outp = outp + PEAP_TLV_RESULT_LEN;
                RAND_bytes(psm->nonce, PEAP_TLV_NONCE_LEN);
                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->tk, psm->nonce, outp, 1);
+               generate_cmk(psm->ipmk, psm->tk, psm->nonce, outp, 1);
+#ifdef MPPE
                /* set mppe keys */
                /* set mppe keys */
-               generate_mppe_keys();
+               generate_mppe_keys(psm->ipmk, 1);
+#endif
                used = PEAP_TLV_LEN;
                used = PEAP_TLV_LEN;
-       } else {
+               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;
        }
 
                /* send compressed EAP NAK for any unknown packet */
                PUTCHAR(EAPT_NAK, outp);
                ++used;
        }
 
-       if (debug)
-               dump(psm->out_buf, used);
+done:
+
+       dbglog("PEAP: EAP (out): %.*B", used, psm->out_buf);
        *out_len = used;
 }
 
        *out_len = used;
 }
 
-void allocate_buffers(void)
+int peap_init(struct peap_state **ctx, const char *rhostname)
 {
        const SSL_METHOD *method;
 
 {
        const SSL_METHOD *method;
 
-       psm = malloc(sizeof(*psm));
+       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)
                novm("peap psm struct");
        psm->in_buf = malloc(TLS_RECORD_MAX_SIZE);
@@ -360,20 +480,31 @@ void allocate_buffers(void)
        psm->out_buf = malloc(TLS_RECORD_MAX_SIZE);
        if (!psm->out_buf)
                novm("peap tls buffer");
        psm->out_buf = malloc(TLS_RECORD_MAX_SIZE);
        if (!psm->out_buf)
                novm("peap tls buffer");
-       method = TLS_method();
+       method = tls_method();
        if (!method)
                novm("TLS_method() failed");
        psm->ctx = SSL_CTX_new(method);
        if (!psm->ctx)
                novm("SSL_CTX_new() failed");
 
        if (!method)
                novm("TLS_method() failed");
        psm->ctx = SSL_CTX_new(method);
        if (!psm->ctx)
                novm("SSL_CTX_new() failed");
 
-       if (!tls_verify_cert)
-               SSL_CTX_set_verify(psm->ctx, SSL_VERIFY_NONE, NULL);
-       else
-               SSL_CTX_set_verify(psm->ctx, SSL_VERIFY_PEER, NULL);
-       info("PEAP: SSL certificate validation is %s", tls_verify_cert ? "enabled" : "disabled");
+       /* Configure the default options */
+       tls_set_opts(psm->ctx);
+
+       /* Configure the max TLS version */
+       tls_set_version(psm->ctx, max_tls_version);
 
 
-       SSL_CTX_set_options(psm->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
+       /* 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());
 
        psm->out_bio = BIO_new(BIO_s_mem());
        psm->in_bio = BIO_new(BIO_s_mem());
@@ -382,27 +513,49 @@ void allocate_buffers(void)
        psm->ssl = SSL_new(psm->ctx);
        SSL_set_bio(psm->ssl, psm->in_bio, psm->out_bio);
        SSL_set_connect_state(psm->ssl);
        psm->ssl = SSL_new(psm->ctx);
        SSL_set_bio(psm->ssl, psm->in_bio, psm->out_bio);
        SSL_set_connect_state(psm->ssl);
-       peap_phase = PEAP_PHASE_1;
+       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;
+       }
 }
 
 }
 
-void peap_process(eap_state *esp, u_char id, u_char *inp, int len, char *rhostname)
+int peap_process(eap_state *esp, u_char id, u_char *inp, int len)
 {
        int ret;
        int out_len;
 
 {
        int ret;
        int out_len;
 
-       if (!init)
-               ssl_init();
+       struct peap_state *psm = esp->ea_peap;
 
        if (esp->es_client.ea_id == id) {
                info("PEAP: retransmits are not supported..");
 
        if (esp->es_client.ea_id == id) {
                info("PEAP: retransmits are not supported..");
-               return;
+               return -1;
        }
 
        switch (*inp) {
        case PEAP_S_FLAG_SET:
        }
 
        switch (*inp) {
        case PEAP_S_FLAG_SET:
-               allocate_buffers();
-               if (debug)
-                       info("PEAP: S bit is set, starting PEAP phase 1");
+               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);
                ret = SSL_do_handshake(psm->ssl);
                if (ret != 1) {
                        ret = SSL_get_error(psm->ssl, ret);
@@ -415,16 +568,14 @@ void peap_process(eap_state *esp, u_char id, u_char *inp, int len, char *rhostna
                break;
 
        case PEAP_LM_FLAG_SET:
                break;
 
        case PEAP_LM_FLAG_SET:
-               if (debug)
-                       info("PEAP TLS: LM bits are set, need to get more TLS fragments");
+               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:
                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:
-               if (debug)
-                       info("PEAP TLS: M bit is set, need to get more TLS fragments");
+               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);
                inp = inp + PEAP_FLAGS_FIELD;
                psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
                peap_ack(esp, id);
@@ -433,20 +584,17 @@ void peap_process(eap_state *esp, u_char id, u_char *inp, int len, char *rhostna
        case PEAP_L_FLAG_SET:
        case PEAP_NO_FLAGS:
                if (*inp == PEAP_L_FLAG_SET) {
        case PEAP_L_FLAG_SET:
        case PEAP_NO_FLAGS:
                if (*inp == PEAP_L_FLAG_SET) {
-                       if (debug)
-                               info("PEAP TLS: L bit is 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 {
                        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 {
-                       if (debug)
-                               info("PEAP TLS: all bits are off");
+                       dbglog("PEAP TLS: all bits are off");
                        inp = inp + PEAP_FLAGS_FIELD;
                        psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
                }
 
                        inp = inp + PEAP_FLAGS_FIELD;
                        psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
                }
 
-               if (peap_phase == PEAP_PHASE_1) {
-                       if (debug)
-                               info("PEAP TLS: continue handshake");
+               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);
                        ret = SSL_do_handshake(psm->ssl);
                        if (ret != 1) {
                                ret = SSL_get_error(psm->ssl, ret);
@@ -454,7 +602,7 @@ void peap_process(eap_state *esp, u_char id, u_char *inp, int len, char *rhostna
                                        fatal("SSL_do_handshake(): %s", ERR_error_string(ret, NULL));
                        }
                        if (SSL_is_init_finished(psm->ssl))
                                        fatal("SSL_do_handshake(): %s", ERR_error_string(ret, NULL));
                        }
                        if (SSL_is_init_finished(psm->ssl))
-                               peap_phase = PEAP_PHASE_2;
+                               psm->phase = PEAP_PHASE_2;
                        if (BIO_ctrl_pending(psm->out_bio) == 0) {
                                peap_ack(esp, id);
                                break;
                        if (BIO_ctrl_pending(psm->out_bio) == 0) {
                                peap_ack(esp, id);
                                break;
@@ -468,12 +616,153 @@ void peap_process(eap_state *esp, u_char id, u_char *inp, int len, char *rhostna
                psm->read = SSL_read(psm->ssl, psm->in_buf,
                                TLS_RECORD_MAX_SIZE);
                out_len = TLS_RECORD_MAX_SIZE;
                psm->read = SSL_read(psm->ssl, psm->in_buf,
                                TLS_RECORD_MAX_SIZE);
                out_len = TLS_RECORD_MAX_SIZE;
-               do_inner_eap(psm->in_buf, psm->read, esp, id, rhostname,
+               peap_do_inner_eap(psm->in_buf, psm->read, esp, id,
                                psm->out_buf, &out_len);
                                psm->out_buf, &out_len);
-               psm->written = SSL_write(psm->ssl, psm->out_buf, out_len);
-               psm->read = BIO_read(psm->out_bio, psm->out_buf,
+               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);
                                TLS_RECORD_MAX_SIZE);
-               peap_response(esp, id, psm->out_buf, psm->read);
+                       peap_response(esp, id, psm->out_buf, psm->read);
+               }
                break;
        }
                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
index fd002d2970c50a03950d16a192aa73f77b43d0e2..49e28e8375f321f9d6142b352070dc01dd7d1213 100644 (file)
@@ -1,16 +1,35 @@
 /*
 /*
- *      Copyright (c) 2011
+ * Copyright (c) 2011 Rustam Kovhaev. All rights reserved.
+ * Copyright (c) 2021 Eivind Næss. All rights reserved.
  *
  *
- * Authors:
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
  *
- *     Rustam Kovhaev <rkovhaev@gmail.com>
+ * 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
 
  */
 
 #ifndef PPP_PEAP_H
 #define        PPP_PEAP_H
 
-#define        EAPT_MSCHAPV2                   26
-
 #define        PEAP_PHASE_1                    1
 #define        PEAP_PHASE_2                    2
 
 #define        PEAP_PHASE_1                    1
 #define        PEAP_PHASE_2                    2
 
@@ -22,8 +41,6 @@
 #define PEAP_CAPABILITIES_TYPE         254
 #define PEAP_CAPABILITIES_LEN          12
 
 #define PEAP_CAPABILITIES_TYPE         254
 #define PEAP_CAPABILITIES_LEN          12
 
-#define        SHA_HASH_LEN                    20
-
 #define PEAP_TLV_TYPE                  12
 #define PEAP_TLV_LENGTH_FIELD          56
 #define PEAP_TLV_SUBTYPE_REQUEST       0
 #define PEAP_TLV_TYPE                  12
 #define PEAP_TLV_LENGTH_FIELD          56
 #define PEAP_TLV_SUBTYPE_REQUEST       0
 #define        EAP_TLS_KEY_LEN                 0x40
 #define        TLS_RECORD_MAX_SIZE             0x4000
 
 #define        EAP_TLS_KEY_LEN                 0x40
 #define        TLS_RECORD_MAX_SIZE             0x4000
 
-void peap_process(eap_state *esp, u_char id, u_char *inp,
-               int len, char *rhostname);
+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 */
 
 #endif /* PPP_PEAP_H */
index cd4b9eba3a49ceb5bd87eaea702500b864f9552f..b0074062097e930bec31532d99401378a4ccc35e 100644 (file)
@@ -260,10 +260,16 @@ compression in the corresponding direction.  Use \fInobsdcomp\fR or
 \fIbsdcomp 0\fR to disable BSD-Compress compression entirely.
 .TP
 .B ca \fIca-file
 \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
 (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
 .TP
 .B cdtrcts
 Use a non-standard hardware flow control (i.e. DTR/CTS) to control
@@ -320,15 +326,15 @@ negotiation by sending its first LCP packet.  The default value is
 or \fBpty\fR option is used.
 .TP
 .B crl \fIfilename
 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
 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
 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.
 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
 Also see the \fBcrl\fR option.
 .TP
 .B debug
@@ -724,6 +730,11 @@ network control protocol comes up).
 Terminate after \fIn\fR consecutive failed connection attempts.  A
 value of 0 means no limit.  The default value is 10.
 .TP
 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
 .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
@@ -1173,6 +1184,16 @@ The device used by pppd with this option must have sync support.
 Currently supports Microgate SyncLink adapters
 under Linux and FreeBSD 2.2.8 and later.
 .TP
 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
 .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
index ba62ca5a7fc91c33d643573bd346e5c0514fea50..ab8f674dda5e3901acab82d35b766e921fcbf4ac 100644 (file)
@@ -336,19 +336,25 @@ extern bool       dump_options;   /* print out option values */
 extern bool    dryrun;         /* check everything, print options, exit */
 extern int     child_wait;     /* # seconds to wait for children at end */
 
 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"
 
 
 #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;
 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
 #endif /* USE_EAPTLS */
 
 #ifdef MAXOCTETS
diff --git a/pppd/tls.c b/pppd/tls.c
new file mode 100644 (file)
index 0000000..28c2e57
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/pppd/tls.h b/pppd/tls.h
new file mode 100644 (file)
index 0000000..39fdef7
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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 */