tizen 2.4 release
[framework/security/key-manager.git] / src / manager / service / ocsp.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co.
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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
15  *
16  *
17  * @file        ocsp.cpp
18  * @author      Dongsun Lee (ds73.lee@samsung.com)
19  * @version     1.0
20  * @brief       OCSP implementation.
21  */
22
23 #include <string>
24 #include <ocsp.h>
25 #include <openssl/pem.h>
26 #include <openssl/ocsp.h>
27 #include <openssl/err.h>
28 #include <openssl/ssl.h>
29 #include <fts.h>
30 #include <unistd.h>
31 #include <dpl/log/log.h>
32 #include <certificate-impl.h>
33 #include <openssl_utils.h>
34 #include <ckm/ckm-error.h>
35
36 /* Maximum leeway in validity period: default 5 minutes */
37 #define MAX_VALIDITY_PERIOD     (5 * 60)
38
39 namespace CKM {
40
41 namespace {
42 typedef std::unique_ptr<BIO, std::function<void(BIO*)>> BioUniquePtr;
43
44 void BIO_write_and_free(BIO* bio) {
45     if (!bio)
46         return;
47
48     std::vector<char> message(1024);
49     int size = BIO_read(bio, message.data(), message.size());
50     if (size > 0) {
51         message.resize(size);
52         LogError("OCSP error description:" << std::string(message.begin(), message.end()));
53     }
54
55     BIO_free_all(bio);
56 }
57
58 } // namespace anonymous
59
60 OCSPModule::OCSPModule() {
61     // Do nothing.
62 }
63
64 OCSPModule::~OCSPModule(){
65     // Do nothing.
66 }
67
68 int OCSPModule::verify(const CertificateImplVector &certificateChain) {
69     bool unsupported = false; // ocsp is unsupported in certificate in chain (except root CA)
70
71     // create trusted store
72     X509_STACK_PTR trustedCerts = create_x509_stack();
73
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;
78         }
79         sk_X509_push(trustedCerts.get(), certificateChain[i].getX509());
80         // these trusted certs will be changed while verifying ocsp status.
81     }
82
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;
87         }
88
89         X509 *cert   = certificateChain[i].getX509();
90         X509 *issuer = certificateChain[i+1].getX509();
91
92         std::string url = certificateChain[i].getOCSPURL();
93
94         if (url.empty()) {
95             LogError("Certificate in certchain[" << i << "] does not provide OCSP extension.");
96             unsupported = true;
97             continue;
98         }
99
100         int result = ocsp_verify(cert, issuer, trustedCerts.get(), url);
101         // remove first element from trustedCerts store
102         sk_X509_delete(trustedCerts.get(), 0);
103
104         if(result != CKM_API_OCSP_STATUS_GOOD) {
105             LogError("Fail to OCSP certification check. Errorcode=[" << result <<
106                 "], on certChain[" << i << "]");
107             return result;
108         }
109     }
110
111     if (unsupported)
112         return CKM_API_OCSP_STATUS_UNSUPPORTED;
113
114     return CKM_API_OCSP_STATUS_GOOD;
115 }
116
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;
122     BIO *cbio = 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;
128     int use_ssl = 0;
129     int ocspStatus = -1;
130     int i = 0 ,tmpIdx = 0;
131     long nsec = MAX_VALIDITY_PERIOD, maxage = -1;
132     char subj_buf[256];
133     int reason = 0;
134     //    const char *reason_str = NULL;0
135     X509_STORE *trustedStore=NULL;
136     BioUniquePtr bioLogger(BIO_new(BIO_s_mem()), BIO_write_and_free);
137
138     std::vector<char> url(constUrl.begin(), constUrl.end());
139     url.push_back(0);
140
141     if (!OCSP_parse_url(url.data(), &host, &port, &path, &use_ssl)) {
142         /* report error */
143         return CKM_API_OCSP_STATUS_INVALID_URL;
144     }
145
146     LogDebug("Host: " << host);
147     LogDebug("Port: " << port);
148     LogDebug("Path: " << path);
149     LogDebug("Use_ssl: " << use_ssl);
150
151     cbio = BIO_new_connect(host);
152     if (cbio == NULL) {
153         /*BIO_printf(bio_err, "Error creating connect BIO\n");*/
154         /* report error */
155         return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
156     }
157
158         if (port != NULL) {
159                 BIO_set_conn_port(cbio, port);
160     }
161
162     if (use_ssl == 1) {
163         BIO *sbio = NULL;
164         use_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
165         if (use_ssl_ctx == NULL) {
166             /* report error */
167             return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
168         }
169
170         SSL_CTX_set_mode(use_ssl_ctx, SSL_MODE_AUTO_RETRY);
171         sbio = BIO_new_ssl(use_ssl_ctx, 1);
172         if (sbio == NULL) {
173             /* report error */
174             return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
175         }
176
177         cbio = BIO_push(sbio, cbio);
178         if (cbio == NULL) {
179             /* report error */
180             return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
181         }
182     }
183
184     if (BIO_do_connect(cbio) <= 0) {
185         LogError("Error in BIO_do_connect.");
186         ERR_print_errors(bioLogger.get());
187         /* report error */
188
189         /* free stuff */
190         if (host != NULL) {
191             OPENSSL_free(host);
192         }
193
194         if (port != NULL) {
195             OPENSSL_free(port);
196         }
197
198         if (path != NULL) {
199             OPENSSL_free(path);
200         }
201         host = port = path = NULL;
202
203         if (use_ssl && use_ssl_ctx) {
204             SSL_CTX_free(use_ssl_ctx);
205         }
206         use_ssl_ctx = NULL;
207
208         if (cbio != NULL) {
209             BIO_free_all(cbio);
210         }
211         cbio = NULL;
212
213         return CKM_API_OCSP_STATUS_NET_ERROR;
214     }
215
216     req = OCSP_REQUEST_new();
217
218     if(req == NULL) {
219         LogError("Error in OCPS_REQUEST_new");
220         return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
221     }
222     certid = OCSP_cert_to_id(NULL, cert, issuer);
223     if(certid == NULL)  {
224         LogError("Error in OCSP_cert_to_id");
225         return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
226     }
227
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;
231     }
232
233     resp = OCSP_sendreq_bio(cbio, path, req);
234
235     /* free some stuff we no longer need */
236     if (host != NULL) {
237         OPENSSL_free(host);
238     }
239
240     if (port != NULL) {
241         OPENSSL_free(port);
242     }
243
244     if (path != NULL) {
245         OPENSSL_free(path);
246     }
247     host = port = path = NULL;
248
249     if (use_ssl && use_ssl_ctx) {
250         SSL_CTX_free(use_ssl_ctx);
251     }
252     use_ssl_ctx = NULL;
253
254     if (cbio != NULL) {
255         BIO_free_all(cbio);
256     }
257     cbio = NULL;
258
259     if (!resp) {
260         /*BIO_printf(bio_err, "Error querying OCSP responsder\n");*/
261         /* report error */
262         /* free stuff */
263         OCSP_REQUEST_free(req);
264         return CKM_API_OCSP_STATUS_NET_ERROR;
265     }
266
267     i = OCSP_response_status(resp);
268
269     if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
270         /* report error */
271         ERR_print_errors(bioLogger.get());
272         /* free stuff */
273         OCSP_REQUEST_free(req);
274         OCSP_RESPONSE_free(resp);
275         return CKM_API_OCSP_STATUS_REMOTE_ERROR;
276     }
277
278     bs = OCSP_response_get1_basic(resp);
279     if (!bs) {
280         /* report error */
281         ERR_print_errors(bioLogger.get());
282         /* free stuff */
283         OCSP_REQUEST_free(req);
284         OCSP_RESPONSE_free(resp);
285
286         LogError("Error in OCSP_response_get1_basic");
287         return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
288     }
289
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));
294         }
295         X509_STORE_add_cert(trustedStore, issuer);
296     }
297
298     int response = OCSP_basic_verify(bs, NULL, trustedStore, 0);
299     if (response <= 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;
306     }
307
308     if ((i = OCSP_check_nonce(req, bs)) <= 0) {
309         if (i == -1) {
310             ERR_print_errors(bioLogger.get());
311         } else {
312             /* report error */
313             ERR_print_errors(bioLogger.get());
314             /* free stuff */
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;
321         }
322     }
323
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)) {
327         /* report error */
328         ERR_print_errors(bioLogger.get());
329         /* free stuff */
330         OCSP_RESPONSE_free(resp);
331         OCSP_REQUEST_free(req);
332         OCSP_BASICRESP_free(bs);
333         X509_STORE_free(trustedStore);
334
335         LogError("Error in OCSP_resp_find_status");
336         return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
337     }
338
339
340     /* Check validity: if invalid write to output BIO so we
341      * know which response this refers to.
342      */
343     if (!OCSP_check_validity(thisupd, nextupd, nsec, maxage)) {
344         /* report error */
345         ERR_print_errors(bioLogger.get());
346         /* free stuff */
347         OCSP_REQUEST_free(req);
348         OCSP_RESPONSE_free(resp);
349         OCSP_BASICRESP_free(bs);
350         X509_STORE_free(trustedStore);
351
352         LogError("Error in OCSP_check_validity");
353         return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
354     }
355
356     if (req != NULL) {
357         OCSP_REQUEST_free(req);
358         req = NULL;
359     }
360
361     if (resp != NULL) {
362         OCSP_RESPONSE_free(resp);
363         resp = NULL;
364     }
365
366     if (bs != NULL) {
367         OCSP_BASICRESP_free(bs);
368         bs = NULL;
369     }
370
371     if(trustedStore != NULL) {
372         X509_STORE_free(trustedStore);
373         trustedStore = NULL;
374     }
375
376     switch(ocspStatus) {
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;
383     default:
384         LogError("Internal openssl error: Certificate status have value is out of bound.");
385         return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
386     }
387 }
388
389 } // namespace CKM
390