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 for (unsigned int i=1; i < certificateChain.size(); i++) { // except leaf certificate
75 if (certificateChain[i].empty()) {
76 LogError("Error. Broken certificate chain.");
77 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
79 sk_X509_push(trustedCerts.get(), certificateChain[i].getX509());
80 // these trusted certs will be changed while verifying ocsp status.
83 for (unsigned int i=0; i < certificateChain.size() -1; i++) {// except root certificate
84 if (certificateChain[i].empty() || certificateChain[i+1].empty()) {
85 LogError("Error. Broken certificate chain.");
86 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
89 X509 *cert = certificateChain[i].getX509();
90 X509 *issuer = certificateChain[i+1].getX509();
92 std::string url = certificateChain[i].getOCSPURL();
95 LogError("Certificate in certchain[" << i << "] does not provide OCSP extension.");
100 int result = ocsp_verify(cert, issuer, trustedCerts.get(), url);
101 // remove first element from trustedCerts store
102 sk_X509_delete(trustedCerts.get(), 0);
104 if(result != CKM_API_OCSP_STATUS_GOOD) {
105 LogError("Fail to OCSP certification check. Errorcode=[" << result <<
106 "], on certChain[" << i << "]");
112 return CKM_API_OCSP_STATUS_UNSUPPORTED;
114 return CKM_API_OCSP_STATUS_GOOD;
117 int OCSPModule::ocsp_verify(X509 *cert, X509 *issuer, STACK_OF(X509) *trustedCerts, const std::string &constUrl) {
118 OCSP_REQUEST *req = NULL;
119 OCSP_RESPONSE *resp = NULL;
120 OCSP_BASICRESP *bs = NULL;
121 OCSP_CERTID *certid = NULL;
123 SSL_CTX *use_ssl_ctx = NULL;
124 char *host = NULL, *port = NULL, *path = NULL;
125 ASN1_GENERALIZEDTIME *rev = NULL;
126 ASN1_GENERALIZEDTIME *thisupd = NULL;
127 ASN1_GENERALIZEDTIME *nextupd = NULL;
130 int i = 0 ,tmpIdx = 0;
131 long nsec = MAX_VALIDITY_PERIOD, maxage = -1;
134 // const char *reason_str = NULL;0
135 X509_STORE *trustedStore=NULL;
136 BioUniquePtr bioLogger(BIO_new(BIO_s_mem()), BIO_write_and_free);
138 std::vector<char> url(constUrl.begin(), constUrl.end());
141 if (!OCSP_parse_url(url.data(), &host, &port, &path, &use_ssl)) {
143 return CKM_API_OCSP_STATUS_INVALID_URL;
146 LogDebug("Host: " << host);
147 LogDebug("Port: " << port);
148 LogDebug("Path: " << path);
149 LogDebug("Use_ssl: " << use_ssl);
151 cbio = BIO_new_connect(host);
153 /*BIO_printf(bio_err, "Error creating connect BIO\n");*/
155 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
159 BIO_set_conn_port(cbio, port);
164 use_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
165 if (use_ssl_ctx == NULL) {
167 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
170 SSL_CTX_set_mode(use_ssl_ctx, SSL_MODE_AUTO_RETRY);
171 sbio = BIO_new_ssl(use_ssl_ctx, 1);
174 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
177 cbio = BIO_push(sbio, cbio);
180 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
184 if (BIO_do_connect(cbio) <= 0) {
185 LogError("Error in BIO_do_connect.");
186 ERR_print_errors(bioLogger.get());
201 host = port = path = NULL;
203 if (use_ssl && use_ssl_ctx) {
204 SSL_CTX_free(use_ssl_ctx);
213 return CKM_API_OCSP_STATUS_NET_ERROR;
216 req = OCSP_REQUEST_new();
219 LogError("Error in OCPS_REQUEST_new");
220 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
222 certid = OCSP_cert_to_id(NULL, cert, issuer);
224 LogError("Error in OCSP_cert_to_id");
225 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
228 if(OCSP_request_add0_id(req, certid) == NULL) {
229 LogError("Error in OCSP_request_add0_id");
230 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
233 resp = OCSP_sendreq_bio(cbio, path, req);
235 /* free some stuff we no longer need */
247 host = port = path = NULL;
249 if (use_ssl && use_ssl_ctx) {
250 SSL_CTX_free(use_ssl_ctx);
260 /*BIO_printf(bio_err, "Error querying OCSP responsder\n");*/
263 OCSP_REQUEST_free(req);
264 return CKM_API_OCSP_STATUS_NET_ERROR;
267 i = OCSP_response_status(resp);
269 if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
271 ERR_print_errors(bioLogger.get());
273 OCSP_REQUEST_free(req);
274 OCSP_RESPONSE_free(resp);
275 return CKM_API_OCSP_STATUS_REMOTE_ERROR;
278 bs = OCSP_response_get1_basic(resp);
281 ERR_print_errors(bioLogger.get());
283 OCSP_REQUEST_free(req);
284 OCSP_RESPONSE_free(resp);
286 LogError("Error in OCSP_response_get1_basic");
287 return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
290 if(trustedCerts != NULL) {
291 trustedStore = X509_STORE_new();
292 for(tmpIdx=0; tmpIdx<sk_X509_num(trustedCerts); tmpIdx++) {
293 X509_STORE_add_cert(trustedStore, sk_X509_value(trustedCerts, tmpIdx));
295 X509_STORE_add_cert(trustedStore, issuer);
298 int response = OCSP_basic_verify(bs, NULL, trustedStore, 0);
300 OCSP_REQUEST_free(req);
301 OCSP_RESPONSE_free(resp);
302 OCSP_BASICRESP_free(bs);
303 X509_STORE_free(trustedStore);
304 ERR_print_errors(bioLogger.get());
305 return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
308 if ((i = OCSP_check_nonce(req, bs)) <= 0) {
310 ERR_print_errors(bioLogger.get());
313 ERR_print_errors(bioLogger.get());
315 OCSP_REQUEST_free(req);
316 OCSP_RESPONSE_free(resp);
317 OCSP_BASICRESP_free(bs);
318 X509_STORE_free(trustedStore);
319 LogError("Error in OCSP_check_nonce");
320 return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
324 (void)X509_NAME_oneline(X509_get_subject_name(cert), subj_buf, 255);
325 if(!OCSP_resp_find_status(bs, certid, &ocspStatus, &reason,
326 &rev, &thisupd, &nextupd)) {
328 ERR_print_errors(bioLogger.get());
330 OCSP_RESPONSE_free(resp);
331 OCSP_REQUEST_free(req);
332 OCSP_BASICRESP_free(bs);
333 X509_STORE_free(trustedStore);
335 LogError("Error in OCSP_resp_find_status");
336 return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
340 /* Check validity: if invalid write to output BIO so we
341 * know which response this refers to.
343 if (!OCSP_check_validity(thisupd, nextupd, nsec, maxage)) {
345 ERR_print_errors(bioLogger.get());
347 OCSP_REQUEST_free(req);
348 OCSP_RESPONSE_free(resp);
349 OCSP_BASICRESP_free(bs);
350 X509_STORE_free(trustedStore);
352 LogError("Error in OCSP_check_validity");
353 return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
357 OCSP_REQUEST_free(req);
362 OCSP_RESPONSE_free(resp);
367 OCSP_BASICRESP_free(bs);
371 if(trustedStore != NULL) {
372 X509_STORE_free(trustedStore);
377 case V_OCSP_CERTSTATUS_GOOD:
378 return CKM_API_OCSP_STATUS_GOOD;
379 case V_OCSP_CERTSTATUS_REVOKED:
380 return CKM_API_OCSP_STATUS_REVOKED;
381 case V_OCSP_CERTSTATUS_UNKNOWN:
382 return CKM_API_OCSP_STATUS_UNKNOWN;
384 LogError("Internal openssl error: Certificate status have value is out of bound.");
385 return CKM_API_OCSP_STATUS_INTERNAL_ERROR;