Coverage Report

Created: 2025-01-09 07:36

/src/osquery/plugins/config/parsers/auto_constructed_tables.cpp
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * Copyright (c) 2014-present, The osquery authors
3
 *
4
 * This source code is licensed as defined by the LICENSE file found in the
5
 * root directory of this source tree.
6
 *
7
 * SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
8
 */
9
10
#include <boost/algorithm/string.hpp>
11
12
#include <osquery/config/config.h>
13
#include <osquery/core/system.h>
14
#include <osquery/core/tables.h>
15
#include <osquery/database/database.h>
16
#include <osquery/filesystem/filesystem.h>
17
#include <osquery/logger/logger.h>
18
#include <osquery/registry/registry_factory.h>
19
#include <osquery/sql/sql.h>
20
#include <osquery/sql/sqlite_util.h>
21
#include <osquery/utils/conversions/join.h>
22
#include <plugins/config/parsers/auto_constructed_tables.h>
23
24
namespace rj = rapidjson;
25
26
namespace osquery {
27
28
490
TableAttributes ATCPlugin::attributes() const {
29
490
  return table_attributes_;
30
490
}
31
32
612
void ATCPlugin::setActive() {
33
612
  table_attributes_ = TableAttributes::NONE;
34
612
}
35
36
0
TableRows ATCPlugin::generate(QueryContext& context) {
37
0
  TableRows result;
38
0
  std::vector<std::string> paths;
39
0
  auto s = resolveFilePattern(path_, paths);
40
0
  if (!s.ok()) {
41
0
    LOG(WARNING) << "ATC Table: Could not glob: " << path_ << " skipping";
42
0
    return result;
43
0
  }
44
0
  for (const auto& path : paths) {
45
0
    s = getSqliteJournalMode(path);
46
0
    bool preserve_locking = false;
47
0
    if (!s.ok()) {
48
0
      VLOG(1) << "ATC Table: Unable to detect journal mode, applying default "
49
0
                 "locking policy"
50
0
              << " for path " << path;
51
0
    } else {
52
0
      preserve_locking = s.getMessage() == "wal";
53
0
    }
54
0
    s = genTableRowsForSqliteTable(
55
0
        path, sqlite_query_, result, preserve_locking);
56
0
    if (!s.ok()) {
57
0
      LOG(WARNING) << "ATC Table: Error Code: " << s.getCode()
58
0
                   << " Could not generate data: " << s.getMessage()
59
0
                   << " for path " << path_;
60
0
    }
61
0
  }
62
0
  return result;
63
0
}
64
65
/// Remove these ATC tables from the registry and database
66
Status ATCConfigParserPlugin::removeATCTables(
67
1.54k
    const std::set<std::string>& detach_tables) {
68
1.54k
  auto registry_table = RegistryFactory::get().registry("table");
69
1.54k
  std::set<std::string> failed_tables;
70
1.62k
  for (const auto& table : detach_tables) {
71
1.62k
    if (registry_table->exists(table)) {
72
1.00k
      std::string value;
73
1.00k
      if (getDatabaseValue(
74
1.00k
              kPersistentSettings, kDatabaseKeyPrefix + table, value)
75
1.00k
              .ok()) {
76
80
        registry_table->remove(table);
77
80
        PluginResponse resp;
78
80
        Registry::call(
79
80
            "sql", "sql", {{"action", "detatch"}, {"table", table}}, resp);
80
80
        VLOG(1) << "ATC table: " << table << " Removed";
81
929
      } else {
82
929
        failed_tables.insert(table);
83
929
      }
84
1.00k
    }
85
1.62k
    deleteDatabaseValue(kPersistentSettings, kDatabaseKeyPrefix + table);
86
1.62k
  }
87
1.54k
  if (failed_tables.empty()) {
88
616
    return Status();
89
616
  }
90
929
  return Status(
91
929
      1, "Attempted to remove non ATC tables: " + join(failed_tables, ", "));
92
1.54k
}
93
94
/// Get all ATC tables that should be registered from the database
95
184
std::set<std::string> ATCConfigParserPlugin::registeredATCTables() {
96
184
  std::vector<std::string> tables;
97
184
  scanDatabaseKeys(kPersistentSettings, tables, kDatabaseKeyPrefix);
98
184
  std::set<std::string> set_tables;
99
100
184
  for (const auto& table : tables) {
101
152
    set_tables.insert(table.substr(kDatabaseKeyPrefix.size()));
102
152
  }
103
184
  return set_tables;
104
184
}
105
106
0
Status ATCConfigParserPlugin::setUp() {
107
0
  VLOG(1) << "Removing stale ATC entries";
108
0
  std::vector<std::string> keys;
109
0
  scanDatabaseKeys(kPersistentSettings, keys, kDatabaseKeyPrefix);
110
0
  for (const auto& key : keys) {
111
0
    auto s = deleteDatabaseValue(kPersistentSettings, key);
112
0
    if (!s.ok()) {
113
0
      LOG(INFO) << "ATC table: Could not clear ATC key " << key
114
0
                << "from database";
115
0
    }
116
0
  }
117
0
  return Status();
118
0
}
119
120
Status ATCConfigParserPlugin::update(const std::string& source,
121
57.1k
                                     const ParserConfig& config) {
122
57.1k
  auto cv = config.find(kParserKey);
123
57.1k
  if (cv == config.end() || !cv->second.doc().IsObject()) {
124
56.9k
    return Status::success();
125
56.9k
  }
126
127
184
  {
128
184
    auto doc = JSON::newObject();
129
184
    auto obj = doc.getObject();
130
184
    doc.copyFrom(cv->second.doc(), obj);
131
184
    doc.add(kParserKey, obj);
132
184
    data_ = std::move(doc);
133
184
  }
134
135
184
  const auto& ac_tables = data_.doc()[kParserKey];
136
184
  auto tables = RegistryFactory::get().registry("table");
137
184
  auto registered = registeredATCTables();
138
139
2.61k
  for (const auto& ac_table : ac_tables.GetObject()) {
140
2.61k
    if (!ac_table.name.IsString() || !ac_table.value.IsObject()) {
141
      // This entry is not formatted correctly.
142
159
      continue;
143
159
    }
144
145
2.45k
    std::string table_name{ac_table.name.GetString()};
146
2.45k
    auto params = ac_table.value.GetObject();
147
148
2.45k
    std::string query{params.HasMember("query") && params["query"].IsString()
149
2.45k
                          ? params["query"].GetString()
150
2.45k
                          : ""};
151
2.45k
    std::string path{params.HasMember("path") && params["path"].IsString()
152
2.45k
                         ? params["path"].GetString()
153
2.45k
                         : ""};
154
2.45k
    std::string platform{params.HasMember("platform") &&
155
2.45k
                                 params["platform"].IsString()
156
2.45k
                             ? params["platform"].GetString()
157
2.45k
                             : ""};
158
159
2.45k
    if (query.empty() || path.empty()) {
160
451
      LOG(WARNING) << "ATC Table: Skipping " << table_name
161
451
                   << " because it is misconfigured (missing query or path)";
162
451
      continue;
163
451
    }
164
165
2.00k
    if (!checkPlatform(platform)) {
166
0
      VLOG(1) << "ATC table: Skipping " << table_name
167
0
              << " because platform doesn't match";
168
0
      continue;
169
0
    }
170
171
2.00k
    TableColumns columns;
172
2.00k
    std::string columns_value;
173
2.00k
    columns_value.reserve(256);
174
175
2.00k
    if (!params.HasMember("columns") || !params["columns"].IsArray()) {
176
30
      LOG(WARNING) << "ATC Table: Skipping " << table_name
177
30
                   << " because it is misconfigured (no columns)";
178
30
      continue;
179
30
    }
180
181
1.97k
    std::string user_defined_path_column;
182
183
2.58k
    for (const auto& column : params["columns"].GetArray()) {
184
2.58k
      if (!column.IsString()) {
185
3
        LOG(WARNING) << "ATC Table: " << table_name
186
3
                     << " is misconfigured. (non-string column)";
187
3
        continue;
188
3
      }
189
190
2.58k
      if (boost::iequals(column.GetString(), "path")) {
191
1.03k
        user_defined_path_column = column.GetString();
192
1.03k
      }
193
194
2.58k
      columns.push_back(make_tuple(
195
2.58k
          std::string(column.GetString()), TEXT_TYPE, ColumnOptions::DEFAULT));
196
2.58k
      columns_value += std::string(column.GetString()) + ",";
197
2.58k
    }
198
199
1.97k
    if (!user_defined_path_column.empty()) {
200
516
      LOG(WARNING) << "ATC Table: " << table_name
201
516
                   << " is misconfigured. The configuration includes `"
202
516
                   << user_defined_path_column
203
516
                   << "`. This is a reserved column name";
204
1.45k
    } else {
205
      // Add implicit path column
206
1.45k
      columns.push_back(
207
1.45k
          make_tuple(std::string("path"), TEXT_TYPE, ColumnOptions::DEFAULT));
208
1.45k
      columns_value += "path,";
209
1.45k
    }
210
211
1.97k
    registered.erase(table_name);
212
1.97k
    std::string table_settings{table_name + query + columns_value + path};
213
1.97k
    std::string old_setting;
214
1.97k
    auto s = getDatabaseValue(
215
1.97k
        kPersistentSettings, kDatabaseKeyPrefix + table_name, old_setting);
216
217
    // The ATC table hasn't changed so we skip ahead
218
1.97k
    if (table_settings == old_setting) {
219
429
      continue;
220
429
    }
221
222
    // Remove the old table to replace with the new one
223
1.54k
    s = removeATCTables({table_name});
224
1.54k
    if (!s.ok()) {
225
929
      LOG(WARNING) << "ATC Table: " << table_name
226
929
                   << " overrides core table; Refusing registration";
227
929
      continue;
228
929
    }
229
230
612
    s = setDatabaseValue(
231
612
        kPersistentSettings, kDatabaseKeyPrefix + table_name, table_settings);
232
612
    if (!s.ok()) {
233
0
      LOG(WARNING) << "ATC Table: " << table_name
234
0
                   << " could not write to database";
235
0
      continue;
236
0
    }
237
238
612
    auto plugin = std::make_shared<ATCPlugin>(path, columns, query);
239
612
    s = tables->add(table_name, plugin, true);
240
612
    if (!s.ok()) {
241
0
      LOG(WARNING) << "ATC Table: " << table_name << ": " << s.getMessage();
242
0
      deleteDatabaseValue(kPersistentSettings, kDatabaseKeyPrefix + table_name);
243
0
      continue;
244
0
    }
245
246
612
    PluginResponse resp;
247
612
    s = Registry::call(
248
612
        "sql", "sql", {{"action", "attach"}, {"table", table_name}}, resp);
249
612
    if (!s.ok()) {
250
532
      LOG(WARNING) << "ATC Table: " << table_name << ": " << s.getMessage();
251
532
      deleteDatabaseValue(kPersistentSettings, kDatabaseKeyPrefix + table_name);
252
532
    }
253
612
    plugin->setActive();
254
255
612
    LOG(INFO) << "ATC table: " << table_name << " Registered";
256
612
  }
257
258
184
  if (registered.size() > 0) {
259
4
    VLOG(1)
260
0
        << "Removing any ATC tables that were removed in this configuration "
261
0
           "change";
262
4
    removeATCTables(registered);
263
4
  }
264
184
  return Status();
265
57.1k
}
266
267
REGISTER_INTERNAL(ATCConfigParserPlugin,
268
                  "config_parser",
269
                  "auto_constructed_tables");
270
} // namespace osquery