]> git.ozlabs.org Git - ppp.git/commitdiff
Add support for EAP-TLS (including experimental TLS v1.3 support).
authorJan Just Keijser <jan.just.keijser@gmail.com>
Mon, 19 Oct 2020 15:57:36 +0000 (17:57 +0200)
committerJan Just Keijser <jan.just.keijser@gmail.com>
Mon, 19 Oct 2020 16:00:54 +0000 (18:00 +0200)
Signed-off-by: Jan Just Keijser <jan.just.keijser@gmail.com>
21 files changed:
README.eap-tls [new file with mode: 0644]
etc.ppp/eaptls-client [new file with mode: 0644]
etc.ppp/eaptls-server [new file with mode: 0644]
etc.ppp/openssl.cnf [new file with mode: 0644]
linux/Makefile.top
pppd/Makefile.linux
pppd/Makefile.sol2
pppd/auth.c
pppd/ccp.c
pppd/eap-tls.c [new file with mode: 0644]
pppd/eap-tls.c.rej [new file with mode: 0644]
pppd/eap-tls.h [new file with mode: 0644]
pppd/eap.c
pppd/eap.h
pppd/pathnames.h
pppd/plugins/Makefile.linux
pppd/plugins/passprompt.c
pppd/plugins/passwordfd.c
pppd/pppd.8
pppd/pppd.h
pppd/sys-solaris.c

diff --git a/README.eap-tls b/README.eap-tls
new file mode 100644 (file)
index 0000000..ab3794e
--- /dev/null
@@ -0,0 +1,229 @@
+EAP-TLS authentication support for PPP
+======================================
+
+1. Intro
+
+    The Extensible Authentication Protocol (EAP; RFC 3748) is a
+    security protocol that can be used with PPP.  It provides a means
+    to plug in multiple optional authentication methods.
+
+    Transport Level Security (TLS; RFC 5216) provides for mutual 
+    authentication, integrity-protected ciphersuite negotiation and 
+    key exchange between two endpoints.  It also provides for optional
+    MPPE encryption.
+
+    EAP-TLS (RFC 2716) incapsulates the TLS messages in EAP packets,
+    allowing TLS mutual authentication to be used as a generic EAP
+    mechanism. It also provides optional encryption using the MPPE
+    protocol.
+
+    This patch provide EAP-TLS support to pppd.
+    This authentication method can be used in both client or server
+    mode.
+
+2. Building
+
+    To build pppd with EAP-TLS support, OpenSSL (http://www.openssl.org)
+    is required. Any version from 0.9.7 should work.
+    
+    Configure, compile, and install as usual. 
+
+3. Configuration
+
+    On the client side there are two ways to configure EAP-TLS:
+
+    1. supply the appropriate 'ca', 'cert' and 'key' command-line parameters
+
+    2. edit the /etc/ppp/eaptls-client file.
+    Insert a line for each system with which you use EAP-TLS.
+    The line is composed of this fields separated by tab:
+
+      - Client name 
+        The name used by the client for authentication, can be *
+      - Server name
+        The name of the server, can be *
+      - Client certificate file 
+        The file containing the certificate chain for the 
+        client in PEM format
+      - Server certificate file
+        If you want to specify the certificate that the 
+        server is allowed to use, put the certificate file name.
+        Else put a dash '-'.
+      - CA certificate file
+        The file containing the trusted CA certificates in PEM
+        format.
+      - Client private key file
+        The file containing the client private key in PEM format.
+
+
+    On the server side edit the /etc/ppp/eaptls-server file.
+    Insert a line for each system with which you use EAP-TLS.
+    The line is composed of this fields separated by tab:
+
+      - Client name
+        The name used by the client for authentication, can be *
+      - Server name
+        The name of the server, can be *
+      - Client certificate file
+        If you want to specify the certificate that the
+        client is allowed to use, put the certificate file name.
+        Else put a dash '-'.
+      - Server certificate file
+        The file containing the certificate chain for the
+        server in PEM format
+      - CA certificate file
+        The file containing the trusted CA certificates in PEM format.
+      - Client private key file
+        The file containing the server private key in PEM format.
+      - addresses
+        A list of IP addresses the client is allowed to use.
+
+
+    OpenSSL engine support is included starting with v0.95 of this patch. 
+    Currently the only engine tested is the 'pkcs11' engine (hardware token
+    support). To use the 'pksc11' engine:
+      - Use a special private key fileiname in the /etc/ppp/eaptls-client file:
+          <engine>:<identifier>
+        e.g.
+          pkcs11:123456
+
+      - The certificate can also be loaded from the 'pkcs11' engine using
+        a special client certificate filename in the /etc/ppp/eaptls-client file:
+          <engine>:<identifier>
+        e.g.
+          pkcs11:123456
+
+      - Create an /etc/ppp/openssl.cnf file to load the right OpenSSL engine prior
+        to starting 'pppd'. A sample openssl.cnf file is
+
+        openssl_conf = openssl_def
+
+        [ openssl_def ]
+        engines = engine_section
+
+        [ engine_section ]
+        pkcs11 = pkcs11_section
+
+        [ pkcs11_section ]
+        engine_id = pkcs11
+        dynamic_path = /usr/lib64/openssl/engines/engine_pkcs11.so
+        MODULE_PATH = /usr/lib64/libeTPkcs11.so
+        init = 0
+
+      - There are two ways to specify a password/PIN for the PKCS11 engine:
+          - inside the openssl.cnf file using
+              PIN = your-secret-pin
+            Note The keyword 'PIN' is case sensitive!
+          - Using the 'password' in the ppp options file.
+        From v0.97 of the eap-tls patch the password can also be supplied
+        using the appropriate 'eaptls_passwd_hook' (see plugins/passprompt.c
+        for an example).
+
+
+4. Options
+
+    These pppd options are available:
+
+      ca <ca-file>
+        Use the CA public certificate found in <ca-file> in PEM format
+      ca-path <directory>
+        Use the directory <directory> as the CA public certificate directory
+      cert <cert-file>
+        Use the client public certificate found in <cert-file> in PEM format
+        or in engine:engine_id format
+      key <key-file>
+        Use the client private key found in <key-file> in PEM format
+        or in engine:engine_id format
+      crl <crl-file>
+        Use the Certificate Revocation List (CRL) file <crl-file> in PEM format.
+      crl-dir <dir>
+        Use CRL files from directory <dir>. It contains CRL files in PEM
+        format and each file contains a CRL. The files are looked up 
+        by the issuer name hash value. Use the c_rehash utility 
+        to create necessary links.
+      need-peer-eap
+        If the peer doesn't ask us to authenticate or doesn't use eap
+        to authenticate us, disconnect.
+      max-tls-version <1.0|1.1|1.2 (default)|1.3>
+        Specify the maximum TLS protocol version to negotiate with peers. Defaults
+        to TLSv1.2 as the TLSv1.3 code is experimental.
+
+    Note: 
+      password-encrypted certificates can be used as of v0.94 of this 
+      patch. The password for the eap-tls.key file is specified using 
+      the regular
+          password ....
+      statement in the ppp options file, or by using the appropriate
+      plugin which supplies a 'eaptls_passwd_hook' routine.
+
+5. Connecting
+
+    If you're setting up a pppd server, edit the EAP-TLS configuration file 
+    as written above and then run pppd with the 'auth' option to authenticate
+    the client. The EAP-TLS method will be used if the other eap methods can't
+    be used (no secrets).
+
+    If you're setting up a client, edit the configuration file and then run
+    pppd with 'remotename' option to specify the server name. Add the 
+    'need-peer-eap' option if you want to be sure the peer ask you to
+    authenticate (and to use eap) and to disconnect if it doesn't.
+
+6. Example
+
+    The following example can be used to connect a Linux client with the 'pptp'
+    package to a Linux server running the 'pptpd' (PoPToP) package. The server
+    was configured with a certificate with name (CN) 'pptp-server', the client
+    was configured with a certificate with name (CN) 'pptp-client', both 
+    signed by the same Certificate Authority (CA).
+
+    Server side:
+      - /etc/pptpd.conf file:
+          option /etc/ppp/options-pptpd-eaptls
+          localip 172.16.1.1
+          remoteip 172.16.1.10-20 
+      - /etc/ppp/options-pptpd-eaptls file:
+          name pptp-server
+          lock 
+          mtu 1500 
+          mru 1450
+          auth 
+          lcp-echo-failure 3 
+          lcp-echo-interval 5 
+          nodeflate 
+          nobsdcomp
+          nopredictor1
+          nopcomp
+          noaccomp
+          
+          require-eap
+          require-mppe-128
+          
+          crl /home/janjust/ppp/keys/crl.pem
+          
+          debug
+          logfile /tmp/pppd.log
+
+      - /etc/ppp/eaptls-server file:
+           * pptp-server - /etc/ppp/pptp-server.crt /etc/ppp/ca.crt /etc/ppp/pptp-server.key *
+
+      - On the server, run 
+          pptdp --conf /etc/pptpd.conf
+       
+    Client side:
+      - Run
+          pppd noauth require-eap require-mppe-128 \
+            ipcp-accept-local ipcp-accept-remote noipdefault \
+            cert  /etc/ppp/keys/pptp-client.crt \
+            key   /etc/ppp/keys/pptp-client.key \
+            ca    /etc/ppp/keys/ca.crt \
+            name pptp-client remotename pptp-server \
+            debug logfile /tmp/pppd.log
+            pty "pptp pptp-server.example.com --nolaunchpppd"
+
+    Check /var/log/messages and the files /tmp/pppd.log on both sides for debugging info.
+
+7. Notes
+
+    This is experimental code.
+    Send suggestions and comments to Jan Just Keijser <janjust@nikhef.nl>
+
diff --git a/etc.ppp/eaptls-client b/etc.ppp/eaptls-client
new file mode 100644 (file)
index 0000000..7782f0e
--- /dev/null
@@ -0,0 +1,10 @@
+# Parameters for authentication using EAP-TLS (client)
+
+# client name (can be *)
+# server name (can be *)
+# client certificate file (required)
+# server certificate file (optional, if unused put '-')
+# CA certificate file (required)
+# client private key file (required)
+
+#client        server  /root/cert/client.crt   -       /root/cert/ca.crt       /root/cert/client.key
diff --git a/etc.ppp/eaptls-server b/etc.ppp/eaptls-server
new file mode 100644 (file)
index 0000000..fa53cbd
--- /dev/null
@@ -0,0 +1,11 @@
+# Parameters for authentication using EAP-TLS (server)
+
+# client name (can be *)
+# server name (can be *)
+# client certificate file (optional, if unused put '-')
+# server certificate file (required)
+# CA certificate file (required)
+# server private key file (required)
+# allowed addresses (required, can be *)
+
+#client        server  -       /root/cert/server.crt   /root/cert/ca.crt       /root/cert/server.key   192.168.1.0/24
diff --git a/etc.ppp/openssl.cnf b/etc.ppp/openssl.cnf
new file mode 100644 (file)
index 0000000..dd32f30
--- /dev/null
@@ -0,0 +1,14 @@
+openssl_conf = openssl_def
+
+[ openssl_def ]
+engines = engine_section
+
+[ engine_section ]
+pkcs11 = pkcs11_section
+
+[ pkcs11_section ]
+engine_id = pkcs11
+dynamic_path = /usr/lib64/openssl/engines/engine_pkcs11.so
+MODULE_PATH = /usr/lib64/libeTPkcs11.so
+init = 0
+
index f63d45e58a78ab2e275c8dbffbac8469126ed8a4..894f8f32c9e4bd4284ad614382f6b5f2b29a959a 100644 (file)
@@ -26,7 +26,7 @@ install-progs:
        cd pppdump; $(MAKE) $(MFLAGS) install
 
 install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
-       $(ETCDIR)/chap-secrets
+       $(ETCDIR)/chap-secrets $(ETCDIR)/eaptls-server $(ETCDIR)/eaptls-client
 
 install-devel:
        cd pppd; $(MAKE) $(MFLAGS) install-devel
@@ -37,6 +37,10 @@ $(ETCDIR)/pap-secrets:
        $(INSTALL) -c -m 600 etc.ppp/pap-secrets $@
 $(ETCDIR)/chap-secrets:
        $(INSTALL) -c -m 600 etc.ppp/chap-secrets $@
+$(ETCDIR)/eaptls-server:
+       $(INSTALL) -c -m 600 etc.ppp/eaptls-server $@
+$(ETCDIR)/eaptls-client:
+       $(INSTALL) -c -m 600 etc.ppp/eaptls-client $@
 
 $(BINDIR):
        $(INSTALL) -d -m 755 $@
index a22dcfab9d74f9658a1522bada021a41249aa90d..44d47ad7f46f35bce09a86998cf4ebaab2ebd15f 100644 (file)
@@ -81,6 +81,9 @@ PLUGIN=y
 # Use libutil
 USE_LIBUTIL=y
 
+# Enable EAP-TLS authentication (requires MPPE support, libssl and libcrypto)
+USE_EAPTLS=y
+
 MAXOCTETS=y
 
 INCLUDE_DIRS= -I../include
@@ -106,7 +109,8 @@ endif
 # EAP SRP-SHA1
 ifdef USE_SRP
 CFLAGS += -DUSE_SRP -DOPENSSL -I/usr/local/ssl/include
-LIBS   += -lsrp -L/usr/local/ssl/lib -lcrypto
+LIBS   += -lsrp -L/usr/local/ssl/lib
+NEEDCRYPTOLIB = y
 TARGETS        += srp-entry
 EXTRAINSTALL = $(INSTALL) -s -c -m 555 srp-entry $(BINDIR)/srp-entry
 MANPAGES += srp-entry.8
@@ -121,6 +125,16 @@ HEADERS += sha1.h
 PPPDOBJS += sha1.o
 endif
 
+# EAP-TLS
+ifdef USE_EAPTLS
+CFLAGS += -DUSE_EAPTLS=1
+LIBS += -lssl
+NEEDCRYPTOLIB = y
+PPPDSRC += eap-tls.c
+HEADERS += eap-tls.h
+PPPDOBJS += eap-tls.o
+endif
+
 ifdef HAS_SHADOW
 CFLAGS   += -DHAS_SHADOW
 #LIBS     += -lshadow $(LIBS)
@@ -139,7 +153,7 @@ endif
 ifdef NEEDDES
 ifndef USE_CRYPT
 CFLAGS   += -I$(shell $(CC) --print-sysroot)/usr/include/openssl
-LIBS     += -lcrypto
+NEEDCRYPTOLIB = y
 else
 CFLAGS   += -DUSE_CRYPT=1
 endif
@@ -147,6 +161,10 @@ PPPDOBJS += pppcrypt.o
 HEADERS += pppcrypt.h
 endif
 
+ifdef NEEDCRYPTOLIB
+LIBS     += -lcrypto
+endif
+
 # For "Pluggable Authentication Modules", see ftp.redhat.com:/pub/pam/.
 ifdef USE_PAM
 CFLAGS   += -DUSE_PAM
index 45b6b6269bd2a096a433bf284b7eac67b2d88cc7..809cb4b3519fcd4af10e67601300dd707af84921 100644 (file)
@@ -39,6 +39,14 @@ OBJS += ipv6cp.o eui64.o
 CFLAGS += -DUSE_CRYPT -DCHAPMS -DMSLANMAN -DHAVE_CRYPT_H
 OBJS += chap_ms.o pppcrypt.o md4.o sha1.o
 
+# Uncomment to enable MPPE (in both CHAP and EAP-TLS)
+CFLAGS += -DMPPE
+
+# Uncomment to enable EAP-TLS
+CFLAGS += -DUSE_EAPTLS
+LIBS += -lcrypto -lssl
+OBJS += eap-tls.o
+
 # Uncomment for CBCP
 #CFLAGS += -DCBCP_SUPPORT
 #OBJS += cbcp.o
index 3641b6ea701f11af845331926d308c366103a4d5..601241f18164a576f01496755b7725a9c6e6fb24 100644 (file)
@@ -78,6 +78,7 @@
 #include <pwd.h>
 #include <grp.h>
 #include <string.h>
+#include <strings.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include "upap.h"
 #include "chap-new.h"
 #include "eap.h"
+#ifdef USE_EAPTLS
+#include "eap-tls.h"
+#endif
 #ifdef CBCP_SUPPORT
 #include "cbcp.h"
 #endif
@@ -186,6 +190,11 @@ int (*chap_check_hook) __P((void)) = NULL;
 /* Hook for a plugin to get the CHAP password for authenticating us */
 int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL;
 
+#ifdef USE_EAPTLS
+/* Hook for a plugin to get the EAP-TLS password for authenticating us */
+int (*eaptls_passwd_hook) __P((char *user, char *passwd)) = NULL;
+#endif
+
 /* Hook for a plugin to say whether it is OK if the peer
    refuses to authenticate. */
 int (*null_auth_hook) __P((struct wordlist **paddrs,
@@ -241,6 +250,16 @@ 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 *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) */
+bool need_peer_eap = 0;                        /* Require peer to authenticate us */
+#endif
 
 static char *uafname;          /* name of most recent +ua file */
 
@@ -257,6 +276,19 @@ static int  have_pap_secret __P((int *));
 static int  have_chap_secret __P((char *, char *, int, int *));
 static int  have_srp_secret __P((char *client, char *server, int need_ip,
     int *lacks_ipp));
+
+#ifdef USE_EAPTLS
+static int  have_eaptls_secret_server
+__P((char *client, char *server, int need_ip, int *lacks_ipp));
+static int  have_eaptls_secret_client __P((char *client, char *server));
+static int  scan_authfile_eaptls __P((FILE * f, char *client, char *server,
+                              char *cli_cert, char *serv_cert,
+                              char *ca_cert, char *pk,
+                              struct wordlist ** addrs,
+                              struct wordlist ** opts,
+                              char *filename, int flags));
+#endif
+
 static int  ip_addr_check __P((u_int32_t, struct permitted_ip *));
 static int  scan_authfile __P((FILE *, char *, char *, char *,
                               struct wordlist **, struct wordlist **,
@@ -404,6 +436,18 @@ option_t auth_options[] = {
       "Set telephone number(s) which are allowed to connect",
       OPT_PRIV | OPT_A2LIST },
 
+#ifdef USE_EAPTLS
+    { "ca", o_string, &cacert_file,   "EAP-TLS CA certificate in PEM format" },
+    { "capath", o_string, &ca_path,   "EAP-TLS CA certificate directory" },
+    { "cert", o_string, &cert_file,   "EAP-TLS client certificate in PEM format" },
+    { "key", o_string, &privkey_file, "EAP-TLS client private key in PEM format" },
+    { "crl-dir", o_string, &crl_dir,  "Use CRLs in directory" },
+    { "crl", o_string, &crl_file,     "Use specific CRL file" },
+    { "max-tls-version", o_string, &max_tls_version,
+      "Maximum TLS version (1.0/1.1/1.2 (default)/1.3)" },
+    { "need-peer-eap", o_bool, &need_peer_eap,
+      "Require the peer to authenticate us", 1 },
+#endif /* USE_EAPTLS */
     { NULL }
 };
 
@@ -737,6 +781,9 @@ link_established(unit)
     lcp_options *wo = &lcp_wantoptions[unit];
     lcp_options *go = &lcp_gotoptions[unit];
     lcp_options *ho = &lcp_hisoptions[unit];
+#ifdef USE_EAPTLS
+    lcp_options *ao = &lcp_allowoptions[unit];
+#endif
     int i;
     struct protent *protp;
 
@@ -771,6 +818,22 @@ link_established(unit)
        }
     }
 
+#ifdef USE_EAPTLS
+    if (need_peer_eap && !ao->neg_eap) {
+       warn("eap required to authenticate us but no suitable secrets");
+       lcp_close(unit, "couldn't negotiate eap");
+       status = EXIT_AUTH_TOPEER_FAILED;
+       return;
+    }
+
+    if (need_peer_eap && !ho->neg_eap) {
+       warn("peer doesn't want to authenticate us with eap");
+       lcp_close(unit, "couldn't negotiate eap");
+       status = EXIT_PEER_AUTH_FAILED;
+       return;
+    }
+#endif
+
     new_phase(PHASE_AUTHENTICATE);
     auth = 0;
     if (go->neg_eap) {
@@ -1291,6 +1354,15 @@ auth_check_options()
                                    our_name, 1, &lacks_ip);
     }
 
