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