blob: 6952d85ba4f31decbacc60549fc4d898e8a77885 [file] [log] [blame]
Prithvi Pai62be74f2025-08-26 17:18:05 +05301/*
2 * SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION &
3 * AFFILIATES. All rights reserved.
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
Prithvi Pai6b580c72025-06-05 11:13:34 +05307#include "config.h"
8
9#include "oemcommands.hpp"
10
11#include <openssl/evp.h>
12#include <openssl/rand.h>
13#include <openssl/sha.h>
14
15#include <ipmid/api.hpp>
16#include <ipmid/types.hpp>
17#include <nlohmann/json.hpp>
18#include <phosphor-logging/lg2.hpp>
19
20#include <array>
21#include <cstdint>
22#include <fstream>
23#include <string>
24#include <vector>
25
26constexpr char biosPasswordFilePath[] =
27 "/var/lib/bios-settings-manager/seedData";
28constexpr int biosPasswordIter = 1000;
29constexpr uint8_t biosPasswordSaltSize = 32;
30constexpr uint8_t biosPasswordMaxHashSize = 64;
31constexpr uint8_t biosPasswordTypeNoChange = 0x00;
32constexpr uint8_t biosPasswordSelectorAdmin = 0x01;
33constexpr uint8_t biosPasswordTypeNoPassowrd = 0x01;
34constexpr uint8_t biosPasswordTypePbkdf2Sha256 = 0x02;
35constexpr uint8_t biosPasswordTypePbkdf2Sha384 = 0x03;
36
37void registerBiosConfigCommands() __attribute__((constructor));
38
39namespace ipmi
40{
41ipmi::RspType<> ipmiSetBiosPassword(
42 uint8_t id, uint8_t type, std::array<uint8_t, biosPasswordSaltSize> salt,
43 std::array<uint8_t, biosPasswordMaxHashSize> hash)
44{
45 nlohmann::json json;
46
47 if (id != biosPasswordSelectorAdmin)
48 {
49 return ipmi::responseInvalidFieldRequest();
50 }
51 // key names for json object
52 constexpr char keyHashAlgo[] = "HashAlgo";
53 constexpr char keySeed[] = "Seed";
54 constexpr char keyAdminPwdHash[] = "AdminPwdHash";
55 constexpr char keyIsAdminPwdChanged[] = "IsAdminPwdChanged";
56 constexpr char keyIsUserPwdChanged[] = "IsUserPwdChanged";
57 constexpr char keyUserPwdHash[] = "UserPwdHash";
58
59 switch (type)
60 {
61 case biosPasswordTypeNoPassowrd:
62 json[keyHashAlgo] = "SHA256";
63 RAND_bytes(salt.data(), salt.size());
64 // password is only Null-terminated character
65 PKCS5_PBKDF2_HMAC("", 1, salt.data(), salt.size(), biosPasswordIter,
66 EVP_sha256(), SHA256_DIGEST_LENGTH, hash.data());
67 json[keySeed] = salt;
68 json[keyAdminPwdHash] = hash;
69 break;
70 case biosPasswordTypePbkdf2Sha256:
71 json[keyHashAlgo] = "SHA256";
72 json[keySeed] = salt;
73 json[keyAdminPwdHash] = hash;
74 break;
75 case biosPasswordTypePbkdf2Sha384:
76 json[keyHashAlgo] = "SHA384";
77 json[keySeed] = salt;
78 json[keyAdminPwdHash] = hash;
79 break;
80 default:
81 return ipmi::responseInvalidFieldRequest();
82 }
83
84 json[keyIsAdminPwdChanged] = false;
85 json[keyIsUserPwdChanged] = false;
86
87 // initializing with 0 as user password hash field
88 // is not used presently
89 constexpr std::array<uint8_t, biosPasswordMaxHashSize> userPwdHash = {0};
90 json[keyUserPwdHash] = userPwdHash;
91
92 try
93 {
94 std::ofstream ofs(biosPasswordFilePath, std::ios::out);
95 const auto& writeData = json.dump();
96 ofs << writeData;
97 ofs.close();
98 }
99 catch (std::exception& e)
100 {
101 lg2::error("Failed to save BIOS Password information: {ERROR}", "ERROR",
102 e.what());
103 return ipmi::responseUnspecifiedError();
104 }
105 return ipmi::responseSuccess();
106}
107
108ipmi::RspType<uint8_t, // action
109 std::array<uint8_t, biosPasswordSaltSize>, // salt
110 std::array<uint8_t, biosPasswordMaxHashSize> // hash
111 >
112 ipmiGetBiosPassword(uint8_t id)
113{
114 uint8_t action = biosPasswordTypeNoChange;
115 std::array<uint8_t, biosPasswordSaltSize> salt = {0};
116 std::array<uint8_t, biosPasswordMaxHashSize> hash = {0};
117
118 if (id != biosPasswordSelectorAdmin)
119 {
120 return ipmi::responseParmOutOfRange();
121 }
122
123 std::ifstream ifs(biosPasswordFilePath);
124 if (!ifs.is_open())
125 {
126 // return No change if no file
127 return ipmi::responseSuccess(action, salt, hash);
128 }
129
130 // key names for json object
131 constexpr char keyIsAdminPwdChanged[] = "IsAdminPwdChanged";
132 constexpr char keyHashAlgo[] = "HashAlgo";
133 constexpr char keySeed[] = "Seed";
134 constexpr char keyAdminPwdHash[] = "AdminPwdHash";
135
136 nlohmann::json json = nlohmann::json::parse(ifs, nullptr, false);
137 if (json.is_discarded() || !json.contains(keyIsAdminPwdChanged) ||
138 !json.contains(keyHashAlgo) || !json.contains(keySeed) ||
139 !json.contains(keyAdminPwdHash))
140 {
141 return ipmi::responseResponseError();
142 }
143 bool IsAdminPwdChanged = json[keyIsAdminPwdChanged];
144 if (IsAdminPwdChanged == false)
145 {
146 return ipmi::responseSuccess(action, salt, hash);
147 }
148
149 salt = json[keySeed];
150 hash = json[keyAdminPwdHash];
151
152 std::string HashAlgo = json[keyHashAlgo];
153 auto digest = EVP_sha256();
154 int keylen = SHA256_DIGEST_LENGTH;
155
156 if (HashAlgo == "SHA256")
157 {
158 action = biosPasswordTypePbkdf2Sha256;
159 }
160 else if (HashAlgo == "SHA384")
161 {
162 action = biosPasswordTypePbkdf2Sha384;
163 digest = EVP_sha384();
164 keylen = SHA384_DIGEST_LENGTH;
165 }
166
167 std::array<uint8_t, biosPasswordMaxHashSize> nullHash = {0};
168 PKCS5_PBKDF2_HMAC("", 1, salt.data(), salt.size(), biosPasswordIter, digest,
169 keylen, nullHash.data());
170 if (hash == nullHash)
171 {
172 action = biosPasswordTypeNoPassowrd;
173 salt.fill(0x00);
174 hash.fill(0x00);
175 }
176
177 return ipmi::responseSuccess(action, salt, hash);
178}
179} // namespace ipmi
180
181void registerBiosConfigCommands()
182{
183 ipmi::registerHandler(ipmi::prioOemBase, ipmi::groupNvidia,
184 ipmi::bios_password::cmdSetBiosPassword,
185 ipmi::Privilege::Admin, ipmi::ipmiSetBiosPassword);
186 ipmi::registerHandler(ipmi::prioOemBase, ipmi::groupNvidia,
187 ipmi::bios_password::cmdGetBiosPassword,
188 ipmi::Privilege::Admin, ipmi::ipmiGetBiosPassword);
189}