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