]> 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"]))
-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
@@ -232,14 +240,14 @@ AM_CONDITIONAL(WITH_OPENSSL, test "${with_openssl}" != "no")
 
 #
 # Check if OpenSSL has compiled in support for various ciphers
-AS_IF([test "${with_openssl}" != "no" ], [
+AS_IF([test "x${with_openssl}" != "xno" ], [
     AX_CHECK_OPENSSL_DEFINE([OPENSSL_NO_MD4], [md4])
     AX_CHECK_OPENSSL_DEFINE([OPENSSL_NO_MD5], [md5])
     AX_CHECK_OPENSSL_DEFINE([OPENSSL_NO_DES], [des])
     AX_CHECK_OPENSSL_DEFINE([OPENSSL_NO_SHA], [sha])
 ], [
-    AS_IF([test "x$enable_eaptls" != "xno"],
-        [AC_MSG_ERROR([OpenSSL not found, and if this is your intention then run configure --disable-eaptls])])
+    AS_IF([test "x${enable_eaptls}" != "xno" || test "x${enable_peap}" != "xno"],
+        [AC_MSG_ERROR([OpenSSL not found, and if this is your intention then run configure --disable-eaptls and --disable-peap])])
 ])
 
 AM_CONDITIONAL([OPENSSL_HAVE_MD4], test "x${ac_cv_openssl_md4}" = "xyes")
@@ -414,4 +422,5 @@ Features enabled
     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
-check_PROGRAMS = \
-    utest_chap
+check_PROGRAMS =
 
 utest_chap_SOURCES = chap_ms.c pppcrypt.c utils.c
 utest_chap_CPPFLAGS = -DUNIT_TEST
 utest_chap_LDFLAGS =
 
-TESTS = $(check_PROGRAMS)
+utest_peap_SOURCES = peap.c utils.c mppe.c
+utest_peap_CPPFLAGS = -DUNIT_TEST -I${top_srcdir}/include
+utest_peap_LDFLAGS =
 
 if WITH_SRP
 sbin_PROGRAMS += srp-entry
@@ -37,14 +38,16 @@ pppd_include_HEADERS = \
     md4.h \
     md5.h \
     mppe.h \
-    pppdconf.h \
     patchlevel.h \
     pathnames.h \
+    peap.h \
     pppcrypt.h \
     pppd.h \
+    pppdconf.h \
     session.h \
     sha1.h \
     spinlock.h \
+    tls.h \
     tdb.h \
     upap.h 
 
@@ -84,6 +87,7 @@ endif
 if WITH_CHAPMS
 pppd_SOURCES += chap_ms.c
 pppd_SOURCES += pppcrypt.c
+check_PROGRAMS += utest_chap
 else
 if WITH_SRP
 pppd_SOURCES += pppcrypt.c
@@ -133,35 +137,42 @@ pppd_LIBS += -lpam -ldl
 endif
 
 if WITH_EAPTLS
-pppd_SOURCES += eap-tls.c
+pppd_SOURCES += eap-tls.c tls.c
+else
+if WITH_PEAP
+pppd_SOURCES += tls.c
+endif
 endif
 
-if !WITH_OPENSSL
-pppd_SOURCES += md5.c md4.c sha1.c
-utest_chap_SOURCES += md5.c md4.c sha1.c
-else
-pppd_CPPFLAGS += $(OPENSSL_INCLUDES)
-pppd_LDFLAGS += $(OPENSSL_LDFLAGS)
+if WITH_PEAP
+pppd_SOURCES += peap.c
+check_PROGRAMS += utest_peap
+endif
 
-utest_chap_CPPFLAGS += $(OPENSSL_INCLUDES)
-utest_chap_LDFLAGS += $(OPENSSL_LDFLAGS)
-utest_chap_LDADD = $(OPENSSL_LIBS)
+noinst_LTLIBRARIES = libppp_crypt.la
+libppp_crypt_la_SOURCES=
 
-pppd_LIBS += $(OPENSSL_LIBS)
+if !WITH_OPENSSL
+libppp_crypt_la_SOURCES += md4.c md5.c sha1.c
+else
+libppp_crypt_la_CPPFLAGS=$(OPENSSL_INCLUDES)
+libppp_crypt_la_LDFLAGS=$(OPENSSL_LDFLAGS)
+libppp_crypt_la_LIBADD=$(OPENSSL_LIBS)
 if !OPENSSL_HAVE_SHA
-pppd_SOURCES += sha1.c
-utest_chap_SOURCES += sha1.c
-endif
-if !OPENSSL_HAVE_MD4
-pppd_SOURCES += md4.c
-utest_chap_SOURCES += md4.c
+libppp_crypt_la_SOURCES += sha1.c
 endif
 if !OPENSSL_HAVE_MD5
-pppd_SOURCES += md5.c
-utest_chap_SOURCES += md5.c
+libppp_crypt_la_SOURCES += md5.c
+endif
+if !OPENSSL_HAVE_MD4
+libppp_crypt_la_SOURCES += md4.c
 endif
 endif
 
+utest_peap_LDADD = libppp_crypt.la
+utest_chap_LDADD = libppp_crypt.la
+pppd_LIBS += libppp_crypt.la
+
 if WITH_SYSTEMD
 pppd_LIBS += -lsystemd
 endif
@@ -181,3 +192,6 @@ pppd_LDADD = $(pppd_LIBS)
 
 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 */
-#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
-bool tls_verify_cert = 0;      /* Do not verify server's SSL certificate */
 
 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 },
 
-    { "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)" },
+#endif
+
+#if defined(USE_EAPTLS)
+    { "cert", o_string, &cert_file,     "client certificate in PEM format" },
+    { "key", o_string, &privkey_file,   "client private key in PEM format" },
+    { "pkcs12", o_string, &pkcs12_file, "EAP-TLS client credentials in PKCS12 format" },
     { "need-peer-eap", o_bool, &need_peer_eap,
       "Require the peer to authenticate us", 1 },
-#endif /* USE_EAPTLS */
+#endif
     { NULL }
 };
 
