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/alf_services.table
|
||||||
python tools/gentable.py osquery/tables/specs/apps.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/launchd.table
|
||||||
|
python tools/gentable.py osquery/tables/specs/cacerts.table
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd build && cmake .. && make -j5
|
cd build && cmake .. && make -j5
|
||||||
|
|
||||||
|
@ -220,6 +220,35 @@ getSerializedScheduledQueryLogItemJSON() {
|
|||||||
return std::make_pair(ss.str(), results.second);
|
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 getEtcHostsContent() {
|
||||||
std::string content = R"(
|
std::string content = R"(
|
||||||
##
|
##
|
||||||
|
@ -78,6 +78,9 @@ getSerializedScheduledQueryLogItem();
|
|||||||
std::pair<std::string, osquery::db::ScheduledQueryLogItem>
|
std::pair<std::string, osquery::db::ScheduledQueryLogItem>
|
||||||
getSerializedScheduledQueryLogItemJSON();
|
getSerializedScheduledQueryLogItemJSON();
|
||||||
|
|
||||||
|
// generate content for a PEM-encoded certificate
|
||||||
|
std::string getCACertificateContent();
|
||||||
|
|
||||||
// generate the content that would be found in an /etc/hosts file
|
// generate the content that would be found in an /etc/hosts file
|
||||||
std::string getEtcHostsContent();
|
std::string getEtcHostsContent();
|
||||||
|
|
||||||
|
@ -16,13 +16,15 @@ ADD_LIBRARY(osquery_tables
|
|||||||
system/firewall.cpp
|
system/firewall.cpp
|
||||||
system/apps.cpp
|
system/apps.cpp
|
||||||
system/launchd.cpp
|
system/launchd.cpp
|
||||||
|
system/cacerts.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
TARGET_LINK_LIBRARIES(osquery_tables boost_filesystem)
|
TARGET_LINK_LIBRARIES(osquery_tables boost_filesystem)
|
||||||
TARGET_LINK_LIBRARIES(osquery_tables glog)
|
TARGET_LINK_LIBRARIES(osquery_tables glog)
|
||||||
TARGET_LINK_LIBRARIES(osquery_tables osquery_filesystem)
|
TARGET_LINK_LIBRARIES(osquery_tables osquery_filesystem)
|
||||||
TARGET_LINK_LIBRARIES(osquery_tables osquery_sqlite)
|
TARGET_LINK_LIBRARIES(osquery_tables osquery_sqlite)
|
||||||
TARGET_LINK_LIBRARIES(osquery_tables "-Wl,-all_load")
|
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)
|
ADD_EXECUTABLE(etc_hosts_tests networking/etc_hosts_tests.cpp)
|
||||||
TARGET_LINK_LIBRARIES(etc_hosts_tests gtest)
|
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_database)
|
||||||
TARGET_LINK_LIBRARIES(launchd_tests osquery_filesystem)
|
TARGET_LINK_LIBRARIES(launchd_tests osquery_filesystem)
|
||||||
TARGET_LINK_LIBRARIES(launchd_tests osquery_tables)
|
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