2 * Copyright (c) 2014 Samsung Electronics Co.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
18 * @author Dongsun Lee (ds73.lee@samsung.com)
20 * @brief OCSP implementation.
25 #include <openssl/pem.h>
26 #include <openssl/ocsp.h>
27 #include <openssl/err.h>
28 #include <openssl/ssl.h>
31 #include <dpl/log/log.h>
32 #include <certificate-impl.h>
33 #include <openssl_utils.h>
34 #include <ckm/ckm-error.h>
36 /* Maximum leeway in validity period: default 5 minutes */
37 #define MAX_VALIDITY_PERIOD (5 * 60)
42 typedef std::unique_ptr<BIO, std::function<void(BIO*)>> BioUniquePtr;
44 void BIO_write_and_free(BIO* bio) {
48 std::vector<char> message(1024);
49 int size = BIO_read(bio, message.data(), message.size());
52 LogError("OCSP error description:" << std::string(message.begin(), message.end()));
58 } // namespace anonymous
60 OCSPModule::OCSPModule() {
64 OCSPModule::~OCSPModule(){
68 int OCSPModule::verify(const CertificateImplVector &certificateChain) {
69 bool unsupported = false; // ocsp is unsupported in certificate in chain (except root CA)
71 // create trusted store
72 X509_STACK_PTR trustedCerts = create_x509_stack();
74 // skip first 2 certificates
75 for (auto it=certificateChain.cbegin()+2; it < certificateChain.cend(); it++)
78 LogError("Error. Broken certificate chain.");
79 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
81 sk_X509_push(trustedCerts.get(), it->getX509());
84 for (int i=0; i < static_cast<int>(certificateChain.size())-1; i++) {// except root certificate
85 if (certificateChain[i].empty() || certificateChain[i+1].empty()) {
86 LogError("Error. Broken certificate chain.");
87 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
90 X509 *cert = certificateChain[i].getX509();
91 X509 *issuer = certificateChain[i+1].getX509();
93 std::string url = certificateChain[i].getOCSPURL();
96 LogError("Certificate in certchain[" << i << "] does not provide OCSP extension.");
101 int result = ocsp_verify(cert, issuer, trustedCerts.get(), url);
102 // remove first element from trustedCerts store
103 sk_X509_delete(trustedCerts.get(), 0);
105 if(result != CKM_API_OCSP_STATUS_GOOD) {
106 LogError("Fail to OCSP certification check. Errorcode=[" << result <<
107 "], on certChain[" << i << "]");
113 return CKM_API_OCSP_STATUS_UNSUPPORTED;
115 return CKM_API_OCSP_STATUS_GOOD;
118 int OCSPModule::ocsp_verify(X509 *cert, X509 *issuer, STACK_OF(X509) *trustedCerts, const std::string &constUrl) {
119 OCSP_REQUEST *req = NULL;
120 OCSP_RESPONSE *resp = NULL;
121 OCSP_BASICRESP *bs = NULL;
122 OCSP_CERTID *certid = NULL;
124 SSL_CTX *use_ssl_ctx = NULL;
125 char *host = NULL, *port = NULL, *path = NULL;
126 ASN1_GENERALIZEDTIME *rev = NULL;
127 ASN1_GENERALIZEDTIME *thisupd = NULL;
128 ASN1_GENERALIZEDTIME *nextupd = NULL;
131 int i = 0 ,tmpIdx = 0;
132 long nsec = MAX_VALIDITY_PERIOD, maxage = -1;
135 // const char *reason_str = NULL;0
136 X509_STORE *trustedStore=NULL;
137 BioUniquePtr bioLogger(BIO_new(BIO_s_mem()), BIO_write_and_free);
139 std::vector<char> url(constUrl.begin(), constUrl.end());
142 if (!OCSP_parse_url(url.data(), &host, &port, &path, &use_ssl)) {
144 return CKM_API_OCSP_STATUS_INVALID_URL;
147 LogDebug("Host: " << host);
148 LogDebug("Port: " << port);
149 LogDebug("Path: " << path);
150 LogDebug("Use_ssl: " << use_ssl);
152 cbio = BIO_new_connect(host);
154 /*BIO_printf(bio_err, "Error creating connect BIO\n");*/
156 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
160 BIO_set_conn_port(cbio, port);
165 use_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
166 if (use_ssl_ctx == NULL) {
168 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
171 SSL_CTX_set_mode(use_ssl_ctx, SSL_MODE_AUTO_RETRY);
172 sbio = BIO_new_ssl(use_ssl_ctx, 1);
175 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
178 cbio = BIO_push(sbio, cbio);
181 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
185 if (BIO_do_connect(cbio) <= 0) {
186 LogDebug("Error in BIO_do_connect.");
187 ERR_print_errors(bioLogger.get());
202 host = port = path = NULL;
204 if (use_ssl && use_ssl_ctx) {
205 SSL_CTX_free(use_ssl_ctx);
214 return CKM_API_OCSP_STATUS_NET_ERROR;
217 req = OCSP_REQUEST_new();
220 LogDebug("Error in OCPS_REQUEST_new");
221 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
223 certid = OCSP_cert_to_id(NULL, cert, issuer);
225 LogDebug("Error in OCSP_cert_to_id");
226 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
229 if(OCSP_request_add0_id(req, certid) == NULL) {
230 LogDebug("Error in OCSP_request_add0_id");
231 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
234 resp = OCSP_sendreq_bio(cbio, path, req);
236 /* free some stuff we no longer need */
248 host = port = path = NULL;
250 if (use_ssl && use_ssl_ctx) {
251 SSL_CTX_free(use_ssl_ctx);
261 /*BIO_printf(bio_err, "Error querying OCSP responsder\n");*/
264 OCSP_REQUEST_free(req);
265 return CKM_API_OCSP_STATUS_NET_ERROR;
268 i = OCSP_response_status(resp);
270 if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
272 ERR_print_errors(bioLogger.get());
274 OCSP_REQUEST_free(req);
275 OCSP_RESPONSE_free(resp);
276 return CKM_API_OCSP_STATUS_REMOTE_ERROR;
279 bs = OCSP_response_get1_basic(resp);
282 ERR_print_errors(bioLogger.get());
284 OCSP_REQUEST_free(req);
285 OCSP_RESPONSE_free(resp);
287 LogDebug("Error in OCSP_response_get1_basic");
288 return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
291 if(trustedCerts != NULL) {
292 trustedStore = X509_STORE_new();
293 for(tmpIdx=0; tmpIdx<sk_X509_num(trustedCerts); tmpIdx++) {
294 X509_STORE_add_cert(trustedStore, sk_X509_value(trustedCerts, tmpIdx));
296 X509_STORE_add_cert(trustedStore, issuer);
299 int response = OCSP_basic_verify(bs, NULL, trustedStore, 0);
301 OCSP_REQUEST_free(req);
302 OCSP_RESPONSE_free(resp);
303 OCSP_BASICRESP_free(bs);
304 X509_STORE_free(trustedStore);
305 ERR_print_errors(bioLogger.get());
306 return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
309 if ((i = OCSP_check_nonce(req, bs)) <= 0) {
311 ERR_print_errors(bioLogger.get());
314 ERR_print_errors(bioLogger.get());
316 OCSP_REQUEST_free(req);
317 OCSP_RESPONSE_free(resp);
318 OCSP_BASICRESP_free(bs);
319 X509_STORE_free(trustedStore);
320 LogDebug("Error in OCSP_check_nonce");
321 return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
325 (void)X509_NAME_oneline(X509_get_subject_name(cert), subj_buf, 255);
326 if(!OCSP_resp_find_status(bs, certid, &ocspStatus, &reason,
327 &rev, &thisupd, &nextupd)) {
329 ERR_print_errors(bioLogger.get());
331 OCSP_RESPONSE_free(resp);
332 OCSP_REQUEST_free(req);
333 OCSP_BASICRESP_free(bs);
334 X509_STORE_free(trustedStore);
336 LogDebug("Error in OCSP_resp_find_status");
337 return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
341 /* Check validity: if invalid write to output BIO so we
342 * know which response this refers to.
344 if (!OCSP_check_validity(thisupd, nextupd, nsec, maxage)) {
346 ERR_print_errors(bioLogger.get());
348 OCSP_REQUEST_free(req);
349 OCSP_RESPONSE_free(resp);
350 OCSP_BASICRESP_free(bs);
351 X509_STORE_free(trustedStore);
353 LogDebug("Error in OCSP_check_validity");
354 return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
358 OCSP_REQUEST_free(req);
363 OCSP_RESPONSE_free(resp);
368 OCSP_BASICRESP_free(bs);
372 if(trustedStore != NULL) {
373 X509_STORE_free(trustedStore);
378 case V_OCSP_CERTSTATUS_GOOD:
379 return CKM_API_OCSP_STATUS_GOOD;
380 case V_OCSP_CERTSTATUS_REVOKED:
381 return CKM_API_OCSP_STATUS_REVOKED;
382 case V_OCSP_CERTSTATUS_UNKNOWN:
383 return CKM_API_OCSP_STATUS_UNKNOWN;
385 LogError("Internal openssl error: Certificate status have value is out of bound.");
386 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;