From 5749374dc2accc607b1aa2b3a4c6c71c0fb9f5cf Mon Sep 17 00:00:00 2001 From: =?utf8?q?Eivind=20N=C3=A6ss?= Date: Fri, 4 Jun 2021 18:30:16 -0700 Subject: [PATCH] pppd: Support for use of PKCS12 certificates (#264) 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 --- README.eap-tls | 3 + pppd/auth.c | 15 ++++- pppd/eap-tls.c | 165 +++++++++++++++++++++++++++++++++++-------------- pppd/eap-tls.h | 4 +- pppd/pppd.h | 1 + 5 files changed, 139 insertions(+), 49 deletions(-) diff --git a/README.eap-tls b/README.eap-tls index 7895b2b..ad81ab2 100644 --- a/README.eap-tls +++ b/README.eap-tls @@ -134,6 +134,9 @@ EAP-TLS authentication support for PPP key Use the client private key found in in PEM format or in engine:engine_id format + pkcs12 + Use a pkcs12 envelope as a substitute for cert and key. A password may be + required to use this file. crl Use the Certificate Revocation List (CRL) file in PEM format. crl-dir diff --git a/pppd/auth.c b/pppd/auth.c index 6ccdbf8..0a49f53 100644 --- a/pppd/auth.c +++ b/pppd/auth.c @@ -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); diff --git a/pppd/eap-tls.c b/pppd/eap-tls.c index bfcf199..c9730ba 100644 --- a/pppd/eap-tls.c +++ b/pppd/eap-tls.c @@ -42,6 +42,7 @@ #include #include #include +#include #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; diff --git a/pppd/eap-tls.h b/pppd/eap-tls.h index b935ec5..b19a905 100644 --- a/pppd/eap-tls.h +++ b/pppd/eap-tls.h @@ -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); diff --git a/pppd/pppd.h b/pppd/pppd.h index 05e8e37..6a19091 100644 --- a/pppd/pppd.h +++ b/pppd/pppd.h @@ -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; -- 2.39.2