]> git.ozlabs.org Git - ppp.git/commitdiff
pppd: Support for use of PKCS12 certificates (#264)
authorEivind Næss <eivnaes@yahoo.com>
Sat, 5 Jun 2021 01:30:16 +0000 (18:30 -0700)
committerGitHub <noreply@github.com>
Sat, 5 Jun 2021 01:30:16 +0000 (11:30 +1000)
This implements the ability to specify the option 'pkcs12' to allow
users to provide a PKCS12 formatted file as user credentials.

Signed-off-by: Eivind Naess <eivnaes@yahoo.com>
README.eap-tls
pppd/auth.c
pppd/eap-tls.c
pppd/eap-tls.h
pppd/pppd.h

index 7895b2b2eaa15c53f70aeca06f52f7dd9c92d4b6..ad81ab25fbcd443369252b54bd3a74a4b4ca7b92 100644 (file)
@@ -134,6 +134,9 @@ EAP-TLS authentication support for PPP
       key <key-file>
         Use the client private key found in <key-file> in PEM format
         or in engine:engine_id format
+      pkcs12 <pkcs12-file>
+        Use a pkcs12 envelope as a substitute for cert and key. A password may be
+        required to use this file. 
       crl <crl-file>
         Use the Certificate Revocation List (CRL) file <crl-file> in PEM format.
       crl-dir <dir>
index 6ccdbf8dde8a035572734ba23f259c185076f97e..0a49f53e9f3711ef481856b6fbdffeae29ba496c 100644 (file)
@@ -255,6 +255,7 @@ char *cacert_file  = NULL;  /* CA certificate file (pem format) */
 char *ca_path      = NULL;     /* directory with CA certificates */
 char *cert_file    = NULL;     /* client certificate file (pem format) */
 char *privkey_file = NULL;     /* client private key file (pem format) */
+char *pkcs12_file  = NULL;     /* client private key envelope file (pkcs12 format) */
 char *crl_dir      = NULL;     /* directory containing CRL files */
 char *crl_file     = NULL;     /* Certificate Revocation List (CRL) file (pem format) */
 char *max_tls_version = NULL;  /* Maximum TLS protocol version (default=1.2) */
@@ -445,6 +446,7 @@ option_t auth_options[] = {
     { "key", o_string, &privkey_file, "EAP-TLS client private key in PEM format" },
     { "crl-dir", o_string, &crl_dir,  "Use CRLs in directory" },
     { "crl", o_string, &crl_file,     "Use specific CRL file" },
+    { "pkcs12", o_string, &pkcs12_file, "EAP-TLS client credentials in PKCS12 format" },
     { "max-tls-version", o_string, &max_tls_version,
       "Maximum TLS version (1.0/1.1/1.2 (default)/1.3)" },
     { "tls-verify-key-usage", o_bool, &tls_verify_key_usage,
@@ -2464,6 +2466,8 @@ have_eaptls_secret_client(char *client, char *server)
 
        if ((cacert_file || ca_path) && cert_file && privkey_file)
                return 1;
+       if (pkcs12_file)
+               return 1;
 
     filename = _PATH_EAPTLSCLIFILE;
     f = fopen(filename, "r");
@@ -2647,7 +2651,7 @@ scan_authfile_eaptls(FILE *f, char *client, char *server,
 int
 get_eaptls_secret(int unit, char *client, char *server,
                  char *clicertfile, char *servcertfile, char *cacertfile,
-                 char *capath, char *pkfile, int am_server)
+                 char *capath, char *pkfile, char *pkcs12, int am_server)
 {
     FILE *fp;
     int ret;
@@ -2661,6 +2665,7 @@ get_eaptls_secret(int unit, char *client, char *server,
        bzero(cacertfile, MAXWORDLEN);
        bzero(capath, MAXWORDLEN);
        bzero(pkfile, MAXWORDLEN);
+       bzero(pkcs12, MAXWORDLEN);
 
        /* the ca+cert+privkey can also be specified as options */
        if (!am_server && (cacert_file || ca_path) && cert_file && privkey_file )
@@ -2672,6 +2677,14 @@ get_eaptls_secret(int unit, char *client, char *server,
                        strlcpy( capath, ca_path, MAXWORDLEN );
                strlcpy( pkfile, privkey_file, MAXWORDLEN );
        }
+    else if (!am_server && pkcs12_file)
+       {
+               strlcpy( pkcs12, pkcs12_file, MAXWORDLEN );
+               if (cacert_file)
+                       strlcpy( cacertfile, cacert_file, MAXWORDLEN );
+               if (ca_path)
+                       strlcpy( capath, ca_path, MAXWORDLEN );
+       }
        else
        {
                filename = (am_server ? _PATH_EAPTLSSERVFILE : _PATH_EAPTLSCLIFILE);
index bfcf199635d7532990cb3a5153cbc3e6269edcb1..c9730bac34595fbf104464bf106df3f8b503bb20 100644 (file)
@@ -42,6 +42,7 @@
 #include <openssl/err.h>
 #include <openssl/ui.h>
 #include <openssl/x509v3.h>
+#include <openssl/pkcs12.h>
 
 #include "pppd.h"
 #include "eap.h"
@@ -283,7 +284,7 @@ ENGINE *eaptls_ssl_load_engine( char *engine_name )
  * 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 *certfile, char *peer_certfile, char *privkeyfile, char *pkcs12)
 {
 #ifndef OPENSSL_NO_ENGINE
     char        *cert_engine_name = NULL;
@@ -296,7 +297,13 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
     X509_STORE  *certstore;
     X509_LOOKUP *lookup;
     X509        *tmp;
+    X509        *cert = NULL;
+    PKCS12      *p12 = NULL;
+    EVP_PKEY    *pkey = NULL;
+    STACK_OF(X509) *chain = NULL;
+    BIO         *input;
     int          ret;
+    int          reason;
 #if defined(TLS1_2_VERSION)
     long         tls_version = TLS1_2_VERSION; 
 #elif defined(TLS1_1_VERSION)
@@ -308,22 +315,25 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
     /*
      * Without these can't continue 
      */
-    if (!(cacertfile[0] || capath[0]))
+    if (!pkcs12[0]) 
     {
-        error("EAP-TLS: CA certificate file or path missing");
-        return NULL;
-    }
+        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 (!certfile[0])
+        {
+            error("EAP-TLS: Certificate missing");
+            return NULL;
+        }
 
-    if (!privkeyfile[0])
-    {
-        error("EAP-TLS: Private key missing");
-        return NULL;
+        if (!privkeyfile[0])
+        {
+            error("EAP-TLS: Private key missing");
+            return NULL;
+        }
     }
 
     SSL_library_init();
@@ -458,13 +468,9 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
 
         if (cert_info.cert)
         {
-            dbglog( "Got the certificate, adding it to SSL context" );
+            dbglog( "Got the certificate" );
             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;
-            }
+            cert = cert_info.cert;
         }
         else
         {
@@ -475,13 +481,63 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
     else
 #endif
     {
-        if (!SSL_CTX_use_certificate_chain_file(ctx, certfile))
+        if (pkcs12[0])
         {
-            error( "EAP-TLS: Cannot use public certificate %s", certfile );
-            goto fail;
+            input = BIO_new_file(pkcs12, "r");
+            if (input == NULL)
+            {
+                error("EAP-TLS: Cannot open `%s' PKCS12 for input", pkcs12);
+                goto fail;
+            }
+
+            p12 = d2i_PKCS12_bio(input, NULL);
+            BIO_free(input);
+            if (!p12)
+            {
+                error("EAP-TLS: Cannot load PKCS12 certificate");
+                goto fail;
+            }
+
+            if (PKCS12_parse(p12, passwd, &pkey, &cert, &chain) != 1)
+            {
+                error("EAP-TLS: Cannot parse PKCS12 certificate, invalid password");
+                PKCS12_free(p12);
+                goto fail;
+            }
+
+            PKCS12_free(p12);
+        }
+        else 
+        {
+            if (!SSL_CTX_use_certificate_chain_file(ctx, certfile))
+            {
+                error( "EAP-TLS: Cannot load certificate %s", certfile );
+                goto fail;
+            }
         }
     }
 
+    if (cert)
+    {
+        if (!SSL_CTX_use_certificate(ctx, cert))
+        {
+            error("EAP-TLS: Cannot use load certificate");
+            goto fail;
+        }
+
+        if (chain)
+        {
+            int i;
+            for (i = 0; i < sk_X509_num(chain); i++)
+            {
+                if (!SSL_CTX_add_extra_chain_cert(ctx, sk_X509_value(chain, i)))
+                {
+                    error("EAP-TLS: Cannot add extra chain certificate");
+                    goto fail;
+                }
+            }
+        }
+    }
 
     /*
      *  Check the Before and After dates of the certificate
@@ -513,7 +569,6 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
 #ifndef OPENSSL_NO_ENGINE
     if (pkey_engine)
     {
-        EVP_PKEY   *pkey = NULL;
         PW_CB_DATA  cb_data;
 
         cb_data.password = passwd;
@@ -547,33 +602,38 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
             dbglog( "Loading private key '%s' from engine", pkey_identifier );
             pkey = ENGINE_load_private_key(pkey_engine, pkey_identifier, NULL, NULL);
         }
-        if (pkey)
+    }
+    else 
+#endif
+    {
+        if (!pkey)
         {
-            dbglog( "Got the private key, adding it to SSL context" );
-            if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0)
+            input = BIO_new_file(privkeyfile, "r");
+            if (!input)
             {
-                error("EAP-TLS: Cannot use PKCS11 key %s", pkey_identifier);
+                error("EAP-TLS: Could not open private key, %s", privkeyfile);
+                goto fail;
+            }
+
+            pkey = PEM_read_bio_PrivateKey(input, NULL, password_callback, NULL);
+            BIO_free(input);
+            if (!pkey)
+            {
+                error("EAP-TLS: Cannot load private key, %s", privkeyfile);
                 goto fail;
             }
-        }
-        else
-        {
-            warn("EAP-TLS: Cannot load PKCS11 key %s", pkey_identifier);
-            log_ssl_errors();
         }
     }
-    else
-#endif
+
+    if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1)
     {
-        if (!SSL_CTX_use_PrivateKey_file(ctx, privkeyfile, SSL_FILETYPE_PEM))
-        { 
-            error("EAP-TLS: Cannot use private key %s", privkeyfile);
-            goto fail;
-        }
+        error("EAP-TLS: Cannot use private key");
+        goto fail;
     }
 
-    if (SSL_CTX_check_private_key(ctx) != 1) {
-        error("EAP-TLS: Private key %s fails security check", privkeyfile);
+    if (SSL_CTX_check_private_key(ctx) != 1)
+    {
+        error("EAP-TLS: Private key fails security check");
         goto fail;
     }
 
@@ -696,6 +756,16 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
     return ctx;
 
 fail:
+
+    if (cert)
+        X509_free(cert);
+
+    if (pkey)
+        EVP_PKEY_free(pkey);
+
+    if (chain)
+        sk_X509_pop_free(chain, X509_free);
+
     log_ssl_errors();
     SSL_CTX_free(ctx);
     return NULL;
@@ -734,6 +804,8 @@ int eaptls_init_ssl_server(eap_state * esp)
     char cacertfile[MAXWORDLEN];
     char capath[MAXWORDLEN];
     char pkfile[MAXWORDLEN];
+    char pkcs12[MAXWORDLEN];
+
     /*
      * Allocate new eaptls session 
      */
@@ -753,7 +825,7 @@ int eaptls_init_ssl_server(eap_state * esp)
     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)) {
+                   servcertfile, cacertfile, capath, pkfile, pkcs12, 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;
@@ -761,7 +833,7 @@ int eaptls_init_ssl_server(eap_state * esp)
 
     ets->mtu = eaptls_get_mtu(esp->es_unit);
 
-    ets->ctx = eaptls_init_ssl(1, cacertfile, capath, servcertfile, clicertfile, pkfile);
+    ets->ctx = eaptls_init_ssl(1, cacertfile, capath, servcertfile, clicertfile, pkfile, pkcs12);
     if (!ets->ctx)
         goto fail;
 
@@ -825,6 +897,7 @@ int eaptls_init_ssl_client(eap_state * esp)
     char cacertfile[MAXWORDLEN];
     char capath[MAXWORDLEN];
     char pkfile[MAXWORDLEN];
+    char pkcs12[MAXWORDLEN];
 
     /*
      * Allocate new eaptls session 
@@ -849,14 +922,14 @@ int eaptls_init_ssl_client(eap_state * esp)
     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)) {
+                   servcertfile, cacertfile, capath, pkfile, pkcs12, 0)) {
         error( "EAP-TLS: Cannot get secret/password for client \"%s\", server \"%s\"",
                 esp->es_client.ea_name, ets->peer );
         return 0;
     }
 
     dbglog( "calling eaptls_init_ssl" );
-    ets->ctx = eaptls_init_ssl(0, cacertfile, capath, clicertfile, servcertfile, pkfile);
+    ets->ctx = eaptls_init_ssl(0, cacertfile, capath, clicertfile, servcertfile, pkfile, pkcs12);
     if (!ets->ctx)
         goto fail;
 
index b935ec524321d5952979b03299f6ba9b862f69e4..b19a90575db9b0672fb1304e61497956cfbdcf74 100644 (file)
@@ -70,7 +70,7 @@ struct eaptls_session
 
 
 SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath,
-            char *certfile, char *peer_certfile, char *privkeyfile);
+            char *certfile, char *peer_certfile, char *privkeyfile, char *pkcs12);
 int eaptls_init_ssl_server(eap_state * esp);
 int eaptls_init_ssl_client(eap_state * esp);
 void eaptls_free_session(struct eaptls_session *ets);
@@ -83,7 +83,7 @@ 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);
+              char *capath, char *pkfile, char *pkcs12, int am_server);
 
 #ifdef MPPE
 void eaptls_gen_mppe_keys(struct eaptls_session *ets, int client);
index 05e8e37439c78bdce97498eeb19bb246d91aa9d8..6a19091c04c73fa7d2d782565f5706c7261730dd 100644 (file)
@@ -344,6 +344,7 @@ extern int  child_wait;     /* # seconds to wait for children at end */
 
 extern char    *crl_dir;
 extern char    *crl_file;
+extern char *pkcs12_file;
 extern char *max_tls_version;
 extern bool tls_verify_key_usage;
 extern char *tls_verify_method;