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     uint32_t hashSize;
55     uint32_t ivSize;
56     uint32_t dataSize;
57     uint32_t padSize;
58     uint32_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 SecureString PasswdMgr::getPasswdByUserName(const std::string& userName)
91 {
92     checkAndReload();
93     auto iter = passwdMapList.find(userName);
94     if (iter == passwdMapList.end())
95     {
96         return SecureString();
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 
179     if (!ctx)
180     {
181         log<level::DEBUG>("Error: EVP_CIPHER_CTX is NULL");
182         return -ENOMEM;
183     }
184 
185     EVP_CIPHER_CTX_set_padding(ctx.get(), 1);
186 
187     // Set key & IV
188     int retval = EVP_CipherInit_ex(ctx.get(), cipher, NULL, key, iv,
189                                    static_cast<int>(doEncrypt));
190     if (!retval)
191     {
192         log<level::DEBUG>("EVP_CipherInit_ex failed",
193                           entry("RET_VAL=%d", retval));
194         return -EIO;
195     }
196 
197     int outLen = 0, outEVPLen = 0;
198     if ((retval = EVP_CipherUpdate(ctx.get(), outBytes + outLen, &outEVPLen,
199                                    inBytes, inBytesLen)))
200     {
201         outLen += outEVPLen;
202         if ((retval =
203                  EVP_CipherFinal(ctx.get(), outBytes + outLen, &outEVPLen)))
204         {
205             outLen += outEVPLen;
206             *outBytesLen = outLen;
207         }
208         else
209         {
210             log<level::DEBUG>("EVP_CipherFinal fails",
211                               entry("RET_VAL=%d", retval));
212             return -EIO;
213         }
214     }
215     else
216     {
217         log<level::DEBUG>("EVP_CipherUpdate fails",
218                           entry("RET_VAL=%d", 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             log<level::DEBUG>("Failed to create authentication");
229             return -EIO;
230         }
231     }
232     return 0;
233 }
234 
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         log<level::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 
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         log<level::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         log<level::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         log<level::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         log<level::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         log<level::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         log<level::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         log<level::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         log<level::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 
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         log<level::DEBUG>("Error in reading the encrypted pass file");
379         return -EIO;
380     }
381 
382     if (dataBuf.size() != 0)
383     {
384         inBytesLen =
385             dataBuf.size() + newUserName.size() + 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         log<level::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         log<level::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         log<level::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         log<level::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         log<level::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         log<level::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         log<level::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         log<level::DEBUG>("Hash genertion failed, bailing out");
502         return -EIO;
503     }
504     if (NULL == HMAC(digest, keyBuff.data(), keyBuff.size(), hash.data(),
505                      hashLen, key.data(),
506                      reinterpret_cast<unsigned int*>(&keyLen)))
507     {
508         log<level::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         log<level::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             log<level::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         log<level::DEBUG>("Error in writing meta data");
547         return -EIO;
548     }
549 
550     if (fwrite(&hash[0], 1, hashLen, (temp)()) != hashLen)
551     {
552         log<level::DEBUG>("Error in writing hash data");
553         return -EIO;
554     }
555 
556     if (fwrite(&iv[0], 1, ivLen, (temp)()) != ivLen)
557     {
558         log<level::DEBUG>("Error in writing IV data");
559         return -EIO;
560     }
561 
562     if (fwrite(&outBytes[0], 1, outBytesLen, (temp)()) != outBytesLen)
563     {
564         log<level::DEBUG>("Error in writing encrypted data");
565         return -EIO;
566     }
567 
568     if (fwrite(&mac[0], 1, macLen, (temp)()) != macLen)
569     {
570         log<level::DEBUG>("Error in writing MAC data");
571         return -EIO;
572     }
573 
574     if (fflush((temp)()))
575     {
576         log<level::DEBUG>(
577             "File fflush error while writing entries to special file");
578         return -EIO;
579     }
580 
581     OPENSSL_cleanse(iv.data(), ivLen);
582 
583     // Rename the tmp  file to actual file
584     if (std::rename(strTempFileName.data(), passwdFileName) != 0)
585     {
586         log<level::DEBUG>("Failed to rename tmp file to ipmi-pass");
587         return -EIO;
588     }
589 
590     return 0;
591 }
592 
593 std::time_t PasswdMgr::getUpdatedFileTime()
594 {
595     struct stat fileStat = {};
596     if (stat(passwdFileName, &fileStat) != 0)
597     {
598         log<level::DEBUG>("Error - Getting passwd file time stamp");
599         return -EIO;
600     }
601     return fileStat.st_mtime;
602 }
603 
604 } // namespace ipmi
605