X-Git-Url: https://git.ozlabs.org/?a=blobdiff_plain;f=pppd%2Feap-tls.c;h=b15d801565f54807165435a6a09a585f1c5cd665;hb=d625b437597fc8c3f86b93ceca4fbb9224a6313b;hp=5740f308a89ae6c2e645222474a2ba6497cde9e7;hpb=b5599f6001d9b024b3a572ab62c92027d94f052f;p=ppp.git diff --git a/pppd/eap-tls.c b/pppd/eap-tls.c index 5740f30..b15d801 100644 --- a/pppd/eap-tls.c +++ b/pppd/eap-tls.c @@ -29,6 +29,10 @@ * */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include @@ -37,17 +41,22 @@ #include #include +#ifndef OPENSSL_NO_ENGINE #include +#endif #include #include #include #include +#include #include "pppd.h" #include "eap.h" #include "eap-tls.h" #include "fsm.h" #include "lcp.h" +#include "chap_ms.h" +#include "mppe.h" #include "pathnames.h" typedef struct pw_cb_data @@ -56,15 +65,17 @@ typedef struct pw_cb_data const char *prompt_info; } PW_CB_DATA; +#ifndef OPENSSL_NO_ENGINE /* 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; +#endif /* TLSv1.3 do we have a session ticket ? */ static int have_session_ticket = 0; -int ssl_verify_callback(int, X509_STORE_CTX *); +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); @@ -72,10 +83,6 @@ 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 @@ -117,6 +124,8 @@ static inline int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, long tls_ver_max) #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ +#ifdef MPPE +#define EAPTLS_MPPE_KEY_LEN 32 /* * Generate keys according to RFC 2716 and add to reply @@ -159,24 +168,17 @@ void eaptls_gen_mppe_keys(struct eaptls_session *ets, int client) */ 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) ); + mppe_set_keys(out, out + EAPTLS_MPPE_KEY_LEN, EAPTLS_MPPE_KEY_LEN); } 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_set_keys(out + EAPTLS_MPPE_KEY_LEN, out, EAPTLS_MPPE_KEY_LEN); } - - mppe_keys_set = 1; } #endif /* MPPE */ + void log_ssl_errors( void ) { unsigned long ssl_err = ERR_get_error(); @@ -219,7 +221,9 @@ CONF *eaptls_ssl_load_config( void ) } dbglog( "Loading OpenSSL built-ins" ); +#ifndef OPENSSL_NO_ENGINE ENGINE_load_builtin_engines(); +#endif OPENSSL_load_builtin_modules(); dbglog( "Loading OpenSSL configured modules" ); @@ -233,6 +237,7 @@ CONF *eaptls_ssl_load_config( void ) return config; } +#ifndef OPENSSL_NO_ENGINE ENGINE *eaptls_ssl_load_engine( char *engine_name ) { ENGINE *e = NULL; @@ -277,26 +282,50 @@ ENGINE *eaptls_ssl_load_engine( char *engine_name ) return e; } +#endif + + +#ifndef OPENSSL_NO_ENGINE +static int eaptls_UI_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; +} +static int eaptls_UI_stub(UI* ui) { + return 1; +} +static int eaptls_UI_reader(UI *ui, UI_STRING *uis) { + return 1; +} +#endif /* * 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 *certfile, char *peer_certfile, char *privkeyfile, char *pkcs12) { +#ifndef OPENSSL_NO_ENGINE char *cert_engine_name = NULL; - char *cert_identifier = NULL; char *pkey_engine_name = NULL; - char *pkey_identifier = NULL; + char *idx; +#endif SSL_CTX *ctx; SSL *ssl; 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,32 +337,37 @@ 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(); SSL_load_error_strings(); +#ifndef OPENSSL_NO_ENGINE /* 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(); +#endif ctx = SSL_CTX_new(TLS_method()); @@ -342,25 +376,20 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath, goto fail; } +#ifndef OPENSSL_NO_ENGINE /* 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 ) + if ( (idx = index( certfile, ':' )) != NULL ) { - cert_identifier = index( certfile, ':' ); + cert_engine_name = strdup( certfile ); + cert_engine_name[idx - certfile] = 0; - 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 ); - } + dbglog( "Using engine '%s' for certificate, URI: '%s'", + cert_engine_name, certfile ); } /* if the privatekey filename is of the form engine:id. e.g. @@ -369,39 +398,33 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath, 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 ) + if ( (idx = index( privkeyfile, ':' )) != NULL ) { - pkey_identifier = index( privkeyfile, ':' ); + pkey_engine_name = strdup( privkeyfile ); + pkey_engine_name[idx - privkeyfile] = 0; - 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 ); - } + dbglog( "Using engine '%s' for private key, URI: '%s'", + pkey_engine_name, privkeyfile ); } - if (cert_identifier && pkey_identifier) + if (cert_engine_name && pkey_engine_name) { - if (strlen( cert_identifier ) == 0) + if (strlen( certfile ) - strlen( cert_engine_name ) == 1) { - if (strlen( pkey_identifier ) == 0) + if (strlen( privkeyfile ) - strlen( pkey_engine_name ) == 1) error( "EAP-TLS: both the certificate and privatekey identifiers are missing!" ); else { dbglog( "Substituting privatekey identifier for certificate identifier" ); - cert_identifier = pkey_identifier; + certfile = privkeyfile; } } else { - if (strlen( pkey_identifier ) == 0) + if (strlen( privkeyfile ) - strlen( pkey_engine_name ) == 1) { dbglog( "Substituting certificate identifier for privatekey identifier" ); - pkey_identifier = cert_identifier; + privkeyfile = certfile; } } } @@ -418,6 +441,14 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath, pkey_engine = eaptls_ssl_load_engine( pkey_engine_name ); } + if (cert_engine_name) + free(cert_engine_name); + + if (pkey_engine_name) + free(pkey_engine_name); + +#endif + SSL_CTX_set_default_passwd_cb (ctx, password_callback); if (strlen(cacertfile) == 0) cacertfile = NULL; @@ -434,6 +465,7 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath, if (init_server) SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(cacertfile)); +#ifndef OPENSSL_NO_ENGINE if (cert_engine) { struct @@ -442,40 +474,87 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath, X509 *cert; } cert_info; - cert_info.s_slot_cert_id = cert_identifier; + cert_info.s_slot_cert_id = certfile; 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 ); + error( "EAP-TLS: Error loading certificate with URI '%s' from engine", certfile ); goto fail; } 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 { - warn("EAP-TLS: Cannot load PKCS11 key %s", cert_identifier); + warn("EAP-TLS: Cannot load key with URI: '%s'", certfile ); log_ssl_errors(); } } 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 @@ -504,68 +583,65 @@ SSL_CTX *eaptls_init_ssl(int init_server, char *cacertfile, char *capath, } SSL_free(ssl); +#ifndef OPENSSL_NO_ENGINE if (pkey_engine) { - EVP_PKEY *pkey = NULL; PW_CB_DATA cb_data; cb_data.password = passwd; - cb_data.prompt_info = pkey_identifier; + cb_data.prompt_info = privkeyfile; 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); + UI_method_set_writer(transfer_pin, eaptls_UI_writer); + UI_method_set_opener(transfer_pin, eaptls_UI_stub); + UI_method_set_closer(transfer_pin, eaptls_UI_stub); + UI_method_set_flusher(transfer_pin, eaptls_UI_stub); + UI_method_set_reader(transfer_pin, eaptls_UI_reader); + + dbglog( "Using our private key URI: '%s' in engine", privkeyfile ); + pkey = ENGINE_load_private_key(pkey_engine, privkeyfile, transfer_pin, &cb_data); if (transfer_pin) UI_destroy_method(transfer_pin); } else { - dbglog( "Loading private key '%s' from engine", pkey_identifier ); - pkey = ENGINE_load_private_key(pkey_engine, pkey_identifier, NULL, NULL); + dbglog( "Loading private key URI: '%s' from engine", privkeyfile ); + pkey = ENGINE_load_private_key(pkey_engine, privkeyfile, 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 + + 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; } @@ -688,6 +764,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; @@ -726,6 +812,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 */ @@ -733,6 +821,7 @@ int eaptls_init_ssl_server(eap_state * esp) if (!esp->es_server.ea_session) fatal("Allocation error"); ets = esp->es_server.ea_session; + ets->client = 0; if (!esp->es_server.ea_peer) { error("EAP-TLS: Error: client name not set (BUG)"); @@ -744,7 +833,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; @@ -752,7 +841,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; @@ -816,6 +905,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 @@ -824,6 +914,7 @@ int eaptls_init_ssl_client(eap_state * esp) if (!esp->es_client.ea_session) fatal("Allocation error"); ets = esp->es_client.ea_session; + ets->client = 1; /* * If available, copy server name in ets; it will be used in cert @@ -839,14 +930,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; @@ -1146,6 +1237,7 @@ int ssl_verify_callback(int ok, X509_STORE_CTX * ctx) int err, depth; SSL *ssl; struct eaptls_session *ets; + char *ptr1 = NULL, *ptr2 = NULL; peer_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); @@ -1181,34 +1273,71 @@ int ssl_verify_callback(int ok, X509_STORE_CTX * ctx) if (!depth) { - /* This is the peer certificate */ - - X509_NAME_oneline(X509_get_subject_name(peer_cert), - subject, 256); + /* Verify certificate based on certificate type and extended key usage */ + if (tls_verify_key_usage) { + int purpose = ets->client ? X509_PURPOSE_SSL_SERVER : X509_PURPOSE_SSL_CLIENT ; + if (X509_check_purpose(peer_cert, purpose, 0) == 0) { + error("Certificate verification error: nsCertType mismatch"); + return 0; + } - X509_NAME_get_text_by_NID(X509_get_subject_name(peer_cert), - NID_commonName, cn_str, 256); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + int flags = ets->client ? XKU_SSL_SERVER : XKU_SSL_CLIENT; + if (!(X509_get_extended_key_usage(peer_cert) & flags)) { + error("Certificate verification error: invalid extended key usage"); + return 0; + } +#endif + info("Certificate key usage: OK"); + } /* * If acting as client and the name of the server wasn't specified * explicitely, we can't verify the server authenticity */ - if (!ets->peer[0]) { - warn("Peer name not specified: no check"); + if (!tls_verify_method) + tls_verify_method = TLS_VERIFY_NONE; + + if (!ets->peer[0] || !strcmp(TLS_VERIFY_NONE, tls_verify_method)) { + warn("Certificate verication disabled or no peer name was specified"); return ok; } - /* - * Check the CN - */ - if (strcmp(cn_str, ets->peer)) { - error - ("Certificate verification error: CN (%s) != peer_name (%s)", - cn_str, ets->peer); + /* This is the peer certificate */ + X509_NAME_oneline(X509_get_subject_name(peer_cert), + subject, 256); + + X509_NAME_get_text_by_NID(X509_get_subject_name(peer_cert), + NID_commonName, cn_str, 256); + + /* Verify based on subject name */ + ptr1 = ets->peer; + if (!strcmp(TLS_VERIFY_SUBJECT, tls_verify_method)) { + ptr2 = subject; + } + + /* Verify based on common name (default) */ + if (strlen(tls_verify_method) == 0 || + !strcmp(TLS_VERIFY_NAME, tls_verify_method)) { + ptr2 = cn_str; + } + + /* Match the suffix of common name */ + if (!strcmp(TLS_VERIFY_SUFFIX, tls_verify_method)) { + int len = strlen(ptr1); + int off = strlen(cn_str) - len; + ptr2 = cn_str; + if (off > 0) { + ptr2 = cn_str + off; + } + } + + if (strcmp(ptr1, ptr2)) { + error("Certificate verification error: CN (%s) != %s", ptr1, ptr2); return 0; } - warn("Certificate CN: %s , peer name %s", cn_str, ets->peer); + info("Certificate CN: %s, peer name %s", cn_str, ets->peer); /* * If a peer certificate file was specified, here we check it