+#ifdef USE_EAPTLS
+    if (!can_auth && wo->neg_eap) {
+       can_auth =
+           have_eaptls_secret_server((explicit_remote ? remote_name :
+                                      NULL), our_name, 1, &lacks_ip);
+
+    }
+#endif
+
     if (auth_required && !can_auth && noauth_addrs == NULL) {
        if (default_auth) {
            option_error(
@@ -1345,7 +1417,11 @@ auth_reset(unit)
        passwd[0] != 0 ||
        (hadchap == 1 || (hadchap == -1 && have_chap_secret(user,
            (explicit_remote? remote_name: NULL), 0, NULL))) ||
-       have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL));
+       have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL)
+#ifdef USE_EAPTLS
+               || have_eaptls_secret_client(user, (explicit_remote? remote_name: NULL))
+#endif
+       );
 
     hadchap = -1;
     if (go->neg_upap && !uselogin && !have_pap_secret(NULL))
@@ -1360,7 +1436,12 @@ auth_reset(unit)
            !have_chap_secret((explicit_remote? remote_name: NULL), our_name,
                1, NULL))) &&
        !have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1,
-           NULL))
+           NULL)
+#ifdef USE_EAPTLS
+        && !have_eaptls_secret_server((explicit_remote? remote_name: NULL),
+                                  our_name, 1, NULL)
+#endif
+               )
        go->neg_eap = 0;
 }
 
@@ -2373,3 +2454,344 @@ auth_script(script)
 
     auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL, 0);
 }
