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