index b15d801565f54807165435a6a09a585f1c5cd665..b9bab842bbf2c290f7b635bfb38ad8e8e522cf36 100644 (file)
@@ -44,6 +44,7 @@
 #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>
@@ -51,6 +52,7 @@
 #include <openssl/pkcs12.h>
 
 #include "pppd.h"
+#include "tls.h"
 #include "eap.h"
 #include "eap-tls.h"
 #include "fsm.h"
 #include "mppe.h"
 #include "pathnames.h"
 
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#define SSL3_RT_HEADER  0x100
+#endif
+
 typedef struct pw_cb_data
 {
     const void *password;
@@ -75,55 +81,10 @@ static ENGINE *pkey_engine = NULL;
 /* TLSv1.3 do we have a session ticket ? */
 static int have_session_ticket = 0;
 
-int ssl_verify_callback(int, X509_STORE_CTX *);
 void ssl_msg_callback(int write_p, int version, int ct, const void *buf,
               size_t len, SSL * ssl, void *arg);
 int ssl_new_session_cb(SSL *s, SSL_SESSION *sess);
 
-X509 *get_X509_from_file(char *filename);
-int ssl_cmp_certs(char *filename, X509 * a); 
-
-/*
- *  OpenSSL 1.1+ introduced a generic TLS_method()
- *  For older releases we substitute the appropriate method
- */
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-
-#define TLS_method SSLv23_method
-
-#define SSL3_RT_HEADER  0x100
-
-#ifndef SSL_CTX_set_max_proto_version
-/** Mimics SSL_CTX_set_max_proto_version for OpenSSL < 1.1 */
-static inline int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, long tls_ver_max)
-{
-    long sslopt = 0;
-
-    if (tls_ver_max < TLS1_VERSION)
-    {
-        sslopt |= SSL_OP_NO_TLSv1;
-    }
-#ifdef SSL_OP_NO_TLSv1_1
-    if (tls_ver_max < TLS1_1_VERSION)
-    {
-        sslopt |= SSL_OP_NO_TLSv1_1;
-    }
-#endif
-#ifdef SSL_OP_NO_TLSv1_2
-    if (tls_ver_max < TLS1_2_VERSION)
-    {
-        sslopt |= SSL_OP_NO_TLSv1_2;
-    }
-#endif
-    SSL_CTX_set_options(ctx, sslopt);
-
-    return 1;
-}
-#endif /* SSL_CTX_set_max_proto_version */
-
-#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
-
 #ifdef MPPE
 #define EAPTLS_MPPE_KEY_LEN     32
 
