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