1 #include "config.h" 2 3 #include "image_verify.hpp" 4 5 #include "version.hpp" 6 7 #include <fcntl.h> 8 #include <openssl/err.h> 9 #include <sys/stat.h> 10 11 #include <fstream> 12 #include <phosphor-logging/elog-errors.hpp> 13 #include <phosphor-logging/elog.hpp> 14 #include <phosphor-logging/log.hpp> 15 #include <set> 16 #include <xyz/openbmc_project/Common/error.hpp> 17 18 namespace openpower 19 { 20 namespace software 21 { 22 namespace image 23 { 24 25 using namespace phosphor::logging; 26 using namespace openpower::software::updater; 27 using InternalFailure = 28 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 29 30 constexpr auto keyTypeTag = "KeyType"; 31 constexpr auto hashFunctionTag = "HashType"; 32 33 Signature::Signature(const fs::path& imageDirPath, 34 const std::string& pnorFileName, 35 const fs::path& signedConfPath) : 36 imageDirPath(imageDirPath), 37 pnorFileName(pnorFileName), signedConfPath(signedConfPath) 38 { 39 fs::path file(imageDirPath / MANIFEST_FILE); 40 41 auto keyValues = 42 Version::getValue(file, {{keyTypeTag, " "}, {hashFunctionTag, " "}}); 43 keyType = keyValues.at(keyTypeTag); 44 hashType = keyValues.at(hashFunctionTag); 45 } 46 47 AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const 48 { 49 AvailableKeyTypes keyTypes{}; 50 51 // Find the path of all the files 52 if (!fs::is_directory(signedConfPath)) 53 { 54 log<level::ERR>("Signed configuration path not found in the system"); 55 elog<InternalFailure>(); 56 } 57 58 // Look for all the hash and public key file names get the key value 59 // For example: 60 // /etc/activationdata/OpenPOWER/publickey 61 // /etc/activationdata/OpenPOWER/hashfunc 62 // /etc/activationdata/GA/publickey 63 // /etc/activationdata/GA/hashfunc 64 // Set will have OpenPOWER, GA 65 66 for (const auto& p : fs::recursive_directory_iterator(signedConfPath)) 67 { 68 if ((p.path().filename() == HASH_FILE_NAME) || 69 (p.path().filename() == PUBLICKEY_FILE_NAME)) 70 { 71 // extract the key types 72 // /etc/activationdata/GA/ -> get GA from the path 73 auto key = p.path().parent_path(); 74 keyTypes.insert(key.filename()); 75 } 76 } 77 78 return keyTypes; 79 } 80 81 inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const 82 { 83 fs::path hashpath(signedConfPath / key / HASH_FILE_NAME); 84 fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME); 85 86 return std::make_pair(std::move(hashpath), std::move(keyPath)); 87 } 88 89 bool Signature::verify() 90 { 91 try 92 { 93 // Verify the MANIFEST and publickey file using available 94 // public keys and hash on the system. 95 if (false == systemLevelVerify()) 96 { 97 log<level::ERR>("System level Signature Validation failed"); 98 return false; 99 } 100 101 // image specific publickey file name. 102 fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME); 103 104 // Validate the PNOR image file. 105 // Build Image File name 106 fs::path file(imageDirPath); 107 file /= pnorFileName; 108 109 // Build Signature File name 110 std::string fileName = file.filename(); 111 fs::path sigFile(imageDirPath); 112 sigFile /= fileName + SIGNATURE_FILE_EXT; 113 114 // Verify the signature. 115 auto valid = verifyFile(file, sigFile, publicKeyFile, hashType); 116 if (valid == false) 117 { 118 log<level::ERR>("Image file Signature Validation failed", 119 entry("IMAGE=%s", pnorFileName.c_str())); 120 return false; 121 } 122 123 log<level::DEBUG>("Successfully completed Signature vaildation."); 124 125 return true; 126 } 127 catch (const InternalFailure& e) 128 { 129 return false; 130 } 131 catch (const std::exception& e) 132 { 133 log<level::ERR>(e.what()); 134 return false; 135 } 136 } 137 138 bool Signature::systemLevelVerify() 139 { 140 // Get available key types from the system. 141 auto keyTypes = getAvailableKeyTypesFromSystem(); 142 if (keyTypes.empty()) 143 { 144 log<level::ERR>("Missing Signature configuration data in system"); 145 elog<InternalFailure>(); 146 } 147 148 // Build publickey and its signature file name. 149 fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME); 150 fs::path pkeyFileSig(pkeyFile); 151 pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT); 152 153 // Build manifest and its signature file name. 154 fs::path manifestFile(imageDirPath / MANIFEST_FILE); 155 fs::path manifestFileSig(manifestFile); 156 manifestFileSig.replace_extension(SIGNATURE_FILE_EXT); 157 158 auto valid = false; 159 160 // Verify the file signature with available key types 161 // public keys and hash function. 162 // For any internal failure during the key/hash pair specific 163 // validation, should continue the validation with next 164 // available Key/hash pair. 165 for (const auto& keyType : keyTypes) 166 { 167 auto keyHashPair = getKeyHashFileNames(keyType); 168 auto keyValues = 169 Version::getValue(keyHashPair.first, {{hashFunctionTag, " "}}); 170 auto hashFunc = keyValues.at(hashFunctionTag); 171 172 try 173 { 174 // Verify manifest file signature 175 valid = verifyFile(manifestFile, manifestFileSig, 176 keyHashPair.second, hashFunc); 177 if (valid) 178 { 179 // Verify publickey file signature. 180 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second, 181 hashFunc); 182 if (valid) 183 { 184 break; 185 } 186 } 187 } 188 catch (const InternalFailure& e) 189 { 190 valid = false; 191 } 192 } 193 return valid; 194 } 195 196 bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile, 197 const fs::path& publicKey, 198 const std::string& hashFunc) 199 { 200 201 // Check existence of the files in the system. 202 if (!(fs::exists(file) && fs::exists(sigFile))) 203 { 204 log<level::ERR>("Failed to find the Data or signature file.", 205 entry("FILE=%s", file.c_str())); 206 elog<InternalFailure>(); 207 } 208 209 // Create RSA. 210 auto publicRSA = createPublicRSA(publicKey); 211 if (publicRSA == nullptr) 212 { 213 log<level::ERR>("Failed to create RSA", 214 entry("FILE=%s", publicKey.c_str())); 215 elog<InternalFailure>(); 216 } 217 218 // Assign key to RSA. 219 EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free); 220 EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA); 221 222 // Initializes a digest context. 223 EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free); 224 225 // Adds all digest algorithms to the internal table 226 OpenSSL_add_all_digests(); 227 228 // Create Hash structure. 229 auto hashStruct = EVP_get_digestbyname(hashFunc.c_str()); 230 if (!hashStruct) 231 { 232 log<level::ERR>("EVP_get_digestbynam: Unknown message digest", 233 entry("HASH=%s", hashFunc.c_str())); 234 elog<InternalFailure>(); 235 } 236 237 auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct, 238 nullptr, pKeyPtr.get()); 239 240 if (result <= 0) 241 { 242 log<level::ERR>("Error occurred during EVP_DigestVerifyInit", 243 entry("ERRCODE=%lu", ERR_get_error())); 244 elog<InternalFailure>(); 245 } 246 247 // Hash the data file and update the verification context 248 auto size = fs::file_size(file); 249 auto dataPtr = mapFile(file, size); 250 251 result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size); 252 if (result <= 0) 253 { 254 log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate", 255 entry("ERRCODE=%lu", ERR_get_error())); 256 elog<InternalFailure>(); 257 } 258 259 // Verify the data with signature. 260 size = fs::file_size(sigFile); 261 auto signature = mapFile(sigFile, size); 262 263 result = EVP_DigestVerifyFinal( 264 rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()), 265 size); 266 267 // Check the verification result. 268 if (result < 0) 269 { 270 log<level::ERR>("Error occurred during EVP_DigestVerifyFinal", 271 entry("ERRCODE=%lu", ERR_get_error())); 272 elog<InternalFailure>(); 273 } 274 275 if (result == 0) 276 { 277 log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed", 278 entry("PATH=%s", sigFile.c_str())); 279 return false; 280 } 281 return true; 282 } 283 284 inline RSA* Signature::createPublicRSA(const fs::path& publicKey) 285 { 286 RSA* rsa = nullptr; 287 auto size = fs::file_size(publicKey); 288 289 // Read public key file 290 auto data = mapFile(publicKey, size); 291 292 BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free); 293 if (keyBio.get() == nullptr) 294 { 295 log<level::ERR>("Failed to create new BIO Memory buffer"); 296 elog<InternalFailure>(); 297 } 298 299 rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr); 300 301 return rsa; 302 } 303 304 CustomMap Signature::mapFile(const fs::path& path, size_t size) 305 { 306 307 CustomFd fd(open(path.c_str(), O_RDONLY)); 308 309 return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0), 310 size); 311 } 312 313 } // namespace image 314 } // namespace software 315 } // namespace openpower 316