@@ -178,20 +139,6 @@ void eaptls_gen_mppe_keys(struct eaptls_session *ets, int client)
 
 #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)
@@ -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" );
-        log_ssl_errors();
+        tls_log_sslerr();
         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 );
-                log_ssl_errors();
+                tls_log_sslerr();
                 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" );
-            log_ssl_errors();
+            tls_log_sslerr();
             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,
-            char *certfile, char *peer_certfile, char *privkeyfile, char *pkcs12)
+            char *certfile, char *privkeyfile, char *pkcs12)
 {
 #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;
-    X509_STORE  *certstore;
-    X509_LOOKUP *lookup;
     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;
-#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 
@@ -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
@@ -369,8 +306,7 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
         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;
@@ -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);
 
-    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;
     }
 
@@ -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 );
-            log_ssl_errors();
+            tls_log_sslerr();
         }
     }
     else
@@ -645,21 +574,8 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
         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
@@ -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);
 
-    /* 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;
@@ -774,7 +613,7 @@ fail:
     if (chain)
         sk_X509_pop_free(chain, X509_free);
 
-    log_ssl_errors();
+    tls_log_sslerr();
     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;
-    ets->client = 0;
 
     if (!esp->es_server.ea_peer) {
         error("EAP-TLS: Error: client name not set (BUG)");
         return 0;
     }
 
-    strlcpy(ets->peer, esp->es_server.ea_peer, MAXWORDLEN-1);
-
     dbglog( "getting eaptls secret" );
     if (!get_eaptls_secret(esp->es_unit, esp->es_server.ea_peer,
                    esp->es_server.ea_name, clicertfile,
@@ -841,13 +677,17 @@ int eaptls_init_ssl_server(eap_state * esp)
 
     ets->mtu = eaptls_get_mtu(esp->es_unit);
 
-    ets->ctx = eaptls_init_ssl(1, cacertfile, capath, servcertfile, clicertfile, pkfile, pkcs12);
+    ets->ctx = eaptls_init_ssl(1, cacertfile, capath, servcertfile, pkfile, pkcs12);
     if (!ets->ctx)
         goto fail;
 
     if (!(ets->ssl = SSL_new(ets->ctx)))
         goto fail;
 
+    if (tls_set_verify_info(ets->ssl, esp->es_server.ea_peer,
+            clicertfile, 0, &ets->info))
+        goto fail;
+
     /*
      * Set auto-retry to avoid timeouts on BIO_read
      */
@@ -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);
 
-    /*
-     * 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;
@@ -877,16 +711,6 @@ int eaptls_init_ssl_server(eap_state * esp)
     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:
@@ -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;
-    ets->client = 1;
-
-    /*
-     * If available, copy server name in ets; it will be used in cert
-     * verify 
-     */
-    if (esp->es_client.ea_peer)
-        strlcpy(ets->peer, esp->es_client.ea_peer, MAXWORDLEN-1);
-    else
-        ets->peer[0] = 0;
-    
     ets->mtu = eaptls_get_mtu(esp->es_unit);
 
     dbglog( "calling get_eaptls_secret" );
     if (!get_eaptls_secret(esp->es_unit, esp->es_client.ea_name,
-                   ets->peer, clicertfile,
+                   esp->es_client.ea_peer, clicertfile,
                    servcertfile, cacertfile, capath, pkfile, pkcs12, 0)) {
         error( "EAP-TLS: Cannot get secret/password for client \"%s\", server \"%s\"",
-                esp->es_client.ea_name, ets->peer );
+                esp->es_client.ea_name, esp->es_client.ea_peer);
         return 0;
     }
 
     dbglog( "calling eaptls_init_ssl" );
