/
githubmirror
/
aria2
Обзор
Документация
Войти
/
githubmirror
/
aria2
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/AppleTLSContext.cc
393 строки
11 KB
a1346054
Fix spelling
05 ноя 2023, 04:33
05 ноя 2023, 04:33
a6cd75a
Код
Авторство
О чём код?
/* <!-- copyright */ /* * aria2 - The high speed download utility * * Copyright (C) 2013 Nils Maier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ /* copyright --> */ #include "AppleTLSContext.h" #include <algorithm> #include <functional> #include <sstream> #ifdef __MAC_10_6 # include <Security/SecImportExport.h> #endif #include "LogFactory.h" #include "BufferedFile.h" #include "Logger.h" #include "MessageDigest.h" #include "fmt.h" #include "message.h" #include "util.h" namespace { using namespace aria2; #if defined(__MAC_10_6) # if defined(__MAC_10_7) static const void* query_keys[] = {kSecClass, kSecReturnRef, kSecMatchPolicy, kSecMatchLimit}; # endif // defined(__MAC_10_7) template <typename T> class CFRef { T ref_; public: CFRef() : ref_(nullptr) {} CFRef(T ref) : ref_(ref) {} ~CFRef() { reset(nullptr); } void reset(T ref) { if (ref_) { CFRelease(ref_); } ref_ = ref; } T get() { return ref_; } const T get() const { return ref_; } operator bool() const { return !!ref_; } }; static inline bool isWhitespace(char c) { // Fingerprints are often separated by colons return isspace(c) || c == ':'; } static inline std::string stripWhitespace(std::string str) { str.erase(std::remove_if(std::begin(str), std::end(str), isWhitespace), std::end(str)); return str; } struct hash_validator { const std::string& hash_; hash_validator(const std::string& hash) : hash_(hash) {} inline bool operator()(std::string type) const { return MessageDigest::isValidHash(type, hash_); } }; struct hash_finder { CFDataRef data_; const std::string& hash_; hash_finder(CFDataRef data, const std::string& hash) : data_(data), hash_(hash) { } inline bool operator()(std::string type) const { std::string hash = MessageDigest::create(type) ->update(CFDataGetBytePtr(data_), CFDataGetLength(data_)) .digest(); hash = util::toHex(hash); return hash == hash_; } }; std::string errToString(OSStatus err) { std::string rv = "Unknown error"; CFRef<CFStringRef> cerr(SecCopyErrorMessageString(err, nullptr)); if (!cerr) { return rv; } size_t len = CFStringGetLength(cerr.get()) * 4; auto buf = make_unique<char[]>(len); if (CFStringGetCString(cerr.get(), buf.get(), len, kCFStringEncodingUTF8)) { rv = buf.get(); } return rv; } bool checkIdentity(const SecIdentityRef id, const std::string& fingerPrint, const std::vector<std::string> supported) { CFRef<SecCertificateRef> ref; SecCertificateRef raw_ref = nullptr; if (SecIdentityCopyCertificate(id, &raw_ref) != errSecSuccess) { A2_LOG_ERROR("Failed to get a certref!"); return false; } ref.reset(raw_ref); CFRef<CFDataRef> data(SecCertificateCopyData(ref.get())); if (!data) { A2_LOG_ERROR("Failed to get a data!"); return false; } // Do try all supported hash algorithms. // Usually the fingerprint would be sha1 or md5, however this is more // future-proof. Also "usually" doesn't cut it; there is already software // using SHA-2 class algos, and SHA-3 is standardized and potential users // cannot be far. return std::find_if(std::begin(supported), std::end(supported), hash_finder(data.get(), fingerPrint)) != std::end(supported); } #endif // defined(__MAC_10_6) } // namespace namespace aria2 { TLSContext* TLSContext::make(TLSSessionSide side, TLSVersion ver) { return new AppleTLSContext(side, ver); } AppleTLSContext::~AppleTLSContext() { if (credentials_) { CFRelease(credentials_); credentials_ = nullptr; } } bool AppleTLSContext::addCredentialFile(const std::string& certfile, const std::string& keyfile) { if (certfile.empty()) { return false; } if (tryAsFingerprint(certfile)) { return true; } if (tryAsPKCS12(certfile)) { return true; } A2_LOG_WARN("Only PKCS12/PFX files with a blank password and fingerprints of " "certificates in your KeyChain are supported. See the manual."); return false; } bool AppleTLSContext::addTrustedCACertFile(const std::string& certfile) { A2_LOG_INFO("TLS CA bundle files are not supported. Use the KeyChain to " "manage your certificates."); return false; } SecIdentityRef AppleTLSContext::getCredentials() { return credentials_; } bool AppleTLSContext::tryAsFingerprint(const std::string& fingerprint) { auto fp = stripWhitespace(fingerprint); // Verify this is a valid hex representation and normalize. fp = util::toHex(util::fromHex(std::begin(fp), std::end(fp))); // Verify this can represent a hash auto ht = MessageDigest::getSupportedHashTypes(); if (std::find_if(std::begin(ht), std::end(ht), hash_validator(fp)) == std::end(ht)) { A2_LOG_INFO(fmt("%s is not a fingerprint, invalid hash representation", fingerprint.c_str())); return false; } #if defined(__MAC_10_7) A2_LOG_DEBUG(fmt("Looking for cert with fingerprint %s", fp.c_str())); // Build and run the KeyChain the query. CFRef<SecPolicyRef> policy(SecPolicyCreateSSL(true, nullptr)); if (!policy) { A2_LOG_ERROR("Failed to create SecPolicy"); return false; } const void* query_values[] = {kSecClassIdentity, kCFBooleanTrue, policy.get(), kSecMatchLimitAll}; CFRef<CFDictionaryRef> query(CFDictionaryCreate( nullptr, query_keys, query_values, 4, nullptr, nullptr)); if (!query) { A2_LOG_ERROR("Failed to create identity query"); return false; } CFArrayRef identities; OSStatus err = SecItemCopyMatching(query.get(), (CFTypeRef*)&identities); if (err != errSecSuccess) { A2_LOG_ERROR("Query failed: " + errToString(err)); return false; } // Alrighty, search the fingerprint. const size_t nvals = CFArrayGetCount(identities); for (size_t i = 0; i < nvals; ++i) { SecIdentityRef id = (SecIdentityRef)CFArrayGetValueAtIndex(identities, i); if (!id) { A2_LOG_ERROR("Failed to get a value!"); continue; } if (!checkIdentity(id, fp, ht)) { continue; } A2_LOG_INFO("Found cert with matching fingerprint"); credentials_ = id; CFRetain(id); return true; } A2_LOG_ERROR( fmt("Failed to lookup %s in your KeyChain", fingerprint.c_str())); return false; #else // defined(__MAC_10_7) # if defined(__MAC_10_6) CFRef<SecIdentitySearchRef> search; SecIdentitySearchRef raw_search; // Deprecated as of 10.7 OSStatus err = SecIdentitySearchCreate(0, CSSM_KEYUSE_SIGN, &raw_search); if (err != errSecSuccess) { A2_LOG_ERROR("Certificate search failed: " + errToString(err)); } search.reset(raw_search); SecIdentityRef id; while (SecIdentitySearchCopyNext(search, &id) == errSecSuccess) { if (!checkIdentity(id, fp, ht)) { continue; } A2_LOG_INFO("Found cert with matching fingerprint"); credentials_ = id; return true; } A2_LOG_ERROR( fmt("Failed to lookup %s in your KeyChain", fingerprint.c_str())); return false; # else // defined(__MAC_10_6) A2_LOG_ERROR("Your system does not support creditials via fingerprints; " "Upgrade to OSX 10.6 or later"); return false; # endif // defined(__MAC_10_6) #endif // defined(__MAC_10_7) } bool AppleTLSContext::tryAsPKCS12(const std::string& certfile) { #if defined(__MAC_10_6) std::stringstream ss; BufferedFile(certfile.c_str(), BufferedFile::READ).transfer(ss); auto data = ss.str(); if (data.empty()) { A2_LOG_ERROR("Couldn't read certificate file."); return false; } CFRef<CFDataRef> dataRef( CFDataCreate(nullptr, (const UInt8*)data.c_str(), data.size())); if (!dataRef) { A2_LOG_ERROR("Couldn't allocate PKCS12 data"); return false; } return tryAsPKCS12(dataRef.get(), "") || tryAsPKCS12(dataRef.get(), nullptr); #else // defined(__MAC_10_6) A2_LOG_INFO("PKCS12 files are only supported in OSX 10.6 or later."); return false; #endif // defined(__MAC_10_6) } bool AppleTLSContext::tryAsPKCS12(CFDataRef data, const char* password) { #if defined(__MAC_10_6) CFRef<CFStringRef> passwordRef; if (password) { passwordRef.reset(CFStringCreateWithBytes(nullptr, (const UInt8*)password, strlen(password), kCFStringEncodingUTF8, false)); } const void* keys[] = {kSecImportExportPassphrase}; const void* values[] = {passwordRef.get()}; CFRef<CFDictionaryRef> options( CFDictionaryCreate(nullptr, keys, values, 1, nullptr, nullptr)); if (!options) { A2_LOG_ERROR("Failed to create options"); return false; } CFRef<CFArrayRef> items; CFArrayRef raw_items = nullptr; OSStatus rv = SecPKCS12Import(data, options.get(), &raw_items); if (rv != errSecSuccess) { A2_LOG_DEBUG( fmt("Failed to parse PKCS12 data: %s", errToString(rv).c_str())); return false; } items.reset(raw_items); CFDictionaryRef idAndTrust = (CFDictionaryRef)CFArrayGetValueAtIndex(items.get(), 0); if (!idAndTrust) { A2_LOG_ERROR("Failed to get identity and trust from PKCS12 data"); return false; } credentials_ = (SecIdentityRef)CFDictionaryGetValue(idAndTrust, kSecImportItemIdentity); if (!credentials_) { A2_LOG_ERROR("Failed to get credentials PKCS12 data"); return false; } CFRetain(credentials_); A2_LOG_INFO("Loaded certificate from file"); return true; #else // defined(__MAC_10_6) return false; #endif // defined(__MAC_10_6) } } // namespace aria2