+
+
+#ifdef USE_EAPTLS
+static int
+have_eaptls_secret_server(client, server, need_ip, lacks_ipp)
+    char *client;
+    char *server;
+    int need_ip;
+    int *lacks_ipp;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs;
+    char servcertfile[MAXWORDLEN];
+    char clicertfile[MAXWORDLEN];
+    char cacertfile[MAXWORDLEN];
+    char pkfile[MAXWORDLEN];
+
+    filename = _PATH_EAPTLSSERVFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+               return 0;
+
+    if (client != NULL && client[0] == 0)
+               client = NULL;
+    else if (server != NULL && server[0] == 0)
+               server = NULL;
+
+    ret =
+       scan_authfile_eaptls(f, client, server, clicertfile, servcertfile,
+                            cacertfile, pkfile, &addrs, NULL, filename,
+                            0);
+
+    fclose(f);
+
+/*
+    if (ret >= 0 && !eaptls_init_ssl(1, cacertfile, servcertfile,
+                               clicertfile, pkfile))
+               ret = -1;
+*/
+
+       if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+               if (lacks_ipp != 0)
+                       *lacks_ipp = 1;
+               ret = -1;
+    }
+    if (addrs != 0)
+               free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+
+static int
+have_eaptls_secret_client(client, server)
+    char *client;
+    char *server;
+{
+    FILE *f;
+    int ret;
+    char *filename;
+    struct wordlist *addrs = NULL;
+    char servcertfile[MAXWORDLEN];
+    char clicertfile[MAXWORDLEN];
+    char cacertfile[MAXWORDLEN];
+    char pkfile[MAXWORDLEN];
+
+    if (client != NULL && client[0] == 0)
+               client = NULL;
+    else if (server != NULL && server[0] == 0)
+               server = NULL;
+
+       if ((cacert_file || ca_path) && cert_file && privkey_file)
+               return 1;
+
+    filename = _PATH_EAPTLSCLIFILE;
+    f = fopen(filename, "r");
+    if (f == NULL)
+               return 0;
+
+    ret =
+       scan_authfile_eaptls(f, client, server, clicertfile, servcertfile,
+                            cacertfile, pkfile, &addrs, NULL, filename,
+                            0);
+    fclose(f);
+
+/*
+    if (ret >= 0 && !eaptls_init_ssl(0, cacertfile, clicertfile,
+                               servcertfile, pkfile))
+               ret = -1;
+*/
+
+    if (addrs != 0)
+               free_wordlist(addrs);
+
+    return ret >= 0;
+}
+
+
+static int
+scan_authfile_eaptls(f, client, server, cli_cert, serv_cert, ca_cert, pk,
+                    addrs, opts, filename, flags)
+    FILE *f;
+    char *client;
+    char *server;
+    char *cli_cert;
+    char *serv_cert;
+    char *ca_cert;
+    char *pk;
+    struct wordlist **addrs;
+    struct wordlist **opts;
+    char *filename;
+    int flags;
+{
+    int newline;
+    int got_flag, best_flag;
+    struct wordlist *ap, *addr_list, *alist, **app;
+    char word[MAXWORDLEN];
+
+    if (addrs != NULL)
+       *addrs = NULL;
+    if (opts != NULL)
+       *opts = NULL;
+    addr_list = NULL;
+    if (!getword(f, word, &newline, filename))
+       return -1;              /* file is empty??? */
+    newline = 1;
+    best_flag = -1;
+    for (;;) {
+       /*
+        * Skip until we find a word at the start of a line.
+        */
+       while (!newline && getword(f, word, &newline, filename));
+       if (!newline)
+           break;              /* got to end of file */
+
+       /*
+        * Got a client - check if it's a match or a wildcard.
+        */
+       got_flag = 0;
+       if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) {
+           newline = 0;
+           continue;
+       }
+       if (!ISWILD(word))
+           got_flag = NONWILD_CLIENT;
+
+       /*
+        * Now get a server and check if it matches.
+        */
+       if (!getword(f, word, &newline, filename))
+           break;
+       if (newline)
+           continue;
+       if (!ISWILD(word)) {
+           if (server != NULL && strcmp(word, server) != 0)
+               continue;
+           got_flag |= NONWILD_SERVER;
+       }
+
+       /*
+        * Got some sort of a match - see if it's better than what
+        * we have already.
+        */
+       if (got_flag <= best_flag)
+           continue;
+
+       /*
+        * Get the cli_cert
+        */
+       if (!getword(f, word, &newline, filename))
+           break;
+       if (newline)
+           continue;
+       if (strcmp(word, "-") != 0) {
+           strlcpy(cli_cert, word, MAXWORDLEN);
+       } else
+           cli_cert[0] = 0;
+
+       /*
+        * Get serv_cert
+        */
+       if (!getword(f, word, &newline, filename))
+           break;
+       if (newline)
+           continue;
+       if (strcmp(word, "-") != 0) {
+           strlcpy(serv_cert, word, MAXWORDLEN);
+       } else
+           serv_cert[0] = 0;
+
+       /*
+        * Get ca_cert
+        */
+       if (!getword(f, word, &newline, filename))
+           break;
+       if (newline)
+           continue;
+       strlcpy(ca_cert, word, MAXWORDLEN);
+
+       /*
+        * Get pk
+        */
+       if (!getword(f, word, &newline, filename))
+           break;
+       if (newline)
+           continue;
+       strlcpy(pk, word, MAXWORDLEN);
+
+
+       /*
+        * Now read address authorization info and make a wordlist.
+        */
+       app = &alist;
+       for (;;) {
+           if (!getword(f, word, &newline, filename) || newline)
+               break;
+           ap = (struct wordlist *)
+               malloc(sizeof(struct wordlist) + strlen(word) + 1);
+           if (ap == NULL)
+               novm("authorized addresses");
+           ap->word = (char *) (ap + 1);
+           strcpy(ap->word, word);
+           *app = ap;
+           app = &ap->next;
+       }
+       *app = NULL;
+       /*
+        * This is the best so far; remember it.
+        */
+       best_flag = got_flag;
+       if (addr_list)
+           free_wordlist(addr_list);
+       addr_list = alist;
+
+       if (!newline)
+           break;
+    }
+
+    /* scan for a -- word indicating the start of options */
+    for (app = &addr_list; (ap = *app) != NULL; app = &ap->next)
+       if (strcmp(ap->word, "--") == 0)
+           break;
+    /* ap = start of options */
+    if (ap != NULL) {
+       ap = ap->next;          /* first option */
+       free(*app);             /* free the "--" word */
+       *app = NULL;            /* terminate addr list */
+    }
+    if (opts != NULL)
+       *opts = ap;
+    else if (ap != NULL)
+       free_wordlist(ap);
+    if (addrs != NULL)
+       *addrs = addr_list;
+    else if (addr_list != NULL)
+       free_wordlist(addr_list);
+
+    return best_flag;
+}
+
+
+int
+get_eaptls_secret(unit, client, server, clicertfile, servcertfile,
+                 cacertfile, capath, pkfile, am_server)
+    int unit;
+    char *client;
+    char *server;
+    char *clicertfile;
+    char *servcertfile;
+    char *cacertfile;
+    char *capath;
+    char *pkfile;
+    int am_server;
+{
+    FILE *fp;
+    int ret;
+    char *filename         = NULL;
+    struct wordlist *addrs = NULL;
+    struct wordlist *opts  = NULL;
+
+       /* maybe overkill, but it eases debugging */
+       bzero(clicertfile, MAXWORDLEN);
+       bzero(servcertfile, MAXWORDLEN);
+       bzero(cacertfile, MAXWORDLEN);
+       bzero(capath, MAXWORDLEN);
+       bzero(pkfile, MAXWORDLEN);
+
+       /* the ca+cert+privkey can also be specified as options */
+       if (!am_server && (cacert_file || ca_path) && cert_file && privkey_file )
+       {
+               strlcpy( clicertfile, cert_file, MAXWORDLEN );
+               if (cacert_file)
+                       strlcpy( cacertfile, cacert_file, MAXWORDLEN );
+               if (ca_path)
+                       strlcpy( capath, ca_path, MAXWORDLEN );
+               strlcpy( pkfile, privkey_file, MAXWORDLEN );
+       }
+       else
+       {
+               filename = (am_server ? _PATH_EAPTLSSERVFILE : _PATH_EAPTLSCLIFILE);
+               addrs = NULL;
+
+               fp = fopen(filename, "r");
+               if (fp == NULL)
+               {
+                       error("Can't open eap-tls secret file %s: %m", filename);
+                       return 0;
+               }
+
+               check_access(fp, filename);
+
+               ret = scan_authfile_eaptls(fp, client, server, clicertfile, servcertfile,
+                               cacertfile, pkfile, &addrs, &opts, filename, 0);
+
+               fclose(fp);
+
+               if (ret < 0) return 0;
+       }
+
+    if (eaptls_passwd_hook)
+    {
+               dbglog( "Calling eaptls password hook" );
+               if ( (*eaptls_passwd_hook)(pkfile, passwd) < 0)
+               {
+                        error("Unable to obtain EAP-TLS password for %s (%s) from plugin",
+                               client, pkfile);
+                   return 0;
+               }
+       }
+    if (am_server)
+               set_allowed_addrs(unit, addrs, opts);
+    else if (opts != NULL)
+               free_wordlist(opts);
+    if (addrs != NULL)
+               free_wordlist(addrs);
+
+    return 1;
+}
+#endif
index 61947d96992c55872874253a5a8219372b66c59b..dbc567e3c037b608b62a44039f1a66b547dd0dd5 100644 (file)
@@ -539,6 +539,9 @@ ccp_resetci(f)
     if (go->mppe) {
        ccp_options *ao = &ccp_allowoptions[f->unit];
        int auth_mschap_bits = auth_done[f->unit];
+#ifdef USE_EAPTLS
+       int auth_eap_bits = auth_done[f->unit];
+#endif
        int numbits;
 
        /*
@@ -566,8 +569,23 @@ ccp_resetci(f)
            lcp_close(f->unit, "MPPE required but not available");
            return;
        }
+
+#ifdef USE_EAPTLS
+    /*
+     * MPPE is also possible in combination with EAP-TLS.
+     * It is not possible to detect if we're doing EAP or EAP-TLS
+     * at this stage, hence we accept all forms of EAP. If TLS is
+     * not used then the MPPE keys will not be derived anyway.
+     */
+       /* Leave only the eap auth bits set */
+       auth_eap_bits &= (EAP_WITHPEER | EAP_PEER );
+
+       if ((numbits == 0) && (auth_eap_bits == 0)) {
+           error("MPPE required, but MS-CHAP[v2] nor EAP-TLS auth are performed.");
+#else
        if (!numbits) {
            error("MPPE required, but MS-CHAP[v2] auth not performed.");
+#endif
            lcp_close(f->unit, "MPPE required but not available");
            return;
        }
diff --git a/pppd/eap-tls.c b/pppd/eap-tls.c
new file mode 100644 (file)
index 0000000..3d8fdc7
--- /dev/null
@@ -0,0 +1,1427 @@
+/* * eap-tls.c - EAP-TLS implementation for PPP
+ *
+ * Copyright (c) Beniamino Galvani 2005 All rights reserved.
+ *               Jan Just Keijser  2006-2019 All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <openssl/conf.h>
+#include <openssl/engine.h>
+#include <openssl/hmac.h>
+#include <openssl/err.h>
+#include <openssl/ui.h>
+#include <openssl/x509v3.h>
+
+#include "pppd.h"
+#include "eap.h"
+#include "eap-tls.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "pathnames.h"
+
+typedef struct pw_cb_data
+{
+    const void *password;
+    const char *prompt_info;
+} PW_CB_DATA;
+
+/* The openssl configuration file and engines can be loaded only once */
+static CONF   *ssl_config  = NULL;
+static ENGINE *cert_engine = NULL;
+static ENGINE *pkey_engine = NULL;
+
+/* 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); 
+
+#ifdef MPPE
+
+#define EAPTLS_MPPE_KEY_LEN     32
+
+/*
+ *  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 */
+
+
+/*
+ *  Generate keys according to RFC 2716 and add to reply
+ */
+void eaptls_gen_mppe_keys(struct eaptls_session *ets, int client)
+{
+    unsigned char  out[4*EAPTLS_MPPE_KEY_LEN];
+    const char    *prf_label;
+    size_t         prf_size;
+    unsigned char  eap_tls13_context[] = { EAPT_TLS };
+    unsigned char *context = NULL;
+    size_t         context_len = 0;
+    unsigned char *p;
+
+    dbglog("EAP-TLS generating MPPE keys");
+    if (ets->tls_v13)
+    {
+        prf_label = "EXPORTER_EAP_TLS_Key_Material";
+        context   = eap_tls13_context;
+        context_len = 1;
+    }
+    else
+    {
+        prf_label = "client EAP encryption";
+    }
+
+    dbglog("EAP-TLS PRF label = %s", prf_label);
+    prf_size = strlen(prf_label);
+    if (SSL_export_keying_material(ets->ssl, out, sizeof(out), prf_label, prf_size, 
+                                   context, context_len, 0) != 1)
+    {
+        warn( "EAP-TLS: Failed generating keying material" );
+        return;
+    }   
+
+    /* 
+     * We now have the master send and receive keys.
+     * From these, generate the session send and receive keys.
+     * (see RFC3079 / draft-ietf-pppext-mppe-keys-03.txt for details)
+     */
+    if (client)
+    {
+        p = out;
+        BCOPY( p, mppe_send_key, sizeof(mppe_send_key) );
+        p += EAPTLS_MPPE_KEY_LEN;
+        BCOPY( p, mppe_recv_key, sizeof(mppe_recv_key) );
+    }
+    else
+    {
+        p = out;
+        BCOPY( p, mppe_recv_key, sizeof(mppe_recv_key) );
+        p += EAPTLS_MPPE_KEY_LEN;
+        BCOPY( p, mppe_send_key, sizeof(mppe_send_key) );
+    }
+
+    mppe_keys_set = 1;
+}
+
+#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)
+    {
+        strlcpy (buf, passwd, size);
+        return strlen (buf);
+    }
+    return 0;
+}
+
+
+CONF *eaptls_ssl_load_config( void )
+{
+    CONF        *config;
+    int          ret_code;
+    long         error_line = 33;
+
+    config = NCONF_new( NULL );
+    dbglog( "Loading OpenSSL config file" );
+    ret_code = NCONF_load( config, _PATH_OPENSSLCONFFILE, &error_line );
+    if (ret_code == 0)
+    {
+        warn( "EAP-TLS: Error in OpenSSL config file %s at line %d", _PATH_OPENSSLCONFFILE, error_line );
+        NCONF_free( config );
+        config = NULL;
+        ERR_clear_error();
+    }
+
+    dbglog( "Loading OpenSSL built-ins" );
+    ENGINE_load_builtin_engines();
+    OPENSSL_load_builtin_modules();
+   
+    dbglog( "Loading OpenSSL configured modules" );
+    if (CONF_modules_load( config, NULL, 0 ) <= 0 )
+    {
+        warn( "EAP-TLS: Error loading OpenSSL modules" );
+        log_ssl_errors();
+        config = NULL;
+    }
+
+    return config;
+}
+
+ENGINE *eaptls_ssl_load_engine( char *engine_name )
+{
+    ENGINE      *e = NULL;
+
+    dbglog( "Enabling OpenSSL auto engines" );
+    ENGINE_register_all_complete();
+
+    dbglog( "Loading OpenSSL '%s' engine support", engine_name );
+    e = ENGINE_by_id( engine_name );
+    if (!e) 
+    {
+        dbglog( "EAP-TLS: Cannot load '%s' engine support, trying 'dynamic'", engine_name );
+        e = ENGINE_by_id( "dynamic" );
+        if (e)
+        {
+            if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine_name, 0)
+             || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0))
+            {
+                warn( "EAP-TLS: Error loading dynamic engine '%s'", engine_name );
+                log_ssl_errors();
+                ENGINE_free(e);
+                e = NULL;
+            }
+        }
+        else
+        {
+            warn( "EAP-TLS: Cannot load dynamic engine support" );
+        }
+    }
+
+    if (e)
+    {
+        dbglog( "Initialising engine" );
+        if(!ENGINE_set_default(e, ENGINE_METHOD_ALL))
+        {
+            warn( "EAP-TLS: Cannot use that engine" );
+            log_ssl_errors();
+            ENGINE_free(e);
+            e = NULL;
+        }
+    }
+
+    return e;
+}
+
+
+
+/*
+ * Initialize the SSL stacks and tests if certificates, key and crl
+ * for client or server use can be loaded.
+ */
+SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
+            char *certfile, char *peer_certfile, char *privkeyfile)
+{
+    char        *cert_engine_name = NULL;
+    char        *cert_identifier = NULL;
+    char        *pkey_engine_name = NULL;
+    char        *pkey_identifier = NULL;
+    SSL_CTX     *ctx;
+    SSL         *ssl;
+    X509_STORE  *certstore;
+    X509_LOOKUP *lookup;
+    X509        *tmp;
+    int          ret;
+#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 
+     */
+    if (!(cacertfile[0] || capath[0]))
+    {
+        error("EAP-TLS: CA certificate file or path missing");
+        return NULL;
+    }
+
+    if (!certfile[0])
+    {
+        error("EAP-TLS: Certificate missing");
+        return NULL;
+    }
+
+    if (!privkeyfile[0])
+    {
+        error("EAP-TLS: Private key missing");
+        return NULL;
+    }
+
+    SSL_library_init();
+    SSL_load_error_strings();
+
+    /* load the openssl config file only once and load it before triggering
+       the loading of a global openssl config file via SSL_CTX_new()
+     */
+    if (!ssl_config)
+        ssl_config = eaptls_ssl_load_config();
+
+    ctx = SSL_CTX_new(TLS_method());
+
+    if (!ctx) {
+        error("EAP-TLS: Cannot initialize SSL CTX context");
+        goto fail;
+    }
+
+    /* if the certificate filename is of the form engine:id. e.g.
+        pkcs11:12345
+       then we try to load and use this engine.
+       If the certificate filename starts with a / or . then we
+       ALWAYS assume it is a file and not an engine/pkcs11 identifier
+     */
+    if ( index( certfile, '/' ) == NULL && index( certfile, '.') == NULL )
+    {
+        cert_identifier = index( certfile, ':' );
+
+        if (cert_identifier)
+        {
+            cert_engine_name = certfile;
+            *cert_identifier = '\0';
+            cert_identifier++;
+
+            dbglog( "Found certificate engine '%s'", cert_engine_name );
+            dbglog( "Found certificate identifier '%s'", cert_identifier );
+        }
+    }
+
+    /* if the privatekey filename is of the form engine:id. e.g.
+        pkcs11:12345
+       then we try to load and use this engine.
+       If the privatekey filename starts with a / or . then we
+       ALWAYS assume it is a file and not an engine/pkcs11 identifier
+     */
+    if ( index( privkeyfile, '/' ) == NULL && index( privkeyfile, '.') == NULL )
+    {
+        pkey_identifier = index( privkeyfile, ':' );
+
+        if (pkey_identifier)
+        {
+            pkey_engine_name = privkeyfile;
+            *pkey_identifier = '\0';
+            pkey_identifier++;
+
+            dbglog( "Found privatekey engine '%s'", pkey_engine_name );
+            dbglog( "Found privatekey identifier '%s'", pkey_identifier );
+        }
+    }
+
+    if (cert_identifier && pkey_identifier)
+    {
+        if (strlen( cert_identifier ) == 0)
+        {
+            if (strlen( pkey_identifier ) == 0)
+                error( "EAP-TLS: both the certificate and privatekey identifiers are missing!" );
+            else
+            {
+                dbglog( "Substituting privatekey identifier for certificate identifier" );
+                cert_identifier = pkey_identifier;
+            }
+        }
+        else
+        {
+            if (strlen( pkey_identifier ) == 0)
+            {
+                dbglog( "Substituting certificate identifier for privatekey identifier" );
+                pkey_identifier = cert_identifier;
+            }
+        }
+    }
+
+    if (ssl_config && cert_engine_name)
+        cert_engine = eaptls_ssl_load_engine( cert_engine_name );
+
+    if (ssl_config && pkey_engine_name)
+    {
+        /* don't load the same engine twice */
+        if ( cert_engine && strcmp( cert_engine_name, pkey_engine_name) == 0 )
+            pkey_engine = cert_engine;
+        else
+            pkey_engine = eaptls_ssl_load_engine( pkey_engine_name );
+    }
+
+    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);
+        goto fail;
+    }
+
+    if (init_server)
+        SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(cacertfile));
+
+    if (cert_engine)
+    {
+        struct
+        {
+            const char *s_slot_cert_id;
+            X509 *cert;
+        } cert_info;
+
+        cert_info.s_slot_cert_id = cert_identifier;
+        cert_info.cert = NULL;
+        
+        if (!ENGINE_ctrl_cmd( cert_engine, "LOAD_CERT_CTRL", 0, &cert_info, NULL, 0 ) )
+        {
+            error( "EAP-TLS: Error loading certificate with id '%s' from engine", cert_identifier );
+            goto fail;
+        }
+
+        if (cert_info.cert)
+        {
+            dbglog( "Got the certificate, adding it to SSL context" );
+            dbglog( "subject = %s", X509_NAME_oneline( X509_get_subject_name( cert_info.cert ), NULL, 0 ) );
+            if (SSL_CTX_use_certificate(ctx, cert_info.cert) <= 0)
+            {
+                error("EAP-TLS: Cannot use PKCS11 certificate %s", cert_identifier);
+                goto fail;
+            }
+        }
+        else
+        {
+            warn("EAP-TLS: Cannot load PKCS11 key %s", cert_identifier);
+            log_ssl_errors();
+        }
+    }
+    else
+    {
+        if (!SSL_CTX_use_certificate_chain_file(ctx, certfile))
+        {
+            error( "EAP-TLS: Cannot use public certificate %s", certfile );
+            goto fail;
+        }
+    }
+
+
+    /*
+     *  Check the Before and After dates of the certificate
+     */
+    ssl = SSL_new(ctx);
+    tmp = SSL_get_certificate(ssl);
+
+    ret = X509_cmp_time(X509_get_notBefore(tmp), NULL);
+    if (ret == 0)
+    {    
+        warn( "EAP-TLS: Failed to read certificate notBefore field.");
+    }    
+    if (ret > 0) 
+    {    
+        warn( "EAP-TLS: Your certificate is not yet valid!");
+    }    
+
+    ret = X509_cmp_time(X509_get_notAfter(tmp), NULL);
+    if (ret == 0)
+    {    
+        warn( "EAP-TLS: Failed to read certificate notAfter field.");
+    }    
+    if (ret < 0)
+    {
+        warn( "EAP-TLS: Your certificate has expired!");
+    }
+    SSL_free(ssl);
+
+    if (pkey_engine)
+    {
+        EVP_PKEY   *pkey = NULL;
+        PW_CB_DATA  cb_data;
+        UI_METHOD* transfer_pin = NULL;
+
+        cb_data.password = passwd;
+        cb_data.prompt_info = pkey_identifier;
+
+        if (passwd[0] != 0)
+        {
+            UI_METHOD* transfer_pin = UI_create_method("transfer_pin");
+
+            int writer (UI *ui, UI_STRING *uis)
+            {
+                PW_CB_DATA* cb_data = (PW_CB_DATA*)UI_get0_user_data(ui);
+                UI_set_result(ui, uis, cb_data->password);
+                return 1;
+            };
+            int stub (UI* ui) {return 1;};
+            int stub_reader (UI *ui, UI_STRING *uis) {return 1;};
+
+            UI_method_set_writer(transfer_pin,  writer);
+            UI_method_set_opener(transfer_pin,  stub);
+            UI_method_set_closer(transfer_pin,  stub);
+            UI_method_set_flusher(transfer_pin, stub);
+            UI_method_set_reader(transfer_pin,  stub_reader);
+
+            dbglog( "Using our private key '%s' in engine", pkey_identifier );
+            pkey = ENGINE_load_private_key(pkey_engine, pkey_identifier, transfer_pin, &cb_data);
+        }
+        else {
+            dbglog( "Loading private key '%s' from engine", pkey_identifier );
+            pkey = ENGINE_load_private_key(pkey_engine, pkey_identifier, NULL, NULL);
+        }
+        if (pkey)
+        {
+            dbglog( "Got the private key, adding it to SSL context" );
+            if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0)
+            {
+                error("EAP-TLS: Cannot use PKCS11 key %s", pkey_identifier);
+                goto fail;
+            }
+        }
+        else
+        {
+            warn("EAP-TLS: Cannot load PKCS11 key %s", pkey_identifier);
+            log_ssl_errors();
+        }
+
+        if (transfer_pin) UI_destroy_method(transfer_pin);
+    }
+    else
+    {
+        if (!SSL_CTX_use_PrivateKey_file(ctx, privkeyfile, SSL_FILETYPE_PEM))
+        { 
+            error("EAP-TLS: Cannot use private key %s", privkeyfile);
+            goto fail;
+        }
+    }
+
+    if (SSL_CTX_check_private_key(ctx) != 1) {
+        error("EAP-TLS: Private key %s fails security check", privkeyfile);
+        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");
+     */
+
+
+    /* Set up a SSL Session cache with a callback. This is needed for TLSv1.3+.
+     * During the initial handshake the server signals to the client early on
+     * that the handshake is finished, even before the client has sent its
+     * credentials to the server. The actual connection (and moment that the
+     * client sends its credentials) only starts after the arrival of the first
+     * session ticket. The 'ssl_new_session_cb' catches this ticket.
+     */
+    SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE);
+    SSL_CTX_sess_set_new_cb(ctx, ssl_new_session_cb);
+
+    /* 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);
+
+    }
+
+    /*
+     * 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);
+    }
+
+    return ctx;
+
+fail:
+    log_ssl_errors();
+    SSL_CTX_free(ctx);
+    return NULL;
+}
+
+/*
+ * Determine the maximum packet size by looking at the LCP handshake
+ */
+
+int eaptls_get_mtu(int unit)
+{
+    int mtu, mru;
+
+    lcp_options *wo = &lcp_wantoptions[unit];
+    lcp_options *go = &lcp_gotoptions[unit];
+    lcp_options *ho = &lcp_hisoptions[unit];
+    lcp_options *ao = &lcp_allowoptions[unit];
+
+    mtu = ho->neg_mru? ho->mru: PPP_MRU;
+    mru = go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU;
+    mtu = MIN(MIN(mtu, mru), ao->mru)- PPP_HDRLEN - 10;
+
+    dbglog("MTU = %d", mtu);
+    return mtu;
+}
+
+
+/*
+ * Init the ssl handshake (server mode)
+ */
+int eaptls_init_ssl_server(eap_state * esp)
+{
+    struct eaptls_session *ets;
+    char servcertfile[MAXWORDLEN];
+    char clicertfile[MAXWORDLEN];
+    char cacertfile[MAXWORDLEN];
+    char capath[MAXWORDLEN];
+    char pkfile[MAXWORDLEN];
+    /*
+     * Allocate new eaptls session 
+     */
+    esp->es_server.ea_session = malloc(sizeof(struct eaptls_session));
+    if (!esp->es_server.ea_session)
+        fatal("Allocation error");
+    ets = esp->es_server.ea_session;
+
+    if (!esp->es_server.ea_peer) {
+        error("EAP-TLS: Error: client name not set (BUG)");
+        return 0;
+    }
+
+    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,
+                   servcertfile, cacertfile, capath, pkfile, 1)) {
+        error( "EAP-TLS: Cannot get secret/password for client \"%s\", server \"%s\"",
+                esp->es_server.ea_peer, esp->es_server.ea_name );
+        return 0;
+    }
+
+    ets->mtu = eaptls_get_mtu(esp->es_unit);
+
+    ets->ctx = eaptls_init_ssl(1, cacertfile, capath, servcertfile, clicertfile, pkfile);
+    if (!ets->ctx)
+        goto fail;
+
+    if (!(ets->ssl = SSL_new(ets->ctx)))
+        goto fail;
+
+    /*
+     * Set auto-retry to avoid timeouts on BIO_read
+     */
+    SSL_set_mode(ets->ssl, SSL_MODE_AUTO_RETRY);
+
+    /*
+     * Initialize the BIOs we use to read/write to ssl engine 
+     */
+    ets->into_ssl = BIO_new(BIO_s_mem());
+    ets->from_ssl = BIO_new(BIO_s_mem());
+    SSL_set_bio(ets->ssl, ets->into_ssl, ets->from_ssl);
+
+    SSL_set_msg_callback(ets->ssl, ssl_msg_callback);
+    SSL_set_msg_callback_arg(ets->ssl, ets);
+
+    /*
+     * Attach the session struct to the connection, so we can later
+     * retrieve it when doing certificate verification
+     */
+    SSL_set_ex_data(ets->ssl, 0, ets);
+
+    SSL_set_accept_state(ets->ssl);
+
+    ets->tls_v13 = 0;
+
+    ets->data = NULL;
+    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:
+    SSL_CTX_free(ets->ctx);
+    return 0;
+}
+
+/*
+ * Init the ssl handshake (client mode)
+ */
+int eaptls_init_ssl_client(eap_state * esp)
+{
+    struct eaptls_session *ets;
+    char servcertfile[MAXWORDLEN];
+    char clicertfile[MAXWORDLEN];
+    char cacertfile[MAXWORDLEN];
+    char capath[MAXWORDLEN];
+    char pkfile[MAXWORDLEN];
+
+    /*
+     * Allocate new eaptls session 
+     */
+    esp->es_client.ea_session = malloc(sizeof(struct eaptls_session));
+    if (!esp->es_client.ea_session)
+        fatal("Allocation error");
+    ets = esp->es_client.ea_session;
+
+    /*
+     * 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,
+                   servcertfile, cacertfile, capath, pkfile, 0)) {
+        error( "EAP-TLS: Cannot get secret/password for client \"%s\", server \"%s\"",
+                esp->es_client.ea_name, ets->peer );
+        return 0;
+    }
+
+    dbglog( "calling eaptls_init_ssl" );
+    ets->ctx = eaptls_init_ssl(0, cacertfile, capath, clicertfile, servcertfile, pkfile);
+    if (!ets->ctx)
+        goto fail;
+
+    ets->ssl = SSL_new(ets->ctx);
+
+    if (!ets->ssl)
+        goto fail;
+
+    /*
+     * Initialize the BIOs we use to read/write to ssl engine 
+     */
+    dbglog( "Initializing SSL BIOs" );
+    ets->into_ssl = BIO_new(BIO_s_mem());
+    ets->from_ssl = BIO_new(BIO_s_mem());
+    SSL_set_bio(ets->ssl, ets->into_ssl, ets->from_ssl);
+
+    SSL_set_msg_callback(ets->ssl, ssl_msg_callback);
+    SSL_set_msg_callback_arg(ets->ssl, ets);
+
+    /*
+     * Attach the session struct to the connection, so we can later
+     * retrieve it when doing certificate verification
+     */
+    SSL_set_ex_data(ets->ssl, 0, ets);
+
+    SSL_set_connect_state(ets->ssl);
+
+    ets->tls_v13 = 0;
+
+    ets->data = NULL;
+    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:
+    dbglog( "eaptls_init_ssl_client: fail" );
+    SSL_CTX_free(ets->ctx);
+    return 0;
+
+}
+
+void eaptls_free_session(struct eaptls_session *ets)
+{
+    if (ets->ssl)
+        SSL_free(ets->ssl);
+
+    if (ets->ctx)
+        SSL_CTX_free(ets->ctx);
+
+    free(ets);
+}
+
+
+int eaptls_is_init_finished(struct eaptls_session *ets)
+{
+    if (ets->ssl && SSL_is_init_finished(ets->ssl))
+    {
+        if (ets->tls_v13) 
+            return have_session_ticket;
+        else
+            return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Handle a received packet, reassembling fragmented messages and
+ * passing them to the ssl engine
+ */
+int eaptls_receive(struct eaptls_session *ets, u_char * inp, int len)
+{
+    u_char flags;
+    u_int tlslen = 0;
+    u_char dummy[65536];
+
+    if (len < 1) {
+        warn("EAP-TLS: received no or invalid data");
+        return 1;
+    }
+        
+    GETCHAR(flags, inp);
+    len--;
+
+    if (flags & EAP_TLS_FLAGS_LI && len > 4) {
+        /*
+         * LenghtIncluded flag set -> this is the first packet of a message
+        */
+
+        /*
+         * the first 4 octets are the length of the EAP-TLS message
+         */
+        GETLONG(tlslen, inp);
+        len -= 4;
+
+        if (!ets->data) {
+
+            if (tlslen > EAP_TLS_MAX_LEN) {
+                error("EAP-TLS: TLS message length > %d, truncated", EAP_TLS_MAX_LEN);
+                tlslen = EAP_TLS_MAX_LEN;
+            }
+
+            /*
+             * Allocate memory for the whole message
+            */
+            ets->data = malloc(tlslen);
+            if (!ets->data)
+                fatal("EAP-TLS: allocation error\n");
+
+            ets->datalen = 0;
+            ets->tlslen = tlslen;
+        }
+        else
+            warn("EAP-TLS: non-first LI packet? that's odd...");
+    }
+    else if (!ets->data) {
+        /*
+         * A non fragmented message without LI flag
+        */
+        ets->data = malloc(len);
+        if (!ets->data)
+            fatal("EAP-TLS: allocation error\n");
+        ets->datalen = 0;
+        ets->tlslen = len;
+    }
+
+    if (flags & EAP_TLS_FLAGS_MF)
+        ets->frag = 1;
+    else
+        ets->frag = 0;
+
+    if (len < 0) {
+        warn("EAP-TLS: received malformed data");
+        return 1;
+    }
+
+    if (len + ets->datalen > ets->tlslen) {
+        warn("EAP-TLS: received data > TLS message length");
+        return 1;
+    }
+
+    BCOPY(inp, ets->data + ets->datalen, len);
+    ets->datalen += len;
+
+    if (!ets->frag) {
+
+        /*
+         * If we have the whole message, pass it to ssl 
+         */
+
+        if (ets->datalen != ets->tlslen) {
+            warn("EAP-TLS: received data != TLS message length");
+            return 1;
+        }
+
+        if (BIO_write(ets->into_ssl, ets->data, ets->datalen) == -1)
+            log_ssl_errors();
+
+        SSL_read(ets->ssl, dummy, 65536);
+
+        free(ets->data);
+        ets->data = NULL;
+        ets->datalen = 0;
+    }
+
+    return 0;
+}
+
+/*
+ * Return an eap-tls packet in outp.
+ * A TLS message read from the ssl engine is buffered in ets->data.
+ * At each call we control if there is buffered data and send a 
+ * packet of mtu bytes.
+ */
+int eaptls_send(struct eaptls_session *ets, u_char ** outp)
+{
+    bool first = 0;
+    int size;
+    u_char fromtls[65536];
+    int res;
+    u_char *start;
+
+    start = *outp;
+
+    if (!ets->data)
+    {
+        if(!ets->alert_sent)
+        {
+            res = SSL_read(ets->ssl, fromtls, 65536);
+        }
+
+        /*
+         * Read from ssl 
+         */
+        if ((res = BIO_read(ets->from_ssl, fromtls, 65536)) == -1)
+        {
+            warn("EAP-TLS send: No data from BIO_read");
+            return 1;
+        }
+
+        ets->datalen = res;
+
+        ets->data = malloc(ets->datalen);
+        BCOPY(fromtls, ets->data, ets->datalen);
+
+        ets->offset = 0;
+        first = 1;
+
+    }
+
+    size = ets->datalen - ets->offset;
+    
+    if (size > ets->mtu) {
+        size = ets->mtu;
+        ets->frag = 1;
+    } else
+        ets->frag = 0;
+
+    PUTCHAR(EAPT_TLS, *outp);
+
+    /*
+     * Set right flags and length if necessary 
+     */
+    if (ets->frag && first) {
+        PUTCHAR(EAP_TLS_FLAGS_LI | EAP_TLS_FLAGS_MF, *outp);
+        PUTLONG(ets->datalen, *outp);
+    } else if (ets->frag) {
+        PUTCHAR(EAP_TLS_FLAGS_MF, *outp);
+    } else
+        PUTCHAR(0, *outp);
+
+    /*
+     * Copy the data in outp 
+     */
+    BCOPY(ets->data + ets->offset, *outp, size);
+    INCPTR(size, *outp);
+
+    /*
+     * Copy the packet in retransmission buffer 
+     */
+    BCOPY(start, &ets->rtx[0], *outp - start);
+    ets->rtx_len = *outp - start;
+
+    ets->offset += size;
+
+    if (ets->offset >= ets->datalen) {
+
+        /*
+         * The whole message has been sent 
+         */
+
+        free(ets->data);
+        ets->data = NULL;
+        ets->datalen = 0;
+        ets->offset = 0;
+    }
+
+    return 0;
+}
+
+/*
+ * Get the sent packet from the retransmission buffer
+ */
+void eaptls_retransmit(struct eaptls_session *ets, u_char ** outp)
+{
+    BCOPY(ets->rtx, *outp, ets->rtx_len);
+    INCPTR(ets->rtx_len, *outp);
+}
+
+/*
+ * 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;
+
+    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) 
+    {
+        /* 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);
+
+        /*
+         * If acting as client and the name of the server wasn't specified
+         * explicitely, we can't verify the server authenticity 
+         */
+        if (!ets->peer[0]) {
+            warn("Peer name not specified: no check");
+            return ok;
+        }
+
+        /*
+         * Check the CN 
+         */
+        if (strcmp(cn_str, ets->peer)) {
+            error
+                ("Certificate verification error: CN (%s) != peer_name (%s)",
+                 cn_str, ets->peer);
+            return 0;
+        }
+
+        warn("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
+ * we can print debug information about TLS handshake.
+ */
+void
+ssl_msg_callback(int write_p, int version, int content_type,
+         const void *buf, size_t len, SSL * ssl, void *arg)
+{
+    char string[256];
+    struct eaptls_session *ets = (struct eaptls_session *)arg;
+    unsigned char code;
+    const unsigned char*msg = buf;
+    int hvers = msg[1] << 8 | msg[2];
+
+    if(write_p)
+        strcpy(string, " -> ");
+    else
+        strcpy(string, " <- ");
+
+    switch(content_type) {
+
+    case SSL3_RT_HEADER:
+        strcat(string, "SSL/TLS Header: ");
+        switch(hvers) {
+        case SSL3_VERSION:
+                strcat(string, "SSL 3.0");
+                break;
+        case TLS1_VERSION:
+                strcat(string, "TLS 1.0");
+                break;
+        case TLS1_1_VERSION:
+                strcat(string, "TLS 1.1");
+                break;
+        case TLS1_2_VERSION:
+                strcat(string, "TLS 1.2");
+                break;
+        default:
+            sprintf(string, "SSL/TLS Header: Unknown version (%d)", hvers);
+        }
+        break;
+
+    case SSL3_RT_ALERT:
+        strcat(string, "Alert: ");
+        code = msg[1];
+
+        if (write_p) {
+            ets->alert_sent = 1;
+            ets->alert_sent_desc = code;
+        } else {
+            ets->alert_recv = 1;
+            ets->alert_recv_desc = code;
+        }
+
+        strcat(string, SSL_alert_desc_string_long(code));
+        break;
+
+    case SSL3_RT_CHANGE_CIPHER_SPEC:
+        strcat(string, "ChangeCipherSpec");
+        break;
+
+#ifdef SSL3_RT_INNER_CONTENT_TYPE
+    case SSL3_RT_INNER_CONTENT_TYPE:
+        strcat(string, "InnerContentType (TLS1.3)");
+        break;
+#endif
+
+    case SSL3_RT_HANDSHAKE:
+
+        strcat(string, "Handshake: ");
+        code = msg[0];
+
+        switch(code) {
+            case SSL3_MT_HELLO_REQUEST:
+                strcat(string,"Hello Request");
+                break;
+            case SSL3_MT_CLIENT_HELLO:
+                strcat(string,"Client Hello");
+                break;
+            case SSL3_MT_SERVER_HELLO:
+                strcat(string,"Server Hello");
+                break;
+#ifdef SSL3_MT_NEWSESSION_TICKET
+            case SSL3_MT_NEWSESSION_TICKET:
+                strcat(string,"New Session Ticket");
+                break;
+#endif
+#ifdef SSL3_MT_END_OF_EARLY_DATA
+            case SSL3_MT_END_OF_EARLY_DATA:
+                strcat(string,"End of Early Data");
+                break;
+#endif
+#ifdef SSL3_MT_ENCRYPTED_EXTENSIONS
+            case SSL3_MT_ENCRYPTED_EXTENSIONS:
+                strcat(string,"Encryped Extensions");
+                break;
+#endif
+            case SSL3_MT_CERTIFICATE:
+                strcat(string,"Certificate");
+                break;
+            case SSL3_MT_SERVER_KEY_EXCHANGE:
+                strcat(string,"Server Key Exchange");
+                break;
+            case SSL3_MT_CERTIFICATE_REQUEST:
+                strcat(string,"Certificate Request");
+                break;
+            case SSL3_MT_SERVER_DONE:
+                strcat(string,"Server Hello Done");
+                break;
+            case SSL3_MT_CERTIFICATE_VERIFY:
+                strcat(string,"Certificate Verify");
+                break;
+            case SSL3_MT_CLIENT_KEY_EXCHANGE:
+                strcat(string,"Client Key Exchange");
+                break;
+            case SSL3_MT_FINISHED:
+                strcat(string,"Finished: ");
+                hvers = SSL_version(ssl);
+                switch(hvers){
+                    case SSL3_VERSION:
+                        strcat(string, "SSL 3.0");
+                        break;
+                    case TLS1_VERSION:
+                        strcat(string, "TLS 1.0");
+                        break;
+                    case TLS1_1_VERSION:
+                        strcat(string, "TLS 1.1");
+                        break;
+                    case TLS1_2_VERSION:
+                        strcat(string, "TLS 1.2");
+                        break;
+#ifdef TLS1_3_VERSION
+                    case TLS1_3_VERSION:
+                        strcat(string, "TLS 1.3 (experimental)");
+                        ets->tls_v13 = 1;
+                        break;
+#endif
+                    default:
+                        strcat(string, "Unknown version");
+                }
+                break;
+            default:
+                sprintf( string, "Handshake: Unknown SSL3 code received: %d", code );
+        }
+        break;
+
+    default:
+        sprintf( string, "SSL message contains unknown content type: %d", content_type );
+    }
+
+    /* Alert messages must always be displayed */
+    if(content_type == SSL3_RT_ALERT)
+        error("%s", string);
+    else
+        dbglog("%s", string);
+}
+
+int 
+ssl_new_session_cb(SSL *s, SSL_SESSION *sess)
+{
+    dbglog("EAP-TLS: Post-Handshake New Session Ticket arrived:");
+    have_session_ticket = 1;
+
+    /* always return success */
+    return 1;
+}
+
diff --git a/pppd/eap-tls.c.rej b/pppd/eap-tls.c.rej
new file mode 100644 (file)
index 0000000..fc09719
--- /dev/null
@@ -0,0 +1,35 @@
+--- pppd/eap-tls.c
++++ pppd/eap-tls.c
+@@ -328,11 +328,12 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
+       SSL_library_init();
+       SSL_load_error_strings();
+-      /* load the openssl config file only once */
+-        if (!ssl_config)
+-        {
+-                ssl_config = eaptls_ssl_load_config();
+-      }
++
++      /* load the openssl config file only once and load it before triggering
++         the loading of a global openssl config file via SSL_CTX_new()
++       */
++      if (!ssl_config)
++              ssl_config = eaptls_ssl_load_config();
+       ctx = SSL_CTX_new(TLS_method());
+@@ -403,13 +404,12 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
+                               pkey_identifier = cert_identifier;
+                       }
+               }
+-
+       }
+       if (ssl_config && cert_engine_name)
+               cert_engine = eaptls_ssl_load_engine( cert_engine_name );
+-      if (pkey_engine_name)
++      if (ssl_config && pkey_engine_name)
+       {
+               /* don't load the same engine twice */
+               if ( cert_engine && strcmp( cert_engine_name, pkey_engine_name) == 0 )
diff --git a/pppd/eap-tls.h b/pppd/eap-tls.h
new file mode 100644 (file)
index 0000000..cdbc9e4
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * eap-tls.h
+ *
+ * Copyright (c) Beniamino Galvani 2005 All rights reserved.
+ *               Jan Just Keijser  2006-2019 All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef __EAP_TLS_H__
+#define __EAP_TLS_H__
+
+#include "eap.h"
+
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+
+#define EAP_TLS_FLAGS_LI        128    /* length included flag */
+#define EAP_TLS_FLAGS_MF        64     /* more fragments flag */
+#define EAP_TLS_FLAGS_START     32     /* start flag */
+
+#define EAP_TLS_MAX_LEN         65536  /* max eap tls packet size */
+
+struct eaptls_session
+{
+    u_char *data;               /* buffered data */
+    int datalen;                /* buffered data len */
+    int offset;                 /* from where to send */
+    int tlslen;                 /* total length of tls data */
+    bool frag;                  /* packet is fragmented */
+    bool tls_v13;               /* whether we've negotiated TLSv1.3 */
+    SSL_CTX *ctx;
+    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;
+    bool alert_recv;
+    u_char alert_recv_desc;
+    char rtx[EAP_TLS_MAX_LEN];  /* retransmission buffer */
+    int rtx_len;
+    int mtu;                    /* unit mtu */
+};
+
+
+SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
+            char *certfile, char *peer_certfile, char *privkeyfile);
+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_is_init_finished(struct eaptls_session *ets);
+
+int eaptls_receive(struct eaptls_session *ets, u_char * inp, int len);
+int eaptls_send(struct eaptls_session *ets, u_char ** outp);
+void eaptls_retransmit(struct eaptls_session *ets, u_char ** outp);
+
+int get_eaptls_secret(int unit, char *client, char *server,
+              char *clicertfile, char *servcertfile, char *cacertfile,
+              char *capath, char *pkfile, int am_server);
+
+#ifdef MPPE
+#include "mppe.h"   /* MPPE_MAX_KEY_LEN */
+extern u_char mppe_send_key[MPPE_MAX_KEY_LEN];
+extern u_char mppe_recv_key[MPPE_MAX_KEY_LEN];
+extern int mppe_keys_set;
+
+void eaptls_gen_mppe_keys(struct eaptls_session *ets, int client);
+#endif
+
+#endif
index 082e95343120954abe1de82991387373344e1ba7..aa14208ec644534d27a9d4d8a89c21124c4fb71c 100644 (file)
  * Based on draft-ietf-pppext-eap-srp-03.txt.
  */
 
