/* * 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 #include #include #include #include #include #include #include #include #include namespace pt = boost::property_tree; using osquery::Status; namespace osquery { ///////////////////////////////////////////////////////////////////////////// // Row - the representation of a row in a set of database results. Row is a // simple map where individual column names are keys, which map to the Row's // respective value ///////////////////////////////////////////////////////////////////////////// void escapeQueryData(const QueryData &oldData, QueryData &newData) { for (const auto& r : oldData) { Row newRow; for (auto& i : r) { newRow[i.first] = pt::json_parser::create_escapes(i.second); } newData.push_back(newRow); } } Status serializeRow(const Row& r, pt::ptree& tree) { try { for (auto& i : r) { tree.put(i.first, i.second); } } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } Status serializeRowJSON(const Row& r, std::string& json) { pt::ptree tree; try { auto status = serializeRow(r, tree); if (!status.ok()) { return status; } std::ostringstream ss; pt::write_json(ss, tree, false); json = ss.str(); } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } Status deserializeRow(const pt::ptree& tree, Row& r) { try { for (auto& i : tree) { if (i.first.length() > 0) { r[i.first] = i.second.data(); } } return Status(0, "OK"); } catch (const std::exception& e) { LOG(ERROR) << e.what(); return Status(1, e.what()); } } Status deserializeRowJSON(const std::string& json, Row& r) { pt::ptree tree; try { std::stringstream j; j << json; pt::read_json(j, tree); } catch (const std::exception& e) { return Status(1, e.what()); } return deserializeRow(tree, r); } ///////////////////////////////////////////////////////////////////////////// // QueryData - the representation of a database query result set. It's a // vector of rows ///////////////////////////////////////////////////////////////////////////// Status serializeQueryData(const QueryData& q, pt::ptree& tree) { try { for (const auto& r : q) { pt::ptree serialized; auto s = serializeRow(r, serialized); if (!s.ok()) { return s; } tree.push_back(std::make_pair("", serialized)); } } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } ///////////////////////////////////////////////////////////////////////////// // DiffResults - the representation of two diffed QueryData result sets. // Given and old and new QueryData, DiffResults indicates the "added" subset // of rows and the "removed" subset of Rows ///////////////////////////////////////////////////////////////////////////// Status serializeDiffResults(const DiffResults& d, pt::ptree& tree) { try { pt::ptree added; auto added_status = serializeQueryData(d.added, added); if (!added_status.ok()) { return added_status; } tree.add_child("added", added); pt::ptree removed; auto removed_status = serializeQueryData(d.removed, removed); if (!removed_status.ok()) { return removed_status; } tree.add_child("removed", removed); } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } Status serializeDiffResultsJSON(const DiffResults& d, std::string& json) { try { pt::ptree tree; auto s = serializeDiffResults(d, tree); if (!s.ok()) { return s; } std::ostringstream ss; pt::write_json(ss, tree, false); json = ss.str(); } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } DiffResults diff(const QueryData& old_, const QueryData& new_) { DiffResults r; QueryData overlap; for (const auto& i : new_) { auto item = std::find(old_.begin(), old_.end(), i); if (item != old_.end()) { overlap.push_back(i); } else { r.added.push_back(i); } } std::multiset overlap_set(overlap.begin(), overlap.end()); std::multiset old_set(old_.begin(), old_.end()); std::set_difference(old_set.begin(), old_set.end(), overlap_set.begin(), overlap_set.end(), std::back_inserter(r.removed)); return r; } ///////////////////////////////////////////////////////////////////////////// // HistoricalQueryResults - the representation of the historical results of // a particlar scheduled database query. ///////////////////////////////////////////////////////////////////////////// Status serializeHistoricalQueryResultsJSON(const HistoricalQueryResults& r, std::string& json) { try { pt::ptree tree; auto s = serializeHistoricalQueryResults(r, tree); if (!s.ok()) { return s; } std::ostringstream ss; pt::write_json(ss, tree, false); json = ss.str(); } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } Status serializeHistoricalQueryResults(const HistoricalQueryResults& r, pt::ptree& tree) { try { pt::ptree mostRecentResults; pt::ptree most_recent_serialized; auto mrr_status = serializeQueryData(r.mostRecentResults.second, most_recent_serialized); if (!mrr_status.ok()) { return mrr_status; } mostRecentResults.add_child( boost::lexical_cast(r.mostRecentResults.first), most_recent_serialized); tree.add_child("mostRecentResults", mostRecentResults); } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } Status deserializeHistoricalQueryResults(const pt::ptree& tree, HistoricalQueryResults& r) { try { for (const auto& v : tree.get_child("mostRecentResults")) { try { int execution = boost::lexical_cast(v.first); r.mostRecentResults.first = execution; } catch (const boost::bad_lexical_cast& e) { return Status(1, e.what()); } QueryData q; for (const auto& each : v.second) { Row row_; for (const auto& item : each.second) { row_[item.first] = item.second.get_value(); } q.push_back(row_); } r.mostRecentResults.second = q; } return Status(0, "OK"); } catch (const std::exception& e) { LOG(ERROR) << e.what(); return Status(1, e.what()); } } Status deserializeHistoricalQueryResultsJSON(const std::string& json, HistoricalQueryResults& r) { pt::ptree tree; try { std::stringstream j; j << json; pt::read_json(j, tree); } catch (const std::exception& e) { return Status(1, e.what()); } return deserializeHistoricalQueryResults(tree, r); } ///////////////////////////////////////////////////////////////////////////// // ScheduledQueryLogItem - the representation of a log result occuring when a // scheduled query yields operating system state change. ///////////////////////////////////////////////////////////////////////////// Status serializeScheduledQueryLogItem(const ScheduledQueryLogItem& i, boost::property_tree::ptree& tree) { try { pt::ptree diffResults; auto diff_results_status = serializeDiffResults(i.diffResults, diffResults); if (!diff_results_status.ok()) { return diff_results_status; } tree.add_child("diffResults", diffResults); tree.put("name", i.name); tree.put("hostIdentifier", i.hostIdentifier); tree.put("calendarTime", i.calendarTime); tree.put("unixTime", i.unixTime); } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } Status serializeEvent(const ScheduledQueryLogItem& item, const boost::property_tree::ptree& event, boost::property_tree::ptree& tree) { tree.put("name", item.name); tree.put("hostIdentifier", item.hostIdentifier); tree.put("calendarTime", item.calendarTime); tree.put("unixTime", item.unixTime); pt::ptree columns; for (auto& i : event) { // Yield results as a "columns." map to avoid namespace collisions. columns.put(i.first, i.second.get_value()); } tree.add_child("columns", columns); return Status(0, "OK"); } Status serializeScheduledQueryLogItemAsEvents( const ScheduledQueryLogItem& item, boost::property_tree::ptree& tree) { try { pt::ptree diff_results; auto status = serializeDiffResults(item.diffResults, diff_results); if (!status.ok()) { return status; } for (auto& i : diff_results) { for (auto& j : i.second) { pt::ptree event; serializeEvent(item, j.second, event); event.put("action", i.first); tree.push_back(std::make_pair("", event)); } } } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } Status serializeScheduledQueryLogItemAsEventsJSON( const ScheduledQueryLogItem& i, std::string& json) { try { pt::ptree tree; auto s = serializeScheduledQueryLogItemAsEvents(i, tree); if (!s.ok()) { return s; } std::ostringstream ss; for (auto& event : tree) { pt::write_json(ss, event.second, false); } json = ss.str(); } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } Status serializeScheduledQueryLogItemJSON(const ScheduledQueryLogItem& i, std::string& json) { try { pt::ptree tree; auto s = serializeScheduledQueryLogItem(i, tree); if (!s.ok()) { return s; } std::ostringstream ss; pt::write_json(ss, tree, false); json = ss.str(); } catch (const std::exception& e) { return Status(1, e.what()); } return Status(0, "OK"); } bool addUniqueRowToQueryData(QueryData& q, const Row& r) { if (std::find(q.begin(), q.end(), r) != q.end()) { return false; } q.push_back(r); return true; } }