1cfbc8dc8SJayanth Othayoth #include "certs_manager.hpp" 2cfbc8dc8SJayanth Othayoth 3dd74bd20SJayanth Othayoth #include <openssl/bio.h> 4dd74bd20SJayanth Othayoth #include <openssl/crypto.h> 5dd74bd20SJayanth Othayoth #include <openssl/err.h> 6dd74bd20SJayanth Othayoth #include <openssl/evp.h> 7dd74bd20SJayanth Othayoth #include <openssl/pem.h> 8dd74bd20SJayanth Othayoth #include <openssl/x509v3.h> 9dd74bd20SJayanth Othayoth 10cfbc8dc8SJayanth Othayoth #include <experimental/filesystem> 11cfbc8dc8SJayanth Othayoth #include <phosphor-logging/elog-errors.hpp> 12cfbc8dc8SJayanth Othayoth #include <phosphor-logging/elog.hpp> 13cfbc8dc8SJayanth Othayoth #include <phosphor-logging/log.hpp> 14cfbc8dc8SJayanth Othayoth #include <sdbusplus/bus.hpp> 15dd74bd20SJayanth Othayoth #include <xyz/openbmc_project/Certs/Install/error.hpp> 16cfbc8dc8SJayanth Othayoth #include <xyz/openbmc_project/Common/error.hpp> 17cfbc8dc8SJayanth Othayoth 18cfbc8dc8SJayanth Othayoth namespace phosphor 19cfbc8dc8SJayanth Othayoth { 20cfbc8dc8SJayanth Othayoth namespace certs 21cfbc8dc8SJayanth Othayoth { 22dd74bd20SJayanth Othayoth // RAII support for openSSL functions. 23dd74bd20SJayanth Othayoth using BIO_MEM_Ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>; 24dd74bd20SJayanth Othayoth using X509_STORE_CTX_Ptr = 25dd74bd20SJayanth Othayoth std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>; 26dd74bd20SJayanth Othayoth using X509_LOOKUP_Ptr = 27dd74bd20SJayanth Othayoth std::unique_ptr<X509_LOOKUP, decltype(&::X509_LOOKUP_free)>; 28*589159f2SJayanth Othayoth using EVP_PKEY_Ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>; 29cfbc8dc8SJayanth Othayoth 30dd74bd20SJayanth Othayoth namespace fs = std::experimental::filesystem; 31cfbc8dc8SJayanth Othayoth using namespace phosphor::logging; 32cfbc8dc8SJayanth Othayoth using InternalFailure = 33cfbc8dc8SJayanth Othayoth sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 34dd74bd20SJayanth Othayoth using InvalidCertificate = 35dd74bd20SJayanth Othayoth sdbusplus::xyz::openbmc_project::Certs::Install::Error::InvalidCertificate; 36dd74bd20SJayanth Othayoth using Reason = xyz::openbmc_project::Certs::Install::InvalidCertificate::REASON; 37cfbc8dc8SJayanth Othayoth 38cfbc8dc8SJayanth Othayoth void Manager::install(const std::string path) 39cfbc8dc8SJayanth Othayoth { 40dd74bd20SJayanth Othayoth // Verify the certificate file 41dd74bd20SJayanth Othayoth auto rc = verifyCert(path); 42dd74bd20SJayanth Othayoth if (rc != X509_V_OK) 43dd74bd20SJayanth Othayoth { 44dd74bd20SJayanth Othayoth if (rc == X509_V_ERR_CERT_HAS_EXPIRED) 45dd74bd20SJayanth Othayoth { 46dd74bd20SJayanth Othayoth elog<InvalidCertificate>(Reason("Expired Certificate")); 47dd74bd20SJayanth Othayoth } 48dd74bd20SJayanth Othayoth // Loging general error here. 49dd74bd20SJayanth Othayoth elog<InvalidCertificate>(Reason("Certificate validation failed")); 50dd74bd20SJayanth Othayoth } 51*589159f2SJayanth Othayoth 52*589159f2SJayanth Othayoth // Compare the Keys 53*589159f2SJayanth Othayoth if (!compareKeys(path)) 54*589159f2SJayanth Othayoth { 55*589159f2SJayanth Othayoth elog<InvalidCertificate>( 56*589159f2SJayanth Othayoth Reason("Private key is not matching with Certificate")); 57*589159f2SJayanth Othayoth } 58*589159f2SJayanth Othayoth 59cfbc8dc8SJayanth Othayoth // Copy the certificate file 60cfbc8dc8SJayanth Othayoth copy(path, certPath); 61cfbc8dc8SJayanth Othayoth 62cfbc8dc8SJayanth Othayoth // Invoke type specific install function. 63cfbc8dc8SJayanth Othayoth auto iter = typeFuncMap.find(type); 64cfbc8dc8SJayanth Othayoth if (iter == typeFuncMap.end()) 65cfbc8dc8SJayanth Othayoth { 66cfbc8dc8SJayanth Othayoth log<level::ERR>("Unsupported Type", entry("TYPE=%s", type.c_str())); 67cfbc8dc8SJayanth Othayoth elog<InternalFailure>(); 68cfbc8dc8SJayanth Othayoth } 69cfbc8dc8SJayanth Othayoth iter->second(); 70cfbc8dc8SJayanth Othayoth } 71cfbc8dc8SJayanth Othayoth 72cfbc8dc8SJayanth Othayoth void Manager::serverInstall() 73cfbc8dc8SJayanth Othayoth { 74cfbc8dc8SJayanth Othayoth if (!unit.empty()) 75cfbc8dc8SJayanth Othayoth { 76cfbc8dc8SJayanth Othayoth reload(unit); 77cfbc8dc8SJayanth Othayoth } 78cfbc8dc8SJayanth Othayoth } 79cfbc8dc8SJayanth Othayoth 80cfbc8dc8SJayanth Othayoth void Manager::clientInstall() 81cfbc8dc8SJayanth Othayoth { 82cfbc8dc8SJayanth Othayoth // Do nothing now 83cfbc8dc8SJayanth Othayoth } 84cfbc8dc8SJayanth Othayoth 85cfbc8dc8SJayanth Othayoth void Manager::reload(const std::string& unit) 86cfbc8dc8SJayanth Othayoth { 87cfbc8dc8SJayanth Othayoth constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 88cfbc8dc8SJayanth Othayoth constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1"; 89cfbc8dc8SJayanth Othayoth constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; 90cfbc8dc8SJayanth Othayoth 91cfbc8dc8SJayanth Othayoth try 92cfbc8dc8SJayanth Othayoth { 93cfbc8dc8SJayanth Othayoth auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 94cfbc8dc8SJayanth Othayoth SYSTEMD_INTERFACE, "ReloadUnit"); 95cfbc8dc8SJayanth Othayoth 96cfbc8dc8SJayanth Othayoth method.append(unit, "replace"); 97cfbc8dc8SJayanth Othayoth 98cfbc8dc8SJayanth Othayoth bus.call_noreply(method); 99cfbc8dc8SJayanth Othayoth } 100cfbc8dc8SJayanth Othayoth catch (const sdbusplus::exception::SdBusError& e) 101cfbc8dc8SJayanth Othayoth { 102cfbc8dc8SJayanth Othayoth log<level::ERR>("Failed to reload service", entry("ERR=%s", e.what()), 103cfbc8dc8SJayanth Othayoth entry("UNIT=%s", unit.c_str())); 104cfbc8dc8SJayanth Othayoth elog<InternalFailure>(); 105cfbc8dc8SJayanth Othayoth } 106cfbc8dc8SJayanth Othayoth } 107cfbc8dc8SJayanth Othayoth 108cfbc8dc8SJayanth Othayoth void Manager::copy(const std::string& src, const std::string& dst) 109cfbc8dc8SJayanth Othayoth { 110cfbc8dc8SJayanth Othayoth namespace fs = std::experimental::filesystem; 111cfbc8dc8SJayanth Othayoth 112cfbc8dc8SJayanth Othayoth try 113cfbc8dc8SJayanth Othayoth { 114cfbc8dc8SJayanth Othayoth auto path = fs::path(dst).parent_path(); 115cfbc8dc8SJayanth Othayoth // create dst path folder by default 116cfbc8dc8SJayanth Othayoth fs::create_directories(path); 117cfbc8dc8SJayanth Othayoth fs::copy_file(src, dst, fs::copy_options::overwrite_existing); 118cfbc8dc8SJayanth Othayoth } 119cfbc8dc8SJayanth Othayoth catch (fs::filesystem_error& e) 120cfbc8dc8SJayanth Othayoth { 121cfbc8dc8SJayanth Othayoth log<level::ERR>("Failed to copy certificate", entry("ERR=%s", e.what()), 122cfbc8dc8SJayanth Othayoth entry("SRC=%s", src.c_str()), 123cfbc8dc8SJayanth Othayoth entry("DST=%s", dst.c_str())); 124cfbc8dc8SJayanth Othayoth elog<InternalFailure>(); 125cfbc8dc8SJayanth Othayoth } 126cfbc8dc8SJayanth Othayoth } 127cfbc8dc8SJayanth Othayoth 128dd74bd20SJayanth Othayoth X509_Ptr Manager::loadCert(const std::string& filePath) 129dd74bd20SJayanth Othayoth { 130dd74bd20SJayanth Othayoth // Read Certificate file 131dd74bd20SJayanth Othayoth X509_Ptr cert(X509_new(), ::X509_free); 132dd74bd20SJayanth Othayoth if (!cert) 133dd74bd20SJayanth Othayoth { 134dd74bd20SJayanth Othayoth log<level::ERR>("Error occured during X509_new call", 135dd74bd20SJayanth Othayoth entry("FILE=%s", filePath.c_str()), 136dd74bd20SJayanth Othayoth entry("ERRCODE=%lu", ERR_get_error())); 137dd74bd20SJayanth Othayoth elog<InternalFailure>(); 138dd74bd20SJayanth Othayoth } 139dd74bd20SJayanth Othayoth 140dd74bd20SJayanth Othayoth BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free); 141dd74bd20SJayanth Othayoth if (!bioCert) 142dd74bd20SJayanth Othayoth { 143dd74bd20SJayanth Othayoth log<level::ERR>("Error occured during BIO_new_file call", 144dd74bd20SJayanth Othayoth entry("FILE=%s", filePath.c_str())); 145dd74bd20SJayanth Othayoth elog<InternalFailure>(); 146dd74bd20SJayanth Othayoth } 147dd74bd20SJayanth Othayoth 148dd74bd20SJayanth Othayoth X509* x509 = cert.get(); 149dd74bd20SJayanth Othayoth if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr)) 150dd74bd20SJayanth Othayoth { 151dd74bd20SJayanth Othayoth log<level::ERR>("Error occured during PEM_read_bio_X509 call", 152dd74bd20SJayanth Othayoth entry("FILE=%s", filePath.c_str())); 153dd74bd20SJayanth Othayoth elog<InternalFailure>(); 154dd74bd20SJayanth Othayoth } 155dd74bd20SJayanth Othayoth return cert; 156dd74bd20SJayanth Othayoth } 157dd74bd20SJayanth Othayoth 158dd74bd20SJayanth Othayoth int32_t Manager::verifyCert(const std::string& filePath) 159dd74bd20SJayanth Othayoth { 160dd74bd20SJayanth Othayoth auto errCode = X509_V_OK; 161dd74bd20SJayanth Othayoth 162dd74bd20SJayanth Othayoth fs::path file(filePath); 163dd74bd20SJayanth Othayoth if (!fs::exists(file)) 164dd74bd20SJayanth Othayoth { 165dd74bd20SJayanth Othayoth log<level::ERR>("File is Missing", entry("FILE=%s", filePath.c_str())); 166dd74bd20SJayanth Othayoth elog<InternalFailure>(); 167dd74bd20SJayanth Othayoth } 168dd74bd20SJayanth Othayoth 169dd74bd20SJayanth Othayoth try 170dd74bd20SJayanth Othayoth { 171dd74bd20SJayanth Othayoth if (fs::file_size(filePath) == 0) 172dd74bd20SJayanth Othayoth { 173dd74bd20SJayanth Othayoth // file is empty 174dd74bd20SJayanth Othayoth log<level::ERR>("File is empty", 175dd74bd20SJayanth Othayoth entry("FILE=%s", filePath.c_str())); 176dd74bd20SJayanth Othayoth elog<InvalidCertificate>(Reason("File is empty")); 177dd74bd20SJayanth Othayoth } 178dd74bd20SJayanth Othayoth } 179dd74bd20SJayanth Othayoth catch (const fs::filesystem_error& e) 180dd74bd20SJayanth Othayoth { 181dd74bd20SJayanth Othayoth // Log Error message 182dd74bd20SJayanth Othayoth log<level::ERR>(e.what(), entry("FILE=%s", filePath.c_str())); 183dd74bd20SJayanth Othayoth elog<InternalFailure>(); 184dd74bd20SJayanth Othayoth } 185dd74bd20SJayanth Othayoth 186dd74bd20SJayanth Othayoth // Defining store object as RAW to avoid double free. 187dd74bd20SJayanth Othayoth // X509_LOOKUP_free free up store object. 188dd74bd20SJayanth Othayoth // Create an empty X509_STORE structure for certificate validation. 189dd74bd20SJayanth Othayoth auto x509Store = X509_STORE_new(); 190dd74bd20SJayanth Othayoth if (!x509Store) 191dd74bd20SJayanth Othayoth { 192dd74bd20SJayanth Othayoth log<level::ERR>("Error occured during X509_STORE_new call"); 193dd74bd20SJayanth Othayoth elog<InternalFailure>(); 194dd74bd20SJayanth Othayoth } 195dd74bd20SJayanth Othayoth 196dd74bd20SJayanth Othayoth OpenSSL_add_all_algorithms(); 197dd74bd20SJayanth Othayoth 198dd74bd20SJayanth Othayoth // ADD Certificate Lookup method. 199dd74bd20SJayanth Othayoth X509_LOOKUP_Ptr lookup(X509_STORE_add_lookup(x509Store, X509_LOOKUP_file()), 200dd74bd20SJayanth Othayoth ::X509_LOOKUP_free); 201dd74bd20SJayanth Othayoth if (!lookup) 202dd74bd20SJayanth Othayoth { 203dd74bd20SJayanth Othayoth // Normally lookup cleanup function interanlly does X509Store cleanup 204dd74bd20SJayanth Othayoth // Free up the X509Store. 205dd74bd20SJayanth Othayoth X509_STORE_free(x509Store); 206dd74bd20SJayanth Othayoth log<level::ERR>("Error occured during X509_STORE_add_lookup call"); 207dd74bd20SJayanth Othayoth elog<InternalFailure>(); 208dd74bd20SJayanth Othayoth } 209dd74bd20SJayanth Othayoth // Load Certificate file. 210dd74bd20SJayanth Othayoth int32_t rc = X509_LOOKUP_load_file(lookup.get(), filePath.c_str(), 211dd74bd20SJayanth Othayoth X509_FILETYPE_PEM); 212dd74bd20SJayanth Othayoth if (rc != 1) 213dd74bd20SJayanth Othayoth { 214dd74bd20SJayanth Othayoth log<level::ERR>("Error occured during X509_LOOKUP_load_file call", 215dd74bd20SJayanth Othayoth entry("FILE=%s", filePath.c_str())); 216dd74bd20SJayanth Othayoth elog<InvalidCertificate>(Reason("Invalid certificate file format")); 217dd74bd20SJayanth Othayoth } 218dd74bd20SJayanth Othayoth 219dd74bd20SJayanth Othayoth // Load Certificate file into the X509 structre. 220dd74bd20SJayanth Othayoth X509_Ptr cert = std::move(loadCert(filePath)); 221dd74bd20SJayanth Othayoth X509_STORE_CTX_Ptr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free); 222dd74bd20SJayanth Othayoth if (!storeCtx) 223dd74bd20SJayanth Othayoth { 224dd74bd20SJayanth Othayoth log<level::ERR>("Error occured during X509_STORE_CTX_new call", 225dd74bd20SJayanth Othayoth entry("FILE=%s", filePath.c_str())); 226dd74bd20SJayanth Othayoth elog<InternalFailure>(); 227dd74bd20SJayanth Othayoth } 228dd74bd20SJayanth Othayoth 229dd74bd20SJayanth Othayoth rc = X509_STORE_CTX_init(storeCtx.get(), x509Store, cert.get(), NULL); 230dd74bd20SJayanth Othayoth if (rc != 1) 231dd74bd20SJayanth Othayoth { 232dd74bd20SJayanth Othayoth log<level::ERR>("Error occured during X509_STORE_CTX_init call", 233dd74bd20SJayanth Othayoth entry("FILE=%s", filePath.c_str())); 234dd74bd20SJayanth Othayoth elog<InternalFailure>(); 235dd74bd20SJayanth Othayoth } 236dd74bd20SJayanth Othayoth 237dd74bd20SJayanth Othayoth // Set time to current time. 238dd74bd20SJayanth Othayoth auto locTime = time(nullptr); 239dd74bd20SJayanth Othayoth 240dd74bd20SJayanth Othayoth X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME, 241dd74bd20SJayanth Othayoth locTime); 242dd74bd20SJayanth Othayoth 243dd74bd20SJayanth Othayoth rc = X509_verify_cert(storeCtx.get()); 244dd74bd20SJayanth Othayoth if (rc == 1) 245dd74bd20SJayanth Othayoth { 246dd74bd20SJayanth Othayoth errCode = X509_V_OK; 247dd74bd20SJayanth Othayoth } 248dd74bd20SJayanth Othayoth else if (rc == 0) 249dd74bd20SJayanth Othayoth { 250dd74bd20SJayanth Othayoth errCode = X509_STORE_CTX_get_error(storeCtx.get()); 251dd74bd20SJayanth Othayoth log<level::ERR>("Certificate verification failed", 252dd74bd20SJayanth Othayoth entry("FILE=%s", filePath.c_str()), 253dd74bd20SJayanth Othayoth entry("ERRCODE=%d", errCode)); 254dd74bd20SJayanth Othayoth } 255dd74bd20SJayanth Othayoth else 256dd74bd20SJayanth Othayoth { 257dd74bd20SJayanth Othayoth log<level::ERR>("Error occured during X509_verify_cert call", 258dd74bd20SJayanth Othayoth entry("FILE=%s", filePath.c_str())); 259dd74bd20SJayanth Othayoth elog<InternalFailure>(); 260dd74bd20SJayanth Othayoth } 261dd74bd20SJayanth Othayoth return errCode; 262dd74bd20SJayanth Othayoth } 263dd74bd20SJayanth Othayoth 264*589159f2SJayanth Othayoth bool Manager::compareKeys(const std::string& filePath) 265*589159f2SJayanth Othayoth { 266*589159f2SJayanth Othayoth X509_Ptr cert(X509_new(), ::X509_free); 267*589159f2SJayanth Othayoth if (!cert) 268*589159f2SJayanth Othayoth { 269*589159f2SJayanth Othayoth log<level::ERR>("Error occured during X509_new call", 270*589159f2SJayanth Othayoth entry("FILE=%s", filePath.c_str()), 271*589159f2SJayanth Othayoth entry("ERRCODE=%lu", ERR_get_error())); 272*589159f2SJayanth Othayoth elog<InternalFailure>(); 273*589159f2SJayanth Othayoth } 274*589159f2SJayanth Othayoth 275*589159f2SJayanth Othayoth BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free); 276*589159f2SJayanth Othayoth if (!bioCert) 277*589159f2SJayanth Othayoth { 278*589159f2SJayanth Othayoth log<level::ERR>("Error occured during BIO_new_file call", 279*589159f2SJayanth Othayoth entry("FILE=%s", filePath.c_str())); 280*589159f2SJayanth Othayoth elog<InternalFailure>(); 281*589159f2SJayanth Othayoth } 282*589159f2SJayanth Othayoth 283*589159f2SJayanth Othayoth X509* x509 = cert.get(); 284*589159f2SJayanth Othayoth PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr); 285*589159f2SJayanth Othayoth 286*589159f2SJayanth Othayoth EVP_PKEY_Ptr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free); 287*589159f2SJayanth Othayoth if (!pubKey) 288*589159f2SJayanth Othayoth { 289*589159f2SJayanth Othayoth log<level::ERR>("Error occurred during X509_get_pubkey", 290*589159f2SJayanth Othayoth entry("FILE=%s", filePath.c_str()), 291*589159f2SJayanth Othayoth entry("ERRCODE=%lu", ERR_get_error())); 292*589159f2SJayanth Othayoth elog<InvalidCertificate>(Reason("Failed to get public key info")); 293*589159f2SJayanth Othayoth } 294*589159f2SJayanth Othayoth 295*589159f2SJayanth Othayoth BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free); 296*589159f2SJayanth Othayoth if (!keyBio) 297*589159f2SJayanth Othayoth { 298*589159f2SJayanth Othayoth log<level::ERR>("Error occured during BIO_s_file call", 299*589159f2SJayanth Othayoth entry("FILE=%s", filePath.c_str())); 300*589159f2SJayanth Othayoth elog<InternalFailure>(); 301*589159f2SJayanth Othayoth } 302*589159f2SJayanth Othayoth BIO_read_filename(keyBio.get(), filePath.c_str()); 303*589159f2SJayanth Othayoth 304*589159f2SJayanth Othayoth EVP_PKEY_Ptr priKey( 305*589159f2SJayanth Othayoth PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr), 306*589159f2SJayanth Othayoth ::EVP_PKEY_free); 307*589159f2SJayanth Othayoth 308*589159f2SJayanth Othayoth if (!priKey) 309*589159f2SJayanth Othayoth { 310*589159f2SJayanth Othayoth log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey", 311*589159f2SJayanth Othayoth entry("FILE=%s", filePath.c_str()), 312*589159f2SJayanth Othayoth entry("ERRCODE=%lu", ERR_get_error())); 313*589159f2SJayanth Othayoth elog<InvalidCertificate>(Reason("Failed to get private key info")); 314*589159f2SJayanth Othayoth } 315*589159f2SJayanth Othayoth 316*589159f2SJayanth Othayoth int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get()); 317*589159f2SJayanth Othayoth if (rc != 1) 318*589159f2SJayanth Othayoth { 319*589159f2SJayanth Othayoth log<level::ERR>("Private key is not matching with Certificate", 320*589159f2SJayanth Othayoth entry("FILE=%s", filePath.c_str()), 321*589159f2SJayanth Othayoth entry("ERRCODE=%d", rc)); 322*589159f2SJayanth Othayoth return false; 323*589159f2SJayanth Othayoth } 324*589159f2SJayanth Othayoth 325*589159f2SJayanth Othayoth return true; 326*589159f2SJayanth Othayoth } 327cfbc8dc8SJayanth Othayoth } // namespace certs 328cfbc8dc8SJayanth Othayoth } // namespace phosphor 329