xref: /openbmc/phosphor-host-ipmid/user_channel/passwd_mgr.cpp (revision b29b5ab3b9ad812949f3621a4422fde8d7c1c8d2)
14654d99fSRichard Marian Thomaiyar /*
24654d99fSRichard Marian Thomaiyar // Copyright (c) 2018 Intel Corporation
34654d99fSRichard Marian Thomaiyar //
44654d99fSRichard Marian Thomaiyar // Licensed under the Apache License, Version 2.0 (the "License");
54654d99fSRichard Marian Thomaiyar // you may not use this file except in compliance with the License.
64654d99fSRichard Marian Thomaiyar // You may obtain a copy of the License at
74654d99fSRichard Marian Thomaiyar //
84654d99fSRichard Marian Thomaiyar //      http://www.apache.org/licenses/LICENSE-2.0
94654d99fSRichard Marian Thomaiyar //
104654d99fSRichard Marian Thomaiyar // Unless required by applicable law or agreed to in writing, software
114654d99fSRichard Marian Thomaiyar // distributed under the License is distributed on an "AS IS" BASIS,
124654d99fSRichard Marian Thomaiyar // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134654d99fSRichard Marian Thomaiyar // See the License for the specific language governing permissions and
144654d99fSRichard Marian Thomaiyar // limitations under the License.
154654d99fSRichard Marian Thomaiyar */
164654d99fSRichard Marian Thomaiyar 
174654d99fSRichard Marian Thomaiyar #include "passwd_mgr.hpp"
184654d99fSRichard Marian Thomaiyar 
19*b29b5ab3SAppaRao Puli #include "file.hpp"
204654d99fSRichard Marian Thomaiyar #include "shadowlock.hpp"
214654d99fSRichard Marian Thomaiyar 
224654d99fSRichard Marian Thomaiyar #include <openssl/hmac.h>
23*b29b5ab3SAppaRao Puli #include <openssl/rand.h>
244654d99fSRichard Marian Thomaiyar #include <openssl/sha.h>
254654d99fSRichard Marian Thomaiyar #include <string.h>
264654d99fSRichard Marian Thomaiyar #include <sys/stat.h>
27*b29b5ab3SAppaRao Puli #include <unistd.h>
284654d99fSRichard Marian Thomaiyar 
29*b29b5ab3SAppaRao Puli #include <cerrno>
304654d99fSRichard Marian Thomaiyar #include <cstring>
314654d99fSRichard Marian Thomaiyar #include <fstream>
32*b29b5ab3SAppaRao Puli #include <iomanip>
334654d99fSRichard Marian Thomaiyar #include <phosphor-logging/log.hpp>
344654d99fSRichard Marian Thomaiyar 
354654d99fSRichard Marian Thomaiyar namespace ipmi
364654d99fSRichard Marian Thomaiyar {
374654d99fSRichard Marian Thomaiyar 
384654d99fSRichard Marian Thomaiyar static const char* passwdFileName = "/etc/ipmi_pass";
394654d99fSRichard Marian Thomaiyar static const char* encryptKeyFileName = "/etc/key_file";
404654d99fSRichard Marian Thomaiyar static const size_t maxKeySize = 8;
414654d99fSRichard Marian Thomaiyar 
42*b29b5ab3SAppaRao Puli #define META_PASSWD_SIG "=OPENBMC="
43*b29b5ab3SAppaRao Puli 
44*b29b5ab3SAppaRao Puli static inline size_t blockRound(size_t odd, size_t blk)
45*b29b5ab3SAppaRao Puli {
46*b29b5ab3SAppaRao Puli     return ((odd) + (((blk) - ((odd) & ((blk)-1))) & ((blk)-1)));
47*b29b5ab3SAppaRao Puli }
484654d99fSRichard Marian Thomaiyar 
494654d99fSRichard Marian Thomaiyar /*
504654d99fSRichard Marian Thomaiyar  * Meta data struct for encrypted password file
514654d99fSRichard Marian Thomaiyar  */
524654d99fSRichard Marian Thomaiyar struct metaPassStruct
534654d99fSRichard Marian Thomaiyar {
544654d99fSRichard Marian Thomaiyar     char signature[10];
554654d99fSRichard Marian Thomaiyar     unsigned char reseved[2];
564654d99fSRichard Marian Thomaiyar     size_t hashSize;
574654d99fSRichard Marian Thomaiyar     size_t ivSize;
584654d99fSRichard Marian Thomaiyar     size_t dataSize;
594654d99fSRichard Marian Thomaiyar     size_t padSize;
604654d99fSRichard Marian Thomaiyar     size_t macSize;
614654d99fSRichard Marian Thomaiyar };
624654d99fSRichard Marian Thomaiyar 
634654d99fSRichard Marian Thomaiyar using namespace phosphor::logging;
644654d99fSRichard Marian Thomaiyar 
654654d99fSRichard Marian Thomaiyar PasswdMgr::PasswdMgr()
664654d99fSRichard Marian Thomaiyar {
674654d99fSRichard Marian Thomaiyar     initPasswordMap();
684654d99fSRichard Marian Thomaiyar }
694654d99fSRichard Marian Thomaiyar 
704654d99fSRichard Marian Thomaiyar std::string PasswdMgr::getPasswdByUserName(const std::string& userName)
714654d99fSRichard Marian Thomaiyar {
724654d99fSRichard Marian Thomaiyar     checkAndReload();
734654d99fSRichard Marian Thomaiyar     auto iter = passwdMapList.find(userName);
744654d99fSRichard Marian Thomaiyar     if (iter == passwdMapList.end())
754654d99fSRichard Marian Thomaiyar     {
764654d99fSRichard Marian Thomaiyar         return std::string();
774654d99fSRichard Marian Thomaiyar     }
784654d99fSRichard Marian Thomaiyar     return iter->second;
794654d99fSRichard Marian Thomaiyar }
804654d99fSRichard Marian Thomaiyar 
81*b29b5ab3SAppaRao Puli int PasswdMgr::clearUserEntry(const std::string& userName)
82*b29b5ab3SAppaRao Puli {
83*b29b5ab3SAppaRao Puli     std::time_t updatedTime = getUpdatedFileTime();
84*b29b5ab3SAppaRao Puli     // Check file time stamp to know passwdMapList is up-to-date.
85*b29b5ab3SAppaRao Puli     // If not up-to-date, then updatePasswdSpecialFile will read and
86*b29b5ab3SAppaRao Puli     // check the user entry existance.
87*b29b5ab3SAppaRao Puli     if (fileLastUpdatedTime == updatedTime && updatedTime != -EIO)
88*b29b5ab3SAppaRao Puli     {
89*b29b5ab3SAppaRao Puli         if (passwdMapList.find(userName) == passwdMapList.end())
90*b29b5ab3SAppaRao Puli         {
91*b29b5ab3SAppaRao Puli             log<level::DEBUG>("User not found");
92*b29b5ab3SAppaRao Puli             return 0;
93*b29b5ab3SAppaRao Puli         }
94*b29b5ab3SAppaRao Puli     }
95*b29b5ab3SAppaRao Puli 
96*b29b5ab3SAppaRao Puli     // Write passwdMap to Encryted file
97*b29b5ab3SAppaRao Puli     if (updatePasswdSpecialFile(userName) != 0)
98*b29b5ab3SAppaRao Puli     {
99*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Passwd file update failed");
100*b29b5ab3SAppaRao Puli         return -EIO;
101*b29b5ab3SAppaRao Puli     }
102*b29b5ab3SAppaRao Puli 
103*b29b5ab3SAppaRao Puli     log<level::DEBUG>("Passwd file updated successfully");
104*b29b5ab3SAppaRao Puli     return 0;
105*b29b5ab3SAppaRao Puli }
106*b29b5ab3SAppaRao Puli 
1074654d99fSRichard Marian Thomaiyar void PasswdMgr::checkAndReload(void)
1084654d99fSRichard Marian Thomaiyar {
109*b29b5ab3SAppaRao Puli     std::time_t updatedTime = getUpdatedFileTime();
110*b29b5ab3SAppaRao Puli     if (fileLastUpdatedTime != updatedTime && updatedTime != -1)
1114654d99fSRichard Marian Thomaiyar     {
1124654d99fSRichard Marian Thomaiyar         log<level::DEBUG>("Reloading password map list");
1134654d99fSRichard Marian Thomaiyar         passwdMapList.clear();
1144654d99fSRichard Marian Thomaiyar         initPasswordMap();
1154654d99fSRichard Marian Thomaiyar     }
1164654d99fSRichard Marian Thomaiyar }
1174654d99fSRichard Marian Thomaiyar 
118*b29b5ab3SAppaRao Puli int PasswdMgr::encryptDecryptData(bool doEncrypt, const EVP_CIPHER* cipher,
119*b29b5ab3SAppaRao Puli                                   uint8_t* key, size_t keyLen, uint8_t* iv,
120*b29b5ab3SAppaRao Puli                                   size_t ivLen, uint8_t* inBytes,
121*b29b5ab3SAppaRao Puli                                   size_t inBytesLen, uint8_t* mac,
122*b29b5ab3SAppaRao Puli                                   size_t* macLen, unsigned char* outBytes,
123*b29b5ab3SAppaRao Puli                                   size_t* outBytesLen)
1244654d99fSRichard Marian Thomaiyar {
1254654d99fSRichard Marian Thomaiyar     if (cipher == NULL || key == NULL || iv == NULL || inBytes == NULL ||
1264654d99fSRichard Marian Thomaiyar         outBytes == NULL || mac == NULL || inBytesLen == 0 ||
1274654d99fSRichard Marian Thomaiyar         (size_t)EVP_CIPHER_key_length(cipher) > keyLen ||
1284654d99fSRichard Marian Thomaiyar         (size_t)EVP_CIPHER_iv_length(cipher) > ivLen)
1294654d99fSRichard Marian Thomaiyar     {
1304654d99fSRichard Marian Thomaiyar         log<level::DEBUG>("Error Invalid Inputs");
131*b29b5ab3SAppaRao Puli         return -EINVAL;
1324654d99fSRichard Marian Thomaiyar     }
1334654d99fSRichard Marian Thomaiyar 
134*b29b5ab3SAppaRao Puli     if (!doEncrypt)
135*b29b5ab3SAppaRao Puli     {
136*b29b5ab3SAppaRao Puli         // verify MAC before decrypting the data.
1374654d99fSRichard Marian Thomaiyar         std::array<uint8_t, EVP_MAX_MD_SIZE> calMac;
1384654d99fSRichard Marian Thomaiyar         size_t calMacLen = calMac.size();
1394654d99fSRichard Marian Thomaiyar         // calculate MAC for the encrypted message.
1404654d99fSRichard Marian Thomaiyar         if (NULL == HMAC(EVP_sha256(), key, keyLen, inBytes, inBytesLen,
1414654d99fSRichard Marian Thomaiyar                          calMac.data(),
1424654d99fSRichard Marian Thomaiyar                          reinterpret_cast<unsigned int*>(&calMacLen)))
1434654d99fSRichard Marian Thomaiyar         {
1444654d99fSRichard Marian Thomaiyar             log<level::DEBUG>("Error: Failed to calculate MAC");
145*b29b5ab3SAppaRao Puli             return -EIO;
1464654d99fSRichard Marian Thomaiyar         }
147*b29b5ab3SAppaRao Puli         if (!((calMacLen == *macLen) &&
1484654d99fSRichard Marian Thomaiyar               (std::memcmp(calMac.data(), mac, calMacLen) == 0)))
1494654d99fSRichard Marian Thomaiyar         {
1504654d99fSRichard Marian Thomaiyar             log<level::DEBUG>("Authenticated message doesn't match");
151*b29b5ab3SAppaRao Puli             return -EBADMSG;
152*b29b5ab3SAppaRao Puli         }
1534654d99fSRichard Marian Thomaiyar     }
1544654d99fSRichard Marian Thomaiyar 
1554654d99fSRichard Marian Thomaiyar     std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)> ctx(
1564654d99fSRichard Marian Thomaiyar         EVP_CIPHER_CTX_new(), ::EVP_CIPHER_CTX_free);
1574654d99fSRichard Marian Thomaiyar     EVP_CIPHER_CTX_set_padding(ctx.get(), 1);
1584654d99fSRichard Marian Thomaiyar 
159*b29b5ab3SAppaRao Puli     // Set key & IV
160*b29b5ab3SAppaRao Puli     int retval = EVP_CipherInit_ex(ctx.get(), cipher, NULL, key, iv,
161*b29b5ab3SAppaRao Puli                                    static_cast<int>(doEncrypt));
1624654d99fSRichard Marian Thomaiyar     if (!retval)
1634654d99fSRichard Marian Thomaiyar     {
1644654d99fSRichard Marian Thomaiyar         log<level::DEBUG>("EVP_CipherInit_ex failed",
1654654d99fSRichard Marian Thomaiyar                           entry("RET_VAL=%d", retval));
166*b29b5ab3SAppaRao Puli         return -EIO;
1674654d99fSRichard Marian Thomaiyar     }
1684654d99fSRichard Marian Thomaiyar 
1694654d99fSRichard Marian Thomaiyar     int outLen = 0, outEVPLen = 0;
1704654d99fSRichard Marian Thomaiyar     if ((retval = EVP_CipherUpdate(ctx.get(), outBytes + outLen, &outEVPLen,
1714654d99fSRichard Marian Thomaiyar                                    inBytes, inBytesLen)))
1724654d99fSRichard Marian Thomaiyar     {
1734654d99fSRichard Marian Thomaiyar         outLen += outEVPLen;
1744654d99fSRichard Marian Thomaiyar         if ((retval =
1754654d99fSRichard Marian Thomaiyar                  EVP_CipherFinal(ctx.get(), outBytes + outLen, &outEVPLen)))
1764654d99fSRichard Marian Thomaiyar         {
1774654d99fSRichard Marian Thomaiyar             outLen += outEVPLen;
1784654d99fSRichard Marian Thomaiyar             *outBytesLen = outLen;
1794654d99fSRichard Marian Thomaiyar         }
1804654d99fSRichard Marian Thomaiyar         else
1814654d99fSRichard Marian Thomaiyar         {
1824654d99fSRichard Marian Thomaiyar             log<level::DEBUG>("EVP_CipherFinal fails",
1834654d99fSRichard Marian Thomaiyar                               entry("RET_VAL=%d", retval));
184*b29b5ab3SAppaRao Puli             return -EIO;
1854654d99fSRichard Marian Thomaiyar         }
1864654d99fSRichard Marian Thomaiyar     }
1874654d99fSRichard Marian Thomaiyar     else
1884654d99fSRichard Marian Thomaiyar     {
1894654d99fSRichard Marian Thomaiyar         log<level::DEBUG>("EVP_CipherUpdate fails",
1904654d99fSRichard Marian Thomaiyar                           entry("RET_VAL=%d", retval));
191*b29b5ab3SAppaRao Puli         return -EIO;
192*b29b5ab3SAppaRao Puli     }
193*b29b5ab3SAppaRao Puli 
194*b29b5ab3SAppaRao Puli     if (doEncrypt)
195*b29b5ab3SAppaRao Puli     {
196*b29b5ab3SAppaRao Puli         // Create MAC for the encrypted message
197*b29b5ab3SAppaRao Puli         if (NULL == HMAC(EVP_sha256(), key, keyLen, outBytes, *outBytesLen, mac,
198*b29b5ab3SAppaRao Puli                          reinterpret_cast<unsigned int*>(macLen)))
199*b29b5ab3SAppaRao Puli         {
200*b29b5ab3SAppaRao Puli             log<level::DEBUG>("Failed to create authentication");
201*b29b5ab3SAppaRao Puli             return -EIO;
202*b29b5ab3SAppaRao Puli         }
2034654d99fSRichard Marian Thomaiyar     }
2044654d99fSRichard Marian Thomaiyar     return 0;
2054654d99fSRichard Marian Thomaiyar }
2064654d99fSRichard Marian Thomaiyar 
2074654d99fSRichard Marian Thomaiyar void PasswdMgr::initPasswordMap(void)
2084654d99fSRichard Marian Thomaiyar {
2094654d99fSRichard Marian Thomaiyar     phosphor::user::shadow::Lock lock();
210*b29b5ab3SAppaRao Puli     std::vector<uint8_t> dataBuf;
2114654d99fSRichard Marian Thomaiyar 
212*b29b5ab3SAppaRao Puli     if (readPasswdFileData(dataBuf) != 0)
2134654d99fSRichard Marian Thomaiyar     {
214*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in reading the encrypted pass file");
2154654d99fSRichard Marian Thomaiyar         return;
2164654d99fSRichard Marian Thomaiyar     }
2174654d99fSRichard Marian Thomaiyar 
218*b29b5ab3SAppaRao Puli     if (dataBuf.size() != 0)
2194654d99fSRichard Marian Thomaiyar     {
2204654d99fSRichard Marian Thomaiyar         // populate the user list with password
221*b29b5ab3SAppaRao Puli         char* outPtr = reinterpret_cast<char*>(dataBuf.data());
2224654d99fSRichard Marian Thomaiyar         char* nToken = NULL;
2234654d99fSRichard Marian Thomaiyar         char* linePtr = strtok_r(outPtr, "\n", &nToken);
2244654d99fSRichard Marian Thomaiyar         size_t userEPos = 0, lineSize = 0;
2254654d99fSRichard Marian Thomaiyar         while (linePtr != NULL)
2264654d99fSRichard Marian Thomaiyar         {
2274654d99fSRichard Marian Thomaiyar             std::string lineStr(linePtr);
2284654d99fSRichard Marian Thomaiyar             if ((userEPos = lineStr.find(":")) != std::string::npos)
2294654d99fSRichard Marian Thomaiyar             {
2304654d99fSRichard Marian Thomaiyar                 lineSize = lineStr.size();
2314654d99fSRichard Marian Thomaiyar                 passwdMapList.emplace(
2324654d99fSRichard Marian Thomaiyar                     lineStr.substr(0, userEPos),
2334654d99fSRichard Marian Thomaiyar                     lineStr.substr(userEPos + 1, lineSize - (userEPos + 1)));
2344654d99fSRichard Marian Thomaiyar             }
2354654d99fSRichard Marian Thomaiyar             linePtr = strtok_r(NULL, "\n", &nToken);
2364654d99fSRichard Marian Thomaiyar         }
237*b29b5ab3SAppaRao Puli     }
238*b29b5ab3SAppaRao Puli 
2394654d99fSRichard Marian Thomaiyar     // Update the timestamp
240*b29b5ab3SAppaRao Puli     fileLastUpdatedTime = getUpdatedFileTime();
241*b29b5ab3SAppaRao Puli     return;
242*b29b5ab3SAppaRao Puli }
243*b29b5ab3SAppaRao Puli 
244*b29b5ab3SAppaRao Puli int PasswdMgr::readPasswdFileData(std::vector<uint8_t>& outBytes)
245*b29b5ab3SAppaRao Puli {
246*b29b5ab3SAppaRao Puli     std::array<uint8_t, maxKeySize> keyBuff;
247*b29b5ab3SAppaRao Puli     std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary);
248*b29b5ab3SAppaRao Puli     if (!keyFile.is_open())
249*b29b5ab3SAppaRao Puli     {
250*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in opening encryption key file");
251*b29b5ab3SAppaRao Puli         return -EIO;
252*b29b5ab3SAppaRao Puli     }
253*b29b5ab3SAppaRao Puli     keyFile.read(reinterpret_cast<char*>(keyBuff.data()), keyBuff.size());
254*b29b5ab3SAppaRao Puli     if (keyFile.fail())
255*b29b5ab3SAppaRao Puli     {
256*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in reading encryption key file");
257*b29b5ab3SAppaRao Puli         return -EIO;
258*b29b5ab3SAppaRao Puli     }
259*b29b5ab3SAppaRao Puli 
260*b29b5ab3SAppaRao Puli     std::ifstream passwdFile(passwdFileName, std::ios::in | std::ios::binary);
261*b29b5ab3SAppaRao Puli     if (!passwdFile.is_open())
262*b29b5ab3SAppaRao Puli     {
263*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in opening ipmi password file");
264*b29b5ab3SAppaRao Puli         return -EIO;
265*b29b5ab3SAppaRao Puli     }
266*b29b5ab3SAppaRao Puli 
267*b29b5ab3SAppaRao Puli     // calculate file size and read the data
268*b29b5ab3SAppaRao Puli     passwdFile.seekg(0, std::ios::end);
269*b29b5ab3SAppaRao Puli     ssize_t fileSize = passwdFile.tellg();
270*b29b5ab3SAppaRao Puli     passwdFile.seekg(0, std::ios::beg);
271*b29b5ab3SAppaRao Puli     std::vector<uint8_t> input(fileSize);
272*b29b5ab3SAppaRao Puli     passwdFile.read(reinterpret_cast<char*>(input.data()), fileSize);
273*b29b5ab3SAppaRao Puli     if (passwdFile.fail())
274*b29b5ab3SAppaRao Puli     {
275*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in reading encryption key file");
276*b29b5ab3SAppaRao Puli         return -EIO;
277*b29b5ab3SAppaRao Puli     }
278*b29b5ab3SAppaRao Puli 
279*b29b5ab3SAppaRao Puli     // verify the signature first
280*b29b5ab3SAppaRao Puli     metaPassStruct* metaData = reinterpret_cast<metaPassStruct*>(input.data());
281*b29b5ab3SAppaRao Puli     if (std::strncmp(metaData->signature, META_PASSWD_SIG,
282*b29b5ab3SAppaRao Puli                      sizeof(metaData->signature)))
283*b29b5ab3SAppaRao Puli     {
284*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error signature mismatch in password file");
285*b29b5ab3SAppaRao Puli         return -EBADMSG;
286*b29b5ab3SAppaRao Puli     }
287*b29b5ab3SAppaRao Puli 
288*b29b5ab3SAppaRao Puli     size_t inBytesLen = metaData->dataSize + metaData->padSize;
289*b29b5ab3SAppaRao Puli     // If data is empty i.e no password map then return success
290*b29b5ab3SAppaRao Puli     if (inBytesLen == 0)
291*b29b5ab3SAppaRao Puli     {
292*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Empty password file");
293*b29b5ab3SAppaRao Puli         return 0;
294*b29b5ab3SAppaRao Puli     }
295*b29b5ab3SAppaRao Puli 
296*b29b5ab3SAppaRao Puli     // compute the key needed to decrypt
297*b29b5ab3SAppaRao Puli     std::array<uint8_t, EVP_MAX_KEY_LENGTH> key;
298*b29b5ab3SAppaRao Puli     auto keyLen = key.size();
299*b29b5ab3SAppaRao Puli     if (NULL == HMAC(EVP_sha256(), keyBuff.data(), keyBuff.size(),
300*b29b5ab3SAppaRao Puli                      input.data() + sizeof(*metaData), metaData->hashSize,
301*b29b5ab3SAppaRao Puli                      key.data(), reinterpret_cast<unsigned int*>(&keyLen)))
302*b29b5ab3SAppaRao Puli     {
303*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Failed to create MAC for authentication");
304*b29b5ab3SAppaRao Puli         return -EIO;
305*b29b5ab3SAppaRao Puli     }
306*b29b5ab3SAppaRao Puli 
307*b29b5ab3SAppaRao Puli     // decrypt the data
308*b29b5ab3SAppaRao Puli     uint8_t* iv = input.data() + sizeof(*metaData) + metaData->hashSize;
309*b29b5ab3SAppaRao Puli     size_t ivLen = metaData->ivSize;
310*b29b5ab3SAppaRao Puli     uint8_t* inBytes = iv + ivLen;
311*b29b5ab3SAppaRao Puli     uint8_t* mac = inBytes + inBytesLen;
312*b29b5ab3SAppaRao Puli     size_t macLen = metaData->macSize;
313*b29b5ab3SAppaRao Puli 
314*b29b5ab3SAppaRao Puli     size_t outBytesLen = 0;
315*b29b5ab3SAppaRao Puli     // Resize to actual data size
316*b29b5ab3SAppaRao Puli     outBytes.resize(inBytesLen + EVP_MAX_BLOCK_LENGTH);
317*b29b5ab3SAppaRao Puli     if (encryptDecryptData(false, EVP_aes_128_cbc(), key.data(), keyLen, iv,
318*b29b5ab3SAppaRao Puli                            ivLen, inBytes, inBytesLen, mac, &macLen,
319*b29b5ab3SAppaRao Puli                            outBytes.data(), &outBytesLen) != 0)
320*b29b5ab3SAppaRao Puli     {
321*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in decryption");
322*b29b5ab3SAppaRao Puli         return -EIO;
323*b29b5ab3SAppaRao Puli     }
324*b29b5ab3SAppaRao Puli     // Resize the vector to outBytesLen
325*b29b5ab3SAppaRao Puli     outBytes.resize(outBytesLen);
326*b29b5ab3SAppaRao Puli 
327*b29b5ab3SAppaRao Puli     OPENSSL_cleanse(key.data(), keyLen);
328*b29b5ab3SAppaRao Puli     OPENSSL_cleanse(iv, ivLen);
329*b29b5ab3SAppaRao Puli 
330*b29b5ab3SAppaRao Puli     return 0;
331*b29b5ab3SAppaRao Puli }
332*b29b5ab3SAppaRao Puli 
333*b29b5ab3SAppaRao Puli int PasswdMgr::updatePasswdSpecialFile(const std::string& userName)
334*b29b5ab3SAppaRao Puli {
335*b29b5ab3SAppaRao Puli     phosphor::user::shadow::Lock lock();
336*b29b5ab3SAppaRao Puli 
337*b29b5ab3SAppaRao Puli     size_t bytesWritten = 0;
338*b29b5ab3SAppaRao Puli     size_t inBytesLen = 0;
339*b29b5ab3SAppaRao Puli     size_t isUsrFound = false;
340*b29b5ab3SAppaRao Puli     const EVP_CIPHER* cipher = EVP_aes_128_cbc();
341*b29b5ab3SAppaRao Puli     std::vector<uint8_t> dataBuf;
342*b29b5ab3SAppaRao Puli 
343*b29b5ab3SAppaRao Puli     // Read the encrypted file and get the file data
344*b29b5ab3SAppaRao Puli     // Check user existance and return if not exist.
345*b29b5ab3SAppaRao Puli     if (readPasswdFileData(dataBuf) != 0)
346*b29b5ab3SAppaRao Puli     {
347*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in reading the encrypted pass file");
348*b29b5ab3SAppaRao Puli         return -EIO;
349*b29b5ab3SAppaRao Puli     }
350*b29b5ab3SAppaRao Puli 
351*b29b5ab3SAppaRao Puli     if (dataBuf.size() != 0)
352*b29b5ab3SAppaRao Puli     {
353*b29b5ab3SAppaRao Puli         inBytesLen = dataBuf.size() + EVP_CIPHER_block_size(cipher);
354*b29b5ab3SAppaRao Puli     }
355*b29b5ab3SAppaRao Puli 
356*b29b5ab3SAppaRao Puli     std::vector<uint8_t> inBytes(inBytesLen);
357*b29b5ab3SAppaRao Puli     if (inBytesLen != 0)
358*b29b5ab3SAppaRao Puli     {
359*b29b5ab3SAppaRao Puli         char* outPtr = reinterpret_cast<char*>(dataBuf.data());
360*b29b5ab3SAppaRao Puli         size_t userEPos = 0;
361*b29b5ab3SAppaRao Puli         char* nToken = NULL;
362*b29b5ab3SAppaRao Puli         char* linePtr = strtok_r(outPtr, "\n", &nToken);
363*b29b5ab3SAppaRao Puli         while (linePtr != NULL)
364*b29b5ab3SAppaRao Puli         {
365*b29b5ab3SAppaRao Puli             std::string lineStr(linePtr);
366*b29b5ab3SAppaRao Puli             if ((userEPos = lineStr.find(":")) != std::string::npos)
367*b29b5ab3SAppaRao Puli             {
368*b29b5ab3SAppaRao Puli                 if (userName.compare(lineStr.substr(0, userEPos)) == 0)
369*b29b5ab3SAppaRao Puli                 {
370*b29b5ab3SAppaRao Puli                     isUsrFound = true;
371*b29b5ab3SAppaRao Puli                 }
372*b29b5ab3SAppaRao Puli                 else
373*b29b5ab3SAppaRao Puli                 {
374*b29b5ab3SAppaRao Puli                     bytesWritten += std::snprintf(
375*b29b5ab3SAppaRao Puli                         reinterpret_cast<char*>(&inBytes[0]) + bytesWritten,
376*b29b5ab3SAppaRao Puli                         inBytesLen, "%s\n", lineStr.data());
377*b29b5ab3SAppaRao Puli                 }
378*b29b5ab3SAppaRao Puli             }
379*b29b5ab3SAppaRao Puli             linePtr = strtok_r(NULL, "\n", &nToken);
380*b29b5ab3SAppaRao Puli         }
381*b29b5ab3SAppaRao Puli 
382*b29b5ab3SAppaRao Puli         // Round of to block size and padding remaing bytes with zero.
383*b29b5ab3SAppaRao Puli         inBytesLen = blockRound(bytesWritten, EVP_CIPHER_block_size(cipher));
384*b29b5ab3SAppaRao Puli         std::memset(&inBytes[0] + bytesWritten, 0, inBytesLen - bytesWritten);
385*b29b5ab3SAppaRao Puli     }
386*b29b5ab3SAppaRao Puli     if (!isUsrFound)
387*b29b5ab3SAppaRao Puli     {
388*b29b5ab3SAppaRao Puli         log<level::DEBUG>("User doesn't exist");
389*b29b5ab3SAppaRao Puli         return 0;
390*b29b5ab3SAppaRao Puli     }
391*b29b5ab3SAppaRao Puli 
392*b29b5ab3SAppaRao Puli     // Read the key buff from key file
393*b29b5ab3SAppaRao Puli     std::array<uint8_t, maxKeySize> keyBuff;
394*b29b5ab3SAppaRao Puli     std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary);
395*b29b5ab3SAppaRao Puli     if (!keyFile.good())
396*b29b5ab3SAppaRao Puli     {
397*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in opening encryption key file");
398*b29b5ab3SAppaRao Puli         return -EIO;
399*b29b5ab3SAppaRao Puli     }
400*b29b5ab3SAppaRao Puli     keyFile.read(reinterpret_cast<char*>(keyBuff.data()), keyBuff.size());
401*b29b5ab3SAppaRao Puli     if (keyFile.fail())
402*b29b5ab3SAppaRao Puli     {
403*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in reading encryption key file");
404*b29b5ab3SAppaRao Puli         return -EIO;
405*b29b5ab3SAppaRao Puli     }
406*b29b5ab3SAppaRao Puli     keyFile.close();
407*b29b5ab3SAppaRao Puli 
408*b29b5ab3SAppaRao Puli     // Read the original passwd file mode
409*b29b5ab3SAppaRao Puli     struct stat st = {};
410*b29b5ab3SAppaRao Puli     if (stat(passwdFileName, &st) != 0)
411*b29b5ab3SAppaRao Puli     {
412*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in getting password file fstat()");
413*b29b5ab3SAppaRao Puli         return -EIO;
414*b29b5ab3SAppaRao Puli     }
415*b29b5ab3SAppaRao Puli 
416*b29b5ab3SAppaRao Puli     // Create temporary file for write
417*b29b5ab3SAppaRao Puli     std::string pwdFile(passwdFileName);
418*b29b5ab3SAppaRao Puli     std::vector<char> tempFileName(pwdFile.begin(), pwdFile.end());
419*b29b5ab3SAppaRao Puli     std::vector<char> fileTemplate = {'_', '_', 'X', 'X', 'X',
420*b29b5ab3SAppaRao Puli                                       'X', 'X', 'X', '\0'};
421*b29b5ab3SAppaRao Puli     tempFileName.insert(tempFileName.end(), fileTemplate.begin(),
422*b29b5ab3SAppaRao Puli                         fileTemplate.end());
423*b29b5ab3SAppaRao Puli     int fd = mkstemp((char*)tempFileName.data());
424*b29b5ab3SAppaRao Puli     if (fd == -1)
425*b29b5ab3SAppaRao Puli     {
426*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error creating temp file");
427*b29b5ab3SAppaRao Puli         return -EIO;
428*b29b5ab3SAppaRao Puli     }
429*b29b5ab3SAppaRao Puli 
430*b29b5ab3SAppaRao Puli     std::string strTempFileName(tempFileName.data());
431*b29b5ab3SAppaRao Puli     // Open the temp file for writing from provided fd
432*b29b5ab3SAppaRao Puli     // By "true", remove it at exit if still there.
433*b29b5ab3SAppaRao Puli     // This is needed to cleanup the temp file at exception
434*b29b5ab3SAppaRao Puli     phosphor::user::File temp(fd, strTempFileName, "w", true);
435*b29b5ab3SAppaRao Puli     if ((temp)() == NULL)
436*b29b5ab3SAppaRao Puli     {
437*b29b5ab3SAppaRao Puli         close(fd);
438*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error creating temp file");
439*b29b5ab3SAppaRao Puli         return -EIO;
440*b29b5ab3SAppaRao Puli     }
441*b29b5ab3SAppaRao Puli     fd = -1; // don't use fd anymore, as the File object owns it
442*b29b5ab3SAppaRao Puli 
443*b29b5ab3SAppaRao Puli     // Set the file mode as of actual ipmi-pass file.
444*b29b5ab3SAppaRao Puli     if (fchmod(fileno((temp)()), st.st_mode) < 0)
445*b29b5ab3SAppaRao Puli     {
446*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error setting fchmod for temp file");
447*b29b5ab3SAppaRao Puli         return -EIO;
448*b29b5ab3SAppaRao Puli     }
449*b29b5ab3SAppaRao Puli 
450*b29b5ab3SAppaRao Puli     const EVP_MD* digest = EVP_sha256();
451*b29b5ab3SAppaRao Puli     size_t hashLen = EVP_MD_block_size(digest);
452*b29b5ab3SAppaRao Puli     std::vector<uint8_t> hash(hashLen);
453*b29b5ab3SAppaRao Puli     size_t ivLen = EVP_CIPHER_iv_length(cipher);
454*b29b5ab3SAppaRao Puli     std::vector<uint8_t> iv(ivLen);
455*b29b5ab3SAppaRao Puli     std::array<uint8_t, EVP_MAX_KEY_LENGTH> key;
456*b29b5ab3SAppaRao Puli     size_t keyLen = key.size();
457*b29b5ab3SAppaRao Puli     std::array<uint8_t, EVP_MAX_MD_SIZE> mac;
458*b29b5ab3SAppaRao Puli     size_t macLen = mac.size();
459*b29b5ab3SAppaRao Puli 
460*b29b5ab3SAppaRao Puli     // Create random hash and generate hash key which will be used for
461*b29b5ab3SAppaRao Puli     // encryption.
462*b29b5ab3SAppaRao Puli     if (RAND_bytes(hash.data(), hashLen) != 1)
463*b29b5ab3SAppaRao Puli     {
464*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Hash genertion failed, bailing out");
465*b29b5ab3SAppaRao Puli         return -EIO;
466*b29b5ab3SAppaRao Puli     }
467*b29b5ab3SAppaRao Puli     if (NULL == HMAC(digest, keyBuff.data(), keyBuff.size(), hash.data(),
468*b29b5ab3SAppaRao Puli                      hashLen, key.data(),
469*b29b5ab3SAppaRao Puli                      reinterpret_cast<unsigned int*>(&keyLen)))
470*b29b5ab3SAppaRao Puli     {
471*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Failed to create MAC for authentication");
472*b29b5ab3SAppaRao Puli         return -EIO;
473*b29b5ab3SAppaRao Puli     }
474*b29b5ab3SAppaRao Puli 
475*b29b5ab3SAppaRao Puli     // Generate IV values
476*b29b5ab3SAppaRao Puli     if (RAND_bytes(iv.data(), ivLen) != 1)
477*b29b5ab3SAppaRao Puli     {
478*b29b5ab3SAppaRao Puli         log<level::DEBUG>("UV genertion failed, bailing out");
479*b29b5ab3SAppaRao Puli         return -EIO;
480*b29b5ab3SAppaRao Puli     }
481*b29b5ab3SAppaRao Puli 
482*b29b5ab3SAppaRao Puli     // Encrypt the input data
483*b29b5ab3SAppaRao Puli     std::vector<uint8_t> outBytes(inBytesLen + EVP_MAX_BLOCK_LENGTH);
484*b29b5ab3SAppaRao Puli     size_t outBytesLen = 0;
485*b29b5ab3SAppaRao Puli     if (inBytesLen != 0)
486*b29b5ab3SAppaRao Puli     {
487*b29b5ab3SAppaRao Puli         if (encryptDecryptData(true, EVP_aes_128_cbc(), key.data(), keyLen,
488*b29b5ab3SAppaRao Puli                                iv.data(), ivLen, inBytes.data(), inBytesLen,
489*b29b5ab3SAppaRao Puli                                mac.data(), &macLen, outBytes.data(),
490*b29b5ab3SAppaRao Puli                                &outBytesLen) != 0)
491*b29b5ab3SAppaRao Puli         {
492*b29b5ab3SAppaRao Puli             log<level::DEBUG>("Error while encrypting the data");
493*b29b5ab3SAppaRao Puli             return -EIO;
494*b29b5ab3SAppaRao Puli         }
495*b29b5ab3SAppaRao Puli         outBytes[outBytesLen] = 0;
496*b29b5ab3SAppaRao Puli     }
497*b29b5ab3SAppaRao Puli     OPENSSL_cleanse(key.data(), keyLen);
498*b29b5ab3SAppaRao Puli 
499*b29b5ab3SAppaRao Puli     // Update the meta password structure.
500*b29b5ab3SAppaRao Puli     metaPassStruct metaData = {META_PASSWD_SIG, {0, 0}, 0, 0, 0, 0, 0};
501*b29b5ab3SAppaRao Puli     metaData.hashSize = hashLen;
502*b29b5ab3SAppaRao Puli     metaData.ivSize = ivLen;
503*b29b5ab3SAppaRao Puli     metaData.dataSize = bytesWritten;
504*b29b5ab3SAppaRao Puli     metaData.padSize = outBytesLen - bytesWritten;
505*b29b5ab3SAppaRao Puli     metaData.macSize = macLen;
506*b29b5ab3SAppaRao Puli 
507*b29b5ab3SAppaRao Puli     if (fwrite(&metaData, 1, sizeof(metaData), (temp)()) != sizeof(metaData))
508*b29b5ab3SAppaRao Puli     {
509*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in writing meta data");
510*b29b5ab3SAppaRao Puli         return -EIO;
511*b29b5ab3SAppaRao Puli     }
512*b29b5ab3SAppaRao Puli 
513*b29b5ab3SAppaRao Puli     if (fwrite(&hash[0], 1, hashLen, (temp)()) != hashLen)
514*b29b5ab3SAppaRao Puli     {
515*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in writing hash data");
516*b29b5ab3SAppaRao Puli         return -EIO;
517*b29b5ab3SAppaRao Puli     }
518*b29b5ab3SAppaRao Puli 
519*b29b5ab3SAppaRao Puli     if (fwrite(&iv[0], 1, ivLen, (temp)()) != ivLen)
520*b29b5ab3SAppaRao Puli     {
521*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in writing IV data");
522*b29b5ab3SAppaRao Puli         return -EIO;
523*b29b5ab3SAppaRao Puli     }
524*b29b5ab3SAppaRao Puli 
525*b29b5ab3SAppaRao Puli     if (fwrite(&outBytes[0], 1, outBytesLen, (temp)()) != outBytesLen)
526*b29b5ab3SAppaRao Puli     {
527*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in writing encrypted data");
528*b29b5ab3SAppaRao Puli         return -EIO;
529*b29b5ab3SAppaRao Puli     }
530*b29b5ab3SAppaRao Puli 
531*b29b5ab3SAppaRao Puli     if (fwrite(&mac[0], 1, macLen, (temp)()) != macLen)
532*b29b5ab3SAppaRao Puli     {
533*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error in writing MAC data");
534*b29b5ab3SAppaRao Puli         return -EIO;
535*b29b5ab3SAppaRao Puli     }
536*b29b5ab3SAppaRao Puli 
537*b29b5ab3SAppaRao Puli     if (fflush((temp)()))
538*b29b5ab3SAppaRao Puli     {
539*b29b5ab3SAppaRao Puli         log<level::DEBUG>(
540*b29b5ab3SAppaRao Puli             "File fflush error while writing entries to special file");
541*b29b5ab3SAppaRao Puli         return -EIO;
542*b29b5ab3SAppaRao Puli     }
543*b29b5ab3SAppaRao Puli 
544*b29b5ab3SAppaRao Puli     OPENSSL_cleanse(iv.data(), ivLen);
545*b29b5ab3SAppaRao Puli 
546*b29b5ab3SAppaRao Puli     // Rename the tmp  file to actual file
547*b29b5ab3SAppaRao Puli     if (std::rename(strTempFileName.data(), passwdFileName) != 0)
548*b29b5ab3SAppaRao Puli     {
549*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Failed to rename tmp file to ipmi-pass");
550*b29b5ab3SAppaRao Puli         return -EIO;
551*b29b5ab3SAppaRao Puli     }
552*b29b5ab3SAppaRao Puli 
553*b29b5ab3SAppaRao Puli     return 0;
554*b29b5ab3SAppaRao Puli }
555*b29b5ab3SAppaRao Puli 
556*b29b5ab3SAppaRao Puli std::time_t PasswdMgr::getUpdatedFileTime()
557*b29b5ab3SAppaRao Puli {
5584654d99fSRichard Marian Thomaiyar     struct stat fileStat = {};
5594654d99fSRichard Marian Thomaiyar     if (stat(passwdFileName, &fileStat) != 0)
5604654d99fSRichard Marian Thomaiyar     {
561*b29b5ab3SAppaRao Puli         log<level::DEBUG>("Error - Getting passwd file time stamp");
562*b29b5ab3SAppaRao Puli         return -EIO;
5634654d99fSRichard Marian Thomaiyar     }
564*b29b5ab3SAppaRao Puli     return fileStat.st_mtime;
5654654d99fSRichard Marian Thomaiyar }
5664654d99fSRichard Marian Thomaiyar 
5674654d99fSRichard Marian Thomaiyar } // namespace ipmi
568