-    ets->ctx = eaptls_init_ssl(0, cacertfile, capath, clicertfile, servcertfile, pkfile, pkcs12);
+    ets->ctx = eaptls_init_ssl(0, cacertfile, capath, clicertfile, pkfile, pkcs12);
     if (!ets->ctx)
         goto fail;
 
     ets->ssl = SSL_new(ets->ctx);
-
     if (!ets->ssl)
         goto fail;
 
+    if (tls_set_verify_info(ets->ssl, esp->es_client.ea_peer,
+            servcertfile, 0, &ets->info))
+        goto fail;
+
     /*
      * Initialize the BIOs we use to read/write to ssl engine 
      */
@@ -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);
-
-    /*
-     * 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;
@@ -971,17 +780,6 @@ int eaptls_init_ssl_client(eap_state * esp)
     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:
@@ -999,6 +797,9 @@ void eaptls_free_session(struct eaptls_session *ets)
     if (ets->ctx)
         SSL_CTX_free(ets->ctx);
 
+    if (ets->info)
+        tls_free_verify_info(&ets->info);
+
     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)
-            log_ssl_errors();
+            tls_log_sslerr();
 
         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);
 }
 
-/*
- * 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
index d0c80b6030ff240b82c1467b73795d2a5975393a..9c56687858aeece748575f2a96648483debb2ddd 100644 (file)
@@ -46,6 +46,8 @@
 
 #define EAP_TLS_MAX_LEN         65536  /* max eap tls packet size */
 
+struct tls_info;
+
 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;
-    char peer[MAXWORDLEN];      /* peer name */
     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 */
-    bool client;
+    struct tls_info *info;
 };
 
 
 SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
-            char *certfile, char *peer_certfile, char *privkeyfile, char *pkcs12);
+            char *certfile, char *privkeyfile, char *pkcs12);
 int eaptls_init_ssl_server(eap_state * esp);
 int eaptls_init_ssl_client(eap_state * esp);
 void eaptls_free_session(struct eaptls_session *ets);
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:
-               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 */
 
@@ -2777,6 +2794,10 @@ eap_success(eap_state *esp, u_char *inp, int id, int 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);
 }
@@ -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");
+
+#ifdef USE_PEAP
+       peap_finish(&esp->ea_peap);
+#endif
+
        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 */
+#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 */
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);
 }
 
+#ifndef UNIT_TEST
+
 /*
  * 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
- * 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
@@ -21,6 +43,8 @@
  * 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 <openssl/hmac.h>
 #include <openssl/rand.h>
 #include <openssl/err.h>
+#include <net/ppp_defs.h>
+
 #include "pppd.h"
 #include "eap.h"
+#include "tls.h"
 #include "chap-new.h"
 #include "chap_ms.h"
+#include "mppe.h"
 #include "peap.h"
 
+#ifdef UNIT_TEST
+#define novm(x)
+#endif
+
 struct peap_state {
        SSL_CTX *ctx;
        SSL *ssl;
        BIO *in_bio;
        BIO *out_bio;
 
+       int phase;
        int written, read;
        u_char *in_buf;
        u_char *out_buf;
@@ -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];
-};
-
-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
-       init = 1;
-}
+};
 
 /*
  * 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;
 
-       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");
-       hash = malloc(pfr_len + SHA_HASH_LEN);
+       hash = malloc(pfr_len + SHA_DIGEST_LENGTH);
        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)
-                       j = SHA_HASH_LEN;
+                       j = SHA_DIGEST_LENGTH;
                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++;
-               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");
-               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);
 }
 
-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};
@@ -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;
 
-       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;
@@ -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(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(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");
@@ -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);
 }
 
-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);
-       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");
 }
 
-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;
 
-       if (debug)
-               info("PEAP CB: generate mppe keys");
+       dbglog("PEAP CB: generate mppe keys");
        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.
@@ -190,19 +215,16 @@ static void generate_mppe_keys(void)
         * | 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)
 {
@@ -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)
 {
+       struct peap_state *psm = esp->ea_peap;
        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;
 
-       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;
@@ -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);
 
-       if (peap_phase == PEAP_PHASE_1) {
+       if (psm->phase == PEAP_PHASE_1) {
                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);
 }
 
-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);
@@ -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;
-       } 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 */
-               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);
@@ -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);
-               generate_cmk(psm->tk, psm->nonce, outp, 1);
+               generate_cmk(psm->ipmk, psm->tk, psm->nonce, outp, 1);
+#ifdef MPPE
                /* set mppe keys */
