mirror of
https://github.com/aria2/aria2.git
synced 2025-04-06 22:17:38 +03:00
AppleTLS: Implement PKCS12 loading.
This commit is contained in:
parent
c68297498d
commit
a476fb352e
2 changed files with 144 additions and 31 deletions
|
@ -36,8 +36,14 @@
|
|||
|
||||
#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"
|
||||
|
@ -58,11 +64,30 @@ namespace {
|
|||
};
|
||||
#endif // defined(__MAC_10_7)
|
||||
|
||||
class CFReleaser {
|
||||
const void *ptr_;
|
||||
template<typename T>
|
||||
class CFRef {
|
||||
T ref_;
|
||||
public:
|
||||
inline CFReleaser(const void *ptr) : ptr_(ptr) {}
|
||||
inline ~CFReleaser() { if (ptr_) CFRelease(ptr_); }
|
||||
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)
|
||||
|
@ -70,6 +95,7 @@ namespace {
|
|||
// Fingerprints are often separated by colons
|
||||
return isspace(c) || c == ':';
|
||||
}
|
||||
|
||||
static inline std::string stripWhitespace(std::string str)
|
||||
{
|
||||
str.erase(std::remove_if(str.begin(), str.end(), isWhitespace), str.end());
|
||||
|
@ -78,7 +104,9 @@ namespace {
|
|||
|
||||
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_);
|
||||
}
|
||||
|
@ -87,9 +115,11 @@ namespace {
|
|||
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();
|
||||
|
@ -102,15 +132,14 @@ namespace {
|
|||
std::string errToString(OSStatus err)
|
||||
{
|
||||
std::string rv = "Unkown error";
|
||||
CFStringRef cerr = SecCopyErrorMessageString(err, nullptr);
|
||||
if (cerr) {
|
||||
size_t len = CFStringGetLength(cerr) * 4;
|
||||
auto buf = new char[len];
|
||||
if (CFStringGetCString(cerr, buf, len, kCFStringEncodingUTF8)) {
|
||||
rv = buf;
|
||||
}
|
||||
delete [] buf;
|
||||
CFRelease(cerr);
|
||||
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;
|
||||
}
|
||||
|
@ -118,26 +147,28 @@ namespace {
|
|||
bool checkIdentity(const SecIdentityRef id, const std::string& fingerPrint,
|
||||
const std::vector<std::string> supported)
|
||||
{
|
||||
SecCertificateRef ref = nullptr;
|
||||
if (SecIdentityCopyCertificate(id, &ref) != errSecSuccess) {
|
||||
CFRef<SecCertificateRef> ref;
|
||||
SecCertificateRef raw_ref = nullptr;
|
||||
if (SecIdentityCopyCertificate(id, &raw_ref) != errSecSuccess) {
|
||||
A2_LOG_ERROR("Failed to get a certref!");
|
||||
return false;
|
||||
}
|
||||
CFReleaser del_ref(ref);
|
||||
CFDataRef data = SecCertificateCopyData(ref);
|
||||
ref.reset(raw_ref);
|
||||
|
||||
CFRef<CFDataRef> data(SecCertificateCopyData(ref.get()));
|
||||
if (!data) {
|
||||
A2_LOG_ERROR("Failed to get a data!");
|
||||
return false;
|
||||
}
|
||||
CFReleaser del_data(data);
|
||||
|
||||
// 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(supported.begin(), supported.end(),
|
||||
hash_finder(data, fingerPrint)) != supported.end();
|
||||
return std::find_if(
|
||||
supported.begin(), supported.end(), hash_finder(data.get(), fingerPrint))
|
||||
!= supported.end();
|
||||
}
|
||||
|
||||
#endif // defined(__MAC_10_6)
|
||||
|
@ -162,11 +193,18 @@ AppleTLSContext::~AppleTLSContext()
|
|||
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("TLS credential files are not supported. Use the KeyChain to manage your certificates and provide a fingerprint. See the manual.");
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -198,27 +236,25 @@ bool AppleTLSContext::tryAsFingerprint(const std::string& fingerprint)
|
|||
A2_LOG_DEBUG(fmt("Looking for cert with fingerprint %s", fp.c_str()));
|
||||
|
||||
// Build and run the KeyChain the query.
|
||||
auto policy = SecPolicyCreateSSL(true, nullptr);
|
||||
CFRef<SecPolicyRef> policy(SecPolicyCreateSSL(true, nullptr));
|
||||
if (!policy) {
|
||||
A2_LOG_ERROR("Failed to create SecPolicy");
|
||||
return false;
|
||||
}
|
||||
CFReleaser del_policy(policy);
|
||||
const void *query_values[] = {
|
||||
kSecClassIdentity,
|
||||
kCFBooleanTrue,
|
||||
policy,
|
||||
policy.get(),
|
||||
kSecMatchLimitAll
|
||||
};
|
||||
auto query = CFDictionaryCreate(nullptr, query_keys, query_values, 4,
|
||||
nullptr, nullptr);
|
||||
CFRef<CFDictionaryRef> query(CFDictionaryCreate(
|
||||
nullptr, query_keys, query_values, 4, nullptr, nullptr));
|
||||
if (!query) {
|
||||
A2_LOG_ERROR("Failed to create identity query");
|
||||
return false;
|
||||
}
|
||||
CFReleaser del_query(query);
|
||||
CFArrayRef identities;
|
||||
OSStatus err = SecItemCopyMatching(query, (CFTypeRef*)&identities);
|
||||
OSStatus err = SecItemCopyMatching(query.get(), (CFTypeRef*)&identities);
|
||||
if (err != errSecSuccess) {
|
||||
A2_LOG_ERROR("Query failed: " + errToString(err));
|
||||
return false;
|
||||
|
@ -247,14 +283,15 @@ bool AppleTLSContext::tryAsFingerprint(const std::string& fingerprint)
|
|||
#else // defined(__MAC_10_7)
|
||||
#if defined(__MAC_10_6)
|
||||
|
||||
SecIdentitySearchRef search;
|
||||
CFRef<SecIdentitySearchRef> search;
|
||||
SecIdentitySearchRef raw_search;
|
||||
|
||||
// Deprecated as of 10.7
|
||||
OSStatus err = SecIdentitySearchCreate(0, CSSM_KEYUSE_SIGN, &search);
|
||||
OSStatus err = SecIdentitySearchCreate(0, CSSM_KEYUSE_SIGN, &raw_search);
|
||||
if (err != errSecSuccess) {
|
||||
A2_LOG_ERROR("Certificate search failed: " + errToString(err));
|
||||
}
|
||||
CFReleaser del_search(search);
|
||||
search.reset(raw_search);
|
||||
|
||||
SecIdentityRef id;
|
||||
while (SecIdentitySearchCopyNext(search, &id) == errSecSuccess) {
|
||||
|
@ -278,4 +315,78 @@ bool AppleTLSContext::tryAsFingerprint(const std::string& fingerprint)
|
|||
#endif // defined(__MAC_10_7)
|
||||
}
|
||||
|
||||
bool AppleTLSContext::tryAsPKCS12(const std::string& certfile)
|
||||
{
|
||||
#if defined(__MAC_10_6)
|
||||
std::stringstream ss;
|
||||
BufferedFile(certfile.c_str(), "rb").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
|
||||
|
|
|
@ -90,6 +90,8 @@ private:
|
|||
SecIdentityRef credentials_;
|
||||
|
||||
bool tryAsFingerprint(const std::string& fingerprint);
|
||||
bool tryAsPKCS12(const std::string& certfile);
|
||||
bool tryAsPKCS12(CFDataRef data, const char* password);
|
||||
};
|
||||
|
||||
} // namespace aria2
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue