std::string generateRow(const Row& r, const std::map<std::string, size_t>& lengths, const std::vector<std::string>& order) { std::string out; for (const auto& column : order) { size_t size = 0; // Print a terminator for the previous value or lhs, followed by spaces. out += kToken + ' '; if (r.count(column) == 0 || lengths.count(column) == 0) { size = column.size() - utf8StringSize(FLAGS_nullvalue); out += FLAGS_nullvalue; } else { int buffer_size = static_cast<int>(lengths.at(column) - utf8StringSize(r.at(column))); if (buffer_size >= 0) { size = static_cast<size_t>(buffer_size); out += r.at(column); } } out += std::string(size + 1, ' '); } if (out.size() > 0) { // Only append if a row was added. out += kToken + "\n"; } return out; }
std::string generateRow(const Row& r, const std::map<std::string, int>& lengths, const std::vector<std::string>& order) { std::ostringstream row; row << "|"; for (const auto& each : order) { row << " "; try { row << r.at(each); for (int i = 0; i < (lengths.at(each) - r.at(each).size() + 1); ++i) { row << " "; } } catch (const std::out_of_range& e) { LOG(ERROR) << "printing the faulty row"; for (const auto& foo : r) { LOG(ERROR) << foo.first << " => " << foo.second; } LOG(ERROR) << "Error retreiving the \"" << each << "\" key in generateRow: " << e.what(); } row << "|"; } row << "\n"; return row.str(); }
Status ProcessEventSubscriber::Callback(const ECRef& ec, const SCRef& sc) { // Check and set the valid state change. // If this is an unacceptable change reset the state and clear row data. if (ec->fields.count("success") && ec->fields.at("success") == "no") { return Status(0, "OK"); } if (!validAuditState(ec->type, state_).ok()) { state_ = STATE_SYSCALL; Row().swap(row_); return Status(0, "OK"); } // Fill in row fields based on the event state. updateAuditRow(ec, row_); // Only add the event if finished (aka a PATH event was emitted). if (state_ == STATE_SYSCALL) { // If the EXECVE state was not used, decode the cmdline value. if (row_.at("cmdline_size").size() == 0) { // This allows at most 1 decode call per potentially-encoded item. row_["cmdline"] = decodeAuditValue(row_.at("cmdline")); row_["cmdline_size"] = "1"; } add(row_, getUnixTime()); Row().swap(row_); } return Status(0, "OK"); }
inline void updateAuditRow(const AuditEventContextRef& ec, Row& r) { const auto& fields = ec->fields; if (ec->type == AUDIT_SYSCALL) { r["pid"] = (fields.count("pid")) ? fields.at("pid") : "0"; r["parent"] = fields.count("ppid") ? fields.at("ppid") : "0"; r["uid"] = fields.count("uid") ? fields.at("uid") : "0"; r["euid"] = fields.count("euid") ? fields.at("euid") : "0"; r["gid"] = fields.count("gid") ? fields.at("gid") : "0"; r["egid"] = fields.count("egid") ? fields.at("euid") : "0"; r["path"] = (fields.count("exe")) ? decodeAuditValue(fields.at("exe")) : ""; // This should get overwritten during the EXECVE state. r["cmdline"] = (fields.count("comm")) ? fields.at("comm") : ""; // Do not record a cmdline size. If the final state is reached and no 'argc' // has been filled in then the EXECVE state was not used. r["cmdline_size"] = ""; r["overflows"] = ""; r["env_size"] = "0"; r["env_count"] = "0"; r["env"] = ""; } if (ec->type == AUDIT_EXECVE) { // Reset the temporary storage from the SYSCALL state. r["cmdline"] = ""; for (const auto& arg : fields) { if (arg.first == "argc") { continue; } // Amalgamate all the "arg*" fields. if (r.at("cmdline").size() > 0) { r["cmdline"] += " "; } r["cmdline"] += decodeAuditValue(arg.second); } // There may be a better way to calculate actual size from audit. // Then an overflow could be calculated/determined based on actual/expected. r["cmdline_size"] = std::to_string(r.at("cmdline").size()); } if (ec->type == AUDIT_PATH) { r["mode"] = (fields.count("mode")) ? fields.at("mode") : ""; r["owner_uid"] = fields.count("ouid") ? fields.at("ouid") : "0"; r["owner_gid"] = fields.count("ogid") ? fields.at("ogid") : "0"; auto qd = SQL::selectAllFrom("file", "path", EQUALS, r.at("path")); if (qd.size() == 1) { r["ctime"] = qd.front().at("ctime"); r["atime"] = qd.front().at("atime"); r["mtime"] = qd.front().at("mtime"); r["btime"] = "0"; } // Uptime is helpful for execution-based events. r["uptime"] = std::to_string(tables::getUptime()); } }
Status YARAEventSubscriber::Callback(const FileEventContextRef& ec, const void* user_data) { if (user_data == nullptr) { return Status(1, "No YARA category string provided"); } if (ec->action != "UPDATED" && ec->action != "CREATED") { return Status(1, "Invalid action"); } Row r; r["action"] = ec->action; r["target_path"] = ec->path; r["category"] = *(std::string*)user_data; // Only FSEvents transactions updates (inotify is a no-op). r["transaction_id"] = INTEGER(ec->transaction_id); // These are default values, to be updated in YARACallback. r["count"] = INTEGER(0); r["matches"] = std::string(""); r["strings"] = std::string(""); r["tags"] = std::string(""); ConfigDataInstance config; const auto& parser = config.getParser("yara"); if (parser == nullptr) return Status(1, "ConfigParser unknown."); const auto& yaraParser = std::static_pointer_cast<YARAConfigParserPlugin>(parser); auto rules = yaraParser->rules(); // Use the category as a lookup into the yara file_paths. The value will be // a list of signature groups to scan with. auto category = r.at("category"); const auto& yara_config = config.getParsedData("yara"); const auto& yara_paths = yara_config.get_child("file_paths"); const auto& sig_groups = yara_paths.find(category); for (const auto& rule : sig_groups->second) { const std::string group = rule.second.data(); int result = yr_rules_scan_file(rules[group], ec->path.c_str(), SCAN_FLAGS_FAST_MODE, YARACallback, (void*)&r, 0); if (result != ERROR_SUCCESS) { return Status(1, "YARA error: " + std::to_string(result)); } } if (ec->action != "" && r.at("matches").size() > 0) { add(r, ec->time); } return Status(0, "OK"); }
void genBrowserPlugin(const std::string& uid, const std::string& path, QueryData& results, bool is_disabled = false) { Row r; pt::ptree tree; r["uid"] = uid; auto info_path = path + "/Contents/Info.plist"; // Ensure that what we're processing is actually a plug-in. if (!pathExists(info_path)) { return; } if (osquery::parsePlist(info_path, tree).ok()) { // Plugin did not include an Info.plist, or it was invalid for (const auto& it : kBrowserPluginKeys) { r[it.second] = tree.get(it.first, ""); // Convert bool-types to an integer. jsonBoolAsInt(r[it.second]); } } if (r.count("native") == 0 || r.at("native").size() == 0) { // The default case for native execution is false. r["native"] = "0"; } r["path"] = path; r["disabled"] = (is_disabled) ? "1" : "0"; results.push_back(std::move(r)); }
void EventSubscriberPlugin::expireCheck(bool cleanup) { auto data_key = "data." + dbNamespace(); auto eid_key = "eid." + dbNamespace(); // Min key will be the last surviving key. size_t min_key = 0; { auto limit = getEventsMax(); std::vector<std::string> keys; scanDatabaseKeys(kEvents, keys, data_key); if (keys.size() <= limit) { return; } // There is an overflow of events buffered for this subscriber. LOG(WARNING) << "Expiring events for subscriber: " << getName() << " (limit " << limit << ")"; VLOG(1) << "Subscriber events " << getName() << " exceeded limit " << limit << " by: " << keys.size() - limit; // Inspect the N-FLAGS_events_max -th event's value and expire before the // time within the content. std::string last_key; getDatabaseValue(kEvents, eid_key, last_key); // The EID is the next-index. // EID - events_max is the most last-recent event to keep. min_key = boost::lexical_cast<size_t>(last_key) - getEventsMax(); if (cleanup) { // Scan each of the keys in keys, if their ID portion is < min_key. // Nix them, this requires lots of conversions, use with care. for (const auto& key : keys) { if (std::stoul(key.substr(key.rfind('.') + 1)) < min_key) { deleteDatabaseValue(kEvents, key); } } } } // Convert the key index into a time using the content. // The last-recent event is fetched and the corresponding time is used as // the expiration time for the subscriber. std::string content; getDatabaseValue(kEvents, data_key + "." + std::to_string(min_key), content); // Decode the value into a row structure to extract the time. Row r; if (!deserializeRowJSON(content, r) || r.count("time") == 0) { return; } // The last time will become the implicit expiration time. size_t last_time = boost::lexical_cast<size_t>(r.at("time")); if (last_time > 0) { expire_time_ = last_time; } // Finally, attempt an index query to trigger expirations. // In this case the result set is not used. getIndexes(expire_time_, 0); }
void genBrowserPlugin(const std::string& uid, const std::string& path, QueryData& results) { Row r; pt::ptree tree; r["uid"] = uid; if (osquery::parsePlist(path + "/Contents/Info.plist", tree).ok()) { // Plugin did not include an Info.plist, or it was invalid for (const auto& it : kBrowserPluginKeys) { r[it.second] = tree.get(it.first, ""); // Convert bool-types to an integer. jsonBoolAsInt(r[it.second]); } } if (r.count("native") == 0 || r.at("native").size() == 0) { // The default case for native execution is false. r["native"] = "0"; } r["path"] = path; results.push_back(std::move(r)); }
void genFDEStatusForBSDName(const std::string& bsd_name, const std::string& uuid, QueryData& results) { auto matching_dict = IOBSDNameMatching(kIOMasterPortDefault, kNilOptions, bsd_name.c_str()); if (matching_dict == nullptr) { CFRelease(matching_dict); return; } auto service = IOServiceGetMatchingService(kIOMasterPortDefault, matching_dict); if (!service) { IOObjectRelease(service); return; } CFMutableDictionaryRef properties; IORegistryEntryCreateCFProperties( service, &properties, kCFAllocatorDefault, kNilOptions); Row r; r["name"] = kDeviceNamePrefix + bsd_name; r["uuid"] = uuid; auto encrypted = getIOKitProperty(properties, kCoreStorageIsEncryptedKey_); r["encrypted"] = (encrypted.empty()) ? "0" : encrypted; r["type"] = (r.at("encrypted") == "1") ? kEncryptionType : std::string(); results.push_back(r); CFRelease(properties); IOObjectRelease(service); }
void genControlInfo(const std::string& mib_path, QueryData& results, const std::map<std::string, std::string>& config) { if (isDirectory(mib_path).ok()) { // Iterate through the subitems and items. std::vector<std::string> items; if (listDirectoriesInDirectory(mib_path, items).ok()) { for (const auto& item : items) { genControlInfo(item, results, config); } } if (listFilesInDirectory(mib_path, items).ok()) { for (const auto& item : items) { genControlInfo(item, results, config); } } return; } // This is a file (leaf-control). Row r; r["name"] = mib_path.substr(kSystemControlPath.size()); std::replace(r["name"].begin(), r["name"].end(), '/', '.'); // No known way to convert name MIB to int array. r["subsystem"] = osquery::split(r.at("name"), ".")[0]; if (isReadable(mib_path).ok()) { std::string content; readFile(mib_path, content); boost::trim(content); r["current_value"] = content; } if (config.count(r.at("name")) > 0) { r["config_value"] = config.at(r.at("name")); } r["type"] = "string"; results.push_back(r); }
bool SelectionOperator::next() { assert(isOpen); while (in.next()) { Row input = in.getOutput(); if (*(input.at(index)) == constant) return true; } return false; }
void Config::recordQueryPerformance(const std::string& name, size_t delay, size_t size, const Row& r0, const Row& r1) { // Grab a lock on the schedule structure and check the name. ConfigDataInstance config; if (config.schedule().count(name) == 0) { // Unknown query schedule name. return; } // Grab access to the non-const schedule item. auto& query = getInstance().data_.schedule.at(name); auto diff = strtol(r1.at("user_time").c_str(), nullptr, 10) - strtol(r0.at("user_time").c_str(), nullptr, 10); query.user_time += diff; diff = strtol(r1.at("system_time").c_str(), nullptr, 10) - strtol(r0.at("system_time").c_str(), nullptr, 10); query.system_time += diff; diff = strtol(r1.at("resident_size").c_str(), nullptr, 10) - strtol(r0.at("resident_size").c_str(), nullptr, 10); // Memory is stored as an average of BSS changes between query executions. query.memory = (query.memory * query.executions + diff) / (query.executions + 1); query.wall_time += delay; query.output_size += size; query.executions += 1; }
//add database items into this tree, be careful! //input: the database items pointer p_d //retval: bool; true--> add all of them ok // false--> something goes wrong bool Tree:: addDatabaseItems( const Mat<double> * p_d ) { size_t r,c; TreeNode * current; Mat<double> result; r = p_d->n_rows; c = p_d->n_cols - 1 ; Row< double > tmp; for( size_t i = 0 ; i < r ; i ++ ) { tmp = p_d->row( i ); tmp.at( c ) = 1; //the x sample needs to become this x=[x0 x1 x2 ... 1 ] current = root; while( (current != NULL) && ( current->isInternal() )) { result = tmp * (current->intL).pvector->at(0) ; if( result.at( 0 ) > 0 ) { current = current->intL.left; }else { current = current->intL.right; } } if( NULL == current ) { cerr <<"current is null in the addDatabaseIntems()\b"; return false; } if( NULL == current->leafL.puivector ) { current->leafL.puivector = new vector< unsigned int >; } (current->leafL).puivector->push_back( i ); } return true; }
void genFDEStatusForBSDName(const std::string& bsd_name, const std::string& uuid, QueryData& results) { auto matching_dict = IOBSDNameMatching(kIOMasterPortDefault, kNilOptions, bsd_name.c_str()); if (matching_dict == nullptr) { return; } auto service = IOServiceGetMatchingService(kIOMasterPortDefault, matching_dict); if (!service) { return; } CFMutableDictionaryRef properties; if (IORegistryEntryCreateCFProperties( service, &properties, kCFAllocatorDefault, kNilOptions) != KERN_SUCCESS) { IOObjectRelease(service); return; } Row r; r["name"] = kDeviceNamePrefix + bsd_name; r["uuid"] = uuid; auto encrypted = getIOKitProperty(properties, kCoreStorageIsEncryptedKey_); if (encrypted.empty()) { r["encrypted"] = "0"; } else { r["encrypted"] = encrypted; id_t uid; uuid_string_t uuid_string = {0}; if (genUid(uid, uuid_string).ok()) { r["uid"] = BIGINT(uid); r["user_uuid"] = TEXT(uuid_string); } } r["type"] = (r.at("encrypted") == "1") ? kEncryptionType : std::string(); results.push_back(r); CFRelease(properties); IOObjectRelease(service); }
void genOSXPrefValues(const CFTypeRef& value, const Row& base, QueryData& results, size_t depth) { if (value == nullptr) { return; } // Since we recurse when parsing Arrays/Dicts, monitor stack limits. if (++depth > kPreferenceDepthLimit) { TLOG << "The macOS preference: " << base.at("domain") << " exceeded subkey depth limit: " << kPreferenceDepthLimit; return; } // Emit a string representation for each preference type. Row r = base; if (CFGetTypeID(value) == CFNumberGetTypeID()) { r["value"] = stringFromCFNumber(static_cast<CFDataRef>(value)); } else if (CFGetTypeID(value) == CFStringGetTypeID()) { r["value"] = stringFromCFString(static_cast<CFStringRef>(value)); } else if (CFGetTypeID(value) == CFDateGetTypeID()) { auto unix_time = CFDateGetAbsoluteTime(static_cast<CFDateRef>(value)) + kCFAbsoluteTimeIntervalSince1970; r["value"] = boost::lexical_cast<std::string>(std::llround(unix_time)); } else if (CFGetTypeID(value) == CFBooleanGetTypeID()) { r["value"] = (CFBooleanGetValue(static_cast<CFBooleanRef>(value)) == TRUE) ? "true" : "false"; } else if (CFGetTypeID(value) == CFDataGetTypeID()) { // Do not include data preferences. } else if (CFGetTypeID(value) == CFArrayGetTypeID()) { genOSXListPref(static_cast<CFArrayRef>(value), base, results, depth); return; } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) { // Generate a row for each hash key. TRowResults trow(base, results, depth); CFDictionaryApplyFunction( static_cast<CFDictionaryRef>(value), &genOSXHashPref, &trow); return; } results.push_back(std::move(r)); }
void genExtension(const std::string& path, QueryData& results) { std::string json_data; if (!readFile(path + kManifestFile, json_data).ok()) { VLOG(1) << "Could not read file: " << path + kManifestFile; return; } // Read the extensions data into a JSON blob, then property tree. pt::ptree tree; std::stringstream json_stream; json_stream << json_data; try { pt::read_json(json_stream, tree); } catch (const pt::json_parser::json_parser_error& e) { VLOG(1) << "Could not parse JSON from: " << path + kManifestFile; return; } Row r; // Most of the keys are in the top-level JSON dictionary. for (const auto& it : kExtensionKeys) { try { r[it.second] = tree.get<std::string>(it.first); } catch (const pt::ptree_error& e) { r[it.second] = ""; } // Convert JSON bool-types to an integer. if (r[it.second] == "true") { r[it.second] = INTEGER(1); } else if (r[it.second] == "false") { r[it.second] = INTEGER(0); } } // Set the default persistence setting to false if (r.at("persistent") == "") { r["persistent"] = INTEGER(0); } r["identifier"] = fs::path(path).parent_path().leaf().string(); r["path"] = path; results.push_back(r); }
void EventSubscriberPlugin::expireCheck() { auto db = DBHandle::getInstance(); auto data_key = "data." + dbNamespace(); auto eid_key = "eid." + dbNamespace(); std::vector<std::string> keys; db->ScanPrefix(kEvents, keys, data_key); if (keys.size() <= FLAGS_events_max) { return; } // There is an overflow of events buffered for this subscriber. LOG(WARNING) << "Expiring events for subscriber: " << getName() << " limit (" << FLAGS_events_max << ") exceeded: " << keys.size(); // Inspect the N-FLAGS_events_max -th event's value and expire before the // time within the content. std::string last_key; db->Get(kEvents, eid_key, last_key); // The EID is the next-index. size_t max_key = boost::lexical_cast<size_t>(last_key) - FLAGS_events_max - 1; // Convert the key index into a time using the content. std::string content; db->Get(kEvents, data_key + "." + std::to_string(max_key), content); // Decode the value into a row structure to extract the time. Row r; if (!deserializeRowJSON(content, r) || r.count("time") == 0) { return; } // The last time will become the implicit expiration time. size_t last_time = boost::lexical_cast<size_t>(r.at("time")); if (last_time > 0) { expire_time_ = last_time; } // Finally, attempt an index query to trigger expirations. // In this case the result set is not used. getIndexes(expire_time_ - 1, -1); }
void Config::recordQueryPerformance(const std::string& name, size_t delay, size_t size, const Row& r0, const Row& r1) { // Grab a lock on the schedule structure and check the name. ConfigDataInstance config; if (config.schedule().count(name) == 0) { // Unknown query schedule name. return; } // Grab access to the non-const schedule item. auto& query = getInstance().data_.schedule.at(name); auto diff = AS_LITERAL(BIGINT_LITERAL, r1.at("user_time")) - AS_LITERAL(BIGINT_LITERAL, r0.at("user_time")); if (diff > 0) { query.user_time += diff; } diff = AS_LITERAL(BIGINT_LITERAL, r1.at("system_time")) - AS_LITERAL(BIGINT_LITERAL, r0.at("system_time")); if (diff > 0) { query.system_time += diff; } diff = AS_LITERAL(BIGINT_LITERAL, r1.at("resident_size")) - AS_LITERAL(BIGINT_LITERAL, r0.at("resident_size")); if (diff > 0) { // Memory is stored as an average of RSS changes between query executions. query.average_memory = (query.average_memory * query.executions) + diff; query.average_memory = (query.average_memory / (query.executions + 1)); } query.wall_time += delay; query.output_size += size; query.executions += 1; }
void Config::recordQueryPerformance(const std::string& name, size_t delay, size_t size, const Row& r0, const Row& r1) { RecursiveLock lock(config_performance_mutex_); if (performance_.count(name) == 0) { performance_[name] = QueryPerformance(); } // Grab access to the non-const schedule item. auto& query = performance_.at(name); BIGINT_LITERAL diff = 0; if (!r1.at("user_time").empty() && !r0.at("user_time").empty()) { diff = AS_LITERAL(BIGINT_LITERAL, r1.at("user_time")) - AS_LITERAL(BIGINT_LITERAL, r0.at("user_time")); if (diff > 0) { query.user_time += diff; } } if (!r1.at("system_time").empty() && !r0.at("system_time").empty()) { diff = AS_LITERAL(BIGINT_LITERAL, r1.at("system_time")) - AS_LITERAL(BIGINT_LITERAL, r0.at("system_time")); if (diff > 0) { query.system_time += diff; } } if (!r1.at("resident_size").empty() && !r0.at("resident_size").empty()) { diff = AS_LITERAL(BIGINT_LITERAL, r1.at("resident_size")) - AS_LITERAL(BIGINT_LITERAL, r0.at("resident_size")); if (diff > 0) { // Memory is stored as an average of RSS changes between query executions. query.average_memory = (query.average_memory * query.executions) + diff; query.average_memory = (query.average_memory / (query.executions + 1)); } } query.wall_time += delay; query.output_size += size; query.executions += 1; query.last_executed = getUnixTime(); // Clear the executing query (remove the dirty bit). setDatabaseValue(kPersistentSettings, kExecutingQuery, ""); }
void genControlInfo(int* oid, size_t oid_size, QueryData& results, const std::map<std::string, std::string>& config) { Row r; if (oid_size == 0) { return; } r["oid"] = stringFromMIB(oid, oid_size); // Request the description (the canonical name) for the MIB. char response[CTL_MAX_VALUE] = {0}; size_t response_size = CTL_MAX_VALUE; int request[CTL_MAXNAME + 2] = {0, CTL_DEBUG_DESCRIPTION}; memcpy(request + 2, oid, oid_size * sizeof(int)); if (sysctl(request, oid_size + 2, response, &response_size, 0, 0) != 0) { return; } r["name"] = std::string(response); if (oid[0] > 0 && oid[0] < static_cast<int>(kControlNames.size())) { r["subsystem"] = kControlNames[oid[0]]; } // Now request structure type. request[1] = CTL_DEBUG_TYPE; if (sysctl(request, oid_size + 2, response, &response_size, 0, 0) != 0) { // Cannot request MIB type (int, string, struct, etc). return; } size_t oid_type = 0; if (response_size > 0) { oid_type = ((size_t)response[0] & CTLTYPE); if (oid_type < kControlTypes.size()) { r["type"] = kControlTypes[((int)response[0])]; } } // Finally request MIB value. if (oid_type > CTLTYPE_NODE && oid_type < CTLTYPE_OPAQUE) { size_t value_size = 0; sysctl(oid, oid_size, 0, &value_size, 0, 0); if (value_size > CTL_MAX_VALUE) { // If the value size is larger than the max value, limit. value_size = CTL_MAX_VALUE; } sysctl(oid, oid_size, response, &value_size, 0, 0); if (oid_type == CTLTYPE_INT) { unsigned int value; memcpy(&value, response, sizeof(int)); r["current_value"] = INTEGER(value); } else if (oid_type == CTLTYPE_STRING) { r["current_value"] = std::string(response); } else if (oid_type == CTLTYPE_QUAD) { unsigned long long value; memcpy(&value, response, value_size); } } // If this MIB was set using sysctl.conf add the value. if (config.count(r.at("name")) > 0) { r["config_value"] = config.at(r["name"]); } results.push_back(r); }
QueryData genDrivers(QueryContext& context) { QueryData results; auto devInfoset = setupDevInfoSet(); if (devInfoset == nullptr) { win32LogWARNING("Error getting device handle"); return results; } std::vector<SP_DEVINFO_DATA> devices; auto ret = getDeviceList(devInfoset, devices); if (!ret.ok()) { win32LogWARNING(ret.getMessage(), ret.getCode()); return results; } for (auto& device : devices) { char devId[MAX_DEVICE_ID_LEN] = {0}; if (CM_Get_Device_ID(device.DevInst, devId, MAX_DEVICE_ID_LEN, 0) != CR_SUCCESS) { win32LogWARNING("Failed to get device ID"); return QueryData(); } SP_DRVINFO_DATA drvInfo = {0}; SP_DRVINFO_DETAIL_DATA drvInfoDetail = {0}; ret = getDeviceDriverInfo(devInfoset, device, drvInfo, drvInfoDetail); Row r; r["device_id"] = devId; r["inf"] = drvInfoDetail.InfFileName; r["provider"] = drvInfo.ProviderName; r["manufacturer"] = drvInfo.MfgName; r["date"] = std::to_string(osquery::filetimeToUnixtime(drvInfo.DriverDate)); r["description"] = drvInfo.Description; ULARGE_INTEGER version; version.QuadPart = drvInfo.DriverVersion; r["version"] = std::to_string(HIWORD(version.HighPart)) + "." + std::to_string(HIWORD(version.LowPart)) + "." + std::to_string(LOWORD(version.HighPart)) + "." + std::to_string(LOWORD(version.LowPart)); for (const auto& elem : kAdditionalDeviceProps) { std::string val; ret = getDeviceProperty(devInfoset, device, elem.second, val); r[elem.first] = std::move(val); } if (r.count("driver_key") > 0) { if (!r.at("driver_key").empty()) { r["driver_key"].insert(0, kDriverKeyPath); } } if (r.count("service") > 0) { if (!r.at("service").empty()) { r["service_key"] = kServiceKeyPath + r["service"]; r["image"] = getDriverImagePath(r["service_key"]); } } results.push_back(r); } return results; }
void genControlInfo(int* oid, size_t oid_size, QueryData& results, const std::map<std::string, std::string>& config) { Row r; if (oid_size == 0) { return; } r["oid"] = stringFromMIB(oid, oid_size); // Request the description (the canonical name) for the MIB. char response[CTL_MAX_VALUE] = {0}; size_t response_size = CTL_MAX_VALUE; int request[CTL_MAXNAME + 2] = {0, CTL_DEBUG_DESCRIPTION}; memcpy(request + 2, oid, oid_size * sizeof(int)); if (sysctl(request, oid_size + 2, response, &response_size, 0, 0) != 0) { return; } r["name"] = std::string(response); if (oid[0] > 0 && oid[0] < static_cast<int>(kControlNames.size())) { r["subsystem"] = kControlNames[oid[0]]; } // Now request structure type. response_size = CTL_MAX_VALUE; request[1] = CTL_DEBUG_TYPE; if (sysctl(request, oid_size + 2, response, &response_size, 0, 0) != 0) { // Cannot request MIB type (int, string, struct, etc). return; } size_t oid_type = 0; if (response_size > 0) { oid_type = ((size_t)response[0] & CTLTYPE); if ((oid_type == 0 || oid_type == CTLTYPE_INT) && response_size > 4) { // For whatever reason, macOS defines fewer CTLTYPE's than BSD, and // sometimes uses the format character instead of (or in addition to) // the CTLTYPE to specify the type. Here we detect a few such cases and // map them to CTLTYPE's. // TODO: Both CTLTYPE_INT and CTLTYPE_QUAD can be specified as unsigned // using a similar method. char type_char = response[4]; switch (type_char) { case 'I': oid_type = CTLTYPE_INT; break; case 'L': if (sizeof(long) == sizeof(long long)) { oid_type = CTLTYPE_QUAD; } else if (sizeof(long) == sizeof(int)) { oid_type = CTLTYPE_INT; } break; case 'S': oid_type = CTLTYPE_STRUCT; break; case 'Q': oid_type = CTLTYPE_QUAD; break; // Otherwise leave the type as it was; we have no additional knowledge } } if (oid_type < kControlTypes.size()) { r["type"] = kControlTypes[oid_type]; } } // Finally request MIB value. if (oid_type > CTLTYPE_NODE && oid_type < CTLTYPE_OPAQUE) { size_t value_size = 0; sysctl(oid, oid_size, 0, &value_size, 0, 0); if (value_size > CTL_MAX_VALUE) { // If the value size is larger than the max value, limit. value_size = CTL_MAX_VALUE; } sysctl(oid, oid_size, response, &value_size, 0, 0); if (oid_type == CTLTYPE_INT) { unsigned int value; memcpy(&value, response, sizeof(int)); r["current_value"] = INTEGER(value); } else if (oid_type == CTLTYPE_STRING) { r["current_value"] = std::string(response); } else if (oid_type == CTLTYPE_QUAD) { unsigned long long value; memcpy(&value, response, sizeof(unsigned long long)); r["current_value"] = INTEGER(value); } } // If this MIB was set using sysctl.conf add the value. if (config.count(r.at("name")) > 0) { r["config_value"] = config.at(r["name"]); } results.push_back(r); }
void genOSXDomainPrefs(const CFStringRef& username, const CFStringRef& domain, bool current_host, QueryData& results) { const auto* user = (username != nullptr) ? &username : (isUserAdmin()) ? &kCFPreferencesAnyUser : &kCFPreferencesCurrentUser; const auto* host = (current_host) ? &kCFPreferencesCurrentHost : &kCFPreferencesAnyHost; auto keys = CFPreferencesCopyKeyList(domain, *user, *host); if (keys == nullptr) { return; } auto values = CFPreferencesCopyMultiple(keys, domain, *user, *host); if (values == nullptr) { CFRelease(keys); return; } std::string username_string; if (username != nullptr) { username_string = stringFromCFString(username); } // Iterate over each preference domain's preference name. for (CFIndex j = 0; j < CFArrayGetCount(keys); ++j) { Row r; r["username"] = username_string; r["host"] = (current_host) ? "current" : "any"; r["domain"] = stringFromCFString(domain); auto key = static_cast<CFStringRef>(CFArrayGetValueAtIndex(keys, j)); if (CFStringGetTypeID() != CFGetTypeID(key)) { continue; } // Interesting results/behavior from Microsoft products. r["key"] = stringFromCFString(key); if (r.at("key").find('>') != std::string::npos || r.at("key").find('<') != std::string::npos || r.at("key").size() == 0) { continue; } // Check if the preference key is managed by a profile. auto forced = CFPreferencesAppValueIsForced(key, domain); r["forced"] = (forced) ? '1' : '0'; CFTypeRef value = nullptr; if (forced) { value = static_cast<CFTypeRef>(CFPreferencesCopyAppValue(key, domain)); } else { // Check the key and key type (which may be any CF type). value = static_cast<CFTypeRef>(CFDictionaryGetValue(values, key)); } genOSXPrefValues(value, r, results, 0); if (forced) { CFRelease(value); } } CFRelease(values); CFRelease(keys); }
void genIOMediaDevice(const io_service_t& device, std::vector<std::string>& whole_devices, QueryData& results) { Row r; // Get the device properties CFMutableDictionaryRef properties; IORegistryEntryCreateCFProperties( device, &properties, kCFAllocatorDefault, kNilOptions); r["uuid"] = getIOKitProperty(properties, "UUID"); r["name"] = "/dev/" + getIOKitProperty(properties, "BSD Name"); r["size"] = getIOKitProperty(properties, "Size"); auto type = getIOKitProperty(properties, "Whole"); if (type == "1") { // The "Whole" property applies to the entire disk entry, not partitions. whole_devices.push_back(r["name"]); } else { // Otherwise search the list of whole disks to find the node parent. for (const auto& parent : whole_devices) { if (r.at("name").find(parent) == 0) { r["parent"] = parent; } } } // This is the IOKit name, which is the device's label. io_name_t name; auto kr = IORegistryEntryGetName(device, name); if (kr == KERN_SUCCESS && (char*)name != nullptr) { r["label"] = std::string(name); } // Remaining details come from the Disk Arbitration service. DASessionRef session = DASessionCreate(kCFAllocatorDefault); CFDictionaryRef details; if (session != nullptr) { auto disk = DADiskCreateFromIOMedia(kCFAllocatorDefault, session, device); if (disk != nullptr) { details = DADiskCopyDescription(disk); if (details != nullptr) { r["vendor"] = getIOKitProperty((CFMutableDictionaryRef)details, "DADeviceVendor"); r["model"] = getIOKitProperty((CFMutableDictionaryRef)details, "DADeviceModel"); r["type"] = getIOKitProperty((CFMutableDictionaryRef)details, "DADeviceProtocol"); CFRelease(details); } CFRelease(disk); } CFRelease(session); } results.push_back(r); CFRelease(properties); }