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