-               generate_mppe_keys();
+               generate_mppe_keys(psm->ipmk, 1);
+#endif
                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;
        }
 
-       if (debug)
-               dump(psm->out_buf, used);
+done:
+
+       dbglog("PEAP: EAP (out): %.*B", used, psm->out_buf);
        *out_len = used;
 }
 
-void allocate_buffers(void)
+int peap_init(struct peap_state **ctx, const char *rhostname)
 {
        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);
@@ -360,20 +480,31 @@ void allocate_buffers(void)
        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 (!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());
@@ -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);
-       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;
 
-       if (!init)
-               ssl_init();
+       struct peap_state *psm = esp->ea_peap;
 
        if (esp->es_client.ea_id == id) {
                info("PEAP: retransmits are not supported..");
-               return;
+               return -1;
        }
 
        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);
@@ -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:
-               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:
-               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);
@@ -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) {
-                       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 {
-                       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);
                }
 
-               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);
@@ -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))
-                               peap_phase = PEAP_PHASE_2;
+                               psm->phase = PEAP_PHASE_2;
                        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;
-               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->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);
-               peap_response(esp, id, psm->out_buf, psm->read);
+                       peap_response(esp, id, psm->out_buf, psm->read);
+               }
                break;
        }
+       return 0;
+}
+
+#else
+
+u_char outpacket_buf[255];
+int debug = 1;
+int error_count = 0;
+int unsuccess = 0;
+
+/**
+ * Using the example in MS-PEAP, section 4.4.1.
+ *     see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-peap/5308642b-90c9-4cc4-beec-fb367325c0f9
+ */
+int test_cmk(u_char *ipmk) {
+       u_char nonce[PEAP_TLV_NONCE_LEN] = {
+               0x6C, 0x6B, 0xA3, 0x87, 0x84, 0x23, 0x74, 0x57,
+               0xCC, 0xC9, 0x0B, 0x1A, 0x90, 0x8C, 0xBD, 0xF4,
+               0x71, 0x1B, 0x69, 0x99, 0x4D, 0x0C, 0xFE, 0x8D,
+               0x3D, 0xB4, 0x4E, 0xCB, 0xCD, 0xAD, 0x37, 0xE9
+       };
+
+       u_char tmpkey[PEAP_TLV_TEMPKEY_LEN] = {
+               0x73, 0x8B, 0xB5, 0xF4, 0x62, 0xD5, 0x8E, 0x7E,
+               0xD8, 0x44, 0xE1, 0xF0, 0x0D, 0x0E, 0xBE, 0x50,
+               0xC5, 0x0A, 0x20, 0x50, 0xDE, 0x11, 0x99, 0x77,
+               0x10, 0xD6, 0x5F, 0x45, 0xFB, 0x5F, 0xBA, 0xB7,
+               0xE3, 0x18, 0x1E, 0x92, 0x4F, 0x42, 0x97, 0x38,
+               // 0xDE, 0x40, 0xC8, 0x46, 0xCD, 0xF5, 0x0B, 0xCB,
+               // 0xF9, 0xCE, 0xDB, 0x1E, 0x85, 0x1D, 0x22, 0x52,
+               // 0x45, 0x3B, 0xDF, 0x63
+       };
+
+       u_char expected[60] = {
+               0x00, 0x0C, 0x00, 0x38, 0x00, 0x00, 0x00, 0x01,
+               0x6C, 0x6B, 0xA3, 0x87, 0x84, 0x23, 0x74, 0x57,
+               0xCC, 0xC9, 0x0B, 0x1A, 0x90, 0x8C, 0xBD, 0xF4,
+               0x71, 0x1B, 0x69, 0x99, 0x4D, 0x0C, 0xFE, 0x8D,
+               0x3D, 0xB4, 0x4E, 0xCB, 0xCD, 0xAD, 0x37, 0xE9,
+               0x42, 0xE0, 0x86, 0x07, 0x1D, 0x1C, 0x8B, 0x8C,
+               0x8E, 0x45, 0x8F, 0x70, 0x21, 0xF0, 0x6A, 0x6E,
+               0xAB, 0x16, 0xB6, 0x46
+       };
+
+       u_char inner_mppe_keys[32] = {
+               0x67, 0x3E, 0x96, 0x14, 0x01, 0xBE, 0xFB, 0xA5,
+               0x60, 0x71, 0x7B, 0x3B, 0x5D, 0xDD, 0x40, 0x38,
+               0x65, 0x67, 0xF9, 0xF4, 0x16, 0xFD, 0x3E, 0x9D,
+               0xFC, 0x71, 0x16, 0x3B, 0xDF, 0xF2, 0xFA, 0x95
+       };
+
+       u_char response[60] = {};
+
+       // Set the inner MPPE keys (e.g. from CHAPv2)
+       mppe_set_keys(inner_mppe_keys, inner_mppe_keys + 16, 16);
+
+       // Generate and compare the response
+       generate_cmk(ipmk, tmpkey, nonce, response, 1);
+       if (memcmp(expected, response, sizeof(response)) != 0) {
+               dbglog("Failed CMK key generation\n");
+               dbglog("%.*B", sizeof(response), response);
+               dbglog("%.*B", sizeof(expected), expected);
+               return -1;
+       }
+
+       return 0;
+}
+
+int test_mppe(u_char *ipmk) {
+       u_char outer_mppe_send_key[MPPE_MAX_KEY_SIZE] = {
+               0x6A, 0x02, 0xD7, 0x82, 0x20, 0x1B, 0xC7, 0x13,
+               0x8B, 0xF8, 0xEF, 0xF7, 0x33, 0xB4, 0x96, 0x97,
+               0x0D, 0x7C, 0xAB, 0x30, 0x0A, 0xC9, 0x57, 0x72,
+               0x78, 0xE1, 0xDD, 0xD5, 0xAE, 0xF7, 0x66, 0x97
+       };
+
+       u_char outer_mppe_recv_key[MPPE_MAX_KEY_SIZE] = {
+               0x17, 0x52, 0xD4, 0xE5, 0x84, 0xA1, 0xC8, 0x95,
+               0x03, 0x9B, 0x4D, 0x05, 0xE3, 0xBC, 0x9A, 0x84,
+               0x84, 0xDD, 0xC2, 0xAA, 0x6E, 0x2C, 0xE1, 0x62,
+               0x76, 0x5C, 0x40, 0x68, 0xBF, 0xF6, 0x5A, 0x45
+       };
+
+       u_char result[MPPE_MAX_KEY_SIZE];
+       int len;
+
+       mppe_clear_keys();
+
+       generate_mppe_keys(ipmk, 1);
+
+       len = mppe_get_recv_key(result, sizeof(result));
+       if (len != sizeof(result)) {
+               dbglog("Invalid length of resulting MPPE recv key");
+               return -1;
+       }
+
+       if (memcmp(result, outer_mppe_recv_key, len) != 0) {
+               dbglog("Invalid result for outer mppe recv key");
+               return -1;
+       }
+
+       len = mppe_get_send_key(result, sizeof(result));
+       if (len != sizeof(result)) {
+               dbglog("Invalid length of resulting MPPE send key");
+               return -1;
+       }
+
+       if (memcmp(result, outer_mppe_send_key, len) != 0) {
+               dbglog("Invalid result for outer mppe send key");
+               return -1;
+       }
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+        u_char ipmk[PEAP_TLV_IPMK_LEN] = {
+               0x3A, 0x91, 0x1C, 0x25, 0x54, 0x73, 0xE8, 0x3E,
+               0x9A, 0x0C, 0xC3, 0x33, 0xAE, 0x1F, 0x8A, 0x35,
+               0xCD, 0xC7, 0x41, 0x63, 0xE7, 0xF6, 0x0F, 0x6C,
+               0x65, 0xEF, 0x71, 0xC2, 0x64, 0x42, 0xAA, 0xAC,
+               0xA2, 0xB6, 0xF1, 0xEB, 0x4F, 0x25, 0xEC, 0xA3,
+       };
+       int ret = -1;
+
+       ret = test_cmk(ipmk);
+       if (ret != 0) {
+               return -1;
+       }
+
+       ret = test_mppe(ipmk);
+       if (ret != 0) {
+               return -1;
+       }
+
+       return 0;
 }
