mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-08 02:18:53 +00:00
202 lines
6.5 KiB
Plaintext
202 lines
6.5 KiB
Plaintext
/*
|
|
* Copyright (c) 2014-present, 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 <sstream>
|
|
|
|
#import <Foundation/Foundation.h>
|
|
|
|
#include <boost/filesystem/path.hpp>
|
|
#include <boost/property_tree/json_parser.hpp>
|
|
|
|
#include <osquery/filesystem.h>
|
|
#include <osquery/logger.h>
|
|
|
|
namespace fs = boost::filesystem;
|
|
namespace pt = boost::property_tree;
|
|
|
|
namespace osquery {
|
|
|
|
/**
|
|
* @brief Filter selected data types from deserialized property list.
|
|
*
|
|
* Since a property list supports a superset of JSON types we must pass
|
|
* through each type, focusing on the dictionary and list containers, to
|
|
* cherry-pick JSON-supported types. The resultant boost property tree will
|
|
* serialize/deserialize into JSON. It is the most used data type in osquery.
|
|
*
|
|
* The filter functions support:
|
|
* NSString, NSDate, NSNumber, YES, NO, NSArray, and NSDictionary.
|
|
* For NSData the result will be base64 encoded as a string type.
|
|
*/
|
|
static Status filterDictionary(id plist,
|
|
const std::string& root,
|
|
pt::ptree& tree);
|
|
|
|
/// See filterDictionary, the mimicked logic with a anonymous key more or less.
|
|
static Status filterArray(id plist, const std::string& root, pt::ptree& tree);
|
|
|
|
static inline std::string getValue(id value) {
|
|
if ([value isKindOfClass:[NSString class]]) {
|
|
return [value UTF8String];
|
|
} else if ([value isKindOfClass:[NSNumber class]]) {
|
|
return [[value stringValue] UTF8String];
|
|
} else if ([value isKindOfClass:[NSData class]]) {
|
|
NSString* dataString = [value base64EncodedStringWithOptions:0];
|
|
return [dataString UTF8String];
|
|
} else if ([value isKindOfClass:[NSDate class]]) {
|
|
NSNumber* seconds =
|
|
[[NSNumber alloc] initWithDouble:[value timeIntervalSince1970]];
|
|
return [[seconds stringValue] UTF8String];
|
|
} else if ([value isEqual:@(YES)]) {
|
|
return "true";
|
|
} else if ([value isEqual:@(NO)]) {
|
|
return "false";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static Status filterDictionary(id plist,
|
|
const std::string& root,
|
|
pt::ptree& tree) {
|
|
Status total_status = Status(0, "OK");
|
|
for (id key in [plist allKeys]) {
|
|
if (key == nil || ![key isKindOfClass:[NSString class]]) {
|
|
// Unknown type as dictionary key, most likely a malformed plist.
|
|
continue;
|
|
}
|
|
|
|
id value = [plist objectForKey:key];
|
|
if (value == nil) {
|
|
continue;
|
|
}
|
|
|
|
auto path_node = std::string([key UTF8String]);
|
|
if ([value isKindOfClass:[NSArray class]]) {
|
|
auto status = filterArray(value, path_node, tree);
|
|
if (!status.ok()) {
|
|
total_status = status;
|
|
}
|
|
} else if ([value isKindOfClass:[NSDictionary class]]) {
|
|
pt::ptree child;
|
|
auto status = filterDictionary(value, "", child);
|
|
if (!status.ok()) {
|
|
total_status = status;
|
|
}
|
|
tree.push_back(pt::ptree::value_type(path_node, std::move(child)));
|
|
} else {
|
|
tree.push_back(
|
|
pt::ptree::value_type(path_node, pt::ptree(getValue(value))));
|
|
}
|
|
}
|
|
return total_status;
|
|
}
|
|
|
|
static Status filterArray(id plist, const std::string& root, pt::ptree& tree) {
|
|
Status total_status = Status(0, "OK");
|
|
pt::ptree child_tree;
|
|
for (id value in plist) {
|
|
if (value == nil) {
|
|
continue;
|
|
}
|
|
|
|
pt::ptree child;
|
|
if ([value isKindOfClass:[NSArray class]]) {
|
|
auto status = filterArray(value, "", child);
|
|
if (!status.ok()) {
|
|
total_status = status;
|
|
}
|
|
} else if ([value isKindOfClass:[NSDictionary class]]) {
|
|
auto status = filterDictionary(value, "", child);
|
|
if (!status.ok()) {
|
|
total_status = status;
|
|
}
|
|
} else {
|
|
child.put_value(getValue(value));
|
|
}
|
|
child_tree.push_back(std::make_pair("", std::move(child)));
|
|
}
|
|
tree.push_back(pt::ptree::value_type(root, std::move(child_tree)));
|
|
return total_status;
|
|
}
|
|
|
|
static inline Status filterPlist(NSData* plist, pt::ptree& tree) {
|
|
if ([plist isKindOfClass:[NSDictionary class]]) {
|
|
return filterDictionary((NSMutableDictionary*)plist, "", tree);
|
|
} else {
|
|
return filterArray((NSMutableArray*)plist, "root", tree);
|
|
}
|
|
return Status(0, "OK");
|
|
}
|
|
|
|
Status parsePlistContent(const std::string& content, pt::ptree& tree) {
|
|
tree.clear();
|
|
@autoreleasepool {
|
|
id data = [NSData dataWithBytes:content.c_str() length:content.size()];
|
|
if (data == nil) {
|
|
return Status(1, "Unable to create plist content");
|
|
}
|
|
|
|
// Read file content into a data object, containing potential plist data.
|
|
NSError* error = nil;
|
|
id plist_data = [NSPropertyListSerialization
|
|
propertyListWithData:data
|
|
options:NSPropertyListImmutable
|
|
format:NULL
|
|
error:&error];
|
|
if (plist_data == nil) {
|
|
std::string error_message([[error localizedFailureReason] UTF8String]);
|
|
VLOG(1) << error_message;
|
|
return Status(1, error_message);
|
|
}
|
|
// Parse the plist data into a core foundation dictionary-literal.
|
|
return filterPlist(plist_data, tree);
|
|
}
|
|
}
|
|
|
|
Status parsePlist(const fs::path& path, pt::ptree& tree) {
|
|
tree.clear();
|
|
// Drop privileges, if needed, before parsing plist data.
|
|
auto dropper = DropPrivileges::get();
|
|
dropper->dropToParent(path);
|
|
|
|
auto status = readFile(path);
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
|
|
@autoreleasepool {
|
|
id ns_path = [NSString stringWithUTF8String:path.string().c_str()];
|
|
id stream = [NSInputStream inputStreamWithFileAtPath:ns_path];
|
|
if (stream == nil) {
|
|
return Status(1, "Unable to read plist: " + path.string());
|
|
}
|
|
|
|
// Read file content into a data object, containing potential plist data.
|
|
NSError* error = nil;
|
|
[stream open];
|
|
id plist_data = [NSPropertyListSerialization propertyListWithStream:stream
|
|
options:0
|
|
format:NULL
|
|
error:&error];
|
|
if (plist_data == nil) {
|
|
// The most common error is lack of read permissions.
|
|
std::string error_message([[error localizedFailureReason] UTF8String]);
|
|
VLOG(1) << error_message;
|
|
[stream close];
|
|
return Status(1, error_message);
|
|
}
|
|
// Parse the plist data into a core foundation dictionary-literal.
|
|
status = filterPlist(plist_data, tree);
|
|
[stream close];
|
|
}
|
|
return status;
|
|
}
|
|
}
|