Replace deprecated readdir_r with readdir
[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 ValidationCore {
47
48 namespace {
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 struct dirent *readdir(DIR *dirp) {
73         errno = 0;
74         auto ret = ::readdir(dirp);
75         if (errno != 0)
76                 LogWarning("Error read dir.");
77         return ret;
78 }
79
80 CertificatePtr searchCert(const std::string &dir, const CertificatePtr &certPtr, bool withHash)
81 {
82         try {
83                 std::string hash = certPtr->getNameHash(Certificate::FIELD_ISSUER);
84                 std::unique_ptr<DIR, std::function<int(DIR *)>> dp(::opendir(dir.c_str()),
85                                                                                                                    ::closedir);
86
87                 if (dp == nullptr) {
88                         LogError("Failed open dir[" << dir << "]");
89                         return CertificatePtr();
90                 }
91
92                 while (auto dirp = ValidationCore::readdir(dp.get())) {
93                         if (dirp->d_type == DT_DIR)
94                                 continue;
95
96                         auto fullPath = dir + "/" + dirp->d_name;
97                         if (withHash) {
98                                 if (!isHashMatchedName(dirp->d_name, hash))
99                                         continue;
100                         } else {
101                                 if (!isHashMatchedFile(fullPath, hash))
102                                         continue;
103                         }
104
105                         LogDebug("Found hash matched file! : " << fullPath);
106                         auto candidate = Certificate::createFromFile(fullPath);
107                         if (candidate->getOneLine().compare(
108                                 certPtr->getOneLine(Certificate::FIELD_ISSUER)) != 0)
109                                 continue;
110
111                         return candidate;
112                 }
113
114                 LogWarning("cert not found by hash[" << hash << "]");
115                 return CertificatePtr();
116         } catch (const Certificate::Exception::Base &e) {
117                 VcoreThrowMsg(
118                         CertificateCollection::Exception::CertificateError,
119                         "Error in handling certificate : " << e.DumpToString());
120         } catch (const std::exception &e) {
121                 VcoreThrowMsg(
122                         CertificateCollection::Exception::InternalError,
123                         "std::exception occured : " << e.what());
124         } catch (...) {
125                 VcoreThrowMsg(
126                         CertificateCollection::Exception::InternalError,
127                         "Unknown exception in CertificateCollection.");
128         }
129 }
130
131 CertificatePtr getIssuerCertFromStore(const CertificatePtr &certPtr)
132 {
133         LogDebug("Start to get issuer from store.");
134         CertificatePtr found = searchCert(TZ_SYS_CA_CERTS_TIZEN, certPtr, false);
135
136         if (found.get() != NULL) {
137                 LogDebug("Found issuer cert in tizen root CA dir");
138                 return found;
139         }
140
141         return searchCert(TZ_SYS_CA_CERTS, certPtr, true);
142 }
143
144 } // anonymous namespace
145
146 CertificateCollection::CertificateCollection()
147         : m_collectionStatus(COLLECTION_UNSORTED)
148 {}
149
150 void CertificateCollection::clear(void)
151 {
152         m_collectionStatus = COLLECTION_UNSORTED;
153         m_certList.clear();
154 }
155
156 void CertificateCollection::load(const CertificateList &certList)
157 {
158         m_collectionStatus = COLLECTION_UNSORTED;
159         std::copy(certList.begin(),
160                           certList.end(),
161                           std::back_inserter(m_certList));
162 }
163
164 std::string CertificateCollection::toBase64String() const
165 {
166         std::ostringstream output;
167         int certNum = m_certList.size();
168         output << toBinaryString(certNum);
169
170         for (auto i = m_certList.begin(); i != m_certList.end(); ++i) {
171                 std::string derCert = (*i)->getDER();
172                 output << toBinaryString(derCert.size());
173                 output << derCert;
174         }
175
176         Base64Encoder base64;
177         base64.reset();
178         base64.append(output.str());
179         base64.finalize();
180         return base64.get();
181 }
182
183 CertificateList CertificateCollection::getCertificateList() const
184 {
185         return m_certList;
186 }
187
188 bool CertificateCollection::isChain() const
189 {
190         if (COLLECTION_SORTED != m_collectionStatus)
191                 VcoreThrowMsg(CertificateCollection::Exception::WrongUsage,
192                                           "You must sort certificate first");
193
194         return (COLLECTION_SORTED == m_collectionStatus) ? true : false;
195 }
196
197 bool CertificateCollection::sort()
198 {
199         if (COLLECTION_UNSORTED == m_collectionStatus) {
200                 sortCollection();
201         }
202
203         return (COLLECTION_SORTED == m_collectionStatus) ? true : false;
204 }
205
206 CertificateList CertificateCollection::getChain() const
207 {
208         if (COLLECTION_SORTED != m_collectionStatus)
209                 VcoreThrowMsg(CertificateCollection::Exception::WrongUsage,
210                                           "You must sort certificates first");
211
212         return m_certList;
213 }
214
215 void CertificateCollection::sortCollection()
216 {
217         // sorting is not necessary
218         if (m_certList.empty()) {
219                 m_collectionStatus = COLLECTION_SORTED;
220                 return;
221         }
222
223         CertificateList sorted;
224         std::map<std::string, CertificatePtr> subTransl;
225         std::map<std::string, CertificatePtr> issTransl;
226
227         // Sort all certificate by subject
228         for (auto it = m_certList.begin(); it != m_certList.end(); ++it) {
229                 subTransl.insert(std::make_pair((*it)->getOneLine(), (*it)));
230         }
231
232         // We need one start certificate
233         sorted.push_back(subTransl.begin()->second);
234         subTransl.erase(subTransl.begin());
235
236         // Get the issuer from front certificate and find certificate with this subject in subTransl.
237         // Add this certificate to the front.
238         while (!subTransl.empty()) {
239                 std::string issuer = sorted.back()->getOneLine(Certificate::FIELD_ISSUER);
240                 auto it = subTransl.find(issuer);
241
242                 if (it == subTransl.end()) {
243                         break;
244                 }
245
246                 sorted.push_back(it->second);
247                 subTransl.erase(it);
248         }
249
250         // Sort all certificates by issuer
251         for (auto it = subTransl.begin(); it != subTransl.end(); ++it) {
252                 issTransl.insert(std::make_pair(it->second->getOneLine(Certificate::FIELD_ISSUER), it->second));
253         }
254
255         // Get the subject from last certificate and find certificate with such issuer in issTransl.
256         // Add this certificate at end.
257         while (!issTransl.empty()) {
258                 std::string sub = sorted.front()->getOneLine();
259                 auto it = issTransl.find(sub);
260
261                 if (it == issTransl.end()) {
262                         break;
263                 }
264
265                 sorted.push_front(it->second);
266                 issTransl.erase(it);
267         }
268
269         if (!issTransl.empty()) {
270                 LogWarning("Certificates don't form a valid chain.");
271                 m_collectionStatus = COLLECTION_CHAIN_BROKEN;
272                 return;
273         }
274
275         m_collectionStatus = COLLECTION_SORTED;
276         m_certList = sorted;
277 }
278
279 /*
280  *  Precondition : cert list sorted and has more than one cert
281  */
282 bool CertificateCollection::completeCertificateChain()
283 {
284         CertificatePtr last = m_certList.back();
285
286         if (last->isRootCert())
287                 return true;
288
289         CertificatePtr rootCert = getIssuerCertFromStore(last);
290
291         if (!rootCert.get())
292                 return false;
293
294         if (!rootCert->isRootCert())
295                 return false;
296
297         m_certList.push_back(rootCert);
298         return true;
299 }
300
301 size_t CertificateCollection::size() const
302 {
303         return m_certList.size();
304 }
305
306 bool CertificateCollection::empty() const
307 {
308         return m_certList.empty();
309 }
310
311 CertificateCollection::const_iterator CertificateCollection::begin() const
312 {
313         return m_certList.begin();
314 }
315
316 CertificateCollection::const_iterator CertificateCollection::end() const
317 {
318         return m_certList.end();
319 }
320
321 CertificatePtr CertificateCollection::back() const
322 {
323         return m_certList.back();
324 }
325
326 } // namespace ValidationCore
327