+/*
+ * Modification by Beniamino Galvani, Mar 2005
+ * Implemented EAP-TLS authentication
+ */
+
 #define RCSID  "$Id: eap.c,v 1.4 2004/11/09 22:39:25 paulus Exp $"
 
 /*
@@ -76,6 +81,9 @@
 #define        SHA_DIGESTSIZE 20
 #endif
 
+#ifdef USE_EAPTLS
+#include "eap-tls.h"
+#endif /* USE_EAPTLS */
 
 eap_state eap_states[NUM_PPP];         /* EAP state; one for each unit */
 #ifdef USE_SRP
@@ -208,6 +216,9 @@ int unit;
        esp->es_server.ea_id = (u_char)(drand48() * 0x100);
        esp->es_client.ea_timeout = EAP_DEFREQTIME;
        esp->es_client.ea_maxrequests = EAP_DEFALLOWREQ;
+#ifdef USE_EAPTLS
+       esp->es_client.ea_using_eaptls = 0;
+#endif /* USE_EAPTLS */
 }
 
 /*
@@ -435,8 +446,16 @@ int status;
        u_char vals[2];
        struct b64state bs;
 #endif /* USE_SRP */
+#ifdef USE_EAPTLS
+       struct eaptls_session *ets;
+       int secret_len;
+       char secret[MAXWORDLEN];
+#endif /* USE_EAPTLS */
 
        esp->es_server.ea_timeout = esp->es_savedtime;
