2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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.
17 * @file CertificateCollection.cpp
18 * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
19 * @author Kyungwook Tak (k.tak@samsung.com)
21 * @brief Handles certificate chain, make it complete and sort
33 #include <openssl/pem.h>
34 #include <openssl/x509.h>
36 #include <cert-svc/cinstance.h>
37 #include <cert-svc/ccert.h>
38 #include <cert-svc/cprimitives.h>
40 #include <dpl/log/log.h>
42 #include "vcore/Base64.h"
44 #include "vcore/CertificateCollection.h"
48 using namespace ValidationCore;
50 inline std::string toBinaryString(int data)
52 char buffer[sizeof(int)];
53 memcpy(buffer, &data, sizeof(int));
54 return std::string(buffer, sizeof(int));
57 bool isHashMatchedName(const std::string &name, const std::string &hash)
59 if (name.compare(0, 8, hash) != 0)
65 bool isHashMatchedFile(const std::string &path, const std::string &hash)
67 CertificatePtr certPtr = Certificate::createFromFile(path);
68 std::string name = certPtr->getNameHash(Certificate::FIELD_SUBJECT);
69 return isHashMatchedName(name, hash);
72 CertificatePtr searchCert(const std::string &dir, const CertificatePtr &certPtr, bool withHash)
75 std::string hash = certPtr->getNameHash(Certificate::FIELD_ISSUER);
76 std::unique_ptr<DIR, std::function<int(DIR *)>> dp(::opendir(dir.c_str()), ::closedir);
78 if (dp.get() == NULL) {
79 LogError("Failed open dir[" << dir << "]");
80 return CertificatePtr();
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;
89 while ((ret = readdir_r(dp.get(), pEntry.get(), &dirp)) == 0 && dirp) {
90 if (dirp->d_type == DT_DIR)
93 std::string candidatePath(dir);
95 candidatePath += dirp->d_name;
98 if (!isHashMatchedName(dirp->d_name, hash))
101 if (!isHashMatchedFile(candidatePath, hash))
105 LogDebug("Found hash matched file! : " << candidatePath);
106 CertificatePtr candidate = Certificate::createFromFile(candidatePath);
108 if (candidate->getOneLine().compare(certPtr->getOneLine(Certificate::FIELD_ISSUER)) != 0)
115 LogError("readdir_r error. ret[" << ret << "]");
116 return CertificatePtr();
119 LogWarning("cert not found by hash[" << hash << "]");
120 return CertificatePtr();
121 } catch (const Certificate::Exception::Base &e) {
123 CertificateCollection::Exception::CertificateError,
124 "Error in handling certificate : " << e.DumpToString());
125 } catch (const std::exception &e) {
127 CertificateCollection::Exception::InternalError,
128 "std::exception occured : " << e.what());
131 CertificateCollection::Exception::InternalError,
132 "Unknown exception in CertificateCollection.");
136 CertificatePtr getIssuerCertFromStore(const CertificatePtr &certPtr)
138 LogDebug("Start to get issuer from store.");
139 CertificatePtr found = searchCert(TZ_SYS_CA_CERTS_TIZEN, certPtr, false);
141 if (found.get() != NULL) {
142 LogDebug("Found issuer cert in tizen root CA dir");
146 return searchCert(TZ_SYS_CA_CERTS, certPtr, true);
151 namespace ValidationCore {
153 CertificateCollection::CertificateCollection()
154 : m_collectionStatus(COLLECTION_UNSORTED)
157 void CertificateCollection::clear(void)
159 m_collectionStatus = COLLECTION_UNSORTED;
163 void CertificateCollection::load(const CertificateList &certList)
165 m_collectionStatus = COLLECTION_UNSORTED;
166 std::copy(certList.begin(),
168 std::back_inserter(m_certList));
171 std::string CertificateCollection::toBase64String() const
173 std::ostringstream output;
174 int certNum = m_certList.size();
175 output << toBinaryString(certNum);
177 for (auto i = m_certList.begin(); i != m_certList.end(); ++i) {
178 std::string derCert = (*i)->getDER();
179 output << toBinaryString(derCert.size());
183 Base64Encoder base64;
185 base64.append(output.str());
190 CertificateList CertificateCollection::getCertificateList() const
195 bool CertificateCollection::isChain() const
197 if (COLLECTION_SORTED != m_collectionStatus)
198 VcoreThrowMsg(CertificateCollection::Exception::WrongUsage,
199 "You must sort certificate first");
201 return (COLLECTION_SORTED == m_collectionStatus) ? true : false;
204 bool CertificateCollection::sort()
206 if (COLLECTION_UNSORTED == m_collectionStatus) {
210 return (COLLECTION_SORTED == m_collectionStatus) ? true : false;
213 CertificateList CertificateCollection::getChain() const
215 if (COLLECTION_SORTED != m_collectionStatus)
216 VcoreThrowMsg(CertificateCollection::Exception::WrongUsage,
217 "You must sort certificates first");
222 void CertificateCollection::sortCollection()
224 // sorting is not necessary
225 if (m_certList.empty()) {
226 m_collectionStatus = COLLECTION_SORTED;
230 CertificateList sorted;
231 std::map<std::string, CertificatePtr> subTransl;
232 std::map<std::string, CertificatePtr> issTransl;
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)));
239 // We need one start certificate
240 sorted.push_back(subTransl.begin()->second);
241 subTransl.erase(subTransl.begin());
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);
249 if (it == subTransl.end()) {
253 sorted.push_back(it->second);
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));
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);
268 if (it == issTransl.end()) {
272 sorted.push_front(it->second);
276 if (!issTransl.empty()) {
277 LogWarning("Certificates don't form a valid chain.");
278 m_collectionStatus = COLLECTION_CHAIN_BROKEN;
282 m_collectionStatus = COLLECTION_SORTED;
287 * Precondition : cert list sorted and has more than one cert
289 bool CertificateCollection::completeCertificateChain()
291 CertificatePtr last = m_certList.back();
293 if (last->isRootCert())
296 CertificatePtr rootCert = getIssuerCertFromStore(last);
301 if (!rootCert->isRootCert())
304 m_certList.push_back(rootCert);
308 size_t CertificateCollection::size() const
310 return m_certList.size();
313 bool CertificateCollection::empty() const
315 return m_certList.empty();
318 CertificateCollection::const_iterator CertificateCollection::begin() const
320 return m_certList.begin();
323 CertificateCollection::const_iterator CertificateCollection::end() const
325 return m_certList.end();
328 CertificatePtr CertificateCollection::back() const
330 return m_certList.back();
333 } // namespace ValidationCore