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