dde4750a6318d1745813b3e5bb69197d621be92a
[platform/core/security/cert-svc.git] / src / vcore / CertificateCollection.cpp
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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        CertificateCollection.cpp
18  * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
19  * @author      Kyungwook Tak (k.tak@samsung.com)
20  * @version     1.0
21  * @brief       Handles certificate chain, make it complete and sort
22  */
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stddef.h>
27 #include <unistd.h>
28 #include <dirent.h>
29
30 #include <memory>
31 #include <functional>
32
33 #include <openssl/pem.h>
34 #include <openssl/x509.h>
35
36 #include <cert-svc/cinstance.h>
37 #include <cert-svc/ccert.h>
38 #include <cert-svc/cprimitives.h>
39
40 #include <dpl/log/log.h>
41
42 #include "vcore/Base64.h"
43
44 #include "vcore/CertificateCollection.h"
45
46 namespace {
47
48 using namespace ValidationCore;
49
50 inline std::string toBinaryString(int data)
51 {
52         char buffer[sizeof(int)];
53         memcpy(buffer, &data, sizeof(int));
54         return std::string(buffer, sizeof(int));
55 }
56
57 bool isHashMatchedName(const std::string &name, const std::string &hash)
58 {
59         if (name.compare(0, 8, hash) != 0)
60                 return false;
61
62         return true;
63 }
64
65 bool isHashMatchedFile(const std::string &path, const std::string &hash)
66 {
67         CertificatePtr certPtr = Certificate::createFromFile(path);
68         std::string name = certPtr->getNameHash(Certificate::FIELD_SUBJECT);
69         return isHashMatchedName(name, hash);
70 }
71
72 CertificatePtr searchCert(const std::string &dir, const CertificatePtr &certPtr, bool withHash)
73 {
74         try {
75                 std::string hash = certPtr->getNameHash(Certificate::FIELD_ISSUER);
76                 std::unique_ptr<DIR, std::function<int(DIR *)>> dp(::opendir(dir.c_str()), ::closedir);
77
78                 if (dp.get() == NULL) {
79                         LogError("Failed open dir[" << dir << "]");
80                         return CertificatePtr();
81                 }
82
83                 size_t len = offsetof(struct dirent, d_name) + pathconf(dir.c_str(), _PC_NAME_MAX) + 1;
84                 std::unique_ptr<struct dirent, std::function<void(void *)>>
85                                 pEntry(static_cast<struct dirent *>(::malloc(len)), ::free);
86                 struct dirent *dirp = NULL;
87                 int ret = 0;
88
89                 while ((ret = readdir_r(dp.get(), pEntry.get(), &dirp)) == 0 && dirp) {
90                         if (dirp->d_type == DT_DIR)
91                                 continue;
92
93                         std::string candidatePath(dir);
94                         candidatePath += "/";
95                         candidatePath += dirp->d_name;
96
97                         if (withHash) {
98                                 if (!isHashMatchedName(dirp->d_name, hash))
99                                         continue;
100                         } else {
101                                 if (!isHashMatchedFile(candidatePath, hash))
102                                         continue;
103                         }
104
105                         LogDebug("Found hash matched file! : " << candidatePath);
106                         CertificatePtr candidate = Certificate::createFromFile(candidatePath);
107
108                         if (candidate->getOneLine().compare(certPtr->getOneLine(Certificate::FIELD_ISSUER)) != 0)
109                                 continue;
110
111                         return candidate;
112                 }
113
114                 if (ret != 0) {
115                         LogError("readdir_r error. ret[" << ret << "]");
116                         return CertificatePtr();
117                 }
118
119                 LogWarning("cert not found by hash[" << hash << "]");
120                 return CertificatePtr();
121         } catch (const Certificate::Exception::Base &e) {
122                 VcoreThrowMsg(
123                         CertificateCollection::Exception::CertificateError,
124                         "Error in handling certificate : " << e.DumpToString());
125         } catch (const std::exception &e) {
126                 VcoreThrowMsg(
127                         CertificateCollection::Exception::InternalError,
128                         "std::exception occured : " << e.what());
129         } catch (...) {
130                 VcoreThrowMsg(
131                         CertificateCollection::Exception::InternalError,
132                         "Unknown exception in CertificateCollection.");
133         }
134 }
135
136 CertificatePtr getIssuerCertFromStore(const CertificatePtr &certPtr)
137 {
138         LogDebug("Start to get issuer from store.");
139         CertificatePtr found = searchCert(TZ_SYS_CA_CERTS_TIZEN, certPtr, false);
140
141         if (found.get() != NULL) {
142                 LogDebug("Found issuer cert in tizen root CA dir");
143                 return found;
144         }
145
146         return searchCert(TZ_SYS_CA_CERTS, certPtr, true);
147 }
148
149 } // namespace
150
151 namespace ValidationCore {
152
153 CertificateCollection::CertificateCollection()
154         : m_collectionStatus(COLLECTION_UNSORTED)
155 {}
156
157 void CertificateCollection::clear(void)
158 {
159         m_collectionStatus = COLLECTION_UNSORTED;
160         m_certList.clear();
161 }
162
163 void CertificateCollection::load(const CertificateList &certList)
164 {
165         m_collectionStatus = COLLECTION_UNSORTED;
166         std::copy(certList.begin(),
167                           certList.end(),
168                           std::back_inserter(m_certList));
169 }
170
171 std::string CertificateCollection::toBase64String() const
172 {
173         std::ostringstream output;
174         int certNum = m_certList.size();
175         output << toBinaryString(certNum);
176
177         for (auto i = m_certList.begin(); i != m_certList.end(); ++i) {
178                 std::string derCert = (*i)->getDER();
179                 output << toBinaryString(derCert.size());
180                 output << derCert;
181         }
182
183         Base64Encoder base64;
184         base64.reset();
185         base64.append(output.str());
186         base64.finalize();
187         return base64.get();
188 }
189
190 CertificateList CertificateCollection::getCertificateList() const
191 {
192         return m_certList;
193 }
194
195 bool CertificateCollection::isChain() const
196 {
197         if (COLLECTION_SORTED != m_collectionStatus)
198                 VcoreThrowMsg(CertificateCollection::Exception::WrongUsage,
199                                           "You must sort certificate first");
200
201         return (COLLECTION_SORTED == m_collectionStatus) ? true : false;
202 }
203
204 bool CertificateCollection::sort()
205 {
206         if (COLLECTION_UNSORTED == m_collectionStatus) {
207                 sortCollection();
208         }
209
210         return (COLLECTION_SORTED == m_collectionStatus) ? true : false;
211 }
212
213 CertificateList CertificateCollection::getChain() const
214 {
215         if (COLLECTION_SORTED != m_collectionStatus)
216                 VcoreThrowMsg(CertificateCollection::Exception::WrongUsage,
217                                           "You must sort certificates first");
218
219         return m_certList;
220 }
221
222 void CertificateCollection::sortCollection()
223 {
224         // sorting is not necessary
225         if (m_certList.empty()) {
226                 m_collectionStatus = COLLECTION_SORTED;
227                 return;
228         }
229
230         CertificateList sorted;
231         std::map<std::string, CertificatePtr> subTransl;
232         std::map<std::string, CertificatePtr> issTransl;
233
234         // Sort all certificate by subject
235         for (auto it = m_certList.begin(); it != m_certList.end(); ++it) {
236                 subTransl.insert(std::make_pair((*it)->getOneLine(), (*it)));
237         }
238
239         // We need one start certificate
240         sorted.push_back(subTransl.begin()->second);
241         subTransl.erase(subTransl.begin());
242
243         // Get the issuer from front certificate and find certificate with this subject in subTransl.
244         // Add this certificate to the front.
245         while (!subTransl.empty()) {
246                 std::string issuer = sorted.back()->getOneLine(Certificate::FIELD_ISSUER);
247                 auto it = subTransl.find(issuer);
248
249                 if (it == subTransl.end()) {
250                         break;
251                 }
252
253                 sorted.push_back(it->second);
254                 subTransl.erase(it);
255         }
256
257         // Sort all certificates by issuer
258         for (auto it = subTransl.begin(); it != subTransl.end(); ++it) {
259                 issTransl.insert(std::make_pair(it->second->getOneLine(Certificate::FIELD_ISSUER), it->second));
260         }
261
262         // Get the subject from last certificate and find certificate with such issuer in issTransl.
263         // Add this certificate at end.
264         while (!issTransl.empty()) {
265                 std::string sub = sorted.front()->getOneLine();
266                 auto it = issTransl.find(sub);
267
268                 if (it == issTransl.end()) {
269                         break;
270                 }
271
272                 sorted.push_front(it->second);
273                 issTransl.erase(it);
274         }
275
276         if (!issTransl.empty()) {
277                 LogWarning("Certificates don't form a valid chain.");
278                 m_collectionStatus = COLLECTION_CHAIN_BROKEN;
279                 return;
280         }
281
282         m_collectionStatus = COLLECTION_SORTED;
283         m_certList = sorted;
284 }
285
286 /*
287  *  Precondition : cert list sorted and has more than one cert
288  */
289 bool CertificateCollection::completeCertificateChain()
290 {
291         CertificatePtr last = m_certList.back();
292
293         if (last->isRootCert())
294                 return true;
295
296         CertificatePtr rootCert = getIssuerCertFromStore(last);
297
298         if (!rootCert.get())
299                 return false;
300
301         if (!rootCert->isRootCert())
302                 return false;
303
304         m_certList.push_back(rootCert);
305         return true;
306 }
307
308 size_t CertificateCollection::size() const
309 {
310         return m_certList.size();
311 }
312
313 bool CertificateCollection::empty() const
314 {
315         return m_certList.empty();
316 }
317
318 CertificateCollection::const_iterator CertificateCollection::begin() const
319 {
320         return m_certList.begin();
321 }
322
323 CertificateCollection::const_iterator CertificateCollection::end() const
324 {
325         return m_certList.end();
326 }
327
328 CertificatePtr CertificateCollection::back() const
329 {
330         return m_certList.back();
331 }
332
333 } // namespace ValidationCore
334