]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/eap-tls.c
pppd: add experimental support for PEAP protocol, an extension of EAP
[ppp.git] / pppd / eap-tls.c
index 5740f308a89ae6c2e645222474a2ba6497cde9e7..b15d801565f54807165435a6a09a585f1c5cd665 100644 (file)
  *
  */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #include <string.h>
 #include <strings.h>
 #include <unistd.h>
 #include <fcntl.h>
 
 #include <openssl/conf.h>
+#ifndef OPENSSL_NO_ENGINE
 #include <openssl/engine.h>
+#endif
 #include <openssl/hmac.h>
 #include <openssl/err.h>
 #include <openssl/ui.h>
 #include <openssl/x509v3.h>
+#include <openssl/pkcs12.h>
 
 #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