2015-02-25 22:18:43 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <osquery/core.h>
|
2015-03-02 05:15:42 +00:00
|
|
|
#include <osquery/filesystem.h>
|
2015-02-25 22:18:43 +00:00
|
|
|
#include <osquery/logger.h>
|
2015-03-02 05:15:42 +00:00
|
|
|
#include <osquery/sql.h>
|
2015-02-25 22:18:43 +00:00
|
|
|
|
2015-03-08 09:59:59 +00:00
|
|
|
#include "osquery/tables/system/darwin/keychain.h"
|
2015-02-25 22:18:43 +00:00
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
namespace tables {
|
|
|
|
|
2015-07-12 18:18:49 +00:00
|
|
|
void genCertificate(const SecCertificateRef& SecCert, QueryData& results) {
|
2015-02-25 22:18:43 +00:00
|
|
|
Row r;
|
|
|
|
|
2015-07-12 18:18:49 +00:00
|
|
|
auto der_encoded_data = SecCertificateCopyData(SecCert);
|
|
|
|
if (der_encoded_data == nullptr) {
|
|
|
|
return;
|
2015-02-25 22:18:43 +00:00
|
|
|
}
|
|
|
|
|
2015-07-12 18:18:49 +00:00
|
|
|
auto der_bytes = CFDataGetBytePtr(der_encoded_data);
|
|
|
|
auto length = CFDataGetLength(der_encoded_data);
|
|
|
|
auto cert = d2i_X509(nullptr, &der_bytes, length);
|
|
|
|
|
|
|
|
if (cert == nullptr) {
|
|
|
|
VLOG(1) << "Error decoding DER encoded certificate";
|
|
|
|
CFRelease(der_encoded_data);
|
|
|
|
return;
|
2015-02-25 22:18:43 +00:00
|
|
|
}
|
|
|
|
|
2015-11-29 23:12:01 +00:00
|
|
|
// Generate the common name and subject.
|
|
|
|
// They are very similar OpenSSL API accessors so save some logic and
|
|
|
|
// generate them using output parameters.
|
2015-12-18 01:10:06 +00:00
|
|
|
genCommonName(cert, r["subject"], r["common_name"], r["issuer"]);
|
2015-11-29 23:12:01 +00:00
|
|
|
// Same with algorithm strings.
|
2016-01-05 19:34:57 +00:00
|
|
|
genAlgorithmProperties(
|
|
|
|
cert, r["key_algorithm"], r["signing_algorithm"], r["key_size"]);
|
2015-11-29 23:12:01 +00:00
|
|
|
|
|
|
|
// Most certificate field accessors return strings.
|
2015-07-12 18:18:49 +00:00
|
|
|
r["not_valid_before"] = INTEGER(genEpoch(X509_get_notBefore(cert)));
|
|
|
|
r["not_valid_after"] = INTEGER(genEpoch(X509_get_notAfter(cert)));
|
2015-02-25 22:18:43 +00:00
|
|
|
|
2015-07-12 18:18:49 +00:00
|
|
|
// Get the keychain for the certificate.
|
|
|
|
r["path"] = getKeychainPath((SecKeychainItemRef)SecCert);
|
2015-02-25 22:18:43 +00:00
|
|
|
// Hash is not a certificate property, calculate using raw data.
|
2015-07-12 18:18:49 +00:00
|
|
|
r["sha1"] = genSHA1ForCertificate(der_encoded_data);
|
|
|
|
|
|
|
|
// X509_check_ca() populates key_usage, {authority,subject}_key_id
|
|
|
|
// so it should be called before others.
|
|
|
|
r["ca"] = (CertificateIsCA(cert)) ? INTEGER(1) : INTEGER(0);
|
2015-12-18 01:10:06 +00:00
|
|
|
r["self_signed"] = (CertificateIsSelfSigned(cert)) ? INTEGER(1) : INTEGER(0);
|
2015-07-12 18:18:49 +00:00
|
|
|
r["key_usage"] = genKeyUsage(cert->ex_kusage);
|
|
|
|
r["authority_key_id"] =
|
|
|
|
(cert->akid && cert->akid->keyid)
|
|
|
|
? genKIDProperty(cert->akid->keyid->data, cert->akid->keyid->length)
|
|
|
|
: "";
|
|
|
|
r["subject_key_id"] =
|
|
|
|
(cert->skid) ? genKIDProperty(cert->skid->data, cert->skid->length) : "";
|
|
|
|
|
2015-02-25 22:18:43 +00:00
|
|
|
results.push_back(r);
|
2015-07-12 18:18:49 +00:00
|
|
|
X509_free(cert);
|
|
|
|
CFRelease(der_encoded_data);
|
2015-02-25 22:18:43 +00:00
|
|
|
}
|
|
|
|
|
2015-07-12 18:18:49 +00:00
|
|
|
QueryData genCerts(QueryContext& context) {
|
2015-02-25 22:18:43 +00:00
|
|
|
QueryData results;
|
|
|
|
|
2015-03-02 05:15:42 +00:00
|
|
|
// Allow the caller to set an explicit certificate (keychain) search path.
|
|
|
|
std::set<std::string> keychain_paths;
|
2015-05-29 20:47:04 +00:00
|
|
|
if (context.constraints["path"].exists(EQUALS)) {
|
2015-03-02 05:15:42 +00:00
|
|
|
keychain_paths = context.constraints["path"].getAll(EQUALS);
|
2015-03-08 09:59:59 +00:00
|
|
|
} else {
|
|
|
|
for (const auto& path : kSystemKeychainPaths) {
|
|
|
|
keychain_paths.insert(path);
|
|
|
|
}
|
|
|
|
auto homes = osquery::getHomeDirectories();
|
|
|
|
for (const auto& dir : homes) {
|
|
|
|
for (const auto& keychains_dir : kUserKeychainPaths) {
|
|
|
|
keychain_paths.insert((dir / keychains_dir).string());
|
|
|
|
}
|
|
|
|
}
|
2015-03-02 05:15:42 +00:00
|
|
|
}
|
|
|
|
|
2015-02-25 22:18:43 +00:00
|
|
|
// Keychains/certificate stores belonging to the OS.
|
2015-03-08 09:59:59 +00:00
|
|
|
CFArrayRef certs = CreateKeychainItems(keychain_paths, kSecClassCertificate);
|
2015-02-25 22:18:43 +00:00
|
|
|
// Must have returned an array of matching certificates.
|
2015-04-11 22:57:12 +00:00
|
|
|
if (certs == nullptr) {
|
2015-02-25 22:18:43 +00:00
|
|
|
VLOG(1) << "Could not find OS X Keychain";
|
2015-04-11 22:57:12 +00:00
|
|
|
return {};
|
|
|
|
} else if (CFGetTypeID(certs) != CFArrayGetTypeID()) {
|
|
|
|
CFRelease(certs);
|
|
|
|
return {};
|
2015-02-25 22:18:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Evaluate the certificate data, check for CA in Basic constraints.
|
|
|
|
auto certificate_count = CFArrayGetCount(certs);
|
|
|
|
for (CFIndex i = 0; i < certificate_count; i++) {
|
|
|
|
auto cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
|
|
|
|
genCertificate(cert, results);
|
|
|
|
}
|
|
|
|
|
|
|
|
CFRelease(certs);
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|