From patchwork Mon Oct 1 18:12:53 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jared Bents X-Patchwork-Id: 977362 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=rockwellcollins.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="hKMIPhkf"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=infradead.org header.i=@infradead.org header.b="T8rqvtDj"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42P9Vl13N1z9s3x for ; Tue, 2 Oct 2018 04:17:35 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:Message-Id:Date: Subject:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To: References:List-Owner; bh=2OtCZheGGi4EOazqjzfRWRVUHftsZtTQWBDGWUKZV1M=; b=hKM IPhkfpxItbmUEWZToWA2oTTOXPv/2z7znT4XFqARa0ussiIkA2xiv04ESyl9c2x83YEWSWVbIIMDH GcQi9Ra6E6R5nL3yEhgzjH72HEYXuQw/4Ut48pHCTEoU+xUfpECQsHCYxgANYIfV1D1TH8ncknVOl anywSNsAHSCpFh1VoRww1ADmYCG+yEXkkVYN2H9EzH6DfmAM14nd1+V2Vn8AoUdjGJvJkwYaR8Qvb Yj3gxDD8gKhhkXYqRkXyWIRKhwUgnbhQxFLBryeEk/70NTKYv7OH65IG1IS+6BDSEAI7wzQtIjBEE lp8QJHVytv9v/xP1/5u9mH10KINPEYw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1g72kl-00085E-S6; Mon, 01 Oct 2018 18:17:07 +0000 Received: from casper.infradead.org ([2001:8b0:10b:1236::1]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1g72jj-0007UJ-Gc for hostap@bombadil.infradead.org; Mon, 01 Oct 2018 18:16:03 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id: List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=TcXCVfQaHKSJAlUmfrLXMzOJuR5jrGdstTys9SrSuI0=; b=T8rqvtDjxKBz9RwA1dHpUxczx 3xik1zu4ZDMnt9wDpJ1XZB6PsAph/pP6ALi4kY44k6OW/ug7geF+sh8/f0fHIscLMg9yzr0oI8o0y fIXuyGc5byanSEcbeYYABrdEKPrNppbsIN9ifrXo5ceCIiEshoRqKUzv4D2khwDyNP+JLRCJKLHii C8QHzBtcd09Ow4KfcBNK4h0fabOYOs2WOXiZC/mhnixbDMwtHv8tMzl6gf16s2I8UUmPoNX4DJf0M j8Gnt6JX6+2nspTwpi5oqe6LXfiGWGtMyDYg6XkalwkBqkN9D1Z9wEioPKms4VB8uYAIGuONDGmvd SKn9M9A5A==; Received: from secvs01.rockwellcollins.com ([205.175.225.240]) by casper.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1g72h2-0006qK-6M for hostap@lists.infradead.org; Mon, 01 Oct 2018 18:13:18 +0000 Received: from ofwgwc03.rockwellcollins.com (HELO dtulimr02.rockwellcollins.com) ([205.175.225.12]) by secvs01.rockwellcollins.com with ESMTP; 01 Oct 2018 13:12:59 -0500 X-Received: from largo.rockwellcollins.com (unknown [192.168.140.76]) by dtulimr02.rockwellcollins.com (Postfix) with ESMTP id DD99E20043; Mon, 1 Oct 2018 13:12:58 -0500 (CDT) From: Jared Bents To: hostap@lists.infradead.org Subject: [PATCH v1 1/3] HostAPD: Add option 'check_crl_strict' Date: Mon, 1 Oct 2018 13:12:53 -0500 Message-Id: <1538417575-35315-1-git-send-email-jared.bents@rockwellcollins.com> X-Mailer: git-send-email 1.9.1 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181001_191316_456551_1DDE30D0 X-CRM114-Status: GOOD ( 20.78 ) X-Spam-Score: -5.0 (-----) X-Spam-Report: SpamAssassin version 3.4.1 on casper.infradead.org summary: Content analysis details: (-5.0 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high trust [205.175.225.240 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Sam Voss , Jared Bents MIME-Version: 1.0 Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Add the ability to ignore time-based errors from openssl by specifying a new configuration parameter, "check_crl_strict". This causes the following: - This setting does nothing when CRL checking is not enabled. - When CRL is enabled, "strict mode" will cause CRL time errors to not be ignored and will continue behaving as it currently does. - When CRL is enabled, disabling strict mode will cause CRL time errors to be ignored and will allow connections. By default, check_crl_strict is set to 1, or strict mode, to keep current functionality. Signed-off-by: Sam Voss Signed-off-by: Jared Bents --- hostapd/config_file.c | 2 ++ hostapd/hostapd.conf | 8 ++++++++ src/ap/ap_config.c | 3 +++ src/ap/ap_config.h | 1 + src/ap/authsrv.c | 3 ++- src/crypto/tls.h | 3 ++- src/crypto/tls_openssl.c | 21 ++++++++++++++++++++- 7 files changed, 38 insertions(+), 3 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 5079f69..7b7b33f 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2131,6 +2131,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->private_key_passwd = os_strdup(pos); } else if (os_strcmp(buf, "check_crl") == 0) { bss->check_crl = atoi(pos); + } else if (os_strcmp(buf, "check_crl_strict") == 0) { + bss->check_crl_strict = atoi(pos); } else if (os_strcmp(buf, "tls_session_lifetime") == 0) { bss->tls_session_lifetime = atoi(pos); } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index fa9a855..bc56f8d 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -795,6 +795,14 @@ eap_server=0 # 2 = check all CRLs in the certificate path #check_crl=1 +# Specifiy whether or not to ignore certificate validity time missmatches with +# errors X509_V_ERR_CERT_HAS_EXPIRED and X509_V_ERR_CERT_NOT_YET_VALID +# +# 0 = ignore errors +# 1 = do not ignore errors (default) +#check_crl_strict=0 + + # TLS Session Lifetime in seconds # This can be used to allow TLS sessions to be cached and resumed with an # abbreviated handshake when using EAP-TLS/TTLS/PEAP. diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 228de2b..2e3797b 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -95,6 +95,9 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->radius_das_time_window = 300; bss->sae_anti_clogging_threshold = 5; + + /* Default to strict crl checking. */ + bss->check_crl_strict = 1; } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 8c8f7e2..6220185 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -352,6 +352,7 @@ struct hostapd_bss_config { char *private_key; char *private_key_passwd; int check_crl; + int check_crl_strict; unsigned int tls_session_lifetime; char *ocsp_stapling_response; char *ocsp_stapling_response_multi; diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index cdb49cd..62ddc87 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -183,7 +183,8 @@ int authsrv_init(struct hostapd_data *hapd) } if (tls_global_set_verify(hapd->ssl_ctx, - hapd->conf->check_crl)) { + hapd->conf->check_crl, + hapd->conf->check_crl_strict)) { wpa_printf(MSG_ERROR, "Failed to enable check_crl"); authsrv_deinit(hapd); return -1; diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 11d504a..bb497ce 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -303,9 +303,10 @@ int __must_check tls_global_set_params( * @tls_ctx: TLS context data from tls_init() * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, * 2 = verify CRL for all certificates + * @strict: 0 = allow time errors, 1 = do not allow time errors * Returns: 0 on success, -1 on failure */ -int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl, int strict); /** * tls_connection_set_verify - Set certificate verification options diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 23ac64b..990c938 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -188,6 +188,7 @@ struct tls_context { void *cb_ctx; int cert_in_cb; char *ocsp_stapling_response; + int check_crl_strict; }; static struct tls_context *tls_global = NULL; @@ -227,6 +228,7 @@ struct tls_connection { unsigned int flags; + X509 *peer_cert; X509 *peer_issuer; X509 *peer_issuer_issuer; @@ -1820,6 +1822,13 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "time mismatch"); preverify_ok = 1; } + if (!preverify_ok && (!tls_global->check_crl_strict) && + (err == X509_V_ERR_CRL_HAS_EXPIRED || + err == X509_V_ERR_CRL_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity " + "crl time mismatch"); + preverify_ok = 1; + } err_str = X509_verify_cert_error_string(err); @@ -2185,9 +2194,11 @@ static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) } -int tls_global_set_verify(void *ssl_ctx, int check_crl) +int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) { int flags; + SSL *ssl; + struct tls_connection *conn; if (check_crl) { struct tls_data *data = ssl_ctx; @@ -2202,6 +2213,14 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) if (check_crl == 2) flags |= X509_V_FLAG_CRL_CHECK_ALL; X509_STORE_set_flags(cs, flags); + + if (NULL == tls_global) { + tls_show_errors(MSG_INFO, __func__, "Failed setting " + "strict mode in tls_global context."); + } else { + tls_global->check_crl_strict = strict; + } + } return 0; } From patchwork Mon Oct 1 18:12:54 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jared Bents X-Patchwork-Id: 977360 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=rockwellcollins.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="TF84bX2s"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42P9TL48nJz9s3x for ; Tue, 2 Oct 2018 04:16:22 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=667P6m3nxJwQWyAhMbGlVzevTxzTIFG2nz6PiZVb0tQ=; b=TF84bX2srj+9/c081BEjr/K+QV endZJgcVYCtmkGUOZvNmTtXz6xs+jAgWmVQQ7EzfaeiJL+wSLgNkYEPq9uZc4TgMDqejmlE4OTZy7 2GZ2TgcEOUTJ1ypLSeyuTEwp/MCK/aE55wsmrPSVdf4N9EwZ8AFxtJzUec17HsT9FzNnLUJI7mYGI 8IOUfnCr+WPB4gUTjRMfVC7TVTAtbpqzBMR4zb+LYYiRSi79+qap4i6YRT9R8PmQaRUQwRTJFtP+F E1VBEtQUe+0KMgB6eXUA37z+J11xEhF/Xvm0+6B6TZygvIoWiCpWhna7BJrmbTaV5Lny6Wmvpfhok DHefTdhA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1g72jd-0007Up-DD; Mon, 01 Oct 2018 18:15:57 +0000 Received: from ch3vs03.rockwellcollins.com ([205.175.226.47]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1g72hi-00063Z-AV for hostap@lists.infradead.org; Mon, 01 Oct 2018 18:15:54 +0000 Received: from ofwch3n02.rockwellcollins.com (HELO dtulimr02.rockwellcollins.com) ([205.175.226.14]) by ch3vs03.rockwellcollins.com with ESMTP; 01 Oct 2018 13:13:44 -0500 X-Received: from largo.rockwellcollins.com (unknown [192.168.140.76]) by dtulimr02.rockwellcollins.com (Postfix) with ESMTP id 7A64020043; Mon, 1 Oct 2018 13:13:43 -0500 (CDT) From: Jared Bents To: hostap@lists.infradead.org Subject: [PATCH v1 2/3] HostAPD: Add reload crl support Date: Mon, 1 Oct 2018 13:12:54 -0500 Message-Id: <1538417575-35315-2-git-send-email-jared.bents@rockwellcollins.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1538417575-35315-1-git-send-email-jared.bents@rockwellcollins.com> References: <1538417575-35315-1-git-send-email-jared.bents@rockwellcollins.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181001_111358_499846_05120362 X-CRM114-Status: GOOD ( 21.41 ) X-Spam-Score: -0.0 (/) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [205.175.226.47 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paresh Chaudhary , sam.voss@rockwellcollins.com, Jared Bents MIME-Version: 1.0 Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org This patch has been added new flag 'crl_reload_interval' to reload crl. This can be used to reload ca_cert file on every new tls session if difference between last reload and current reload time(seconds) greater-than crl_reload_interval. Note: If interval time > 0 and < 300 then crl_reload_interval will reset to 300 seconds. For this support 'check_crl' should be 1 or 2. Here, We have selected 300 seconds as a reset value to reduce overhead of crl reload funtionality on new session. We can choose less than 300 seconds but it will increase overhead of crl reload on new session. Signed-off-by: Paresh Chaudhary Signed-off-by: Sam Voss Signed-off-by: Jared Bents --- hostapd/config_file.c | 2 + hostapd/hostapd.conf | 8 ++++ src/ap/ap_config.h | 1 + src/ap/authsrv.c | 8 ++++ src/crypto/tls.h | 11 ++++++ src/crypto/tls_openssl.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 125 insertions(+), 2 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 7b7b33f..f5b8e83 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2133,6 +2133,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->check_crl = atoi(pos); } else if (os_strcmp(buf, "check_crl_strict") == 0) { bss->check_crl_strict = atoi(pos); + } else if (os_strcmp(buf, "crl_reload_interval") == 0) { + bss->crl_reload_interval = atoi(pos); } else if (os_strcmp(buf, "tls_session_lifetime") == 0) { bss->tls_session_lifetime = atoi(pos); } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index bc56f8d..c9c6e41 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -802,6 +802,14 @@ eap_server=0 # 1 = do not ignore errors (default) #check_crl_strict=0 +# crl reload interval in seconds +# This can be used to reload ca_cert file on every new tls session if difference +# between last reload and current reload time(seconds) greater-than +# crl_reload_interval +# Note: If interval time > 0 and < 300 then crl_reload_interval will reset to +# 300 seconds. For this support 'check_crl' should be 1 or 2. +# 0 = do not reload CRLS (default) +# crl_reload_interval = 300 # TLS Session Lifetime in seconds # This can be used to allow TLS sessions to be cached and resumed with an diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 6220185..fef966c 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -353,6 +353,7 @@ struct hostapd_bss_config { char *private_key_passwd; int check_crl; int check_crl_strict; + unsigned int crl_reload_interval; unsigned int tls_session_lifetime; char *ocsp_stapling_response; char *ocsp_stapling_response_multi; diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 62ddc87..2add858 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -157,6 +157,14 @@ int authsrv_init(struct hostapd_data *hapd) os_memset(&conf, 0, sizeof(conf)); conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + if(hapd->conf->crl_reload_interval > 0 && hapd->conf->check_crl <= 0) { + wpa_printf(MSG_INFO, "Failed to enable crl reload functionality," + "It's depend on check_crl."); + } + if(hapd->conf->check_crl > 0 && hapd->conf->crl_reload_interval > 0){ + conf.crl_reload_interval = hapd->conf->crl_reload_interval; + wpa_printf(MSG_INFO, "Enabled crl reload functionality"); + } hapd->ssl_ctx = tls_init(&conf); if (hapd->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize TLS"); diff --git a/src/crypto/tls.h b/src/crypto/tls.h index bb497ce..85b6a5e 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -8,6 +8,7 @@ #ifndef TLS_H #define TLS_H +#include struct tls_connection; @@ -80,6 +81,7 @@ struct tls_config { int cert_in_cb; const char *openssl_ciphers; unsigned int tls_session_lifetime; + unsigned int crl_reload_interval; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -224,6 +226,15 @@ void tls_deinit(void *tls_ctx); int tls_get_errors(void *tls_ctx); /** + * tls_crl_cert_reload - Reload crl cert and init new store + * @ca_cert : ca_cert file + * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, + * 2 = verify CRL for all certificates + * Returns : store pointer on success and NULL on failure + */ +X509_STORE *tls_crl_cert_reload(const char *ca_cert, int check_crl); + +/** * tls_connection_init - Initialize a new TLS connection * @tls_ctx: TLS context data from tls_init() * Returns: Connection context data, conn for other function calls diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 990c938..41fbf1b 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -7,6 +7,7 @@ */ #include "includes.h" +#include #ifndef CONFIG_SMARTCARD #ifndef OPENSSL_NO_ENGINE @@ -189,6 +190,8 @@ struct tls_context { int cert_in_cb; char *ocsp_stapling_response; int check_crl_strict; + int check_crl; + const char *ca_cert; }; static struct tls_context *tls_global = NULL; @@ -197,6 +200,10 @@ static struct tls_context *tls_global = NULL; struct tls_data { SSL_CTX *ssl; unsigned int tls_session_lifetime; + unsigned int crl_reload_interval; + unsigned int crl_last_reload; + X509_STORE *old_x509_store; + pthread_mutex_t mutex; }; struct tls_connection { @@ -968,8 +975,19 @@ void * tls_init(const struct tls_config *conf) return NULL; } data->ssl = ssl; - if (conf) + if (conf) { data->tls_session_lifetime = conf->tls_session_lifetime; + data->crl_reload_interval = conf->crl_reload_interval; + /* Set crl_reload_interval is equal to 5 minutes to reduce overhead of + * crl reload funtionality on new session. We can choose less than 5 + * minutes but it will increase overhead of crl reload on new session. + * */ + if(data->crl_reload_interval > 0 && data->crl_reload_interval < 300) { + wpa_printf(MSG_INFO, + "crl reload interval is set too low, reset it to 300"); + data->crl_reload_interval = 300; + } + } SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); @@ -1028,6 +1046,9 @@ void * tls_init(const struct tls_config *conf) return NULL; } + /* Init mutex */ + pthread_mutex_init(&data->mutex, NULL); + return data; } @@ -1060,6 +1081,7 @@ void tls_deinit(void *ssl_ctx) tls_global = NULL; } + pthread_mutex_destroy(&data->mutex); os_free(data); } @@ -1333,8 +1355,39 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) SSL_CTX *ssl = data->ssl; struct tls_connection *conn; long options; + X509_STORE *new_cert_store; + time_t now; struct tls_context *context = SSL_CTX_get_app_data(ssl); + /* Get current time */ + now = time(NULL); + + /* Replace X509 store if it is time to update crl */ + /* Replace X509 store if difference between current time and previous store + * reload time greater-than crl_reload_interval */ + if (data->crl_reload_interval > 0 && data->crl_last_reload + + data->crl_reload_interval <= now) { + pthread_mutex_lock(&data->mutex); + /* recheck data->crl_last_reload because it may be inaccurate + * without mutex */ + if (data->crl_last_reload + data->crl_reload_interval <= now) { + wpa_printf(MSG_INFO, "Flushing X509 store with ca_cert file"); + new_cert_store = (X509_STORE *)tls_crl_cert_reload + (tls_global->ca_cert, tls_global->check_crl); + if (new_cert_store == NULL) { + wpa_printf(MSG_ERROR, + "Error replacing X509 store with ca_cert file"); + } else { + /*Free old store */ + if (data->old_x509_store) X509_STORE_free(data->old_x509_store); + data->old_x509_store = ssl->cert_store; + ssl->cert_store = new_cert_store; + data->crl_last_reload = now; + } + } + pthread_mutex_unlock(&data->mutex); + } + conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; @@ -2167,6 +2220,35 @@ static int tls_connection_ca_cert(struct tls_data *data, return 0; } +X509_STORE *tls_crl_cert_reload(const char *ca_cert, int check_crl) +{ + int flags; + X509_STORE *store; + + store = X509_STORE_new(); + if (store == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " + "certificate store", __func__); + return NULL; + } + + if (ca_cert) { + if(!X509_STORE_load_locations(store, ca_cert, NULL)) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + return NULL; + } + } + + if (check_crl) + flags = X509_V_FLAG_CRL_CHECK; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + + X509_STORE_set_flags(store, flags); + + return store; +} static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) { @@ -2188,6 +2270,13 @@ static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) SSL_CTX_set_client_CA_list(ssl_ctx, SSL_load_client_CA_file(ca_cert)); #endif /* OPENSSL_NO_STDIO */ + + if (NULL == tls_global) { + tls_show_errors(MSG_INFO, __func__, "Failed setting " + "ca_cert in tls_global context."); + } else { + tls_global->ca_cert = ca_cert; + } } return 0; @@ -2216,11 +2305,15 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) if (NULL == tls_global) { tls_show_errors(MSG_INFO, __func__, "Failed setting " - "strict mode in tls_global context."); + "strict mode and check crl mode in tls_global context."); } else { tls_global->check_crl_strict = strict; + tls_global->check_crl = check_crl; } + /* Store crl last reload time */ + data->crl_last_reload = time(NULL); + data->old_x509_store = NULL; } return 0; } From patchwork Mon Oct 1 18:12:55 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jared Bents X-Patchwork-Id: 977361 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=rockwellcollins.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="bgvd6epe"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42P9Tx5C4Lz9s3x for ; Tue, 2 Oct 2018 04:16:53 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=kPgImPrmsmaEaOksN0cLIRW9wZD+TCw2Ah4a3ZwDdoQ=; b=bgvd6epeKdUnMjygNGaHyDnSUo dzZoUpaObujtchhJNIaBpA1DiG6IGs6A7flodCXokGOiPc8kUAr4awxuxghw16z7cVA5YEhK/lML6 fL+Th1oaK56kvHHqbY6S6zQtEtqMmxeHUPed87hgUaT3kI10e/ISC9QKSE1FLumjRnAu5Xze9oDGt WGY1Iih8jVjvRCF+/UigcRcSdEAcpYWBz+Ms/vnJtEtKXVd5W+YOCILrzpr6+j0OuV/WJTeUmOwUv A/9GN7dbezDqkU6A0SsBwZrkAWJ3xOwm1K82gnmkxbwHovMk3YOjgIKvl+OS4nYjZUW2JlWmTnCkB L0pOj6nQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1g72kD-0007rI-0T; Mon, 01 Oct 2018 18:16:33 +0000 Received: from ch3vs04.rockwellcollins.com ([205.175.226.52]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1g72hi-00064P-H8 for hostap@lists.infradead.org; Mon, 01 Oct 2018 18:15:57 +0000 Received: from ofwch3n02.rockwellcollins.com (HELO dtulimr02.rockwellcollins.com) ([205.175.226.14]) by ch3vs04.rockwellcollins.com with ESMTP; 01 Oct 2018 13:13:47 -0500 X-Received: from largo.rockwellcollins.com (unknown [192.168.140.76]) by dtulimr02.rockwellcollins.com (Postfix) with ESMTP id 1160020072; Mon, 1 Oct 2018 13:13:47 -0500 (CDT) From: Jared Bents To: hostap@lists.infradead.org Subject: [PATCH v1 3/3] HostAPD: Add 'check_cert_subject' support Date: Mon, 1 Oct 2018 13:12:55 -0500 Message-Id: <1538417575-35315-3-git-send-email-jared.bents@rockwellcollins.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1538417575-35315-1-git-send-email-jared.bents@rockwellcollins.com> References: <1538417575-35315-1-git-send-email-jared.bents@rockwellcollins.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181001_111358_773676_F1257C0F X-CRM114-Status: GOOD ( 24.34 ) X-Spam-Score: -0.0 (/) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [205.175.226.52 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paresh Chaudhary , Jared Bents MIME-Version: 1.0 Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org This patch added 'check_cert_subject' support to match the value of every field against the DN of the subject in the client certificate. If the values do not match, the certificate verification will fail and will reject the user. This option allows hostapd to match every individual field in the right order, also allow '*' character as a wildcard (e.g OU=Development*). Note: Hostapd will match string up to 'wildcard' against the DN of the subject in the client certificate for every individual field. Signed-off-by: Paresh Chaudhary Signed-off-by: Jared Bents --- hostapd/config_file.c | 8 ++ hostapd/hostapd.conf | 27 ++++ src/ap/ap_config.c | 1 + src/ap/ap_config.h | 1 + src/ap/authsrv.c | 2 +- src/crypto/tls.h | 23 +++- src/crypto/tls_openssl.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 403 insertions(+), 4 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index f5b8e83..a9bb403 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2129,6 +2129,14 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "private_key_passwd") == 0) { os_free(bss->private_key_passwd); bss->private_key_passwd = os_strdup(pos); + } else if (os_strcmp(buf, "check_cert_subject") == 0) { + os_free(bss->check_cert_subject); + bss->check_cert_subject = os_strdup(pos); + if (!strlen(bss->check_cert_subject)) { + wpa_printf(MSG_ERROR, "Line %d: unknown check_cert_subject '%s'", + line, pos); + return 1; + } } else if (os_strcmp(buf, "check_crl") == 0) { bss->check_crl = atoi(pos); } else if (os_strcmp(buf, "check_crl_strict") == 0) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index c9c6e41..7d69738 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -811,6 +811,33 @@ eap_server=0 # 0 = do not reload CRLS (default) # crl_reload_interval = 300 +# If check_cert_subject is set, the value of every field will be checked +# against the DN of the subject in the client certificate. If the values do +# not match, the certificate verification will fail, rejecting the user. +# This option allows hostapd to match every individual field in the right order +# against the dn of the subject in the client certificate. +# [ +# e.g, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234, In this case, +# hostapd will check every individual dn field of the subject in the client +# certificate, If OU=XYZ comes first in terms of the order in the client certificate +# (Dn field of client cert= C=US/O=XX/OU=XYZ/OU=ABC/CN=1234) dn field then +# hostapd will reject client because the order of 'OU' is not matching the specified +# string in 'check_cert_subject'. +# ] +# This option also allows '*' as a wildcard. This option has some limitation. It's +# only work as per below example. +# [ +# e.g, check_cert_subject=C=US/O=XX/OU=Production*, For example, +# we have two clients and DN of the subject in the first client certificate is +# (C=US/O=XX/OU=Production Unit) and dn of the subject in the second client is +# (C=US/O=XX/OU=Production Factory). In this case, hostapd will allow both clients +# because the value of 'OU' field in both clients certificate is matching with 'OU' +# value in 'check_cert_subject' up to 'wildcard'. +# ] +# * (Allow all client e.g check_cert_subject = *) +# check_cert_subject = string + + # TLS Session Lifetime in seconds # This can be used to allow TLS sessions to be cached and resumed with an # abbreviated handshake when using EAP-TLS/TTLS/PEAP. diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 2e3797b..4907811 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -466,6 +466,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->server_cert); os_free(conf->private_key); os_free(conf->private_key_passwd); + os_free(conf->check_cert_subject); os_free(conf->ocsp_stapling_response); os_free(conf->ocsp_stapling_response_multi); os_free(conf->dh_file); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index fef966c..b168873 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -351,6 +351,7 @@ struct hostapd_bss_config { char *server_cert; char *private_key; char *private_key_passwd; + char *check_cert_subject; int check_crl; int check_crl_strict; unsigned int crl_reload_interval; diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 2add858..f2b7a45 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -192,7 +192,7 @@ int authsrv_init(struct hostapd_data *hapd) if (tls_global_set_verify(hapd->ssl_ctx, hapd->conf->check_crl, - hapd->conf->check_crl_strict)) { + hapd->conf->check_crl_strict, hapd->conf->check_cert_subject)) { wpa_printf(MSG_ERROR, "Failed to enable check_crl"); authsrv_deinit(hapd); return -1; diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 85b6a5e..8915774 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -26,6 +26,25 @@ enum tls_event { TLS_ALERT }; +struct tls_dn_field_order_cnt { + uint8_t cn; + uint8_t c; + uint8_t l; + uint8_t st; + uint8_t o; + uint8_t ou; + uint8_t email; +}; + +enum digest_alg{ + DIGEST_HASH_ALG_MD5, + DIGEST_HASH_ALG_SHA, + DIGEST_HASH_ALG_SHA1, + DIGEST_HASH_ALG_SHA224, + DIGEST_HASH_ALG_SHA256, + DIGEST_HASH_ALG_SHA384, + DIGEST_HASH_ALG_SHA512 +}; /* * Note: These are used as identifier with external programs and as such, the * values must not be changed. @@ -42,6 +61,7 @@ enum tls_fail_reason { TLS_FAIL_SERVER_CHAIN_PROBE = 8, TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, TLS_FAIL_DOMAIN_MISMATCH = 10, + TLS_FAIL_DN_MISMATCH = 11 }; @@ -315,9 +335,10 @@ int __must_check tls_global_set_params( * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, * 2 = verify CRL for all certificates * @strict: 0 = allow time errors, 1 = do not allow time errors + * @check_cert_subject : Hostapd configuration 'check_cert_subject' string pointer * Returns: 0 on success, -1 on failure */ -int __must_check tls_global_set_verify(void *tls_ctx, int check_crl, int strict); +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl, int strict, char *check_cert_subject); /** * tls_connection_set_verify - Set certificate verification options diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 41fbf1b..46e6def 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -182,6 +182,7 @@ static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx, static int tls_openssl_ref_count = 0; static int tls_ex_idx_session = -1; +struct tls_dn_field_order_cnt dn_cnt = {0}; struct tls_context { void (*event_cb)(void *ctx, enum tls_event ev, @@ -192,6 +193,7 @@ struct tls_context { int check_crl_strict; int check_crl; const char *ca_cert; + char *check_cert_subject; }; static struct tls_context *tls_global = NULL; @@ -203,6 +205,7 @@ struct tls_data { unsigned int crl_reload_interval; unsigned int crl_last_reload; X509_STORE *old_x509_store; + char *client_cert_subject; pthread_mutex_t mutex; }; @@ -1082,6 +1085,10 @@ void tls_deinit(void *ssl_ctx) } pthread_mutex_destroy(&data->mutex); + if(data->client_cert_subject) { + os_free(data->client_cert_subject); + data->client_cert_subject = NULL; + } os_free(data); } @@ -1359,6 +1366,11 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) time_t now; struct tls_context *context = SSL_CTX_get_app_data(ssl); + if (data->client_cert_subject) + tls_global->check_cert_subject = data->client_cert_subject; + else + tls_global->check_cert_subject = NULL; + /* Get current time */ now = time(NULL); @@ -1580,6 +1592,319 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, } #endif /* CONFIG_NATIVE_WINDOWS */ +/** + * client_cert_fingerprint - print fingerprint of certificate + * @cert: Certificate + * @alg: hash algorithm + * Returns: Return 1 on success and 0 on Failure +*/ +static int client_cert_fingerprint(X509* cert, enum digest_alg alg) +{ + + /* Maximum size of fingerprint with sha512 is 191 bytes, + so it's enough to hold fingerprint value for supported algorithm */ + uint8_t fingerprint[192]; + char fingerprint_string[192]; + int ret = 0, i; + int pos = 0; + uint32_t len = sizeof(fingerprint); + uint32_t buflen = sizeof(fingerprint_string); + + /* Init out buffers to zero */ + os_memset(fingerprint, 0x00, sizeof(fingerprint)); + os_memset(fingerprint_string, 0x00, sizeof(fingerprint_string)); + + switch (alg) + { + case DIGEST_HASH_ALG_MD5: + /* Get the digest */ + ret = X509_digest(cert, EVP_md5(), fingerprint, &len); + break; + case DIGEST_HASH_ALG_SHA: + /* Get the digest */ + ret = X509_digest(cert, EVP_sha(), fingerprint, &len); + break; + case DIGEST_HASH_ALG_SHA1: + /* Get the digest */ + ret = X509_digest(cert, EVP_sha1(), fingerprint, &len); + break; + case DIGEST_HASH_ALG_SHA224: + /* Get the digest */ + ret = X509_digest(cert, EVP_sha224(), fingerprint, &len); + break; + case DIGEST_HASH_ALG_SHA256: + /* Get the digest */ + ret = X509_digest(cert, EVP_sha256(), fingerprint, &len); + break; + case DIGEST_HASH_ALG_SHA384: + /* Get the digest */ + ret = X509_digest(cert, EVP_sha384(), fingerprint, &len); + break; + case DIGEST_HASH_ALG_SHA512: + /* Get the digest */ + ret = X509_digest(cert, EVP_sha512(), fingerprint, &len); + break; + default: + wpa_printf(MSG_ERROR, "Unknown digest algorithm"); + break; + } + + if (ret != 1) { + wpa_printf(MSG_ERROR, "Cannot get digest from certificate"); + return ret; + } + + + for(i = 0; i < len; ++i) { + if (i > 0) { + pos += snprintf(fingerprint_string + pos, buflen - pos, ":"); + } + pos += snprintf(fingerprint_string + pos, buflen - pos, "%02X", fingerprint[i]); + } + + wpa_printf(MSG_INFO,"Fingerprint: %s\n", fingerprint_string); + + return ret; +} + +/** + * match_dn_field - Match configuration DN field value with Certificate DN field value + * @cert: Certificate + * @nid: NID of DN field + * @value DN field value which is passed from configuration file + * e.g (If configuration have C=US and this argument will point to US) + * Returns: Return 1 on success and 0 on Failure + */ +static int match_dn_field(X509 *cert, int nid, char *value) +{ + int i = -1, ret = 1, len, config_dn_field_index = 0 ; + int match_index = 0; + X509_NAME *name; + + len = os_strlen(value); + name = X509_get_subject_name(cert); + + /* Assign incremented cnt for every field of DN to check DN field in + right order */ + switch (nid) + { + case NID_commonName: + config_dn_field_index = dn_cnt.cn; + break; + case NID_countryName: + config_dn_field_index = dn_cnt.c; + break; + case NID_localityName: + config_dn_field_index = dn_cnt.l; + break; + case NID_stateOrProvinceName: + config_dn_field_index = dn_cnt.st; + break; + case NID_organizationName: + config_dn_field_index = dn_cnt.o; + break; + case NID_organizationalUnitName: + config_dn_field_index = dn_cnt.ou; + break; + case NID_pkcs9_emailAddress: + config_dn_field_index = dn_cnt.email; + break; + default: + wpa_printf(MSG_ERROR, + "TLS: Unknown NID '%d' in 'check_cert_subject' " + "option of hostapd configuration", nid); + return 0; + } + + /* Fetch value based on NID */ + for (;;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + i = X509_NAME_get_index_by_NID(name, nid, i); + if (i == -1) { + ret = 0; + break; + } + e = X509_NAME_get_entry(name, i); + if (e == NULL) + continue; + + cn = X509_NAME_ENTRY_get_data(e); + if (cn == NULL) + continue; + + match_index ++; + + /* check for more than one dn field with same name */ + if(match_index != config_dn_field_index) + continue; + + /* Check wild card at the right end side*/ + /* e.g. if OU=develop* mentioned in configuration file then hostapd will allow if the + 'OU' of the subject in the client certificate start with 'develop*' */ + /* Same applicable for other field of DN*/ + if( '*' == *(value + len - 1)) { + /* Compare actual certificate dn field value with configuration file dn field value upto specified length */ + if (!os_strncasecmp(cn->data, value, len - 1)) { + ret = 1; + break; + } else { + wpa_printf(MSG_ERROR, + "TLS: Failed to match '%s' with " + "Certificate Distinguished Name '%s'", + value, ASN1_STRING_data(cn)); + wpa_printf(MSG_INFO, "TLS: Please check hostapd configuration"); + ret = 0; + break; + } + } else { + /* Compare actual certificate dn field value with configuration file dn field value */ + if (!os_strcmp(cn->data, value)) { + ret = 1; + break; + } else { + wpa_printf(MSG_ERROR, + "TLS: Failed to match '%s' with " + "Certificate Distinguished Name '%s'", + value, ASN1_STRING_data(cn)); + wpa_printf(MSG_INFO, "TLS: Please check hostapd configuration"); + ret = 0; + break; + } + } + } + + return ret; +} + +/** + * get_value_from_field - Get value from DN field + * @cert: Certificate + * @field_str: DN field string which is passed from configuration file (e.g C=US) + * Returns: Return 1 on success and 0 on Failure + */ +static int get_value_from_field(X509 *cert, char *field_str) +{ + int nid = -1, ret = 1; + char *temp, *dbg_dn; + + temp = strtok(field_str,"="); + + /* Compare all hostapd configuration DN field and assign nid based on that to + fetch correct value from certificate subject*/ + if(strcmp(temp,"CN") == 0) { + nid = NID_commonName; + dn_cnt.cn++; + } else if(strcmp(temp,"C") == 0) { + nid = NID_countryName; + dn_cnt.c++; + } else if (strcmp(temp,"L") == 0) { + nid = NID_localityName; + dn_cnt.l++; + } else if (strcmp(temp,"ST") == 0) { + nid = NID_stateOrProvinceName; + dn_cnt.st++; + } else if (strcmp(temp,"O") == 0) { + nid = NID_organizationName; + dn_cnt.o++; + } else if (strcmp(temp,"OU") == 0) { + nid = NID_organizationalUnitName; + dn_cnt.ou++; + } else if (strcmp(temp,"emailAddress") == 0) { + nid = NID_pkcs9_emailAddress; + dn_cnt.email++; + } else if (strcmp(temp,"*") == 0) { + ret = 1; + } else { + wpa_printf(MSG_ERROR, + "TLS: Unknown field '%s' in 'check_cert_subject' " + "option of hostapd configuration", temp); + ret = 0; + } + + dbg_dn = temp; + /* Check for correct NID */ + if ( (nid >= NID_commonName && nid <= NID_organizationalUnitName) || + nid == NID_pkcs9_emailAddress) { + temp = strtok (NULL,"="); + if (temp != NULL) { + ret = match_dn_field(cert, nid, temp); + } + else { + ret = 0; + wpa_printf(MSG_ERROR, + "TLS: Distinguished Name field '%s' value " + "is not defined in 'check_cert_subject'. " + "Please Check hostapd configuration file", dbg_dn); + } + } + + return ret; +} + +/** + * tls_match_dn_field - Match Certificate individual subject field with check_cert_subject + * @cert: Certificate + * @match: check_cert_subject string + * Returns: Return 1 on success and 0 on Failure +*/ +static int tls_match_dn_field(X509 *cert, const char *match) +{ + int match_loop = 0, field_loop = 0, length, last_index = 0, len, ret = 1; + char *field = NULL; + char buf[2048] = {0}; /* NOTE: Print maximum 2048 bytes of subject and issuer */ + enum digest_alg alg = DIGEST_HASH_ALG_SHA256; /* Default calculate certificate fingerprint with SHA256 algorithm */ + + os_memset(&dn_cnt, 0, sizeof(dn_cnt)); + len = os_strlen(match); + + /* Maximum length of each DN field is 128 character */ + field = os_malloc(256); + if (field == NULL) { + wpa_printf(MSG_ERROR, "TLS: Failed to allocate memory"); + return 0; + } + os_memset(field,0,256); + + while ( match[match_loop] != '\0') { + /* Stop at '/' character */ + if (match[match_loop] == '/') { + field[field_loop] = '\0'; + if (strlen(field) > 0) { + if(!get_value_from_field(cert, field)) { + ret = 0; + break; + } + } + os_memset(field,0,256); + field_loop = 0; + last_index = match_loop; + last_index++; + } else { + field[field_loop++] = match[match_loop]; + } + match_loop++; + } + if (strlen(field) > 0 && ret != 0) { + strncpy(field, match+last_index, len - last_index); + if(!get_value_from_field(cert, field)) + ret = 0; + } + if(field != NULL) + os_free(field); + + X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); + wpa_printf(MSG_INFO,"TLS: Certificate subject= %s\n", buf); + os_memset(buf,0,2048); + X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf)); + wpa_printf(MSG_INFO,"TLS: Certificate issuer= %s\n", buf); + /* Return 0 only if client_cert_fingerprint function return failure code, otherwise + return 'tls_match_dn_field' function return code */ + if(!client_cert_fingerprint(cert, alg)) + ret = 0; + + return ret; +} static int tls_match_suffix(X509 *cert, const char *match, int full) { @@ -1935,6 +2260,14 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", preverify_ok, err, err_str, conn->ca_cert_verify, depth, buf); + if (tls_global->check_cert_subject) { + if(depth == 0 && !tls_match_dn_field(err_cert,tls_global->check_cert_subject)) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Distinguished Name", + TLS_FAIL_DN_MISMATCH); + } + } if (depth == 0 && match && os_strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); @@ -2283,14 +2616,22 @@ static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) } -int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) +int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict, char *check_cert_subject) { int flags; SSL *ssl; struct tls_connection *conn; + struct tls_data *data = ssl_ctx; + + os_free(data->client_cert_subject); + data->client_cert_subject = NULL; + if (check_cert_subject) { + data->client_cert_subject = os_strdup(check_cert_subject); + if (data->client_cert_subject == NULL) + return -1; + } if (check_crl) { - struct tls_data *data = ssl_ctx; X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl); if (cs == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to get "