+#ifdef USE_EAPTLS
+       esp->es_server.ea_prev_state = esp->es_server.ea_state;
+#endif /* USE_EAPTLS */
        switch (esp->es_server.ea_state) {
        case eapBadAuth:
                return;
@@ -561,9 +580,81 @@ int status;
                        break;
                }
 #endif /* USE_SRP */
+#ifdef USE_EAPTLS
+                if (!get_secret(esp->es_unit, esp->es_server.ea_peer,
+                    esp->es_server.ea_name, secret, &secret_len, 1)) {
+
+                       esp->es_server.ea_state = eapTlsStart;
+                       break;
+               }
+#endif /* USE_EAPTLS */
+
                esp->es_server.ea_state = eapMD5Chall;
                break;
 
+#ifdef USE_EAPTLS
+       case eapTlsStart:
+               /* Initialize ssl session */
+               if(!eaptls_init_ssl_server(esp)) {
+                       esp->es_server.ea_state = eapBadAuth;
+                       break;
+               }
+
+               esp->es_server.ea_state = eapTlsRecv;
+               break;
+
+       case eapTlsRecv:
+               ets = (struct eaptls_session *) esp->es_server.ea_session;
+
+               if(ets->alert_sent) {
+                       esp->es_server.ea_state = eapTlsSendAlert;
+                       break;
+               }
+
+               if (status) {
+                       esp->es_server.ea_state = eapBadAuth;
+                       break;
+               }
+               ets = (struct eaptls_session *) esp->es_server.ea_session;
+
+               if(ets->frag)
+                       esp->es_server.ea_state = eapTlsSendAck;
+               else
+                       esp->es_server.ea_state = eapTlsSend;
+               break;
+
+       case eapTlsSend:
+               ets = (struct eaptls_session *) esp->es_server.ea_session;
+
+               if(ets->frag)
+                       esp->es_server.ea_state = eapTlsRecvAck;
+               else
+                       if(SSL_is_init_finished(ets->ssl))
+                               esp->es_server.ea_state = eapTlsRecvClient;
+                       else
+                               /* JJK Add "TLS empty record" message here ??? */
+                               esp->es_server.ea_state = eapTlsRecv;
+               break;
+
+       case eapTlsSendAck:
+               esp->es_server.ea_state = eapTlsRecv;
+               break;
+
+       case eapTlsRecvAck:
+               if (status)
+               {
+                       esp->es_server.ea_state = eapBadAuth;
+                       break;
+               }
+
+               esp->es_server.ea_state = eapTlsSend;
+               break;
+
+       case eapTlsSendAlert:
+               esp->es_server.ea_state = eapTlsRecvAlertAck;
+               break;
+#endif /* USE_EAPTLS */
+
        case eapSRP1:
 #ifdef USE_SRP
                ts = (struct t_server *)esp->es_server.ea_session;