+
+#endif
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
 
-#define        EAPT_MSCHAPV2                   26
-
 #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        SHA_HASH_LEN                    20
-
 #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
 
-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 */
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
-(EAP-TLS) Use the file \fIca-file\fR as the X.509 Certificate Authority
+(EAP-TLS, or PEAP) Use the file \fIca-file\fR as the X.509 Certificate Authority
 (CA) file (in PEM format), needed for setting up an EAP-TLS connection.
 This option is used on the client-side in conjunction with the \fBcert\fR
-and \fBkey\fR options.
+and \fBkey\fR options.  Either \fIca\fR, or \fIcapath\fR options are required
+for PEAP. EAP-TLS may also use the entry in eaptls-client or eaptls-server
+for a CA certificate associated with a particular peer.
+.TP
+.B capath \fIpath
+(EAP-TLS, or PEAP) Specify a location that contains public CA certificates.
+Either \fIca\fR, or \fIcapath\fR options are required for PEAP.
 .TP
 .B cdtrcts
 Use a non-standard hardware flow control (i.e. DTR/CTS) to control
@@ -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
-(EAP-TLS) Use the file \fIfilename\fR as the Certificate Revocation List
+(EAP-TLS, or PEAP) Use the file \fIfilename\fR as the Certificate Revocation List
 to check for the validity of the peer's certificate. This option is not
