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     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     return;
264 }
265 
266 int PasswdMgr::readPasswdFileData(std::vector<uint8_t>& outBytes)
267 {
268     std::array<uint8_t, maxKeySize> keyBuff;
269     std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary);
270     if (!keyFile.is_open())
271     {
272         log<level::DEBUG>("Error in opening encryption key file");
273         return -EIO;
274     }
275     keyFile.read(reinterpret_cast<char*>(keyBuff.data()), keyBuff.size());
276     if (keyFile.fail())
277     {
278         log<level::DEBUG>("Error in reading encryption key file");
279         return -EIO;
280     }
281 
282     std::ifstream passwdFile(passwdFileName, std::ios::in | std::ios::binary);
283     if (!passwdFile.is_open())
284     {
285         log<level::DEBUG>("Error in opening ipmi password file");
286         return -EIO;
287     }
288 
289     // calculate file size and read the data
290     passwdFile.seekg(0, std::ios::end);
291     ssize_t fileSize = passwdFile.tellg();
292     passwdFile.seekg(0, std::ios::beg);
293     std::vector<uint8_t> input(fileSize);
294     passwdFile.read(reinterpret_cast<char*>(input.data()), fileSize);
295     if (passwdFile.fail())
296     {
297         log<level::DEBUG>("Error in reading encryption key file");
298         return -EIO;
299     }
300 
301     // verify the signature first
302     MetaPassStruct* metaData = reinterpret_cast<MetaPassStruct*>(input.data());
303     if (std::strncmp(metaData->signature, META_PASSWD_SIG,
304                      sizeof(metaData->signature)))
305     {
306         log<level::DEBUG>("Error signature mismatch in password file");
307         return -EBADMSG;
308     }
309 
310     size_t inBytesLen = metaData->dataSize + metaData->padSize;
311     // If data is empty i.e no password map then return success
312     if (inBytesLen == 0)
313     {
314         log<level::DEBUG>("Empty password file");
315         return 0;
316     }
317 
318     // compute the key needed to decrypt
319     std::array<uint8_t, EVP_MAX_KEY_LENGTH> key;
320     auto keyLen = key.size();
321     if (NULL == HMAC(EVP_sha256(), keyBuff.data(), keyBuff.size(),
322                      input.data() + sizeof(*metaData), metaData->hashSize,
323                      key.data(), reinterpret_cast<unsigned int*>(&keyLen)))
324     {
325         log<level::DEBUG>("Failed to create MAC for authentication");
326         return -EIO;
327     }
328 
329     // decrypt the data
330     uint8_t* iv = input.data() + sizeof(*metaData) + metaData->hashSize;
331     size_t ivLen = metaData->ivSize;
332     uint8_t* inBytes = iv + ivLen;
333     uint8_t* mac = inBytes + inBytesLen;
334     size_t macLen = metaData->macSize;
335 
336     size_t outBytesLen = 0;
337     // Resize to actual data size
338     outBytes.resize(inBytesLen + EVP_MAX_BLOCK_LENGTH);
339     if (encryptDecryptData(false, EVP_aes_128_cbc(), key.data(), keyLen, iv,
340                            ivLen, inBytes, inBytesLen, mac, &macLen,
341                            outBytes.data(), &outBytesLen) != 0)
342     {
343         log<level::DEBUG>("Error in decryption");
344         return -EIO;
345     }
346     // Resize the vector to outBytesLen
347     outBytes.resize(outBytesLen);
348 
349     OPENSSL_cleanse(key.data(), keyLen);
350     OPENSSL_cleanse(iv, ivLen);
351 
352     return 0;
353 }
354 
355 int PasswdMgr::updatePasswdSpecialFile(const std::string& userName,
356                                        const std::string& newUserName)
357 {
358     phosphor::user::shadow::Lock lock();
359 
360     size_t bytesWritten = 0;
361     size_t inBytesLen = 0;
362     size_t isUsrFound = false;
363     const EVP_CIPHER* cipher = EVP_aes_128_cbc();
364     std::vector<uint8_t> dataBuf;
365 
366     // Read the encrypted file and get the file data
367     // Check user existance and return if not exist.
368     if (readPasswdFileData(dataBuf) != 0)
369     {
370         log<level::DEBUG>("Error in reading the encrypted pass file");
371         return -EIO;
372     }
373 
374     if (dataBuf.size() != 0)
375     {
376         inBytesLen =
377             dataBuf.size() + newUserName.size() + EVP_CIPHER_block_size(cipher);
378     }
379 
380     std::vector<uint8_t> inBytes(inBytesLen);
381     if (inBytesLen != 0)
382     {
383         char* outPtr = reinterpret_cast<char*>(dataBuf.data());
384         char* nToken = NULL;
385         char* linePtr = strtok_r(outPtr, "\n", &nToken);
386         while (linePtr != NULL)
387         {
388             size_t userEPos = 0;
389 
390             std::string lineStr(linePtr);
391             if ((userEPos = lineStr.find(":")) != std::string::npos)
392             {
393                 if (userName.compare(lineStr.substr(0, userEPos)) == 0)
394                 {
395                     isUsrFound = true;
396                     if (!newUserName.empty())
397                     {
398                         bytesWritten += std::snprintf(
399                             reinterpret_cast<char*>(&inBytes[0]) + bytesWritten,
400                             (inBytesLen - bytesWritten), "%s%s\n",
401                             newUserName.c_str(),
402                             lineStr.substr(userEPos, lineStr.size()).data());
403                     }
404                 }
405                 else
406                 {
407                     bytesWritten += std::snprintf(
408                         reinterpret_cast<char*>(&inBytes[0]) + bytesWritten,
409                         (inBytesLen - bytesWritten), "%s\n", lineStr.data());
410                 }
411             }
412             linePtr = strtok_r(NULL, "\n", &nToken);
413         }
414         inBytesLen = bytesWritten;
415     }
416     if (!isUsrFound)
417     {
418         log<level::DEBUG>("User doesn't exist");
419         return 0;
420     }
421 
422     // Read the key buff from key file
423     std::array<uint8_t, maxKeySize> keyBuff;
424     std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary);
425     if (!keyFile.good())
426     {
427         log<level::DEBUG>("Error in opening encryption key file");
428         return -EIO;
429     }
430     keyFile.read(reinterpret_cast<char*>(keyBuff.data()), keyBuff.size());
431     if (keyFile.fail())
432     {
433         log<level::DEBUG>("Error in reading encryption key file");
434         return -EIO;
435     }
436     keyFile.close();
437 
438     // Read the original passwd file mode
439     struct stat st = {};
440     if (stat(passwdFileName, &st) != 0)
441     {
442         log<level::DEBUG>("Error in getting password file fstat()");
443         return -EIO;
444     }
445 
446     // Create temporary file for write
447     std::string pwdFile(passwdFileName);
448     std::vector<char> tempFileName(pwdFile.begin(), pwdFile.end());
449     std::vector<char> fileTemplate = {'_', '_', 'X', 'X', 'X',
450                                       'X', 'X', 'X', '\0'};
451     tempFileName.insert(tempFileName.end(), fileTemplate.begin(),
452                         fileTemplate.end());
453     int fd = mkstemp((char*)tempFileName.data());
454     if (fd == -1)
455     {
456         log<level::DEBUG>("Error creating temp file");
457         return -EIO;
458     }
459 
460     std::string strTempFileName(tempFileName.data());
461     // Open the temp file for writing from provided fd
462     // By "true", remove it at exit if still there.
463     // This is needed to cleanup the temp file at exception
464     phosphor::user::File temp(fd, strTempFileName, "w", true);
465     if ((temp)() == NULL)
466     {
467         close(fd);
468         log<level::DEBUG>("Error creating temp file");
469         return -EIO;
470     }
471 
472     // Set the file mode as read-write for owner only
473     if (fchmod(fileno((temp)()), S_IRUSR | S_IWUSR) < 0)
474     {
475         log<level::DEBUG>("Error setting fchmod for temp file");
476         return -EIO;
477     }
478 
479     const EVP_MD* digest = EVP_sha256();
480     size_t hashLen = EVP_MD_block_size(digest);
481     std::vector<uint8_t> hash(hashLen);
482     size_t ivLen = EVP_CIPHER_iv_length(cipher);
483     std::vector<uint8_t> iv(ivLen);
484     std::array<uint8_t, EVP_MAX_KEY_LENGTH> key;
485     size_t keyLen = key.size();
486     std::array<uint8_t, EVP_MAX_MD_SIZE> mac;
487     size_t macLen = mac.size();
488 
489     // Create random hash and generate hash key which will be used for
490     // encryption.
491     if (RAND_bytes(hash.data(), hashLen) != 1)
492     {
493         log<level::DEBUG>("Hash genertion failed, bailing out");
494         return -EIO;
495     }
496     if (NULL == HMAC(digest, keyBuff.data(), keyBuff.size(), hash.data(),
497                      hashLen, key.data(),
498                      reinterpret_cast<unsigned int*>(&keyLen)))
499     {
500         log<level::DEBUG>("Failed to create MAC for authentication");
501         return -EIO;
502     }
503 
504     // Generate IV values
505     if (RAND_bytes(iv.data(), ivLen) != 1)
506     {
507         log<level::DEBUG>("UV genertion failed, bailing out");
508         return -EIO;
509     }
510 
511     // Encrypt the input data
512     std::vector<uint8_t> outBytes(inBytesLen + EVP_MAX_BLOCK_LENGTH);
513     size_t outBytesLen = 0;
514     if (inBytesLen != 0)
515     {
516         if (encryptDecryptData(true, EVP_aes_128_cbc(), key.data(), keyLen,
517                                iv.data(), ivLen, inBytes.data(), inBytesLen,
518                                mac.data(), &macLen, outBytes.data(),
519                                &outBytesLen) != 0)
520         {
521             log<level::DEBUG>("Error while encrypting the data");
522             return -EIO;
523         }
524         outBytes[outBytesLen] = 0;
525     }
526     OPENSSL_cleanse(key.data(), keyLen);
527 
528     // Update the meta password structure.
529     MetaPassStruct metaData = {META_PASSWD_SIG, {0, 0}, 0, 0, 0, 0, 0};
530     metaData.hashSize = hashLen;
531     metaData.ivSize = ivLen;
532     metaData.dataSize = bytesWritten;
533     metaData.padSize = outBytesLen - bytesWritten;
534     metaData.macSize = macLen;
535 
536     if (fwrite(&metaData, 1, sizeof(metaData), (temp)()) != sizeof(metaData))
537     {
538         log<level::DEBUG>("Error in writing meta data");
539         return -EIO;
540     }
541 
542     if (fwrite(&hash[0], 1, hashLen, (temp)()) != hashLen)
543     {
544         log<level::DEBUG>("Error in writing hash data");
545         return -EIO;
546     }
547 
548     if (fwrite(&iv[0], 1, ivLen, (temp)()) != ivLen)
549     {
550         log<level::DEBUG>("Error in writing IV data");
551         return -EIO;
552     }
553 
554     if (fwrite(&outBytes[0], 1, outBytesLen, (temp)()) != outBytesLen)
555     {
556         log<level::DEBUG>("Error in writing encrypted data");
557         return -EIO;
558     }
559 
560     if (fwrite(&mac[0], 1, macLen, (temp)()) != macLen)
561     {
562         log<level::DEBUG>("Error in writing MAC data");
563         return -EIO;
564     }
565 
566     if (fflush((temp)()))
567     {
568         log<level::DEBUG>(
569             "File fflush error while writing entries to special file");
570         return -EIO;
571     }
572 
573     OPENSSL_cleanse(iv.data(), ivLen);
574 
575     // Rename the tmp  file to actual file
576     if (std::rename(strTempFileName.data(), passwdFileName) != 0)
577     {
578         log<level::DEBUG>("Failed to rename tmp file to ipmi-pass");
579         return -EIO;
580     }
581 
582     return 0;
583 }
584 
585 std::time_t PasswdMgr::getUpdatedFileTime()
586 {
587     struct stat fileStat = {};
588     if (stat(passwdFileName, &fileStat) != 0)
589     {
590         log<level::DEBUG>("Error - Getting passwd file time stamp");
591         return -EIO;
592     }
593     return fileStat.st_mtime;
594 }
595 
596 } // namespace ipmi
597