2 * Copyright (c) 2016 - 2020 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.
18 * @author Jacek Migacz (j.migacz@samsung.com)
19 * @author Kyungwook Tak (k.tak@samsung.com)
21 * @brief PKCS#12 container manipulation routines.
28 #include <sys/types.h>
35 #include <openssl/err.h>
36 #include <openssl/pkcs12.h>
37 #include <openssl/sha.h>
38 #include <openssl/x509.h>
39 #include <openssl/pem.h>
41 #include "dpl/log/log.h"
42 #include "cert-svc/cerror.h"
44 #include "vcore/Certificate.h"
45 #include "vcore/Client.h"
46 #include "vcore/pkcs12.h"
48 #define SYSCALL(call) while (((call) == -1) && (errno == EINTR))
52 static const std::string START_CERT = "-----BEGIN CERTIFICATE-----";
53 static const std::string END_CERT = "-----END CERTIFICATE-----";
54 static const std::string START_TRUSTED = "-----BEGIN TRUSTED CERTIFICATE-----";
55 static const std::string END_TRUSTED = "-----END TRUSTED CERTIFICATE-----";
56 static const std::string START_KEY = "-----BEGIN PRIVATE KEY-----";
57 static const std::string END_KEY = "-----END PRIVATE KEY-----";
59 using ValidationCore::CertificatePtr;
60 using ValidationCore::Certificate;
62 using FileUniquePtr = std::unique_ptr<FILE, std::function<int(FILE *)>>;
63 using BioUniquePtr = std::unique_ptr<BIO, std::function<void(BIO *)>>;
64 using PKEYUniquePtr = std::unique_ptr<EVP_PKEY, std::function<void(EVP_PKEY *)>>;
65 using X509UniquePtr = std::unique_ptr<X509, std::function<void(X509 *)>>;
66 using X509StackUniquePtr = std::unique_ptr<STACK_OF(X509), std::function<void(STACK_OF(X509) *)>>;
68 void X509_stack_free(STACK_OF(X509) *stack)
73 inline bool hasStore(CertStoreType types, CertStoreType type)
75 return (types & type) != 0;
78 inline CertStoreType nextStore(CertStoreType type)
101 CertSvcStoreCertList *createStoreListNode(const std::string &gname, const std::string &title,
102 CertStoreType storeType)
104 CertSvcStoreCertList *node = (CertSvcStoreCertList *)malloc(sizeof(CertSvcStoreCertList));
109 node->gname = strdup(gname.c_str());
110 node->title = strdup(title.c_str());
111 node->status = ENABLED;
112 node->storeType = storeType;
115 if (node->gname == NULL || node->title == NULL) {
125 void destroyStoreList(CertSvcStoreCertList **certList, size_t *length)
127 if (certList == NULL || length == NULL) {
131 CertSvcStoreCertList *list = *certList;
134 CertSvcStoreCertList *next = list->next;
144 void addStoreListNode(CertSvcStoreCertList **list, CertSvcStoreCertList *node)
150 int appendStoreListNode(CertSvcStoreCertList **certList, size_t *length,
151 const std::string &gname, const std::string &alias,
152 CertStoreType storeType)
154 if (certList == NULL || length == NULL)
155 return CERTSVC_SUCCESS;
157 CertSvcStoreCertList *node = createStoreListNode(gname, alias, storeType);
159 return CERTSVC_BAD_ALLOC;
162 addStoreListNode(certList, node);
165 return CERTSVC_SUCCESS;
168 std::string generateGname(void)
173 unsigned char d[SHA_DIGEST_LENGTH];
176 SYSCALL(generator = open("/dev/urandom", O_RDONLY));
179 return std::string();
181 SYSCALL(result = read(generator, &random, sizeof(random)));
184 SYSCALL(close(generator));
185 return std::string();
188 SYSCALL(result = close(generator));
191 return std::string();
194 SHA1_Update(&ctx, &random, sizeof(random));
196 result = asprintf(&gname,
197 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
198 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
199 d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9],
200 d[10], d[11], d[12], d[13], d[14], d[15], d[16], d[17], d[18], d[19]);
203 return std::string();
205 std::string ret(gname);
210 std::string getCommonName(CertType type, const std::string &cert)
212 BioUniquePtr bio(BIO_new(BIO_s_mem()), BIO_free_all);
214 if (bio.get() == NULL) {
215 LogError("Failed to allocate memory.");
216 return std::string();
219 auto readCount = BIO_write(bio.get(), (const void *)cert.data(), (int)cert.length());
222 LogError("Failed to load cert into bio.");
223 return std::string();
230 case P12_INTERMEDIATE:
231 x509 = PEM_read_bio_X509_AUX(bio.get(), NULL, 0, NULL);
235 x509 = PEM_read_bio_X509(bio.get(), NULL, 0, NULL);
240 LogError("Failed to create x509 structure.");
241 return std::string();
244 X509UniquePtr x509Ptr(x509, X509_free);
245 const char *subject_c = X509_NAME_oneline(X509_get_subject_name(x509), NULL, 0);
247 if (subject_c == NULL) {
248 LogError("Failed to parse x509 structure");
249 return std::string();
252 return std::string(subject_c);
256 * column / common name / associated gname / prikey gname /
257 * PEM_CRT : common name / gname / none /
258 * P12_END_USER : alias / gname / prikey gname /
259 * P12_TRUSTED : common name / end cert gname / none /
260 * P12_INTERMEDIATE : common name / end cert gname / none /
263 int installPKEY(CertStoreType storeType,
264 const std::string &key,
265 const std::string &gname)
267 return vcore_client_install_certificate_to_store(
278 int installEndCert(CertStoreType storeType,
279 const std::string &cert,
280 const std::string &alias,
281 const std::string &gname,
282 const std::string &prikeyGname)
284 return vcore_client_install_certificate_to_store(
295 int installChainCert(CertStoreType storeType,
296 const std::string &cert,
297 const std::string &gname,
298 const std::string &endCertGname,
299 const std::string &commonName,
302 return vcore_client_install_certificate_to_store(
307 endCertGname.c_str(),
312 int installCert(CertStoreType storeType,
313 const std::string &cert,
314 const std::string &gname,
315 const std::string &alias)
317 return vcore_client_install_certificate_to_store(
328 std::string readFromFile(const std::string &path)
332 if ((fp = fopen(path.c_str(), "rb")) == NULL) {
333 LogError("Fail to open file for reading : " << path);
334 return std::string();
337 FileUniquePtr filePtr(fp, fclose);
338 if (fseek(fp, 0L, SEEK_END) != 0) {
339 LogError("Fail in fseek");
340 return std::string();
344 if (len <= 0 || len == INT_MAX) {
345 LogError("Fail to get proper certificate.");
346 return std::string();
350 char *content = (char *)malloc(sizeof(char) * (len + 1));
352 if (content == NULL) {
353 LogError("Fail to allocate memory");
354 return std::string();
357 memset(content, 0x00, len + 1);
358 size_t readLen = fread(content, sizeof(char), (size_t)len, fp);
360 if (readLen != (size_t)len) {
361 LogError("Fail to read file : " << path);
363 return std::string();
367 std::string ret(content);
372 std::string parseCRT(const std::string &cert)
377 from = cert.find(START_CERT);
378 to = cert.find(END_CERT);
379 tailLen = END_CERT.length();
381 if (from == std::string::npos || to == std::string::npos || from > to) {
382 from = cert.find(START_TRUSTED);
383 to = cert.find(END_TRUSTED);
384 tailLen = END_TRUSTED.length();
387 if (from == std::string::npos || to == std::string::npos || from > to)
388 return std::string();
390 return std::string(cert, from, to - from + tailLen);
393 #define _CERT_SVC_VERIFY_PKCS12
394 int verify_cert_details(X509 *cert, STACK_OF(X509) *certv)
396 int result = CERTSVC_SUCCESS;
397 char *pSubject = NULL;
398 char *pIssuerName = NULL;
399 X509_STORE_CTX *cert_ctx = NULL;
400 X509_STORE *cert_store = NULL;
402 #ifdef _CERT_SVC_VERIFY_PKCS12
405 pSubject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
408 LogError("Failed to get subject name");
409 result = CERTSVC_FAIL;
413 pIssuerName = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
416 LogError("Failed to get issuer name");
417 result = CERTSVC_FAIL;
421 if (strcmp((const char *)pSubject, (const char *)pIssuerName) == 0) {
423 EVP_PKEY *pKey = NULL;
424 pKey = X509_get_pubkey(cert);
427 LogError("Failed to get public key");
428 result = CERTSVC_FAIL;
432 if (X509_verify(cert, pKey) <= 0) {
433 LogError("P12 verification failed");
435 result = CERTSVC_FAIL;
439 LogDebug("P12 verification Success");
442 cert_store = X509_STORE_new();
445 LogError("Memory allocation failed");
446 result = CERTSVC_FAIL;
450 res = X509_STORE_load_locations(cert_store, NULL, TZ_SYS_CA_CERTS);
453 LogError("P12 load certificate store failed");
454 result = CERTSVC_FAIL;
458 res = X509_STORE_set_default_paths(cert_store);
461 LogError("P12 load certificate store path failed");
462 result = CERTSVC_FAIL;
466 /* initialise store and store context */
467 cert_ctx = X509_STORE_CTX_new();
469 if (cert_ctx == NULL) {
470 LogError("Memory allocation failed");
471 result = CERTSVC_FAIL;
475 /* construct store context */
476 if (!X509_STORE_CTX_init(cert_ctx, cert_store, cert, NULL)) {
477 LogError("Memory allocation failed");
478 result = CERTSVC_FAIL;
482 #ifdef P12_VERIFICATION_NEEDED
483 res = X509_verify_cert(cert_ctx);
486 LogError("P12 verification failed");
487 result = CERTSVC_FAIL;
491 LogDebug("P12 verification Success");
494 } else if (certv != NULL) {
496 cert_store = X509_STORE_new();
499 LogError("Memory allocation failed");
500 result = CERTSVC_FAIL;
504 res = X509_STORE_load_locations(cert_store, NULL, TZ_SYS_CA_CERTS);
507 LogError("P12 load certificate store failed");
508 result = CERTSVC_FAIL;
512 res = X509_STORE_set_default_paths(cert_store);
515 LogError("P12 load certificate path failed");
516 result = CERTSVC_FAIL;
520 /* initialise store and store context */
521 cert_ctx = X509_STORE_CTX_new();
523 if (cert_ctx == NULL) {
524 LogError("Memory allocation failed");
525 result = CERTSVC_FAIL;
529 /* construct store context */
530 if (!X509_STORE_CTX_init(cert_ctx, cert_store, cert, NULL)) {
531 LogError("Memory allocation failed");
532 result = CERTSVC_FAIL;
536 X509_STORE_CTX_trusted_stack(cert_ctx, certv);
537 #ifdef P12_VERIFICATION_NEEDED
538 res = X509_verify_cert(cert_ctx);
541 LogError("P12 verification failed");
542 result = CERTSVC_FAIL;
546 LogDebug("P12 verification Success");
550 #endif //_CERT_SVC_VERIFY_PKCS12
553 X509_STORE_free(cert_store);
554 X509_STORE_CTX_free(cert_ctx);
556 OPENSSL_free(pSubject);
557 OPENSSL_free(pIssuerName);
561 enum class OsslType : int {
567 std::string osslToPEM(OsslType type, void *data)
569 std::vector<char> buf(4096);
570 BioUniquePtr bio(BIO_new(BIO_s_mem()), BIO_free_all);
572 if (bio.get() == NULL)
573 return std::string();
577 PEM_write_bio_PrivateKey(bio.get(), static_cast<EVP_PKEY *>(data), NULL, NULL, 0, NULL, NULL);
581 PEM_write_bio_X509(bio.get(), static_cast<X509 *>(data));
584 case OsslType::X509AUX:
585 PEM_write_bio_X509_AUX(bio.get(), static_cast<X509 *>(data));
592 int size = BIO_read(bio.get(), buf.data(), 4096);
595 return std::string();
598 return std::string(buf.data());
601 int extractPkcs12(const std::string &path,
602 const std::string &password,
603 PKEYUniquePtr &keyPtr,
604 X509UniquePtr &certPtr,
605 X509StackUniquePtr &certvPtr)
609 if ((stream = fopen(path.c_str(), "rb")) == NULL) {
610 LogError("Unable to open the file for reading : " << path);
611 return CERTSVC_IO_ERROR;
614 PKCS12 *container = d2i_PKCS12_fp(stream, NULL);
617 if (container == NULL) {
618 LogError("Failed to parse the input file passed.");
622 EVP_PKEY *key = NULL;
624 STACK_OF(X509) *certv = NULL;
625 int result = PKCS12_parse(container, password.c_str(), &key, &cert, &certv);
626 PKCS12_free(container);
629 unsigned long e = ERR_get_error();
631 ERR_error_string_n(e, buf, 1023);
632 LogError("Failed to parse the file passed. openssl err: " << buf);
638 certvPtr.reset(certv);
639 return CERTSVC_SUCCESS;
642 void rollbackStore(CertStoreType storeTypes, const std::string &endCertName)
644 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
645 storeType = nextStore(storeType)) {
646 if (!hasStore(storeTypes, storeType))
649 char **certChainName = NULL;
651 int result = vcore_client_load_certificates_from_store(storeType, endCertName.c_str(),
652 &certChainName, &ncerts);
654 if (result != CERTSVC_SUCCESS) {
655 LogError("Unable to load certificates from store. result : " << result);
659 for (size_t i = 0; i < ncerts; i++) {
660 if (certChainName[i] == NULL)
663 vcore_client_delete_certificate_from_store(storeType, certChainName[i]);
664 free(certChainName[i]);
668 vcore_client_delete_certificate_from_store(storeType, endCertName.c_str());
672 int insertToStore(CertStoreType storeTypes,
673 const std::string &alias,
674 const std::string &prikeyName,
675 const std::string &prikeyBuffer,
676 const std::string &endCertName,
677 const std::string &endCertBuffer,
678 const std::vector<std::string> &certChainName,
679 const std::vector<std::string> &certChainBuffer,
680 CertSvcStoreCertList **certList,
683 size_t ncerts = certChainName.size();
685 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
686 storeType = nextStore(storeType)) {
687 if (!hasStore(storeTypes, storeType))
690 LogDebug("Processing store type : " << storeType);
691 int result = installPKEY(storeType, prikeyBuffer, prikeyName);
693 if (result != CERTSVC_SUCCESS) {
694 LogError("Failed to store the private key contents. result : " << result);
698 result = installEndCert(storeType, endCertBuffer, alias, endCertName, prikeyName);
700 if (result != CERTSVC_SUCCESS) {
701 LogError("Failed to install the end user certificate. result : " << result);
705 int res = appendStoreListNode(certList, length, endCertName, alias, storeType);
706 if (res != CERTSVC_SUCCESS) {
707 LogError("Failed to append store list node.");
711 for (size_t i = 0; i < ncerts; i++) {
712 CertType type = (i == ncerts - 1) ? P12_TRUSTED : P12_INTERMEDIATE;
714 std::string commonName = getCommonName(type, certChainBuffer[i]);
716 result = installChainCert(storeType, certChainBuffer[i], certChainName[i], endCertName,
719 if (result != CERTSVC_SUCCESS) {
720 LogError("Failed to install the ca certificates. result : " << result);
724 int res = appendStoreListNode(certList, length, certChainName[i], commonName, storeType);
725 if (res != CERTSVC_SUCCESS) {
726 LogError("Failed to append store list node.");
732 LogDebug("Success to insert extracted pkcs12 data to db");
733 return CERTSVC_SUCCESS;
736 int insertToStorePEM(CertStoreType storeTypes, const std::string &path, const std::string &gname,
737 const std::string &alias, CertSvcStoreCertList **certList, size_t *length)
739 std::string content = readFromFile(path);
741 if (content.empty()) {
742 LogError("Failed to read the file : " << path);
743 return CERTSVC_IO_ERROR;
746 std::string parsed = parseCRT(content);
748 if (parsed.empty()) {
749 LogError("Failed to parse CRT : " << path);
753 for (CertStoreType storeType = VPN_STORE; storeType < SYSTEM_STORE;
754 storeType = nextStore(storeType)) {
755 if (!hasStore(storeTypes, storeType))
758 int result = installCert(storeType, parsed, gname, alias);
760 if (result != CERTSVC_SUCCESS) {
761 LogError("Failed to install PEM/CRT to db store : " << storeType << " result : " << result);
762 rollbackStore(storeTypes, gname);
766 int res = appendStoreListNode(certList, length, gname, alias, storeType);
767 if (res != CERTSVC_SUCCESS) {
768 rollbackStore(storeTypes, gname);
769 LogError("Failed to append store list node.");
773 LogDebug("Success to install PEM/CRT to db store : " << storeType);
776 LogDebug("Success to install PEM/CRT to db stores : " << storeTypes);
777 return CERTSVC_SUCCESS;
780 } // namespace anonymous
783 int pkcs12_import_from_file_to_store(CertStoreType storeTypes,
785 const char *_password,
787 CertSvcStoreCertList **certList,
792 if (_alias == NULL || _path == NULL || strlen(_path) < 4) {
793 LogError("Invalid input parameter.");
794 return CERTSVC_WRONG_ARGUMENT;
797 std::string path(_path);
798 std::string alias(_alias);
799 std::string password;
801 if (_password != NULL)
802 password = std::string(_password);
804 LogDebug("pkcs12_import_from_file_to_store start. path[" << path << "] password[" << password <<
805 "] alias[" << alias << "]");
807 if (storeTypes & SYSTEM_STORE) {
808 LogError("User should not install any form of certificates in SYSTEM_STORE.");
809 return CERTSVC_INVALID_STORE_TYPE;
813 * Installs CRT and PEM files.
814 * We will passing NULL for private_key_gname and associated_gname parameter
815 * in installFilePEM(). Which means that there is no private key involved
816 * in the certificate which we are installing and there are no other
817 * certificates related with the current certificate which is installed
819 std::string suffix = path.substr(path.length() - 4, 4);
821 if (strcasecmp(suffix.c_str(), ".pem") == 0 || strcasecmp(suffix.c_str(), ".crt") == 0) {
822 std::string gnamePEM = generateGname();
823 result = insertToStorePEM(storeTypes, path, gnamePEM, alias, certList, length);
825 if (result != CERTSVC_SUCCESS) {
826 destroyStoreList(certList, length);
827 LogError("Failed to install PEM/CRT file to store. gname : " << gnamePEM << " result : " << result);
833 LogDebug("Convert ossl type to string start");
834 /* 0. extract pkcs12 data from file */
835 PKEYUniquePtr key(nullptr, EVP_PKEY_free);
836 X509UniquePtr cert(nullptr, X509_free);
837 X509StackUniquePtr certv(nullptr, X509_stack_free);
838 result = extractPkcs12(path, password, key, cert, certv);
840 if (result != CERTSVC_SUCCESS) {
841 LogError("Failed to extract pkcs12 file. result : " << result);
845 LogDebug("extract pkcs12 to unique ptr success");
846 result = verify_cert_details(cert.get(), certv.get());
848 if (result != CERTSVC_SUCCESS) {
849 LogError("Failed to verify p12 certificate. result : " << result);
853 /* 1. handling private key */
854 std::string prikeyName = generateGname();
855 std::string prikeyBuffer = osslToPEM(OsslType::PKEY, key.get());
857 if (prikeyName.empty() || prikeyBuffer.empty()) {
858 LogError("Failed to transform pkey to PEM. result : " << result);
862 LogDebug("Convert pkey to string success");
863 /* 2. handling end user certificate */
864 std::string endCertName = generateGname();
865 std::string endCertBuffer = osslToPEM(OsslType::X509, cert.get());
867 if (endCertName.empty() || endCertBuffer.empty()) {
868 LogError("Failed to transform x509 to PEM. result : " << result);
872 LogDebug("Convert end cert to string success");
873 /* 3. handling certificate chain */
874 std::vector<std::string> certChainName;
875 std::vector<std::string> certChainBuffer;
876 int ncerts = certv ? sk_X509_num(certv.get()) : 0;
878 for (int i = 0; i < ncerts; i++) {
879 std::string tempName = generateGname();
880 std::string tempBuffer = osslToPEM(OsslType::X509AUX, sk_X509_value(certv.get(), i));
882 if (tempName.empty() || tempBuffer.empty()) {
883 LogError("Failed to transform x509 AUX to PEM");
887 certChainName.push_back(std::move(tempName));
888 certChainBuffer.push_back(std::move(tempBuffer));
891 LogDebug("Convert cert chain to string success");
892 /* 4. insert extracted pkcs12 data to db */
893 result = insertToStore(storeTypes,
904 if (result != CERTSVC_SUCCESS) {
905 destroyStoreList(certList, length);
906 rollbackStore(storeTypes, endCertName);
909 LogDebug("Success to import pkcs12 to store");
913 int pkcs12_has_password(const char *filepath, int *passworded)
915 if (filepath == NULL || passworded == NULL)
916 return CERTSVC_WRONG_ARGUMENT;
920 if ((stream = fopen(filepath, "rb")) == NULL)
921 return CERTSVC_IO_ERROR;
923 PKCS12 *container = d2i_PKCS12_fp(stream, NULL);
926 if (container == NULL)
929 EVP_PKEY *pkey = NULL;
931 int result = PKCS12_parse(container, NULL, &pkey, &cert, NULL);
932 PKCS12_free(container);
941 unsigned long e = ERR_get_error();
942 if (ERR_GET_REASON(e) == PKCS12_R_MAC_VERIFY_FAILURE) {
943 LogInfo("verify failed without password. file(" << filepath << ") is password-protected.");
944 *passworded = CERTSVC_TRUE;
947 ERR_error_string_n(e, buf, 1023);
948 LogError("Error on PKCS12_pasre file(" << filepath << "): " << buf);
952 *passworded = CERTSVC_FALSE;
955 return CERTSVC_SUCCESS;