1 /*
2 Copyright (c) 2019. The YARA Authors. All Rights Reserved.
3
4 Redistribution and use in source and binary forms, with or without modification,
5 are permitted provided that the following conditions are met:
6
7 1. Redistributions of source code must retain the above copyright notice, this
8 list of conditions and the following disclaimer.
9
10 2. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation and/or
12 other materials provided with the distribution.
13
14 3. Neither the name of the copyright holder nor the names of its contributors
15 may be used to endorse or promote products derived from this software without
16 specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "sandbox/yara_transaction.h"
31
32 #include <poll.h>
33 #include <sys/eventfd.h>
34 #include <unistd.h>
35
36 #include "absl/strings/str_cat.h"
37 #include "libyara/include/yara/error.h"
38 #include "sandboxed_api/util/status_macros.h"
39
40 namespace yara
41 {
42 absl::Mutex YaraTransaction::mutex_(absl::kConstInit);
43
Create(Options options)44 ::sapi::StatusOr<std::unique_ptr<YaraTransaction>> YaraTransaction::Create(
45 Options options)
46 {
47 auto transaction = absl::WrapUnique(
48 new YaraTransaction(options.scan_timeout));
49 // "Run" the transaction in order to initialize the underlying sandbox.
50 SAPI_RETURN_IF_ERROR(transaction->Run());
51
52 sandbox::YaraApi api(transaction->sandbox());
53 SAPI_RETURN_IF_ERROR(
54 api.YaraInitWorkers(options.num_workers >= 1 ? options.num_workers : 1));
55
56 return transaction;
57 }
58
LoadRules(const std::string & rule_string)59 ::sapi::StatusOr<int> YaraTransaction::LoadRules(const std::string& rule_string)
60 {
61 absl::MutexLock lock(&mutex_);
62 sandbox::YaraApi api(sandbox());
63
64 ::sapi::v::ConstCStr rule_string_sapi(rule_string.c_str());
65 YaraStatus error_status;
66 ::sapi::v::Proto<YaraStatus> error_status_sapi(error_status);
67 SAPI_ASSIGN_OR_RETURN(
68 int num_rules,
69 api.YaraLoadRules(
70 rule_string_sapi.PtrBefore(), error_status_sapi.PtrBoth()));
71 if (num_rules <= 0)
72 {
73 auto error_status_copy = error_status_sapi.GetProtoCopy();
74 if (!error_status_copy)
75 {
76 return absl::UnknownError("Deserialization of response failed");
77 }
78 return absl::InvalidArgumentError(error_status_copy->message());
79 }
80 return num_rules;
81 }
82
ScanFd(int fd)83 ::sapi::StatusOr<YaraMatches> YaraTransaction::ScanFd(int fd)
84 {
85 int local_event_fd = eventfd(0 /* initval */, 0 /* flags */);
86 if (local_event_fd == -1)
87 {
88 return absl::InternalError(
89 absl::StrCat("eventfd() error: ", strerror(errno)));
90 }
91 struct FDCloser
92 {
93 ~FDCloser() { close(event_fd); }
94 int event_fd;
95 } event_fd_closer = {local_event_fd};
96
97 sandbox::YaraApi api(sandbox());
98 uint64_t result_id;
99 {
100 absl::MutexLock lock(&mutex_);
101
102 // Note: These SAPI Fd objects use the underlying sandbox comms to
103 // synchronize. Hence they must live within this locked scope.
104 ::sapi::v::Fd event_fd(local_event_fd);
105 SAPI_RETURN_IF_ERROR(sandbox()->TransferToSandboxee(&event_fd));
106 event_fd.OwnLocalFd(false); // Needs to be valid during poll()
107 event_fd.OwnRemoteFd(false); // Sandboxee will close
108
109 ::sapi::v::Fd data_fd(fd);
110 SAPI_RETURN_IF_ERROR(sandbox()->TransferToSandboxee(&data_fd));
111 data_fd.OwnLocalFd(false); // To be closed by caller
112 data_fd.OwnRemoteFd(false); // Sandboxee will close
113
114 SAPI_ASSIGN_OR_RETURN(
115 result_id,
116 api.YaraAsyncScanFd(
117 data_fd.GetRemoteFd(),
118 event_fd.GetRemoteFd(),
119 absl::ToInt64Seconds(scan_timeout_)));
120 }
121
122 pollfd poll_events{local_event_fd, POLLIN};
123 int poll_result;
124
125 // TEMP_FAILURE_RETRY is a GNU extension that retries if the call returns
126 // EINTR.
127 poll_result = TEMP_FAILURE_RETRY(poll(
128 &poll_events,
129 1 /* nfds */,
130 // Add extra time to allow code inside the sandbox to time out first.
131 absl::ToInt64Milliseconds(scan_timeout_ + absl::Seconds(10))));
132 if (poll_result == 0)
133 {
134 return absl::DeadlineExceededError("Scan timeout during poll()");
135 }
136
137 if (poll_result == -1)
138 {
139 return absl::InternalError(absl::StrCat("poll() error: ", strerror(errno)));
140 }
141 if (poll_events.revents & POLLHUP || poll_events.revents & POLLERR ||
142 poll_events.revents & POLLNVAL)
143 {
144 return absl::InternalError(
145 absl::StrCat("poll() error, revents: ", poll_events.revents));
146 }
147
148 absl::MutexLock lock(&mutex_);
149 YaraMatches matches;
150 ::sapi::v::Proto<YaraMatches> matches_sapi(matches);
151 SAPI_ASSIGN_OR_RETURN(
152 int scan_result,
153 api.YaraGetScanResult(result_id, matches_sapi.PtrBoth()));
154 switch (scan_result)
155 {
156 case ERROR_SUCCESS:
157 case ERROR_TOO_MANY_MATCHES:
158 {
159 auto matches_copy = matches_sapi.GetProtoCopy();
160 if (!matches_copy)
161 {
162 return absl::UnknownError("Deserialization of response failed");
163 }
164 return *matches_copy;
165 }
166
167 case ERROR_SCAN_TIMEOUT:
168 return absl::DeadlineExceededError("Scan timeout");
169 }
170 return absl::InternalError(absl::StrCat("Error during scan: ", scan_result));
171 }
172
173 } // namespace yara
174