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