]> git.ozlabs.org Git - ppp.git/commitdiff
pppd: EAP-TLS: Verify Subject or CommonName by suffix (#261)
authorEivind Næss <eivnaes@yahoo.com>
Sun, 14 Mar 2021 23:17:41 +0000 (16:17 -0700)
committerGitHub <noreply@github.com>
Sun, 14 Mar 2021 23:17:41 +0000 (10:17 +1100)
This feature matches closely what OpenVPN and the network-manager-openvpn plugin do for
certificate verification. It allows the end user to configure the certificate to be matched by
its common name (entire string), its subject name, or the suffix of a subject name.
The latter is especially useful if you are trying to match against a random server in a RADIUS
pool. Lastly, it also allows you to turn off the certificate matching altogether.

tls-verify-method can have the following parameter values:
- none
- subject   - The entire subject, e.g. /CN=some.server.org
- name      - The entire common name, e.g. some.server.org
- suffix    - The latter part of a name, e.g. servers.org

Secondly, it also introduces a new parameter 'tls-verify-key-usage' which permits checking
of the 'server' or 'client' side attributes of nsCertType and X509 extended key attributes.
For example, in client mode, it will verify that received certificate has the 'server' side
attributes enabled.

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 ab3794eaa53566e7a3e72805cce1b4c5988f154b..bc1066ed0436db585c5561b8efab9a51437afc40 100644 (file)
@@ -126,7 +126,7 @@ EAP-TLS authentication support for PPP
 
       ca <ca-file>
         Use the CA public certificate found in <ca-file> in PEM format
-      ca-path <directory>
+      capath <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
@@ -147,6 +147,9 @@ EAP-TLS authentication support for PPP
       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.
+      verify-tls-peer <none|subject|name|suffix>
+        Compare the remotename against the subject, certificate name, or
+        match by suffix. Default is 'name'.
 
     Note: 
       password-encrypted certificates can be used as of v0.94 of this 
index b1271c4f8739e4631740a44880586b4d3e92ceb9..6ccdbf8dde8a035572734ba23f259c185076f97e 100644 (file)
@@ -258,6 +258,8 @@ 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) */
+char *tls_verify_method = NULL;
+bool tls_verify_key_usage = 0;
 bool need_peer_eap = 0;                        /* Require peer to authenticate us */
 #endif
 
@@ -445,6 +447,10 @@ option_t auth_options[] = {
     { "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)" },
+    { "tls-verify-key-usage", o_bool, &tls_verify_key_usage,
+      "Verify certificate type and extended key usage" },
+    { "tls-verify-method", o_string, &tls_verify_method,
+      "Verify peer by method (none|subject|name|suffix)" },
     { "need-peer-eap", o_bool, &need_peer_eap,
       "Require the peer to authenticate us", 1 },
 #endif /* USE_EAPTLS */
index 5740f308a89ae6c2e645222474a2ba6497cde9e7..4759764ff206d0ca56312758d807c159d180bec0 100644 (file)
@@ -64,7 +64,7 @@ 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 *); 
+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);
@@ -733,6 +733,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)");
@@ -824,6 +825,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
@@ -1146,6 +1148,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 +1184,68 @@ 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 (!ets->peer[0] || !strcmp(tls_verify_method, TLS_VERIFY_NONE)) {
+            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 
index cdbc9e4e4f9e90cf6139ea39b1eb13e75e0f581c..c74a831671faee87ef87749f4904044ec9c8f767 100644 (file)
@@ -65,6 +65,7 @@ struct eaptls_session
     char rtx[EAP_TLS_MAX_LEN];  /* retransmission buffer */
     int rtx_len;
     int mtu;                    /* unit mtu */
+    bool client;
 };
 
 
index 612902f55d0d6ceef0646980d7941b7571526e14..05e8e37439c78bdce97498eeb19bb246d91aa9d8 100644 (file)
@@ -336,9 +336,17 @@ extern bool        dryrun;         /* check everything, print options, exit */
 extern int     child_wait;     /* # seconds to wait for children at end */
 
 #ifdef USE_EAPTLS
+
+#define TLS_VERIFY_NONE     "none"
+#define TLS_VERIFY_NAME     "name"
+#define TLS_VERIFY_SUBJECT  "subject"
+#define TLS_VERIFY_SUFFIX   "suffix"
+
 extern char    *crl_dir;
 extern char    *crl_file;
 extern char *max_tls_version;
+extern bool tls_verify_key_usage;
+extern char *tls_verify_method;
 #endif /* USE_EAPTLS */
 
 #ifdef MAXOCTETS