xref: /openbmc/phosphor-host-ipmid/oem/nvidia/biosconfigcommands.cpp (revision 2be03014a9da82b9abcf64cec78a494657ebb9dd)
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