@@ -629,6 +720,10 @@ int status;
        }
        if (esp->es_server.ea_state == eapBadAuth)
                eap_send_failure(esp);
+
+#ifdef USE_EAPTLS
+       dbglog("EAP id=0x%2x '%s' -> '%s'", esp->es_server.ea_id, eap_state_name(esp->es_server.ea_prev_state), eap_state_name(esp->es_server.ea_state));
+#endif /* USE_EAPTLS */
 }
 
 /*
@@ -717,6 +812,30 @@ eap_state *esp;
                INCPTR(esp->es_server.ea_namelen, outp);
                break;
 
+#ifdef USE_EAPTLS
+       case eapTlsStart:
+               PUTCHAR(EAPT_TLS, outp);
+               PUTCHAR(EAP_TLS_FLAGS_START, outp);
+               eap_figure_next_state(esp, 0);
+               break;
+
+       case eapTlsSend:
+               eaptls_send(esp->es_server.ea_session, &outp);
+               eap_figure_next_state(esp, 0);
+               break;
+
+       case eapTlsSendAck:
+               PUTCHAR(EAPT_TLS, outp);
+               PUTCHAR(0, outp);
+               eap_figure_next_state(esp, 0);
+               break;
+
+       case eapTlsSendAlert:
+               eaptls_send(esp->es_server.ea_session, &outp);
+               eap_figure_next_state(esp, 0);
+               break;
+#endif /* USE_EAPTLS */
+
 #ifdef USE_SRP
        case eapSRP1:
                PUTCHAR(EAPT_SRP, outp);
