mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-07 01:55:20 +00:00
[vtable_cacerts] New CA certificates table.
This commit is contained in:
parent
c71241d294
commit
444cea0649
1
Makefile
1
Makefile
@ -15,6 +15,7 @@ build:
|
||||
python tools/gentable.py osquery/tables/specs/alf_services.table
|
||||
python tools/gentable.py osquery/tables/specs/apps.table
|
||||
python tools/gentable.py osquery/tables/specs/launchd.table
|
||||
python tools/gentable.py osquery/tables/specs/cacerts.table
|
||||
mkdir -p build
|
||||
cd build && cmake .. && make -j5
|
||||
|
||||
|
@ -220,6 +220,35 @@ getSerializedScheduledQueryLogItemJSON() {
|
||||
return std::make_pair(ss.str(), results.second);
|
||||
}
|
||||
|
||||
std::string getCACertificateContent() {
|
||||
std::string content = R"(
|
||||
MIIESzCCAzOgAwIBAgIJAI1bGeY2YPlhMA0GCSqGSIb3DQEBBQUAMIG7MQswCQYD
|
||||
VQQGEwItLTESMBAGA1UECAwJU29tZVN0YXRlMREwDwYDVQQHDAhTb21lQ2l0eTEZ
|
||||
MBcGA1UECgwQU29tZU9yZ2FuaXphdGlvbjEfMB0GA1UECwwWU29tZU9yZ2FuaXph
|
||||
dGlvbmFsVW5pdDEeMBwGA1UEAwwVbG9jYWxob3N0LmxvY2FsZG9tYWluMSkwJwYJ
|
||||
KoZIhvcNAQkBFhpyb290QGxvY2FsaG9zdC5sb2NhbGRvbWFpbjAeFw0xNDA4MTkx
|
||||
OTEyMTZaFw0xNTA4MTkxOTEyMTZaMIG7MQswCQYDVQQGEwItLTESMBAGA1UECAwJ
|
||||
U29tZVN0YXRlMREwDwYDVQQHDAhTb21lQ2l0eTEZMBcGA1UECgwQU29tZU9yZ2Fu
|
||||
aXphdGlvbjEfMB0GA1UECwwWU29tZU9yZ2FuaXphdGlvbmFsVW5pdDEeMBwGA1UE
|
||||
AwwVbG9jYWxob3N0LmxvY2FsZG9tYWluMSkwJwYJKoZIhvcNAQkBFhpyb290QGxv
|
||||
Y2FsaG9zdC5sb2NhbGRvbWFpbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||
ggEBAM6EsaVoMaHrYqH/s4YlhF6ke1XmUhzksB2eqpNqdgZw1JcZi9droRpuYmIf
|
||||
bNyvWqUffHW9mKRv+udF5Woueshn+7Kj9YnnL9jfMzFaVEC8WRwWk54RIdNkxgFq
|
||||
dqlaiwBWLvZkNUS9k/nugxVTbNu/GTqQlUG1XsIWBDJ2qRqniRfMKrfBKOxPYCZA
|
||||
l7KeFguRA+xOsA7/71OMXJZKneMSWN8duTQCFt7uYCQXWc/IV6BfKTaR/ZQQ4w7/
|
||||
iEMYPMZPSNprjun7rx0r2zPZGyrkGSCiS+4e+dfy0NbmYXodGHDxb/vBlm4q8CqF
|
||||
OoH9aq0F/3581uZcuvU2ydX/LWcCAwEAAaNQME4wHQYDVR0OBBYEFPK5mwDg7mDV
|
||||
fEJs4+ZOP9xvZBHAMB8GA1UdIwQYMBaAFPK5mwDg7mDVfEJs4+ZOP9xvZBHAMAwG
|
||||
A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKNNP6f0JKxBtfq8hakrhHyl
|
||||
cSN83SmVPcrsTLeaW8w0hi+JOtNOjD9sM8KNSbmLXfhRH4yPqYV+0dpJi5+SeelW
|
||||
DjxZwbcFoI4EEu+zqufTUpu0T51eqnGvIedlIu1i2CiaoAJEmAN2OKQuN7uIQW27
|
||||
2gL/RS+DVkevaidLRh7q2QI23B0n1XZuyEUiUKB1YfTPrupMZkostuyGybAJaxrc
|
||||
ONmxUsB38pWJRCef9N/5APS74uIesfxSvEZXcXfPA+wrQY0yXn+bsEhz9pJOxZvD
|
||||
WxULUHBC6qH9gAlKEqZYS3CwpCEl/Blznwi30r4CwwQ6dLfeXoPQDxAt7LyPpV4=
|
||||
)";
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string getEtcHostsContent() {
|
||||
std::string content = R"(
|
||||
##
|
||||
|
@ -78,6 +78,9 @@ getSerializedScheduledQueryLogItem();
|
||||
std::pair<std::string, osquery::db::ScheduledQueryLogItem>
|
||||
getSerializedScheduledQueryLogItemJSON();
|
||||
|
||||
// generate content for a PEM-encoded certificate
|
||||
std::string getCACertificateContent();
|
||||
|
||||
// generate the content that would be found in an /etc/hosts file
|
||||
std::string getEtcHostsContent();
|
||||
|
||||
|
@ -16,13 +16,15 @@ ADD_LIBRARY(osquery_tables
|
||||
system/firewall.cpp
|
||||
system/apps.cpp
|
||||
system/launchd.cpp
|
||||
system/cacerts.cpp
|
||||
)
|
||||
|
||||
TARGET_LINK_LIBRARIES(osquery_tables boost_filesystem)
|
||||
TARGET_LINK_LIBRARIES(osquery_tables glog)
|
||||
TARGET_LINK_LIBRARIES(osquery_tables osquery_filesystem)
|
||||
TARGET_LINK_LIBRARIES(osquery_tables osquery_sqlite)
|
||||
TARGET_LINK_LIBRARIES(osquery_tables "-Wl,-all_load")
|
||||
TARGET_LINK_LIBRARIES(osquery_tables "-fobjc-arc -fobjc-link-runtime -framework Foundation -framework IOKit -framework CoreFoundation")
|
||||
TARGET_LINK_LIBRARIES(osquery_tables "-fobjc-arc -fobjc-link-runtime -framework Foundation -framework IOKit -framework CoreFoundation -framework Security")
|
||||
|
||||
ADD_EXECUTABLE(etc_hosts_tests networking/etc_hosts_tests.cpp)
|
||||
TARGET_LINK_LIBRARIES(etc_hosts_tests gtest)
|
||||
@ -55,3 +57,12 @@ TARGET_LINK_LIBRARIES(launchd_tests osquery_core)
|
||||
TARGET_LINK_LIBRARIES(launchd_tests osquery_database)
|
||||
TARGET_LINK_LIBRARIES(launchd_tests osquery_filesystem)
|
||||
TARGET_LINK_LIBRARIES(launchd_tests osquery_tables)
|
||||
|
||||
ADD_EXECUTABLE(cacerts_tests system/cacerts_tests.cpp)
|
||||
TARGET_LINK_LIBRARIES(cacerts_tests gtest)
|
||||
TARGET_LINK_LIBRARIES(cacerts_tests glog)
|
||||
TARGET_LINK_LIBRARIES(cacerts_tests osquery_core)
|
||||
TARGET_LINK_LIBRARIES(cacerts_tests osquery_database)
|
||||
TARGET_LINK_LIBRARIES(cacerts_tests osquery_filesystem)
|
||||
TARGET_LINK_LIBRARIES(cacerts_tests osquery_tables)
|
||||
TARGET_LINK_LIBRARIES(osquery_tables "-framework CoreFoundation -framework Security")
|
||||
|
13
osquery/tables/specs/cacerts.table
Normal file
13
osquery/tables/specs/cacerts.table
Normal file
@ -0,0 +1,13 @@
|
||||
table_name("cacerts")
|
||||
schema([
|
||||
Column(name="common_name", type="std::string"),
|
||||
Column(name="not_valid_before", type="int"),
|
||||
Column(name="not_valid_after", type="int"),
|
||||
Column(name="key_algorithm", type="std::string"),
|
||||
Column(name="key_usage", type="std::string"),
|
||||
Column(name="subject_key_id", type="std::string"),
|
||||
Column(name="authority_key_id", type="std::string"),
|
||||
Column(name="sha1", type="std::string"),
|
||||
|
||||
])
|
||||
implementation("osquery/tables/system/cacerts@genCerts")
|
394
osquery/tables/system/cacerts.cpp
Normal file
394
osquery/tables/system/cacerts.cpp
Normal file
@ -0,0 +1,394 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/uuid/sha1.hpp>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Security/Security.h>
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include "osquery/core.h"
|
||||
#include "osquery/database.h"
|
||||
|
||||
using namespace osquery::core;
|
||||
using namespace osquery::db;
|
||||
|
||||
namespace osquery {
|
||||
namespace tables {
|
||||
|
||||
std::string genNumberProperty(const CFDataRef);
|
||||
std::string genKIDProperty(const CFDataRef);
|
||||
std::string genCommonNameProperty(const CFDataRef);
|
||||
std::string genAlgorithmProperty(const CFDataRef);
|
||||
|
||||
typedef std::string (*PropGenerator)(const CFDataRef);
|
||||
typedef std::pair<CFTypeRef, PropGenerator> Property;
|
||||
|
||||
const std::vector<std::string> kSystemKeychainPaths = {
|
||||
"/System/Library/Keychains", "/Library/Keychains", };
|
||||
|
||||
const std::vector<std::string> kUserKeychainPaths = {"/Library/Keychains", };
|
||||
|
||||
const std::map<std::string, Property> kCertificateProperties = {
|
||||
{"common_name", std::make_pair(kSecOIDCommonName, genCommonNameProperty)},
|
||||
{"not_valid_before",
|
||||
std::make_pair(kSecOIDX509V1ValidityNotBefore, genNumberProperty)},
|
||||
{"not_valid_after",
|
||||
std::make_pair(kSecOIDX509V1ValidityNotAfter, genNumberProperty)},
|
||||
{"key_algorithm", std::make_pair(kSecOIDX509V1SubjectPublicKeyAlgorithm,
|
||||
genAlgorithmProperty)},
|
||||
{"key_usage", std::make_pair(kSecOIDKeyUsage, genNumberProperty)},
|
||||
{"subject_key_id",
|
||||
std::make_pair(kSecOIDSubjectKeyIdentifier, genKIDProperty)},
|
||||
{"authority_key_id",
|
||||
std::make_pair(kSecOIDAuthorityKeyIdentifier, genKIDProperty)}, };
|
||||
|
||||
// From SecCertificatePriv.h
|
||||
typedef uint32_t SecKeyUsage;
|
||||
enum {
|
||||
kSecKeyUsageUnspecified = 0,
|
||||
kSecKeyUsageDigitalSignature = 1 << 0,
|
||||
kSecKeyUsageNonRepudiation = 1 << 1,
|
||||
kSecKeyUsageContentCommitment = 1 << 1,
|
||||
kSecKeyUsageKeyEncipherment = 1 << 2,
|
||||
kSecKeyUsageDataEncipherment = 1 << 3,
|
||||
kSecKeyUsageKeyAgreement = 1 << 4,
|
||||
kSecKeyUsageKeyCertSign = 1 << 5,
|
||||
kSecKeyUsageCRLSign = 1 << 6,
|
||||
kSecKeyUsageEncipherOnly = 1 << 7,
|
||||
kSecKeyUsageDecipherOnly = 1 << 8,
|
||||
kSecKeyUsageCritical = 1 << 31,
|
||||
kSecKeyUsageAll = 0x7FFFFFFF
|
||||
};
|
||||
|
||||
std::string safeSecString(const CFStringRef cf_string) {
|
||||
CFIndex length;
|
||||
char *buffer;
|
||||
|
||||
// Access, then convert the CFString. CFStringGetCStringPtr is less-safe.
|
||||
length = CFStringGetLength(cf_string);
|
||||
buffer = (char *)malloc(length + 1);
|
||||
if (!CFStringGetCString(
|
||||
cf_string, buffer, length + 1, kCFStringEncodingASCII)) {
|
||||
free(buffer);
|
||||
return "";
|
||||
}
|
||||
|
||||
// Cleanup allocations.
|
||||
std::string result(buffer);
|
||||
free(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string genNumberProperty(const CFDataRef number) {
|
||||
CFNumberType type;
|
||||
unsigned int value;
|
||||
|
||||
if (CFGetTypeID(number) != CFNumberGetTypeID() ||
|
||||
!CFNumberGetValue((CFNumberRef)number, kCFNumberIntType, &value)) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
// Cast as a string.
|
||||
return boost::lexical_cast<std::string>(value);
|
||||
}
|
||||
|
||||
std::string genKIDProperty(const CFDataRef kid) {
|
||||
CFDataRef kid_data = NULL;
|
||||
CFDictionaryRef kid_dict = NULL;
|
||||
const char *kid_value = 0;
|
||||
|
||||
// Find the key identifier data within the property mess.
|
||||
for (CFIndex i = 0; i < CFArrayGetCount((CFArrayRef)kid); i++) {
|
||||
kid_dict = (CFDictionaryRef)CFArrayGetValueAtIndex((CFArrayRef)kid, i);
|
||||
kid_value =
|
||||
(const char *)CFDictionaryGetValue(kid_dict, kSecPropertyKeyValue);
|
||||
|
||||
if (CFGetTypeID(kid_value) == CFDataGetTypeID()) {
|
||||
kid_data = (CFDataRef)kid_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (kid_data == NULL) {
|
||||
// No key identifier found.
|
||||
return "";
|
||||
}
|
||||
|
||||
// Provide an ASCII-representation of the KID, similar to keychain.
|
||||
std::stringstream ascii_kid;
|
||||
int kid_byte;
|
||||
|
||||
for (CFIndex i = 0; i < CFDataGetLength(kid_data) /* 20 */; i++) {
|
||||
kid_byte = (uint8_t)CFDataGetBytePtr(kid_data)[i];
|
||||
ascii_kid << std::setfill('0') << std::setw(2) << std::hex << kid_byte;
|
||||
// Then make it easy to read.
|
||||
if (i < CFDataGetLength(kid_data) - 1) {
|
||||
ascii_kid << "";
|
||||
}
|
||||
}
|
||||
|
||||
return ascii_kid.str();
|
||||
}
|
||||
|
||||
std::string genCommonNameProperty(const CFDataRef ca) {
|
||||
CFDataRef ca_data = NULL;
|
||||
CFStringRef ca_string = NULL;
|
||||
char *ca_buffer;
|
||||
CFIndex ca_length;
|
||||
|
||||
// Find the key identifier data within the property mess.
|
||||
for (CFIndex i = 0; i < CFArrayGetCount((CFArrayRef)ca); i++) {
|
||||
ca_data = (CFDataRef)CFArrayGetValueAtIndex((CFArrayRef)ca, i);
|
||||
if (CFGetTypeID(ca_data) == CFStringGetTypeID()) {
|
||||
ca_string = (CFStringRef)ca_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ca_string == NULL) {
|
||||
// Could not find a CFString reference within the common name array.
|
||||
return "";
|
||||
}
|
||||
|
||||
// Access, then convert the CFString. CFStringGetCStringPtr is less-safe.
|
||||
return safeSecString(ca_string);
|
||||
}
|
||||
|
||||
std::string genAlgorithmProperty(const CFDataRef alg) {
|
||||
std::string expected_label = "Algorithm";
|
||||
CFStringRef label, value;
|
||||
CFDictionaryRef alg_item;
|
||||
|
||||
// Find the key identifier data within the property mess.
|
||||
for (CFIndex i = 0; i < CFArrayGetCount((CFArrayRef)alg); i++) {
|
||||
alg_item = (CFDictionaryRef)CFArrayGetValueAtIndex((CFArrayRef)alg, i);
|
||||
label = (CFStringRef)CFDictionaryGetValue(alg_item, kSecPropertyKeyLabel);
|
||||
value = (CFStringRef)CFDictionaryGetValue(alg_item, kSecPropertyKeyValue);
|
||||
|
||||
if (expected_label.compare(safeSecString(label)) == 0) {
|
||||
return safeSecString(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown algorithm OID.
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string genSHA1ForCertificate(const SecCertificateRef ca) {
|
||||
boost::uuids::detail::sha1 sha1;
|
||||
CFDataRef ca_data;
|
||||
|
||||
// Access raw data, hash and release.
|
||||
ca_data = SecCertificateCopyData(ca);
|
||||
sha1.process_bytes(CFDataGetBytePtr(ca_data), CFDataGetLength(ca_data));
|
||||
CFRelease(ca_data);
|
||||
|
||||
std::stringstream hash_output;
|
||||
unsigned int hash[5];
|
||||
|
||||
// Return a hex-encoded friendly version of the 20byte-sha1.
|
||||
sha1.get_digest(hash);
|
||||
hash_output << std::hex << std::setfill('0') << std::setw(2);
|
||||
for (std::size_t i = 0; i < sizeof(hash) / sizeof(hash[0]); i++) {
|
||||
hash_output << hash[i];
|
||||
}
|
||||
|
||||
return hash_output.str();
|
||||
}
|
||||
|
||||
CFNumberRef CFNumberCreateCopy(const CFNumberRef number) {
|
||||
// Easy way to get allow releasing numbers existing in arrays/dicts.
|
||||
// This follows Apple's guidance for "Create" APIs, caller controls memory.
|
||||
CFNumberRef copy;
|
||||
unsigned int value;
|
||||
|
||||
if (!CFNumberGetValue(number, kCFNumberIntType, &value)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
copy = CFNumberCreate(NULL, kCFNumberIntType, &value);
|
||||
return copy;
|
||||
}
|
||||
|
||||
CFDataRef CreatePropertyFromCertificate(const SecCertificateRef &cert,
|
||||
const CFTypeRef &oid) {
|
||||
CFDictionaryRef certificate_values;
|
||||
CFDictionaryRef property_values;
|
||||
CFDataRef property;
|
||||
CFMutableArrayRef keys;
|
||||
|
||||
// Set the list of attributes.
|
||||
keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
|
||||
CFArrayAppendValue(keys, oid); // SecCertificateOIDs.h
|
||||
|
||||
// Request dictionary of dictionaries (one for each attribute).
|
||||
certificate_values = SecCertificateCopyValues(cert, keys, NULL);
|
||||
CFRelease(keys);
|
||||
|
||||
if (!CFDictionaryContainsKey(certificate_values, oid)) {
|
||||
// Certificate does not have the requested property.
|
||||
CFRelease(certificate_values);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
property_values =
|
||||
(CFDictionaryRef)CFDictionaryGetValue(certificate_values, oid);
|
||||
if (!CFDictionaryContainsKey(property_values, kSecPropertyKeyValue)) {
|
||||
// Odd, there was not value in the property result.
|
||||
CFRelease(certificate_values);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create copy of the property value, which is an index to owned dict.
|
||||
property =
|
||||
(CFDataRef)CFDictionaryGetValue(property_values, kSecPropertyKeyValue);
|
||||
if (CFGetTypeID(property) == CFArrayGetTypeID()) {
|
||||
property = (CFDataRef)CFArrayCreateCopy(NULL, (CFArrayRef)property);
|
||||
} else if (CFGetTypeID(property) == CFNumberGetTypeID()) {
|
||||
property = (CFDataRef)CFNumberCreateCopy((CFNumberRef)property);
|
||||
} else {
|
||||
LOG(ERROR) << "This property type is unknown...";
|
||||
}
|
||||
|
||||
// Release and give the caller control of the property.
|
||||
CFRelease(certificate_values);
|
||||
return property;
|
||||
}
|
||||
|
||||
bool CertificateIsCA(const SecCertificateRef cert) {
|
||||
std::string expected_label = "Certificate Authority";
|
||||
std::string expected_value = "Yes";
|
||||
CFDataRef constraints;
|
||||
|
||||
// Create copy of the basic constrains OID.
|
||||
constraints = CreatePropertyFromCertificate(cert, kSecOIDBasicConstraints);
|
||||
if (constraints == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must return an array of constrains.
|
||||
if (CFGetTypeID(constraints) != CFArrayGetTypeID()) {
|
||||
CFRelease(constraints);
|
||||
return false;
|
||||
}
|
||||
|
||||
CFStringRef label, value;
|
||||
CFDictionaryRef constraint;
|
||||
bool isCA = false;
|
||||
|
||||
// Find the expected value/label combination constraint.
|
||||
for (CFIndex i = 0; i < CFArrayGetCount((CFArrayRef)constraints); i++) {
|
||||
constraint =
|
||||
(CFDictionaryRef)CFArrayGetValueAtIndex((CFArrayRef)constraints, i);
|
||||
label = (CFStringRef)CFDictionaryGetValue(constraint, kSecPropertyKeyLabel);
|
||||
value = (CFStringRef)CFDictionaryGetValue(constraint, kSecPropertyKeyValue);
|
||||
|
||||
if (expected_label.compare(safeSecString(label)) == 0 &&
|
||||
expected_value.compare(safeSecString(value)) == 0) {
|
||||
isCA = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(constraints);
|
||||
return isCA;
|
||||
}
|
||||
|
||||
bool genOSXAuthorities(CFArrayRef &reference) {
|
||||
CFArrayRef keychain_certs;
|
||||
CFMutableDictionaryRef query;
|
||||
OSStatus status = errSecSuccess;
|
||||
|
||||
query = CFDictionaryCreateMutable(NULL,
|
||||
0,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
|
||||
CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
|
||||
// This can be added to restrict results to x509v3
|
||||
// CFDictionaryAddValue(query, kSecAttrCertificateType, 0x03);
|
||||
CFDictionaryAddValue(query, kSecAttrCanVerify, kCFBooleanTrue);
|
||||
CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll);
|
||||
|
||||
status = SecItemCopyMatching(query, (CFTypeRef *)&keychain_certs);
|
||||
CFRelease(query);
|
||||
|
||||
if (status != errSecSuccess) {
|
||||
reference = NULL;
|
||||
}
|
||||
|
||||
// Limit certificates to authorities (kSecOIDBasicConstraints).
|
||||
CFMutableArrayRef authorities;
|
||||
SecCertificateRef cert;
|
||||
|
||||
// Store just the authority certificates.
|
||||
authorities = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
|
||||
|
||||
// For each certificate returned from the search, get the constraints prop.
|
||||
for (CFIndex i = 0; i < CFArrayGetCount(keychain_certs); i++) {
|
||||
cert = (SecCertificateRef)CFArrayGetValueAtIndex(keychain_certs, i);
|
||||
if (CertificateIsCA(cert)) {
|
||||
CFArrayAppendValue(authorities, cert);
|
||||
}
|
||||
}
|
||||
|
||||
reference = (CFArrayRef)authorities;
|
||||
CFRelease(keychain_certs);
|
||||
return (status == errSecSuccess);
|
||||
}
|
||||
|
||||
QueryData genCerts() {
|
||||
QueryData results;
|
||||
CFArrayRef authorities = NULL;
|
||||
|
||||
// Keychains/certificate stores belonging to the OS.
|
||||
if (!genOSXAuthorities(authorities)) {
|
||||
LOG(ERROR) << "Could not find OSX Keychain Certificate Authorities.";
|
||||
return results;
|
||||
}
|
||||
|
||||
// Must have returned an array of matching certificates.
|
||||
if (CFGetTypeID(authorities) != CFArrayGetTypeID()) {
|
||||
LOG(ERROR) << "Unknown certificate authorities type.";
|
||||
return results;
|
||||
}
|
||||
|
||||
// Evaluate the certificate data, check for CA in Basic constraints.
|
||||
unsigned int certificate_count = 0;
|
||||
SecCertificateRef ca = NULL;
|
||||
CFDataRef property = NULL;
|
||||
|
||||
certificate_count = CFArrayGetCount((CFArrayRef)authorities);
|
||||
for (CFIndex i = 0; i < certificate_count; i++) {
|
||||
Row r;
|
||||
ca = (SecCertificateRef)CFArrayGetValueAtIndex(authorities, i);
|
||||
|
||||
// Iterate through each selected certificate property.
|
||||
for (const auto &property_iterator : kCertificateProperties) {
|
||||
property =
|
||||
CreatePropertyFromCertificate(ca, property_iterator.second.first);
|
||||
if (property == NULL) {
|
||||
continue;
|
||||
}
|
||||
// Each property may be stored differently, apply a generator function.
|
||||
r[property_iterator.first] = property_iterator.second.second(property);
|
||||
CFRelease(property);
|
||||
}
|
||||
|
||||
r["sha1"] = genSHA1ForCertificate(ca);
|
||||
results.push_back(r);
|
||||
}
|
||||
|
||||
CFRelease(authorities);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
129
osquery/tables/system/cacerts_tests.cpp
Normal file
129
osquery/tables/system/cacerts_tests.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include <boost/archive/iterators/transform_width.hpp>
|
||||
#include <boost/archive/iterators/binary_from_base64.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Security/Security.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include "osquery/core/test_util.h"
|
||||
#include "osquery/database.h"
|
||||
|
||||
using namespace osquery::core;
|
||||
using namespace osquery::db;
|
||||
namespace bai = boost::archive::iterators;
|
||||
|
||||
namespace osquery {
|
||||
namespace tables {
|
||||
|
||||
typedef bai::binary_from_base64<const char*> base64_str;
|
||||
typedef bai::transform_width<base64_str, 8, 6> base64_dec;
|
||||
|
||||
bool CertificateIsCA(const SecCertificateRef);
|
||||
CFDataRef CreatePropertyFromCertificate(const SecCertificateRef&,
|
||||
const CFTypeRef&);
|
||||
std::string genSHA1ForCertificate(const SecCertificateRef);
|
||||
|
||||
std::string genCommonNameProperty(const CFDataRef);
|
||||
std::string genNumberProperty(const CFDataRef);
|
||||
std::string genKIDProperty(const CFDataRef);
|
||||
|
||||
std::string base64_decode(const std::string& encoded) {
|
||||
std::string is;
|
||||
std::stringstream os;
|
||||
|
||||
is = encoded;
|
||||
boost::replace_all(is, "\r\n", "");
|
||||
boost::replace_all(is, "\n", "");
|
||||
uint32_t size = is.size();
|
||||
|
||||
// Remove the padding characters
|
||||
if (size && is[size - 1] == '=') {
|
||||
--size;
|
||||
if (size && is[size - 1] == '=') {
|
||||
--size;
|
||||
}
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::copy(base64_dec(is.data()),
|
||||
base64_dec(is.data() + size),
|
||||
std::ostream_iterator<char>(os));
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
class CACertsTests : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
std::string raw;
|
||||
CFDataRef data;
|
||||
|
||||
raw = base64_decode(getCACertificateContent());
|
||||
data = CFDataCreate(NULL, (const UInt8*)raw.c_str(), (CFIndex)raw.size());
|
||||
cert = SecCertificateCreateWithData(NULL, data);
|
||||
CFRelease(data);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
if (cert != NULL) {
|
||||
CFRelease(cert);
|
||||
}
|
||||
}
|
||||
|
||||
SecCertificateRef cert;
|
||||
};
|
||||
|
||||
TEST_F(CACertsTests, test_certificate_is_ca) {
|
||||
EXPECT_EQ(true, CertificateIsCA(cert));
|
||||
}
|
||||
|
||||
TEST_F(CACertsTests, test_certificate_sha1) {
|
||||
std::string sha1;
|
||||
|
||||
sha1 = genSHA1ForCertificate(cert);
|
||||
|
||||
EXPECT_EQ("f149bae28e3c754ff4bb062b2c1b8bac81b8783e", sha1);
|
||||
}
|
||||
|
||||
TEST_F(CACertsTests, test_certificate_properties) {
|
||||
CFDataRef property;
|
||||
CFTypeRef oid;
|
||||
std::string prop_string;
|
||||
|
||||
oid = kSecOIDCommonName;
|
||||
property = CreatePropertyFromCertificate(cert, oid);
|
||||
prop_string = genCommonNameProperty(property);
|
||||
|
||||
EXPECT_EQ("localhost.localdomain", prop_string);
|
||||
CFRelease(property);
|
||||
|
||||
oid = kSecOIDSubjectKeyIdentifier;
|
||||
property = CreatePropertyFromCertificate(cert, oid);
|
||||
prop_string = genKIDProperty(property);
|
||||
|
||||
EXPECT_EQ("f2b99b00e0ee60d57c426ce3e64e3fdc6f6411c0", prop_string);
|
||||
CFRelease(property);
|
||||
|
||||
oid = kSecOIDX509V1ValidityNotBefore;
|
||||
property = CreatePropertyFromCertificate(cert, oid);
|
||||
prop_string = genNumberProperty(property);
|
||||
|
||||
EXPECT_EQ("430168336", prop_string);
|
||||
CFRelease(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
Loading…
Reference in New Issue
Block a user