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