@@ -903,11 +1022,57 @@ static void
 eap_server_timeout(arg)
 void *arg;
 {
+#ifdef USE_EAPTLS
+       u_char *outp;
+       u_char *lenloc;
+       int outlen;
+#endif /* USE_EAPTLS */
+
        eap_state *esp = (eap_state *) arg;
 
        if (!eap_server_active(esp))
                return;
 
+#ifdef USE_EAPTLS
+       switch(esp->es_server.ea_prev_state) {
+
+       /*
+        *  In eap-tls the state changes after a request, so we return to
+        *  previous state ...
+        */
+       case(eapTlsStart):
+       case(eapTlsSendAck):
+               esp->es_server.ea_state = esp->es_server.ea_prev_state;
+               break;
+
+       /*
+        *  ... or resend the stored data
+        */
+       case(eapTlsSend):
+       case(eapTlsSendAlert):
+               outp = outpacket_buf;
+               MAKEHEADER(outp, PPP_EAP);
+               PUTCHAR(EAP_REQUEST, outp);
+               PUTCHAR(esp->es_server.ea_id, outp);
+               lenloc = outp;
+               INCPTR(2, outp);
+
+               eaptls_retransmit(esp->es_server.ea_session, &outp);
+
+               outlen = (outp - outpacket_buf) - PPP_HDRLEN;
+               PUTSHORT(outlen, lenloc);
+               output(esp->es_unit, outpacket_buf, outlen + PPP_HDRLEN);
+               esp->es_server.ea_requests++;
+
+               if (esp->es_server.ea_timeout > 0)
+                       TIMEOUT(eap_server_timeout, esp, esp->es_server.ea_timeout);
+
+               return;
+       default:
+               break;
+       }
+#endif /* USE_EAPTLS */
+
        /* EAP ID number must not change on timeout. */
        eap_send_request(esp);
 }
@@ -1165,6 +1330,79 @@ u_char *str;
 }
 #endif /* USE_SRP */
 
+#ifdef USE_EAPTLS
+/*
+ * Send an EAP-TLS response message with tls data
+ */
+static void
+eap_tls_response(esp, id)
+eap_state *esp;
+u_char id;
+{
+       u_char *outp;
+       int outlen;
+       u_char *lenloc;
+
+       outp = outpacket_buf;
+
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+
+       lenloc = outp;
+       INCPTR(2, outp);
+
+       /*
+          If the id in the request is unchanged, we must retransmit
+          the old data
+       */
+       if(id == esp->es_client.ea_id)
+               eaptls_retransmit(esp->es_client.ea_session, &outp);
+       else
+               eaptls_send(esp->es_client.ea_session, &outp);
+
+       outlen = (outp - outpacket_buf) - PPP_HDRLEN;
+       PUTSHORT(outlen, lenloc);
+
+       output(esp->es_unit, outpacket_buf, PPP_HDRLEN + outlen);
+
+       esp->es_client.ea_id = id;
+}
+
+/*
+ * Send an EAP-TLS ack
+ */
+static void
+eap_tls_sendack(esp, id)
+eap_state *esp;
+u_char id;
+{
+       u_char *outp;
+       int outlen;
+       u_char *lenloc;
+
+       outp = outpacket_buf;
+
+       MAKEHEADER(outp, PPP_EAP);
+
+       PUTCHAR(EAP_RESPONSE, outp);
+       PUTCHAR(id, outp);
+       esp->es_client.ea_id = id;
+
+       lenloc = outp;
+       INCPTR(2, outp);
+
+       PUTCHAR(EAPT_TLS, outp);
+       PUTCHAR(0, outp);
+
+       outlen = (outp - outpacket_buf) - PPP_HDRLEN;
+       PUTSHORT(outlen, lenloc);
+
+       output(esp->es_unit, outpacket_buf, PPP_HDRLEN + outlen);
+}
+#endif /* USE_EAPTLS */
+
 static void
 eap_send_nak(esp, id, type)
 eap_state *esp;
@@ -1319,6 +1557,11 @@ int len;
        char rhostname[256];
        MD5_CTX mdContext;
        u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_EAPTLS
+       u_char flags;
+       struct eaptls_session *ets = esp->es_client.ea_session;
+#endif /* USE_EAPTLS */
+
 #ifdef USE_SRP
        struct t_client *tc;
        struct t_num sval, gval, Nval, *Ap, Bval;
@@ -1461,6 +1704,96 @@ int len;
                    esp->es_client.ea_namelen);
                break;
 
+#ifdef USE_EAPTLS
+       case EAPT_TLS:
+
+               switch(esp->es_client.ea_state) {
+
+               case eapListen:
+
+                       if (len < 1) {
+                               error("EAP: received EAP-TLS Listen packet with no data");
+                               /* Bogus request; wait for something real. */
+                               return;
+                       }
+                       GETCHAR(flags, inp);
+                       if(flags & EAP_TLS_FLAGS_START){
+
+                               esp->es_client.ea_using_eaptls = 1;
+
+                               if (explicit_remote){
+                                       esp->es_client.ea_peer = strdup(remote_name);
+                                       esp->es_client.ea_peerlen = strlen(remote_name);
+                               } else
+                                       esp->es_client.ea_peer = NULL;
+
+                               /* Init ssl session */
+                               if(!eaptls_init_ssl_client(esp)) {
+                                       dbglog("cannot init ssl");
+                                       eap_send_nak(esp, id, EAPT_TLS);
+                                       esp->es_client.ea_using_eaptls = 0;
+                                       break;
+                               }
+
+                               ets = esp->es_client.ea_session;
+                               eap_tls_response(esp, id);
+                               esp->es_client.ea_state = (ets->frag ? eapTlsRecvAck : eapTlsRecv);
+                               break;
+                       }
+
+                       /* The server has sent a bad start packet. */
+                       eap_send_nak(esp, id, EAPT_TLS);
+                       break;
+
+               case eapTlsRecvAck:
+                       eap_tls_response(esp, id);
+                       esp->es_client.ea_state = (ets->frag ? eapTlsRecvAck : eapTlsRecv);
+                       break;
+
+               case eapTlsRecv:
+                       if (len < 1) {
+                               error("EAP: discarding EAP-TLS Receive packet with no data");
+                               /* Bogus request; wait for something real. */
+                               return;
+                       }
+                       eaptls_receive(ets, inp, len);
+
+                       if(ets->frag) {
+                               eap_tls_sendack(esp, id);
+                               esp->es_client.ea_state = eapTlsRecv;
+                               break;
+                       }
+
+                       if(ets->alert_recv) {
+                               eap_tls_sendack(esp, id);
+                               esp->es_client.ea_state = eapTlsRecvFailure;
+                               break;
+                       }
+
+                       /* Check if TLS handshake is finished */
+                       if(eaptls_is_init_finished(ets)) {
+#ifdef MPPE
+                               eaptls_gen_mppe_keys(ets, 1);
+#endif
+                               eaptls_free_session(ets);
+                               eap_tls_sendack(esp, id);
+                               esp->es_client.ea_state = eapTlsRecvSuccess;
+                               break;
+                       }
+
+                       eap_tls_response(esp,id);
+                       esp->es_client.ea_state = (ets->frag ? eapTlsRecvAck : eapTlsRecv);
+                       break;
+
+               default:
+                       eap_send_nak(esp, id, EAPT_TLS);
+                       esp->es_client.ea_using_eaptls = 0;
+                       break;
+               }
+
+               break;
+#endif /* USE_EAPTLS */
+
 #ifdef USE_SRP
        case EAPT_SRP:
                if (len < 1) {
@@ -1740,8 +2073,15 @@ int len;
        struct t_num A;
        SHA1_CTX ctxt;
        u_char dig[SHA_DIGESTSIZE];
+       SHA1_CTX ctxt;
+       u_char dig[SHA_DIGESTSIZE];
 #endif /* USE_SRP */
 
+#ifdef USE_EAPTLS
+       struct eaptls_session *ets;
+       u_char flags;
+#endif /* USE_EAPTLS */
+
        /*
         * Ignore responses if we're not open
         */
@@ -1787,6 +2127,64 @@ int len;
                eap_figure_next_state(esp, 0);
                break;
 
+#ifdef USE_EAPTLS
+       case EAPT_TLS:
+               switch(esp->es_server.ea_state) {
+
+               case eapTlsRecv:
+
+                       ets = (struct eaptls_session *) esp->es_server.ea_session;
+
+                       eap_figure_next_state(esp,
+                               eaptls_receive(esp->es_server.ea_session, inp, len));
+
+                       if(ets->alert_recv) {
+                               eap_send_failure(esp);
+                               break;
+                       }
+                       break;
+
+               case eapTlsRecvAck:
+                       if(len > 1) {
+                               dbglog("EAP-TLS ACK with extra data");
+                       }
+                       eap_figure_next_state(esp, 0);
+                       break;
+
+               case eapTlsRecvClient:
+                       /* Receive authentication response from client */
+                       if (len > 0) {
+                               GETCHAR(flags, inp);
+
+                               if(len == 1 && !flags) {        /* Ack = ok */
+#ifdef MPPE
+                                       eaptls_gen_mppe_keys( esp->es_server.ea_session, 0 );
+#endif
+                                       eap_send_success(esp);
+                               }
+                               else {                  /* failure */
+                                       warn("Server authentication failed");
+                                       eap_send_failure(esp);
+                               }
+                       }
+                       else
+                               warn("Bogus EAP-TLS packet received from client");
+
+                       eaptls_free_session(esp->es_server.ea_session);
+
+                       break;
+
+               case eapTlsRecvAlertAck:
+                       eap_send_failure(esp);
+                       break;
+
+               default:
+                       eap_figure_next_state(esp, 1);
+                       break;
+               }
+               break;
+#endif /* USE_EAPTLS */
+
        case EAPT_NOTIFICATION:
                dbglog("EAP unexpected Notification; response discarded");
                break;
@@ -1818,6 +2216,13 @@ int len;
                        esp->es_server.ea_state = eapMD5Chall;
                        break;
 
+#ifdef USE_EAPTLS
+                       /* Send EAP-TLS start packet */
+               case EAPT_TLS:
+                       esp->es_server.ea_state = eapTlsStart;
+                       break;
+#endif /* USE_EAPTLS */
+
                default:
                        dbglog("EAP: peer requesting unknown Type %d", vallen);
                        switch (esp->es_server.ea_state) {
@@ -2029,13 +2434,27 @@ u_char *inp;
 int id;
 int len;
 {
-       if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp)) {
+       if (esp->es_client.ea_state != eapOpen && !eap_client_active(esp)
+#ifdef USE_EAPTLS
+               && esp->es_client.ea_state != eapTlsRecvSuccess
+#endif /* USE_EAPTLS */
+               ) {
                dbglog("EAP unexpected success message in state %s (%d)",
                    eap_state_name(esp->es_client.ea_state),
                    esp->es_client.ea_state);
                return;
        }
 
+#ifdef USE_EAPTLS
+       if(esp->es_client.ea_using_eaptls && esp->es_client.ea_state !=
+               eapTlsRecvSuccess) {
+               dbglog("EAP-TLS unexpected success message in state %s (%d)",
+                    eap_state_name(esp->es_client.ea_state),
+                    esp->es_client.ea_state);
+               return;
+       }
+#endif /* USE_EAPTLS */
+
        if (esp->es_client.ea_timeout > 0) {
                UNTIMEOUT(eap_client_timeout, (void *)esp);
        }
@@ -2167,6 +2586,9 @@ void *arg;
        int code, id, len, rtype, vallen;
        u_char *pstart;
        u_int32_t uval;
+#ifdef USE_EAPTLS
+       u_char flags;
+#endif /* USE_EAPTLS */
 
        if (inlen < EAP_HEADERLEN)
                return (0);
@@ -2231,6 +2653,24 @@ void *arg;
                        }
                        break;
 
+#ifdef USE_EAPTLS
+               case EAPT_TLS:
+                       if (len < 1)
+                               break;
+                       GETCHAR(flags, inp);
+                       len--;
+
+                        if(flags == 0 && len == 0){
+                                printer(arg, " Ack");
+                                break;
+                        }
+
+                       printer(arg, flags & EAP_TLS_FLAGS_LI ? " L":" -");
+                       printer(arg, flags & EAP_TLS_FLAGS_MF ? "M":"-");
+                       printer(arg, flags & EAP_TLS_FLAGS_START ? "S":"- ");
+                       break;
+#endif /* USE_EAPTLS */
+
                case EAPT_SRP:
                        if (len < 3)
                                goto truncated;
@@ -2342,6 +2782,25 @@ void *arg;
                        }
                        break;
 
