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