/src/osquery/osquery/tables/system/posix/crontab.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 <vector> |
11 | | |
12 | | #include <boost/algorithm/string/trim.hpp> |
13 | | |
14 | | #include <osquery/core/core.h> |
15 | | #include <osquery/core/tables.h> |
16 | | #include <osquery/filesystem/filesystem.h> |
17 | | #include <osquery/logger/logger.h> |
18 | | #include <osquery/utils/conversions/split.h> |
19 | | #include <osquery/worker/ipc/platform_table_container_ipc.h> |
20 | | #include <osquery/worker/logging/glog/glog_logger.h> |
21 | | |
22 | | namespace osquery { |
23 | | namespace tables { |
24 | | |
25 | | const std::string kSystemCron = "/etc/crontab"; |
26 | | |
27 | | const std::vector<std::string> kCronSearchDirs = { |
28 | | "/etc/cron.d/", // system all |
29 | | "/var/at/tabs/", // user mac:lion |
30 | | "/var/spool/cron/", // user linux:centos |
31 | | "/var/spool/cron/crontabs/", // user linux:debian |
32 | | }; |
33 | | |
34 | 6 | std::vector<std::string> cronFromFile(const std::string& path, Logger& logger) { |
35 | 6 | std::string content; |
36 | 6 | std::vector<std::string> cron_lines; |
37 | 6 | if (!isReadable(path).ok()) { |
38 | 3 | return cron_lines; |
39 | 3 | } |
40 | | |
41 | 3 | auto s = readFile(path, content); |
42 | 3 | if (!s.ok()) { |
43 | 0 | logger.log(google::GLOG_WARNING, s.getMessage()); |
44 | 0 | return cron_lines; |
45 | 0 | } |
46 | | |
47 | 3 | auto lines = split(content, "\n"); |
48 | | |
49 | | // Only populate the lines that are not comments or blank. |
50 | 6 | for (auto& line : lines) { |
51 | | // Cheat and use a non-const iteration, to inline trim. |
52 | 6 | boost::trim(line); |
53 | 6 | if (line.size() > 0 && line.at(0) != '#') { |
54 | 6 | cron_lines.push_back(line); |
55 | 6 | } |
56 | 6 | } |
57 | | |
58 | 3 | return cron_lines; |
59 | 3 | } |
60 | | |
61 | | void genCronLine(const std::string& path, |
62 | | const std::string& line, |
63 | 6 | QueryData& results) { |
64 | 6 | Row r; |
65 | | |
66 | 6 | r["path"] = path; |
67 | 6 | auto columns = split(line, " \t"); |
68 | | |
69 | 6 | size_t index = 0; |
70 | 6 | auto iterator = columns.begin(); |
71 | 84 | for (; iterator != columns.end(); ++iterator) { |
72 | 78 | if (index == 0) { |
73 | 6 | if ((*iterator).at(0) == '@') { |
74 | | // If the first value is an 'at' then skip to the command. |
75 | 0 | r["event"] = *iterator; |
76 | 0 | index = 5; |
77 | 0 | continue; |
78 | 0 | } |
79 | 6 | r["minute"] = *iterator; |
80 | 72 | } else if (index == 1) { |
81 | 6 | r["hour"] = *iterator; |
82 | 66 | } else if (index == 2) { |
83 | 6 | r["day_of_month"] = *iterator; |
84 | 60 | } else if (index == 3) { |
85 | 6 | r["month"] = *iterator; |
86 | 54 | } else if (index == 4) { |
87 | 6 | r["day_of_week"] = *iterator; |
88 | 48 | } else if (index == 5) { |
89 | 6 | r["command"] = *iterator; |
90 | 42 | } else { |
91 | | // Long if switch to handle command breaks from space delim. |
92 | 42 | r["command"] += " " + *iterator; |
93 | 42 | } |
94 | 78 | index++; |
95 | 78 | } |
96 | | |
97 | 6 | if (r["command"].size() == 0) { |
98 | | // The line was not well-formed, perhaps it was a variable? |
99 | 0 | return; |
100 | 0 | } |
101 | 6 | r["pid_with_namespace"] = "0"; |
102 | | |
103 | 6 | results.push_back(r); |
104 | 6 | } |
105 | | |
106 | 3 | QueryData genCronTabImpl(QueryContext& context, Logger& logger) { |
107 | 3 | QueryData results; |
108 | 3 | std::vector<std::string> file_list; |
109 | | |
110 | 3 | file_list.push_back(kSystemCron); |
111 | | |
112 | 12 | for (const auto& cron_dir : kCronSearchDirs) { |
113 | 12 | osquery::listFilesInDirectory(cron_dir, file_list); |
114 | 12 | } |
115 | | |
116 | 6 | for (const auto& file_path : file_list) { |
117 | 6 | auto lines = cronFromFile(file_path, logger); |
118 | 6 | for (const auto& line : lines) { |
119 | 6 | genCronLine(file_path, line, results); |
120 | 6 | } |
121 | 6 | } |
122 | | |
123 | 3 | return results; |
124 | 3 | } |
125 | | |
126 | 3 | QueryData genCronTab(QueryContext& context) { |
127 | 3 | if (hasNamespaceConstraint(context)) { |
128 | 0 | return generateInNamespace(context, "crontab", genCronTabImpl); |
129 | 3 | } else { |
130 | 3 | GLOGLogger logger; |
131 | 3 | return genCronTabImpl(context, logger); |
132 | 3 | } |
133 | 3 | } |
134 | | } // namespace tables |
135 | | } // namespace osquery |