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