+#ifdef USE_EAPTLS
+               case EAPT_TLS:
+                       if (len < 1)
+                               break;
+                       GETCHAR(flags, inp);
+                       len--;
+
+                        if(flags == 0 && len == 0){
+                                printer(arg, " Ack");
+                                break;
+                        }
+
+                       printer(arg, flags & EAP_TLS_FLAGS_LI ? " L":" -");
+                       printer(arg, flags & EAP_TLS_FLAGS_MF ? "M":"-");
+                       printer(arg, flags & EAP_TLS_FLAGS_START ? "S":"- ");
+
+                       break;
+#endif /* USE_EAPTLS */
+
                case EAPT_NAK:
                        if (len <= 0) {
                                printer(arg, " <missing hint>");
index 199d1849b8261093ebd0f54a1b100081f27d2f3f..087baad83eed8ca2dd4a2405626165d7e3ca66f7 100644 (file)
@@ -84,6 +84,16 @@ enum eap_state_code {
        eapClosed,      /* Authentication not in use */
        eapListen,      /* Client ready (and timer running) */
        eapIdentify,    /* EAP Identify sent */
+       eapTlsStart,    /* Send EAP-TLS start packet */
+       eapTlsRecv,     /* Receive EAP-TLS tls data */
+       eapTlsSendAck,  /* Send EAP-TLS ack */
+       eapTlsSend,     /* Send EAP-TLS tls data */
+       eapTlsRecvAck,  /* Receive EAP-TLS ack */
+       eapTlsRecvClient,       /* Receive EAP-TLS auth response from client*/
+       eapTlsSendAlert,        /* Send EAP-TLS tls alert (server)*/
+       eapTlsRecvAlertAck,     /* Receive EAP-TLS ack after sending alert */
+       eapTlsRecvSuccess,      /* Receive EAP success */
+       eapTlsRecvFailure,      /* Receive EAP failure */
        eapSRP1,        /* Sent EAP SRP-SHA1 Subtype 1 */
        eapSRP2,        /* Sent EAP SRP-SHA1 Subtype 2 */
        eapSRP3,        /* Sent EAP SRP-SHA1 Subtype 3 */
@@ -95,9 +105,18 @@ enum eap_state_code {
 
 #define        EAP_STATES      \
        "Initial", "Pending", "Closed", "Listen", "Identify", \
+       "TlsStart", "TlsRecv", "TlsSendAck", "TlsSend", "TlsRecvAck", "TlsRecvClient",\
+       "TlsSendAlert", "TlsRecvAlertAck" , "TlsRecvSuccess", "TlsRecvFailure", \
        "SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth"
 
-#define        eap_client_active(esp)  ((esp)->es_client.ea_state == eapListen)
+#ifdef USE_EAPTLS
+#define        eap_client_active(esp)  ((esp)->es_client.ea_state != eapInitial &&\
+                                (esp)->es_client.ea_state != eapPending &&\
+                                (esp)->es_client.ea_state != eapClosed)
+#else
+#define eap_client_active(esp) ((esp)->es_client.ea_state == eapListen)
+#endif /* USE_EAPTLS */
+
 #define        eap_server_active(esp)  \
        ((esp)->es_server.ea_state >= eapIdentify && \
         (esp)->es_server.ea_state <= eapMD5Chall)
@@ -112,11 +131,17 @@ struct eap_auth {
        u_short ea_namelen;     /* Length of our name */
        u_short ea_peerlen;     /* Length of peer's name */
        enum eap_state_code ea_state;
+#ifdef USE_EAPTLS
+       enum eap_state_code ea_prev_state;
+#endif
        u_char ea_id;           /* Current id */
        u_char ea_requests;     /* Number of Requests sent/received */
        u_char ea_responses;    /* Number of Responses */
        u_char ea_type;         /* One of EAPT_* */
        u_int32_t ea_keyflags;  /* SRP shared key usage flags */
+#ifdef USE_EAPTLS
+       bool ea_using_eaptls;
+#endif
 };
 
 /*
@@ -139,7 +164,12 @@ typedef struct eap_state {
  * Timeouts.
  */
 #define        EAP_DEFTIMEOUT          3       /* Timeout (seconds) for rexmit */
+#ifdef USE_EAPTLS
+#define        EAP_DEFTRANSMITS        30      /* max # times to transmit */
+                                       /* certificates can be long ... */
+#else
 #define        EAP_DEFTRANSMITS        10      /* max # times to transmit */
+#endif /* USE_EAPTLS */
 #define        EAP_DEFREQTIME          20      /* Time to wait for peer request */
 #define        EAP_DEFALLOWREQ         20      /* max # times to accept requests */
 
index a33f0466c9d6d7e86e17316e0672477fd31228aa..5c8cfe8aae92d62a9f6c1a5a9108645cc8c67440 100644 (file)
 #define _PATH_UPAPFILE          _ROOT_PATH "/etc/ppp/pap-secrets"
 #define _PATH_CHAPFILE          _ROOT_PATH "/etc/ppp/chap-secrets"
 #define _PATH_SRPFILE   _ROOT_PATH "/etc/ppp/srp-secrets"
+
+#ifdef USE_EAPTLS
+#define _PATH_EAPTLSCLIFILE    _ROOT_PATH "/etc/ppp/eaptls-client"
+#define _PATH_EAPTLSSERVFILE   _ROOT_PATH "/etc/ppp/eaptls-server"
+#define _PATH_OPENSSLCONFFILE  _ROOT_PATH "/etc/ppp/openssl.cnf"
+#endif /* USE_EAPTLS */
+
 #define _PATH_SYSOPTIONS _ROOT_PATH "/etc/ppp/options"
 #define _PATH_IPUP      _ROOT_PATH "/etc/ppp/ip-up"
 #define _PATH_IPDOWN    _ROOT_PATH "/etc/ppp/ip-down"
index af5384381cb07c27fa4043824f80b6ab69807fa9..5a7bd7966215e97f89b774d95645fb37477a7240 100644 (file)
@@ -4,6 +4,9 @@ CFLAGS  = $(COPTS) -I.. -I../../include -fPIC
 LDFLAGS_SHARED = -shared
 INSTALL        = install
 
+# EAP-TLS
+CFLAGS += -DUSE_EAPTLS=1
+
 DESTDIR = $(INSTROOT)@DESTDIR@
 BINDIR = $(DESTDIR)/sbin
 MANDIR = $(DESTDIR)/share/man/man8
index 2091f1361f0c863b5a26c09c77e3f18bdda9b803..ab9f390c2d1c968c8054b60e1e5da7cc81eb5e00 100644 (file)
@@ -113,4 +113,7 @@ void plugin_init(void)
 {
     add_options(options);
     pap_passwd_hook = promptpass;
+#ifdef USE_EAPTLS
+    eaptls_passwd_hook = promptpass;
+#endif
 }
index d718f3bdf81d74fd82d2d221e4b260d69d484040..c3f9793e41a01b141da91d7a5370e04dec51af83 100644 (file)
@@ -79,4 +79,8 @@ void plugin_init (void)
 
     chap_check_hook = pwfd_check;
     chap_passwd_hook = pwfd_passwd;
+
+#ifdef USE_EAPTLS
+    eaptls_passwd_hook = pwfd_passwd;
+#endif
 }
index 1cf0f67db62b181f584903a52eafd8b3aafa45fe..8ad98cb37ce7e7f172c4c9e8de3b1dd515e93323 100644 (file)
@@ -260,6 +260,12 @@ Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables
 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
+(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.
+.TP
 .B cdtrcts
 Use a non-standard hardware flow control (i.e. DTR/CTS) to control
 the flow of data on the serial port.  If neither the \fIcrtscts\fR,
@@ -271,6 +277,12 @@ RTS output. Such serial ports use this mode to implement true
 bi-directional flow control. The sacrifice is that this flow
 control mode does not permit using DTR as a modem control line.
 .TP
+.B cert \fIcertfile
+(EAP-TLS) Use the file \fIcertfile\fR as the X.509 certificate (in PEM
+format), needed for setting up an EAP-TLS connection. This option is
+used on the client-side in conjunction with the \fBca\fR and 
+\fBkey\fR options.
+.TP
 .B chap\-interval \fIn
 If this option is given, pppd will rechallenge the peer every \fIn\fR
 seconds.
@@ -299,6 +311,18 @@ negotiation by sending its first LCP packet.  The default value is
 1000 (1 second).  This wait period only applies if the \fBconnect\fR
 or \fBpty\fR option is used.
 .TP
+.B crl \fIfilename
+(EAP-TLS) 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
+option.
+.TP
+.B crl-dir \fIdirectory
+(EAP-TLS) 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.
+Also see the \fBcrl\fR option.
+.TP
 .B debug
 Enables connection debugging facilities.
 If this option is given, pppd will log the contents of all
@@ -568,6 +592,12 @@ transmitted packets be printed.  On most systems, messages printed by
 the kernel are logged by syslog(1) to a file as directed in the
 /etc/syslog.conf configuration file.
 .TP
+.B key \fIkeyfile
+(EAP-TLS) Use the file \fIkeyfile\fR as the private key file (in PEM
+format), needed for setting up an EAP-TLS connection. This option is
+used on the client-side in conjunction with the \fBca\fR and 
+\fBcert\fR options.
+.TP
 .B ktune
 Enables pppd to alter kernel settings as appropriate.  Under Linux,
 pppd will enable IP forwarding (i.e. set /proc/sys/net/ipv4/ip_forward
@@ -731,6 +761,9 @@ name to \fIname\fR.)
 Disable Address/Control compression in both directions (send and
 receive).
 .TP
+.B need-peer-eap
+(EAP-TLS) Require the peer to verify our authentication credentials.
+.TP
 .B noauth
 Do not require the peer to authenticate itself.  This option is
 privileged.
index f44fe016c82847b6f402a635329aa082eba5d897..a35deaa8e00ac19316ea09c1fd6c1534a142c1a6 100644 (file)
@@ -342,6 +342,12 @@ 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
+extern char    *crl_dir;
+extern char    *crl_file;
+extern char *max_tls_version;
+#endif /* USE_EAPTLS */
+
 #ifdef MAXOCTETS
 extern unsigned int maxoctets;      /* Maximum octetes per session (in bytes) */
 extern int       maxoctets_dir;      /* Direction :
@@ -766,6 +772,10 @@ extern int (*chap_check_hook) __P((void));
 extern int (*chap_passwd_hook) __P((char *user, char *passwd));
 extern void (*multilink_join_hook) __P((void));
 
+#ifdef USE_EAPTLS
+extern int (*eaptls_passwd_hook) __P((char *user, char *passwd));
+#endif
+
 /* Let a plugin snoop sent and received packets.  Useful for L2TP */
 extern void (*snoop_recv_hook) __P((unsigned char *p, int len));
 extern void (*snoop_send_hook) __P((unsigned char *p, int len));
index a85c73301ed2b474ee644f170ff733714811306d..113eb7bdf74c7617e577a41c1dfac9a0e74d8b67 100644 (file)
@@ -1551,6 +1551,26 @@ netif_set_mtu(unit, mtu)
 #endif /* defined(INET6) && defined(SOL2) */
 }
 
+
+
+/*
+ * netif_get_mtu - get the MTU on the PPP network interface.
+ */
+int
+netif_get_mtu(int unit)
+{
+    struct ifreq ifr;
+
+    memset (&ifr, '\0', sizeof (ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+
+    if (ioctl(ipfd, SIOCGIFMTU, (caddr_t) &ifr) < 0) {
+    error("ioctl(SIOCGIFMTU): %m (line %d)", __LINE__);
+    return 0;
+    }
+    return ifr.ifr_mtu;
+}
+
 /*
  * tty_send_config - configure the transmit characteristics of
  * the ppp interface.