Add in-tree nvidia-ipmi-oem provider
This patch adds nvidia-ipmi-oem. The current patch adds support
to 2 commands:
1) GET BIOS PASSWORD
2) SET BIOS PASSWORD
Tested:
1) Tested on patch
https://gerrit.openbmc.org/c/openbmc/openbmc/+/80748
Change-Id: I48599a1b85a3a614c56e7687b8646f7dfe1e7bb1
Signed-off-by: Prithvi Pai <ppai@nvidia.com>
diff --git a/OWNERS b/OWNERS
index 432ca86..a9dd7fb 100644
--- a/OWNERS
+++ b/OWNERS
@@ -45,6 +45,9 @@
- partial_regex: oem/example
owners:
- vernon.mauery@gmail.com
+- partial_regex: oem/nvidia
+ owners:
+ - prithvi24pai@gmail.com
openbmc:
- name: Adriana Kobylak
diff --git a/meson.options b/meson.options
index ed2a4f3..76fbdd8 100644
--- a/meson.options
+++ b/meson.options
@@ -280,6 +280,7 @@
# keep list below sorted alphabetically
#######################################
'example',
+ 'nvidia',
],
value: ['all'],
description: 'Build selected OEM IPMI provider libraries',
diff --git a/oem/meson.build b/oem/meson.build
index 9bae274..54a85ba 100644
--- a/oem/meson.build
+++ b/oem/meson.build
@@ -8,3 +8,7 @@
if 'example' in oem_opt or 'all' in oem_opt
subdir('example')
endif
+
+if 'nvidia' in oem_opt or 'all' in oem_opt
+ subdir('nvidia')
+endif
diff --git a/oem/nvidia/biosconfigcommands.cpp b/oem/nvidia/biosconfigcommands.cpp
new file mode 100644
index 0000000..a4ed842
--- /dev/null
+++ b/oem/nvidia/biosconfigcommands.cpp
@@ -0,0 +1,183 @@
+#include "config.h"
+
+#include "oemcommands.hpp"
+
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+
+#include <ipmid/api.hpp>
+#include <ipmid/types.hpp>
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/lg2.hpp>
+
+#include <array>
+#include <cstdint>
+#include <fstream>
+#include <string>
+#include <vector>
+
+constexpr char biosPasswordFilePath[] =
+ "/var/lib/bios-settings-manager/seedData";
+constexpr int biosPasswordIter = 1000;
+constexpr uint8_t biosPasswordSaltSize = 32;
+constexpr uint8_t biosPasswordMaxHashSize = 64;
+constexpr uint8_t biosPasswordTypeNoChange = 0x00;
+constexpr uint8_t biosPasswordSelectorAdmin = 0x01;
+constexpr uint8_t biosPasswordTypeNoPassowrd = 0x01;
+constexpr uint8_t biosPasswordTypePbkdf2Sha256 = 0x02;
+constexpr uint8_t biosPasswordTypePbkdf2Sha384 = 0x03;
+
+void registerBiosConfigCommands() __attribute__((constructor));
+
+namespace ipmi
+{
+ipmi::RspType<> ipmiSetBiosPassword(
+ uint8_t id, uint8_t type, std::array<uint8_t, biosPasswordSaltSize> salt,
+ std::array<uint8_t, biosPasswordMaxHashSize> hash)
+{
+ nlohmann::json json;
+
+ if (id != biosPasswordSelectorAdmin)
+ {
+ return ipmi::responseInvalidFieldRequest();
+ }
+ // key names for json object
+ constexpr char keyHashAlgo[] = "HashAlgo";
+ constexpr char keySeed[] = "Seed";
+ constexpr char keyAdminPwdHash[] = "AdminPwdHash";
+ constexpr char keyIsAdminPwdChanged[] = "IsAdminPwdChanged";
+ constexpr char keyIsUserPwdChanged[] = "IsUserPwdChanged";
+ constexpr char keyUserPwdHash[] = "UserPwdHash";
+
+ switch (type)
+ {
+ case biosPasswordTypeNoPassowrd:
+ json[keyHashAlgo] = "SHA256";
+ RAND_bytes(salt.data(), salt.size());
+ // password is only Null-terminated character
+ PKCS5_PBKDF2_HMAC("", 1, salt.data(), salt.size(), biosPasswordIter,
+ EVP_sha256(), SHA256_DIGEST_LENGTH, hash.data());
+ json[keySeed] = salt;
+ json[keyAdminPwdHash] = hash;
+ break;
+ case biosPasswordTypePbkdf2Sha256:
+ json[keyHashAlgo] = "SHA256";
+ json[keySeed] = salt;
+ json[keyAdminPwdHash] = hash;
+ break;
+ case biosPasswordTypePbkdf2Sha384:
+ json[keyHashAlgo] = "SHA384";
+ json[keySeed] = salt;
+ json[keyAdminPwdHash] = hash;
+ break;
+ default:
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ json[keyIsAdminPwdChanged] = false;
+ json[keyIsUserPwdChanged] = false;
+
+ // initializing with 0 as user password hash field
+ // is not used presently
+ constexpr std::array<uint8_t, biosPasswordMaxHashSize> userPwdHash = {0};
+ json[keyUserPwdHash] = userPwdHash;
+
+ try
+ {
+ std::ofstream ofs(biosPasswordFilePath, std::ios::out);
+ const auto& writeData = json.dump();
+ ofs << writeData;
+ ofs.close();
+ }
+ catch (std::exception& e)
+ {
+ lg2::error("Failed to save BIOS Password information: {ERROR}", "ERROR",
+ e.what());
+ return ipmi::responseUnspecifiedError();
+ }
+ return ipmi::responseSuccess();
+}
+
+ipmi::RspType<uint8_t, // action
+ std::array<uint8_t, biosPasswordSaltSize>, // salt
+ std::array<uint8_t, biosPasswordMaxHashSize> // hash
+ >
+ ipmiGetBiosPassword(uint8_t id)
+{
+ uint8_t action = biosPasswordTypeNoChange;
+ std::array<uint8_t, biosPasswordSaltSize> salt = {0};
+ std::array<uint8_t, biosPasswordMaxHashSize> hash = {0};
+
+ if (id != biosPasswordSelectorAdmin)
+ {
+ return ipmi::responseParmOutOfRange();
+ }
+
+ std::ifstream ifs(biosPasswordFilePath);
+ if (!ifs.is_open())
+ {
+ // return No change if no file
+ return ipmi::responseSuccess(action, salt, hash);
+ }
+
+ // key names for json object
+ constexpr char keyIsAdminPwdChanged[] = "IsAdminPwdChanged";
+ constexpr char keyHashAlgo[] = "HashAlgo";
+ constexpr char keySeed[] = "Seed";
+ constexpr char keyAdminPwdHash[] = "AdminPwdHash";
+
+ nlohmann::json json = nlohmann::json::parse(ifs, nullptr, false);
+ if (json.is_discarded() || !json.contains(keyIsAdminPwdChanged) ||
+ !json.contains(keyHashAlgo) || !json.contains(keySeed) ||
+ !json.contains(keyAdminPwdHash))
+ {
+ return ipmi::responseResponseError();
+ }
+ bool IsAdminPwdChanged = json[keyIsAdminPwdChanged];
+ if (IsAdminPwdChanged == false)
+ {
+ return ipmi::responseSuccess(action, salt, hash);
+ }
+
+ salt = json[keySeed];
+ hash = json[keyAdminPwdHash];
+
+ std::string HashAlgo = json[keyHashAlgo];
+ auto digest = EVP_sha256();
+ int keylen = SHA256_DIGEST_LENGTH;
+
+ if (HashAlgo == "SHA256")
+ {
+ action = biosPasswordTypePbkdf2Sha256;
+ }
+ else if (HashAlgo == "SHA384")
+ {
+ action = biosPasswordTypePbkdf2Sha384;
+ digest = EVP_sha384();
+ keylen = SHA384_DIGEST_LENGTH;
+ }
+
+ std::array<uint8_t, biosPasswordMaxHashSize> nullHash = {0};
+ PKCS5_PBKDF2_HMAC("", 1, salt.data(), salt.size(), biosPasswordIter, digest,
+ keylen, nullHash.data());
+ if (hash == nullHash)
+ {
+ action = biosPasswordTypeNoPassowrd;
+ salt.fill(0x00);
+ hash.fill(0x00);
+ }
+
+ return ipmi::responseSuccess(action, salt, hash);
+}
+} // namespace ipmi
+
+void registerBiosConfigCommands()
+{
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::groupNvidia,
+ ipmi::bios_password::cmdSetBiosPassword,
+ ipmi::Privilege::Admin, ipmi::ipmiSetBiosPassword);
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::groupNvidia,
+ ipmi::bios_password::cmdGetBiosPassword,
+ ipmi::Privilege::Admin, ipmi::ipmiGetBiosPassword);
+}
diff --git a/oem/nvidia/meson.build b/oem/nvidia/meson.build
new file mode 100644
index 0000000..f22c98e
--- /dev/null
+++ b/oem/nvidia/meson.build
@@ -0,0 +1,26 @@
+# nvidia OEM library build
+
+nvidia_oem_src = ['biosconfigcommands.cpp']
+
+nvidia_oem_deps = [
+ boost,
+ crypto,
+ ipmid_dep,
+ nlohmann_json_dep,
+ phosphor_dbus_interfaces_dep,
+ phosphor_logging_dep,
+ sdbusplus_dep,
+ stdplus_dep,
+]
+
+
+nvidia_ipmi_oem = library(
+ 'nvidia_ipmi_oem',
+ nvidia_oem_src,
+ dependencies: nvidia_oem_deps,
+ include_directories: [root_inc],
+ install: true,
+ install_dir: get_option('libdir') / 'ipmid-providers',
+ version: meson.project_version(),
+ override_options: ipmi_plugin_options,
+)
diff --git a/oem/nvidia/oemcommands.hpp b/oem/nvidia/oemcommands.hpp
new file mode 100644
index 0000000..2aa67ee
--- /dev/null
+++ b/oem/nvidia/oemcommands.hpp
@@ -0,0 +1,15 @@
+#pragma once
+#include <cstdint>
+
+namespace ipmi
+{
+
+using Group = uint8_t;
+constexpr Group groupNvidia = 0x3C;
+
+namespace bios_password
+{
+constexpr auto cmdSetBiosPassword = 0x36;
+constexpr auto cmdGetBiosPassword = 0x37;
+} // namespace bios_password
+} // namespace ipmi