4f4477ec90b7bcab0709489a9270bc5c05eea9b6
[platform/core/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     // skip first 2 certificates
75     for (auto it=certificateChain.cbegin()+2; it != certificateChain.cend(); it++)
76     {
77         if (it->empty()) {
78             LogError("Error. Broken certificate chain.");
79             return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
80         }
81         sk_X509_push(trustedCerts.get(), it->getX509());
82     }
83
84     for (unsigned int i=0; i < 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;
88         }
89
90         X509 *cert   = certificateChain[i].getX509();
91         X509 *issuer = certificateChain[i+1].getX509();
92
93         std::string url = certificateChain[i].getOCSPURL();
94
95         if (url.empty()) {
96             LogError("Certificate in certchain[" << i << "] does not provide OCSP extension.");
97             unsupported = true;
98             continue;
99         }
100
101         int result = ocsp_verify(cert, issuer, trustedCerts.get(), url);
102         // remove first element from trustedCerts store
103         sk_X509_delete(trustedCerts.get(), 0);
104
105         if(result != CKM_API_OCSP_STATUS_GOOD) {
106             LogError("Fail to OCSP certification check. Errorcode=[" << result <<
107                 "], on certChain[" << i << "]");
108             return result;
109         }
110     }
111
112     if (unsupported)
113         return CKM_API_OCSP_STATUS_UNSUPPORTED;
114
115     return CKM_API_OCSP_STATUS_GOOD;
116 }
117
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;
123     BIO *cbio = 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;
129     int use_ssl = 0;
130     int ocspStatus = -1;
131     int i = 0 ,tmpIdx = 0;
132     long nsec = MAX_VALIDITY_PERIOD, maxage = -1;
133     char subj_buf[256];
134     int reason = 0;
135     //    const char *reason_str = NULL;0
136     X509_STORE *trustedStore=NULL;
137     BioUniquePtr bioLogger(BIO_new(BIO_s_mem()), BIO_write_and_free);
138
139     std::vector<char> url(constUrl.begin(), constUrl.end());
140     url.push_back(0);
141
142     if (!OCSP_parse_url(url.data(), &host, &port, &path, &use_ssl)) {
143         /* report error */
144         return CKM_API_OCSP_STATUS_INVALID_URL;
145     }
146
147     LogDebug("Host: " << host);
148     LogDebug("Port: " << port);
149     LogDebug("Path: " << path);
150     LogDebug("Use_ssl: " << use_ssl);
151
152     cbio = BIO_new_connect(host);
153     if (cbio == NULL) {
154         /*BIO_printf(bio_err, "Error creating connect BIO\n");*/
155         /* report error */
156         return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
157     }
158
159         if (port != NULL) {
160                 BIO_set_conn_port(cbio, port);
161     }
162
163     if (use_ssl == 1) {
164         BIO *sbio = NULL;
165         use_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
166         if (use_ssl_ctx == NULL) {
167             /* report error */
168             return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
169         }
170
171         SSL_CTX_set_mode(use_ssl_ctx, SSL_MODE_AUTO_RETRY);
172         sbio = BIO_new_ssl(use_ssl_ctx, 1);
173         if (sbio == NULL) {
174             /* report error */
175             return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
176         }
177
178         cbio = BIO_push(sbio, cbio);
179         if (cbio == NULL) {
180             /* report error */
181             return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
182         }
183     }
184
185     if (BIO_do_connect(cbio) <= 0) {
186         LogDebug("Error in BIO_do_connect.");
187         ERR_print_errors(bioLogger.get());
188         /* report error */
189
190         /* free stuff */
191         if (host != NULL) {
192             OPENSSL_free(host);
193         }
194
195         if (port != NULL) {
196             OPENSSL_free(port);
197         }
198
199         if (path != NULL) {
200             OPENSSL_free(path);
201         }
202         host = port = path = NULL;
203
204         if (use_ssl && use_ssl_ctx) {
205             SSL_CTX_free(use_ssl_ctx);
206         }
207         use_ssl_ctx = NULL;
208
209         if (cbio != NULL) {
210             BIO_free_all(cbio);
211         }
212         cbio = NULL;
213
214         return CKM_API_OCSP_STATUS_NET_ERROR;
215     }
216
217     req = OCSP_REQUEST_new();
218
219     if(req == NULL) {
220         LogDebug("Error in OCPS_REQUEST_new");
221         return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
222     }
223     certid = OCSP_cert_to_id(NULL, cert, issuer);
224     if(certid == NULL)  {
225         LogDebug("Error in OCSP_cert_to_id");
226         return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
227     }
228
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;
232     }
233
234     resp = OCSP_sendreq_bio(cbio, path, req);
235
236     /* free some stuff we no longer need */
237     if (host != NULL) {
238         OPENSSL_free(host);
239     }
240
241     if (port != NULL) {
242         OPENSSL_free(port);
243     }
244
245     if (path != NULL) {
246         OPENSSL_free(path);
247     }
248     host = port = path = NULL;
249
250     if (use_ssl && use_ssl_ctx) {
251         SSL_CTX_free(use_ssl_ctx);
252     }
253     use_ssl_ctx = NULL;
254
255     if (cbio != NULL) {
256         BIO_free_all(cbio);
257     }
258     cbio = NULL;
259
260     if (!resp) {
261         /*BIO_printf(bio_err, "Error querying OCSP responsder\n");*/
262         /* report error */
263         /* free stuff */
264         OCSP_REQUEST_free(req);
265         return CKM_API_OCSP_STATUS_NET_ERROR;
266     }
267
268     i = OCSP_response_status(resp);
269
270     if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
271         /* report error */
272         ERR_print_errors(bioLogger.get());
273         /* free stuff */
274         OCSP_REQUEST_free(req);
275         OCSP_RESPONSE_free(resp);
276         return CKM_API_OCSP_STATUS_REMOTE_ERROR;
277     }
278
279     bs = OCSP_response_get1_basic(resp);
280     if (!bs) {
281         /* report error */
282         ERR_print_errors(bioLogger.get());
283         /* free stuff */
284         OCSP_REQUEST_free(req);
285         OCSP_RESPONSE_free(resp);
286
287         LogDebug("Error in OCSP_response_get1_basic");
288         return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
289     }
290
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));
295         }
296         X509_STORE_add_cert(trustedStore, issuer);
297     }
298
299     int response = OCSP_basic_verify(bs, NULL, trustedStore, 0);
300     if (response <= 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;
307     }
308
309     if ((i = OCSP_check_nonce(req, bs)) <= 0) {
310         if (i == -1) {
311             ERR_print_errors(bioLogger.get());
312         } else {
313             /* report error */
314             ERR_print_errors(bioLogger.get());
315             /* free stuff */
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;
322         }
323     }
324
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)) {
328         /* report error */
329         ERR_print_errors(bioLogger.get());
330         /* free stuff */
331         OCSP_RESPONSE_free(resp);
332         OCSP_REQUEST_free(req);
333         OCSP_BASICRESP_free(bs);
334         X509_STORE_free(trustedStore);
335
336         LogDebug("Error in OCSP_resp_find_status");
337         return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
338     }
339
340
341     /* Check validity: if invalid write to output BIO so we
342      * know which response this refers to.
343      */
344     if (!OCSP_check_validity(thisupd, nextupd, nsec, maxage)) {
345         /* report error */
346         ERR_print_errors(bioLogger.get());
347         /* free stuff */
348         OCSP_REQUEST_free(req);
349         OCSP_RESPONSE_free(resp);
350         OCSP_BASICRESP_free(bs);
351         X509_STORE_free(trustedStore);
352
353         LogDebug("Error in OCSP_check_validity");
354         return CKM_API_OCSP_STATUS_INVALID_RESPONSE;
355     }
356
357     if (req != NULL) {
358         OCSP_REQUEST_free(req);
359         req = NULL;
360     }
361
362     if (resp != NULL) {
363         OCSP_RESPONSE_free(resp);
364         resp = NULL;
365     }
366
367     if (bs != NULL) {
368         OCSP_BASICRESP_free(bs);
369         bs = NULL;
370     }
371
372     if(trustedStore != NULL) {
373         X509_STORE_free(trustedStore);
374         trustedStore = NULL;
375     }
376
377     switch(ocspStatus) {
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;
384     default:
385         LogError("Internal openssl error: Certificate status have value is out of bound.");
386         return CKM_API_OCSP_STATUS_INTERNAL_ERROR;
387     }
388 }
389
390 } // namespace CKM
391