1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "passwd_mgr.hpp"
18 
19 #include "file.hpp"
20 #include "shadowlock.hpp"
21 
22 #include <openssl/hmac.h>
23 #include <openssl/rand.h>
24 #include <openssl/sha.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 
29 #include <cerrno>
30 #include <cstring>
31 #include <fstream>
32 #include <iomanip>
33 #include <phosphor-logging/log.hpp>
34 
35 namespace ipmi
36 {
37 
38 static const char* passwdFileName = "/etc/ipmi_pass";
39 static const char* encryptKeyFileName = "/etc/key_file";
40 static const size_t maxKeySize = 8;
41 
42 constexpr mode_t modeMask =
43     (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
44 
45 #define META_PASSWD_SIG "=OPENBMC="
46 
47 /*
48  * Meta data struct for encrypted password file
49  */
50 struct MetaPassStruct
51 {
52     char signature[10];
53     unsigned char reseved[2];
54     size_t hashSize;
55     size_t ivSize;
56     size_t dataSize;
57     size_t padSize;
58     size_t macSize;
59 };
60 
61 using namespace phosphor::logging;
62 
63 PasswdMgr::PasswdMgr()
64 {
65     restrictFilesPermission();
66     initPasswordMap();
67 }
68 
69 void PasswdMgr::restrictFilesPermission(void)
70 {
71     struct stat st = {};
72     // Restrict file permission to owner read & write
73     if (stat(passwdFileName, &st) == 0)
74     {
75         if ((st.st_mode & modeMask) != (S_IRUSR | S_IWUSR))
76         {
77             chmod(passwdFileName, S_IRUSR | S_IWUSR);
78         }
79     }
80 
81     if (stat(encryptKeyFileName, &st) == 0)
82     {
83         if ((st.st_mode & modeMask) != (S_IRUSR | S_IWUSR))
84         {
85             chmod(encryptKeyFileName, S_IRUSR | S_IWUSR);
86         }
87     }
88 }
89 
90 std::string PasswdMgr::getPasswdByUserName(const std::string& userName)
91 {
92     checkAndReload();
93     auto iter = passwdMapList.find(userName);
94     if (iter == passwdMapList.end())
95     {
96         return std::string();
97     }
98     return iter->second;
99 }
100 
101 int PasswdMgr::updateUserEntry(const std::string& userName,
102                                const std::string& newUserName)
103 {
104     std::time_t updatedTime = getUpdatedFileTime();
105     // Check file time stamp to know passwdMapList is up-to-date.
106     // If not up-to-date, then updatePasswdSpecialFile will read and
107     // check the user entry existance.
108     if (fileLastUpdatedTime == updatedTime && updatedTime != -EIO)
109     {
110         if (passwdMapList.find(userName) == passwdMapList.end())
111         {
112             log<level::DEBUG>("User not found");
113             return 0;
114         }
115     }
116 
117     // Write passwdMap to Encryted file
118     if (updatePasswdSpecialFile(userName, newUserName) != 0)
119     {
120         log<level::DEBUG>("Passwd file update failed");
121         return -EIO;
122     }
123 
124     log<level::DEBUG>("Passwd file updated successfully");
125     return 0;
126 }
127 
128 void PasswdMgr::checkAndReload(void)
129 {
130     std::time_t updatedTime = getUpdatedFileTime();
131     if (fileLastUpdatedTime != updatedTime && updatedTime != -1)
132     {
133         log<level::DEBUG>("Reloading password map list");
134         passwdMapList.clear();
135         initPasswordMap();
136     }
137 }
138 
139 int PasswdMgr::encryptDecryptData(bool doEncrypt, const EVP_CIPHER* cipher,
140                                   uint8_t* key, size_t keyLen, uint8_t* iv,
141                                   size_t ivLen, uint8_t* inBytes,
142                                   size_t inBytesLen, uint8_t* mac,
143                                   size_t* macLen, unsigned char* outBytes,
144                                   size_t* outBytesLen)
145 {
146     if (cipher == NULL || key == NULL || iv == NULL || inBytes == NULL ||
147         outBytes == NULL || mac == NULL || inBytesLen == 0 ||
148         (size_t)EVP_CIPHER_key_length(cipher) > keyLen ||
149         (size_t)EVP_CIPHER_iv_length(cipher) > ivLen)
150     {
151         log<level::DEBUG>("Error Invalid Inputs");
152         return -EINVAL;
153     }
154 
155     if (!doEncrypt)
156     {
157         // verify MAC before decrypting the data.
158         std::array<uint8_t, EVP_MAX_MD_SIZE> calMac;
159         size_t calMacLen = calMac.size();
160         // calculate MAC for the encrypted message.
161         if (NULL == HMAC(EVP_sha256(), key, keyLen, inBytes, inBytesLen,
162                          calMac.data(),
163                          reinterpret_cast<unsigned int*>(&calMacLen)))
164         {
165             log<level::DEBUG>("Error: Failed to calculate MAC");
166             return -EIO;
167         }
168         if (!((calMacLen == *macLen) &&
169               (std::memcmp(calMac.data(), mac, calMacLen) == 0)))
170         {
171             log<level::DEBUG>("Authenticated message doesn't match");
172             return -EBADMSG;
173         }
174     }
175 
176     std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)> ctx(
177         EVP_CIPHER_CTX_new(), ::EVP_CIPHER_CTX_free);
178     EVP_CIPHER_CTX_set_padding(ctx.get(), 1);
179 
180     // Set key & IV
181     int retval = EVP_CipherInit_ex(ctx.get(), cipher, NULL, key, iv,
182                                    static_cast<int>(doEncrypt));
183     if (!retval)
184     {
185         log<level::DEBUG>("EVP_CipherInit_ex failed",
186                           entry("RET_VAL=%d", retval));
187         return -EIO;
188     }
189 
190     int outLen = 0, outEVPLen = 0;
191     if ((retval = EVP_CipherUpdate(ctx.get(), outBytes + outLen, &outEVPLen,
192                                    inBytes, inBytesLen)))
193     {
194         outLen += outEVPLen;
195         if ((retval =
196                  EVP_CipherFinal(ctx.get(), outBytes + outLen, &outEVPLen)))
197         {
198             outLen += outEVPLen;
199             *outBytesLen = outLen;
200         }
201         else
202         {
203             log<level::DEBUG>("EVP_CipherFinal fails",
204                               entry("RET_VAL=%d", retval));
205             return -EIO;
206         }
207     }
208     else
209     {
210         log<level::DEBUG>("EVP_CipherUpdate fails",
211                           entry("RET_VAL=%d", retval));
212         return -EIO;
213     }
214 
215     if (doEncrypt)
216     {
217         // Create MAC for the encrypted message
218         if (NULL == HMAC(EVP_sha256(), key, keyLen, outBytes, *outBytesLen, mac,
219                          reinterpret_cast<unsigned int*>(macLen)))
220         {
221             log<level::DEBUG>("Failed to create authentication");
222             return -EIO;
223         }
224     }
225     return 0;
226 }
227 
228 void PasswdMgr::initPasswordMap(void)
229 {
230     // TODO  phosphor-host-ipmid#170 phosphor::user::shadow::Lock lock{};
231     std::vector<uint8_t> dataBuf;
232 
233     if (readPasswdFileData(dataBuf) != 0)
234     {
235         log<level::DEBUG>("Error in reading the encrypted pass file");
236         return;
237     }
238 
239     if (dataBuf.size() != 0)
240     {
241         // populate the user list with password
242         char* outPtr = reinterpret_cast<char*>(dataBuf.data());
243         char* nToken = NULL;
244         char* linePtr = strtok_r(outPtr, "\n", &nToken);
245         size_t lineSize = 0;
246         while (linePtr != NULL)
247         {
248             size_t userEPos = 0;
249             std::string lineStr(linePtr);
250             if ((userEPos = lineStr.find(":")) != std::string::npos)
251             {
252                 lineSize = lineStr.size();
253                 passwdMapList.emplace(
254                     lineStr.substr(0, userEPos),
255                     lineStr.substr(userEPos + 1, lineSize - (userEPos + 1)));
256             }
257             linePtr = strtok_r(NULL, "\n", &nToken);
258         }
259     }
260 
261     // Update the timestamp
262     fileLastUpdatedTime = getUpdatedFileTime();
263     // Clear sensitive data
264     OPENSSL_cleanse(dataBuf.data(), dataBuf.size());
265     return;
266 }
267 
268 int PasswdMgr::readPasswdFileData(std::vector<uint8_t>& outBytes)
269 {
270     std::array<uint8_t, maxKeySize> keyBuff;
271     std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary);
272     if (!keyFile.is_open())
273     {
274         log<level::DEBUG>("Error in opening encryption key file");
275         return -EIO;
276     }
277     keyFile.read(reinterpret_cast<char*>(keyBuff.data()), keyBuff.size());
278     if (keyFile.fail())
279     {
280         log<level::DEBUG>("Error in reading encryption key file");
281         return -EIO;
282     }
283 
284     std::ifstream passwdFile(passwdFileName, std::ios::in | std::ios::binary);
285     if (!passwdFile.is_open())
286     {
287         log<level::DEBUG>("Error in opening ipmi password file");
288         return -EIO;
289     }
290 
291     // calculate file size and read the data
292     passwdFile.seekg(0, std::ios::end);
293     ssize_t fileSize = passwdFile.tellg();
294     passwdFile.seekg(0, std::ios::beg);
295     std::vector<uint8_t> input(fileSize);
296     passwdFile.read(reinterpret_cast<char*>(input.data()), fileSize);
297     if (passwdFile.fail())
298     {
299         log<level::DEBUG>("Error in reading encryption key file");
300         return -EIO;
301     }
302 
303     // verify the signature first
304     MetaPassStruct* metaData = reinterpret_cast<MetaPassStruct*>(input.data());
305     if (std::strncmp(metaData->signature, META_PASSWD_SIG,
306                      sizeof(metaData->signature)))
307     {
308         log<level::DEBUG>("Error signature mismatch in password file");
309         return -EBADMSG;
310     }
311 
312     size_t inBytesLen = metaData->dataSize + metaData->padSize;
313     // If data is empty i.e no password map then return success
314     if (inBytesLen == 0)
315     {
316         log<level::DEBUG>("Empty password file");
317         return 0;
318     }
319 
320     // compute the key needed to decrypt
321     std::array<uint8_t, EVP_MAX_KEY_LENGTH> key;
322     auto keyLen = key.size();
323     if (NULL == HMAC(EVP_sha256(), keyBuff.data(), keyBuff.size(),
324                      input.data() + sizeof(*metaData), metaData->hashSize,
325                      key.data(), reinterpret_cast<unsigned int*>(&keyLen)))
326     {
327         log<level::DEBUG>("Failed to create MAC for authentication");
328         return -EIO;
329     }
330 
331     // decrypt the data
332     uint8_t* iv = input.data() + sizeof(*metaData) + metaData->hashSize;
333     size_t ivLen = metaData->ivSize;
334     uint8_t* inBytes = iv + ivLen;
335     uint8_t* mac = inBytes + inBytesLen;
336     size_t macLen = metaData->macSize;
337 
338     size_t outBytesLen = 0;
339     // Resize to actual data size
340     outBytes.resize(inBytesLen + EVP_MAX_BLOCK_LENGTH);
341     if (encryptDecryptData(false, EVP_aes_128_cbc(), key.data(), keyLen, iv,
342                            ivLen, inBytes, inBytesLen, mac, &macLen,
343                            outBytes.data(), &outBytesLen) != 0)
344     {
345         log<level::DEBUG>("Error in decryption");
346         return -EIO;
347     }
348     // Resize the vector to outBytesLen
349     outBytes.resize(outBytesLen);
350 
351     OPENSSL_cleanse(key.data(), keyLen);
352     OPENSSL_cleanse(iv, ivLen);
353 
354     return 0;
355 }
356 
357 int PasswdMgr::updatePasswdSpecialFile(const std::string& userName,
358                                        const std::string& newUserName)
359 {
360     // TODO  phosphor-host-ipmid#170 phosphor::user::shadow::Lock lock{};
361 
362     size_t bytesWritten = 0;
363     size_t inBytesLen = 0;
364     size_t isUsrFound = false;
365     const EVP_CIPHER* cipher = EVP_aes_128_cbc();
366     std::vector<uint8_t> dataBuf;
367 
368     // Read the encrypted file and get the file data
369     // Check user existance and return if not exist.
370     if (readPasswdFileData(dataBuf) != 0)
371     {
372         log<level::DEBUG>("Error in reading the encrypted pass file");
373         return -EIO;
374     }
375 
376     if (dataBuf.size() != 0)
377     {
378         inBytesLen =
379             dataBuf.size() + newUserName.size() + EVP_CIPHER_block_size(cipher);
380     }
381 
382     std::vector<uint8_t> inBytes(inBytesLen);
383     if (inBytesLen != 0)
384     {
385         char* outPtr = reinterpret_cast<char*>(dataBuf.data());
386         char* nToken = NULL;
387         char* linePtr = strtok_r(outPtr, "\n", &nToken);
388         while (linePtr != NULL)
389         {
390             size_t userEPos = 0;
391 
392             std::string lineStr(linePtr);
393             if ((userEPos = lineStr.find(":")) != std::string::npos)
394             {
395                 if (userName.compare(lineStr.substr(0, userEPos)) == 0)
396                 {
397                     isUsrFound = true;
398                     if (!newUserName.empty())
399                     {
400                         bytesWritten += std::snprintf(
401                             reinterpret_cast<char*>(&inBytes[0]) + bytesWritten,
402                             (inBytesLen - bytesWritten), "%s%s\n",
403                             newUserName.c_str(),
404                             lineStr.substr(userEPos, lineStr.size()).data());
405                     }
406                 }
407                 else
408                 {
409                     bytesWritten += std::snprintf(
410                         reinterpret_cast<char*>(&inBytes[0]) + bytesWritten,
411                         (inBytesLen - bytesWritten), "%s\n", lineStr.data());
412                 }
413             }
414             linePtr = strtok_r(NULL, "\n", &nToken);
415         }
416         inBytesLen = bytesWritten;
417     }
418     if (!isUsrFound)
419     {
420         log<level::DEBUG>("User doesn't exist");
421         return 0;
422     }
423 
424     // Read the key buff from key file
425     std::array<uint8_t, maxKeySize> keyBuff;
426     std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary);
427     if (!keyFile.good())
428     {
429         log<level::DEBUG>("Error in opening encryption key file");
430         return -EIO;
431     }
432     keyFile.read(reinterpret_cast<char*>(keyBuff.data()), keyBuff.size());
433     if (keyFile.fail())
434     {
435         log<level::DEBUG>("Error in reading encryption key file");
436         return -EIO;
437     }
438     keyFile.close();
439 
440     // Read the original passwd file mode
441     struct stat st = {};
442     if (stat(passwdFileName, &st) != 0)
443     {
444         log<level::DEBUG>("Error in getting password file fstat()");
445         return -EIO;
446     }
447 
448     // Create temporary file for write
449     std::string pwdFile(passwdFileName);
450     std::vector<char> tempFileName(pwdFile.begin(), pwdFile.end());
451     std::vector<char> fileTemplate = {'_', '_', 'X', 'X', 'X',
452                                       'X', 'X', 'X', '\0'};
453     tempFileName.insert(tempFileName.end(), fileTemplate.begin(),
454                         fileTemplate.end());
455     int fd = mkstemp((char*)tempFileName.data());
456     if (fd == -1)
457     {
458         log<level::DEBUG>("Error creating temp file");
459         return -EIO;
460     }
461 
462     std::string strTempFileName(tempFileName.data());
463     // Open the temp file for writing from provided fd
464     // By "true", remove it at exit if still there.
465     // This is needed to cleanup the temp file at exception
466     phosphor::user::File temp(fd, strTempFileName, "w", true);
467     if ((temp)() == NULL)
468     {
469         close(fd);
470         log<level::DEBUG>("Error creating temp file");
471         return -EIO;
472     }
473 
474     // Set the file mode as read-write for owner only
475     if (fchmod(fileno((temp)()), S_IRUSR | S_IWUSR) < 0)
476     {
477         log<level::DEBUG>("Error setting fchmod for temp file");
478         return -EIO;
479     }
480 
481     const EVP_MD* digest = EVP_sha256();
482     size_t hashLen = EVP_MD_block_size(digest);
483     std::vector<uint8_t> hash(hashLen);
484     size_t ivLen = EVP_CIPHER_iv_length(cipher);
485     std::vector<uint8_t> iv(ivLen);
486     std::array<uint8_t, EVP_MAX_KEY_LENGTH> key;
487     size_t keyLen = key.size();
488     std::array<uint8_t, EVP_MAX_MD_SIZE> mac;
489     size_t macLen = mac.size();
490 
491     // Create random hash and generate hash key which will be used for
492     // encryption.
493     if (RAND_bytes(hash.data(), hashLen) != 1)
494     {
495         log<level::DEBUG>("Hash genertion failed, bailing out");
496         return -EIO;
497     }
498     if (NULL == HMAC(digest, keyBuff.data(), keyBuff.size(), hash.data(),
499                      hashLen, key.data(),
500                      reinterpret_cast<unsigned int*>(&keyLen)))
501     {
502         log<level::DEBUG>("Failed to create MAC for authentication");
503         return -EIO;
504     }
505 
506     // Generate IV values
507     if (RAND_bytes(iv.data(), ivLen) != 1)
508     {
509         log<level::DEBUG>("UV genertion failed, bailing out");
510         return -EIO;
511     }
512 
513     // Encrypt the input data
514     std::vector<uint8_t> outBytes(inBytesLen + EVP_MAX_BLOCK_LENGTH);
515     size_t outBytesLen = 0;
516     if (inBytesLen != 0)
517     {
518         if (encryptDecryptData(true, EVP_aes_128_cbc(), key.data(), keyLen,
519                                iv.data(), ivLen, inBytes.data(), inBytesLen,
520                                mac.data(), &macLen, outBytes.data(),
521                                &outBytesLen) != 0)
522         {
523             log<level::DEBUG>("Error while encrypting the data");
524             return -EIO;
525         }
526         outBytes[outBytesLen] = 0;
527     }
528     OPENSSL_cleanse(key.data(), keyLen);
529 
530     // Update the meta password structure.
531     MetaPassStruct metaData = {META_PASSWD_SIG, {0, 0}, 0, 0, 0, 0, 0};
532     metaData.hashSize = hashLen;
533     metaData.ivSize = ivLen;
534     metaData.dataSize = bytesWritten;
535     metaData.padSize = outBytesLen - bytesWritten;
536     metaData.macSize = macLen;
537 
538     if (fwrite(&metaData, 1, sizeof(metaData), (temp)()) != sizeof(metaData))
539     {
540         log<level::DEBUG>("Error in writing meta data");
541         return -EIO;
542     }
543 
544     if (fwrite(&hash[0], 1, hashLen, (temp)()) != hashLen)
545     {
546         log<level::DEBUG>("Error in writing hash data");
547         return -EIO;
548     }
549 
550     if (fwrite(&iv[0], 1, ivLen, (temp)()) != ivLen)
551     {
552         log<level::DEBUG>("Error in writing IV data");
553         return -EIO;
554     }
555 
556     if (fwrite(&outBytes[0], 1, outBytesLen, (temp)()) != outBytesLen)
557     {
558         log<level::DEBUG>("Error in writing encrypted data");
559         return -EIO;
560     }
561 
562     if (fwrite(&mac[0], 1, macLen, (temp)()) != macLen)
563     {
564         log<level::DEBUG>("Error in writing MAC data");
565         return -EIO;
566     }
567 
568     if (fflush((temp)()))
569     {
570         log<level::DEBUG>(
571             "File fflush error while writing entries to special file");
572         return -EIO;
573     }
574 
575     OPENSSL_cleanse(iv.data(), ivLen);
576 
577     // Rename the tmp  file to actual file
578     if (std::rename(strTempFileName.data(), passwdFileName) != 0)
579     {
580         log<level::DEBUG>("Failed to rename tmp file to ipmi-pass");
581         return -EIO;
582     }
583 
584     return 0;
585 }
586 
587 std::time_t PasswdMgr::getUpdatedFileTime()
588 {
589     struct stat fileStat = {};
590     if (stat(passwdFileName, &fileStat) != 0)
591     {
592         log<level::DEBUG>("Error - Getting passwd file time stamp");
593         return -EIO;
594     }
595     return fileStat.st_mtime;
596 }
597 
598 } // namespace ipmi
599