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