-mandatory for setting up an EAP-TLS connection. Also see the \fBcrl-dir\fR
+mandatory for setting up a TLS connection. Also see the \fBcrl-dir\fR
 option.
 .TP
 .B crl-dir \fIdirectory
-(EAP-TLS) Use the directory \fIdirectory\fR to scan for CRL files in
+(EAP-TLS, or PEAP) Use the directory \fIdirectory\fR to scan for CRL files in
 has format ($hash.r0) to check for the validity of the peer's certificate.
-This option is not mandatory for setting up an EAP-TLS connection.
+This option is not mandatory for setting up a TLS connection.
 Also see the \fBcrl\fR option.
 .TP
 .B debug
@@ -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
+.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
@@ -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
+.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
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 */
 
-#ifdef USE_EAPTLS
+#if defined(USE_EAPTLS) || defined(USE_PEAP)
 
 #define TLS_VERIFY_NONE     "none"
 #define TLS_VERIFY_NAME     "name"
 #define TLS_VERIFY_SUBJECT  "subject"
 #define TLS_VERIFY_SUFFIX   "suffix"
 
-extern char    *crl_dir;
-extern char    *crl_file;
-extern char *pkcs12_file;
+extern char *crl_dir;
+extern char *crl_file;
+extern char *ca_path;
+extern char *cacert_file;
+
 extern char *max_tls_version;
 extern bool tls_verify_key_usage;
 extern char *tls_verify_method;
+#endif /* USE_EAPTLS || USE_PEAP */
+
+#ifdef USE_EAPTLS
+extern char *pkcs12_file;
 #endif /* USE_EAPTLS */
 
 #ifdef MAXOCTETS
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 */