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