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