1 #include "certs_manager.hpp" 2 3 #include <openssl/pem.h> 4 #include <unistd.h> 5 6 #include <phosphor-logging/elog-errors.hpp> 7 #include <xyz/openbmc_project/Certs/error.hpp> 8 #include <xyz/openbmc_project/Common/error.hpp> 9 namespace phosphor 10 { 11 namespace certs 12 { 13 using InternalFailure = 14 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 15 using InvalidCertificate = 16 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate; 17 using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON; 18 19 using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>; 20 using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>; 21 using InvalidArgument = 22 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 23 using Argument = xyz::openbmc_project::Common::InvalidArgument; 24 25 constexpr auto SUPPORTED_KEYBITLENGTH = 2048; 26 27 Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event, 28 const char* path, const CertificateType& type, 29 UnitsToRestart&& unit, CertInstallPath&& installPath) : 30 Ifaces(bus, path), 31 bus(bus), event(event), objectPath(path), certType(type), 32 unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)), 33 certParentInstallPath(fs::path(certInstallPath).parent_path()) 34 { 35 // create parent certificate path if not existing 36 try 37 { 38 if (!fs::exists(certParentInstallPath)) 39 { 40 fs::create_directories(certParentInstallPath); 41 } 42 } 43 catch (fs::filesystem_error& e) 44 { 45 log<level::ERR>("Failed to create directory", entry("ERR=%s", e.what()), 46 entry("DIRECTORY=%s", certParentInstallPath.c_str())); 47 report<InternalFailure>(); 48 } 49 50 // Generating RSA private key file if certificate type is server/client 51 if (certType != AUTHORITY) 52 { 53 createRSAPrivateKeyFile(); 54 } 55 56 // restore any existing certificates 57 if (fs::exists(certInstallPath)) 58 { 59 createCertificate(); 60 } 61 62 // watch is not required for authority certificates 63 if (certType != AUTHORITY) 64 { 65 // watch for certificate file create/replace 66 certWatchPtr = std::make_unique< 67 Watch>(event, certInstallPath, [this]() { 68 try 69 { 70 // if certificate file existing update it 71 if (certificatePtr != nullptr) 72 { 73 log<level::INFO>( 74 "Inotify callback to update certificate properties"); 75 certificatePtr->populateProperties(); 76 } 77 else 78 { 79 log<level::INFO>( 80 "Inotify callback to create certificate object"); 81 createCertificate(); 82 } 83 } 84 catch (const InternalFailure& e) 85 { 86 commit<InternalFailure>(); 87 } 88 catch (const InvalidCertificate& e) 89 { 90 commit<InvalidCertificate>(); 91 } 92 }); 93 } 94 } 95 96 void Manager::install(const std::string filePath) 97 { 98 using NotAllowed = 99 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 100 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON; 101 // TODO: Issue#3 At present supporting only one certificate to be 102 // uploaded this need to be revisited to support multiple 103 // certificates 104 if (certificatePtr != nullptr) 105 { 106 elog<NotAllowed>(Reason("Certificate already exist")); 107 } 108 109 auto certObjectPath = objectPath + '/' + '1'; 110 certificatePtr = std::make_unique<Certificate>( 111 bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath, 112 false, certWatchPtr); 113 } 114 115 void Manager::delete_() 116 { 117 // TODO: #Issue 4 when a certificate is deleted system auto generates 118 // certificate file. At present we are not supporting creation of 119 // certificate object for the auto-generated certificate file as 120 // deletion if only applicable for REST server and Bmcweb does not allow 121 // deletion of certificates 122 if (certificatePtr != nullptr) 123 { 124 certificatePtr.reset(nullptr); 125 } 126 } 127 128 std::string Manager::generateCSR( 129 std::vector<std::string> alternativeNames, std::string challengePassword, 130 std::string city, std::string commonName, std::string contactPerson, 131 std::string country, std::string email, std::string givenName, 132 std::string initials, int64_t keyBitLength, std::string keyCurveId, 133 std::string keyPairAlgorithm, std::vector<std::string> keyUsage, 134 std::string organization, std::string organizationalUnit, std::string state, 135 std::string surname, std::string unstructuredName) 136 { 137 // We support only one CSR. 138 csrPtr.reset(nullptr); 139 auto pid = fork(); 140 if (pid == -1) 141 { 142 log<level::ERR>("Error occurred during forking process"); 143 report<InternalFailure>(); 144 } 145 else if (pid == 0) 146 { 147 try 148 { 149 generateCSRHelper(alternativeNames, challengePassword, city, 150 commonName, contactPerson, country, email, 151 givenName, initials, keyBitLength, keyCurveId, 152 keyPairAlgorithm, keyUsage, organization, 153 organizationalUnit, state, surname, 154 unstructuredName); 155 exit(EXIT_SUCCESS); 156 } 157 catch (const InternalFailure& e) 158 { 159 // commit the error reported in child process and exit 160 // Callback method from SDEvent Loop looks for exit status 161 exit(EXIT_FAILURE); 162 commit<InternalFailure>(); 163 } 164 } 165 else 166 { 167 using namespace sdeventplus::source; 168 Child::Callback callback = [this](Child& eventSource, 169 const siginfo_t* si) { 170 eventSource.set_enabled(Enabled::On); 171 if (si->si_status != 0) 172 { 173 this->createCSRObject(Status::FAILURE); 174 } 175 else 176 { 177 this->createCSRObject(Status::SUCCESS); 178 } 179 }; 180 try 181 { 182 sigset_t ss; 183 if (sigemptyset(&ss) < 0) 184 { 185 log<level::ERR>("Unable to initialize signal set"); 186 elog<InternalFailure>(); 187 } 188 if (sigaddset(&ss, SIGCHLD) < 0) 189 { 190 log<level::ERR>("Unable to add signal to signal set"); 191 elog<InternalFailure>(); 192 } 193 194 // Block SIGCHLD first, so that the event loop can handle it 195 if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0) 196 { 197 log<level::ERR>("Unable to block signal"); 198 elog<InternalFailure>(); 199 } 200 if (childPtr) 201 { 202 childPtr.reset(); 203 } 204 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED, 205 std::move(callback)); 206 } 207 catch (const InternalFailure& e) 208 { 209 commit<InternalFailure>(); 210 } 211 } 212 auto csrObjectPath = objectPath + '/' + "csr"; 213 return csrObjectPath; 214 } 215 216 CertificatePtr& Manager::getCertificate() 217 { 218 return certificatePtr; 219 } 220 221 void Manager::generateCSRHelper( 222 std::vector<std::string> alternativeNames, std::string challengePassword, 223 std::string city, std::string commonName, std::string contactPerson, 224 std::string country, std::string email, std::string givenName, 225 std::string initials, int64_t keyBitLength, std::string keyCurveId, 226 std::string keyPairAlgorithm, std::vector<std::string> keyUsage, 227 std::string organization, std::string organizationalUnit, std::string state, 228 std::string surname, std::string unstructuredName) 229 { 230 int ret = 0; 231 232 // set version of x509 req 233 int nVersion = 1; 234 // TODO: Issue#6 need to make version number configurable 235 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free); 236 ret = X509_REQ_set_version(x509Req.get(), nVersion); 237 if (ret == 0) 238 { 239 log<level::ERR>("Error occured during X509_REQ_set_version call"); 240 elog<InternalFailure>(); 241 } 242 243 // set subject of x509 req 244 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get()); 245 246 if (!alternativeNames.empty()) 247 { 248 for (auto& name : alternativeNames) 249 { 250 addEntry(x509Name, "subjectAltName", name); 251 } 252 } 253 addEntry(x509Name, "challengePassword", challengePassword); 254 addEntry(x509Name, "L", city); 255 addEntry(x509Name, "CN", commonName); 256 addEntry(x509Name, "name", contactPerson); 257 addEntry(x509Name, "C", country); 258 addEntry(x509Name, "emailAddress", email); 259 addEntry(x509Name, "GN", givenName); 260 addEntry(x509Name, "initials", initials); 261 addEntry(x509Name, "algorithm", keyPairAlgorithm); 262 if (!keyUsage.empty()) 263 { 264 for (auto& usage : keyUsage) 265 { 266 addEntry(x509Name, "keyUsage", usage); 267 } 268 } 269 addEntry(x509Name, "O", organization); 270 addEntry(x509Name, "ST", state); 271 addEntry(x509Name, "SN", surname); 272 addEntry(x509Name, "unstructuredName", unstructuredName); 273 274 EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free); 275 276 log<level::INFO>("Given Key pair algorithm", 277 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str())); 278 279 // Used EC algorithm as default if user did not give algorithm type. 280 if (keyPairAlgorithm == "RSA") 281 pKey = getRSAKeyPair(keyBitLength); 282 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty())) 283 pKey = generateECKeyPair(keyCurveId); 284 else 285 { 286 log<level::ERR>("Given Key pair algorithm is not supported. Supporting " 287 "RSA and EC only"); 288 elog<InvalidArgument>( 289 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"), 290 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str())); 291 } 292 293 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get()); 294 if (ret == 0) 295 { 296 log<level::ERR>("Error occured while setting Public key"); 297 elog<InternalFailure>(); 298 } 299 300 // Write private key to file 301 writePrivateKey(pKey, PRIV_KEY_FILE_NAME); 302 303 // set sign key of x509 req 304 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256()); 305 if (ret == 0) 306 { 307 log<level::ERR>("Error occured while signing key of x509"); 308 elog<InternalFailure>(); 309 } 310 311 log<level::INFO>("Writing CSR to file"); 312 fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME; 313 writeCSR(csrFilePath.string(), x509Req); 314 } 315 316 EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength) 317 { 318 int ret = 0; 319 // generate rsa key 320 BIGNUM_Ptr bne(BN_new(), ::BN_free); 321 ret = BN_set_word(bne.get(), RSA_F4); 322 if (ret == 0) 323 { 324 log<level::ERR>("Error occured during BN_set_word call"); 325 elog<InternalFailure>(); 326 } 327 328 int64_t keyBitLen = keyBitLength; 329 // set keybit length to default value if not set 330 if (keyBitLen <= 0) 331 { 332 constexpr auto DEFAULT_KEYBITLENGTH = 2048; 333 log<level::INFO>( 334 "KeyBitLength is not given.Hence, using default KeyBitLength", 335 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH)); 336 keyBitLen = DEFAULT_KEYBITLENGTH; 337 } 338 RSA* rsa = RSA_new(); 339 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL); 340 if (ret != 1) 341 { 342 free(rsa); 343 log<level::ERR>("Error occured during RSA_generate_key_ex call", 344 entry("KEYBITLENGTH=%PRIu64", keyBitLen)); 345 elog<InternalFailure>(); 346 } 347 348 // set public key of x509 req 349 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free); 350 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa); 351 if (ret == 0) 352 { 353 free(rsa); 354 log<level::ERR>("Error occured during assign rsa key into EVP"); 355 elog<InternalFailure>(); 356 } 357 358 return pKey; 359 } 360 361 EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId) 362 { 363 std::string curId(curveId); 364 365 if (curId.empty()) 366 { 367 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349 368 constexpr auto DEFAULT_KEYCURVEID = "secp224r1"; 369 log<level::INFO>( 370 "KeyCurveId is not given. Hence using default curve id", 371 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID)); 372 curId = DEFAULT_KEYCURVEID; 373 } 374 375 int ecGrp = OBJ_txt2nid(curId.c_str()); 376 377 if (ecGrp == NID_undef) 378 { 379 log<level::ERR>( 380 "Error occured during convert the curve id string format into NID", 381 entry("KEYCURVEID=%s", curId.c_str())); 382 elog<InternalFailure>(); 383 } 384 385 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp); 386 387 if (ecKey == NULL) 388 { 389 log<level::ERR>( 390 "Error occured during create the EC_Key object from NID", 391 entry("ECGROUP=%d", ecGrp)); 392 elog<InternalFailure>(); 393 } 394 395 // If you want to save a key and later load it with 396 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE 397 // flag on the key. 398 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE); 399 400 int ret = EC_KEY_generate_key(ecKey); 401 402 if (ret == 0) 403 { 404 EC_KEY_free(ecKey); 405 log<level::ERR>("Error occured during generate EC key"); 406 elog<InternalFailure>(); 407 } 408 409 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free); 410 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey); 411 if (ret == 0) 412 { 413 EC_KEY_free(ecKey); 414 log<level::ERR>("Error occured during assign EC Key into EVP"); 415 elog<InternalFailure>(); 416 } 417 418 return pKey; 419 } 420 421 void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey, 422 const std::string& privKeyFileName) 423 { 424 log<level::INFO>("Writing private key to file"); 425 // write private key to file 426 fs::path privKeyPath = certParentInstallPath / privKeyFileName; 427 428 FILE* fp = std::fopen(privKeyPath.c_str(), "w"); 429 if (fp == NULL) 430 { 431 log<level::ERR>("Error occured creating private key file"); 432 elog<InternalFailure>(); 433 } 434 int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL); 435 std::fclose(fp); 436 if (ret == 0) 437 { 438 log<level::ERR>("Error occured while writing private key to file"); 439 elog<InternalFailure>(); 440 } 441 } 442 443 void Manager::addEntry(X509_NAME* x509Name, const char* field, 444 const std::string& bytes) 445 { 446 if (bytes.empty()) 447 { 448 return; 449 } 450 int ret = X509_NAME_add_entry_by_txt( 451 x509Name, field, MBSTRING_ASC, 452 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0); 453 if (ret != 1) 454 { 455 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field), 456 entry("VALUE=%s", bytes.c_str())); 457 elog<InternalFailure>(); 458 } 459 } 460 461 void Manager::createCSRObject(const Status& status) 462 { 463 if (csrPtr) 464 { 465 csrPtr.reset(nullptr); 466 } 467 auto csrObjectPath = objectPath + '/' + "csr"; 468 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(), 469 certInstallPath.c_str(), status); 470 } 471 472 void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req) 473 { 474 if (fs::exists(filePath)) 475 { 476 log<level::INFO>("Removing the existing file", 477 entry("FILENAME=%s", filePath.c_str())); 478 if (!fs::remove(filePath.c_str())) 479 { 480 log<level::ERR>("Unable to remove the file", 481 entry("FILENAME=%s", filePath.c_str())); 482 elog<InternalFailure>(); 483 } 484 } 485 486 FILE* fp = NULL; 487 488 if ((fp = std::fopen(filePath.c_str(), "w")) == NULL) 489 { 490 log<level::ERR>("Error opening the file to write the CSR", 491 entry("FILENAME=%s", filePath.c_str())); 492 elog<InternalFailure>(); 493 } 494 495 int rc = PEM_write_X509_REQ(fp, x509Req.get()); 496 if (!rc) 497 { 498 log<level::ERR>("PEM write routine failed", 499 entry("FILENAME=%s", filePath.c_str())); 500 std::fclose(fp); 501 elog<InternalFailure>(); 502 } 503 std::fclose(fp); 504 } 505 506 void Manager::createCertificate() 507 { 508 try 509 { 510 // TODO: Issue#3 At present supporting only one certificate to be 511 // uploaded this need to be revisited to support multiple 512 // certificates 513 auto certObjectPath = objectPath + '/' + '1'; 514 certificatePtr = std::make_unique<Certificate>( 515 bus, certObjectPath, certType, unitToRestart, certInstallPath, 516 certInstallPath, true, certWatchPtr); 517 } 518 catch (const InternalFailure& e) 519 { 520 report<InternalFailure>(); 521 } 522 catch (const InvalidCertificate& e) 523 { 524 report<InvalidCertificate>( 525 Reason("Existing certificate file is corrupted")); 526 } 527 } 528 529 void Manager::createRSAPrivateKeyFile() 530 { 531 fs::path rsaPrivateKeyFileName = 532 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME; 533 534 try 535 { 536 if (!fs::exists(rsaPrivateKeyFileName)) 537 { 538 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH), 539 RSA_PRIV_KEY_FILE_NAME); 540 } 541 } 542 catch (const InternalFailure& e) 543 { 544 report<InternalFailure>(); 545 } 546 } 547 548 EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength) 549 { 550 if (keyBitLength != SUPPORTED_KEYBITLENGTH) 551 { 552 log<level::ERR>( 553 "Given Key bit length is not supported", 554 entry("GIVENKEYBITLENGTH=%d", keyBitLength), 555 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH)); 556 elog<InvalidArgument>( 557 Argument::ARGUMENT_NAME("KEYBITLENGTH"), 558 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str())); 559 } 560 fs::path rsaPrivateKeyFileName = 561 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME; 562 563 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r"); 564 if (!privateKeyFile) 565 { 566 log<level::ERR>("Unable to open RSA private key file to read", 567 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()), 568 entry("ERRORREASON=%s", strerror(errno))); 569 elog<InternalFailure>(); 570 } 571 572 EVP_PKEY_Ptr privateKey( 573 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr), 574 ::EVP_PKEY_free); 575 std::fclose(privateKeyFile); 576 577 if (!privateKey) 578 { 579 log<level::ERR>("Error occured during PEM_read_PrivateKey call"); 580 elog<InternalFailure>(); 581 } 582 return privateKey; 583 } 584 } // namespace certs 585 } // namespace phosphor 586