• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // The sandbox2::Comms class uses AF_UNIX sockets (man 7 unix) to send pieces of
16 // data between processes. It uses the TLV encoding and provides some useful
17 // helpers.
18 //
19 // The endianess is platform-specific, but as it can be used over abstract
20 // sockets only, that's not a problem. Is some poor soul decides to rewrite it
21 // to work over AF_INET(6), the endianess will have to be dealt with (somehow).
22 
23 #ifndef SANDBOXED_API_SANDBOX2_COMMS_H_
24 #define SANDBOXED_API_SANDBOX2_COMMS_H_
25 
26 #include <sys/un.h>
27 #include <unistd.h>
28 
29 #include <cstddef>
30 #include <cstdint>
31 #include <limits>
32 #include <memory>
33 #include <optional>
34 #include <string>
35 #include <utility>
36 #include <variant>
37 #include <vector>
38 
39 #include "absl/base/attributes.h"
40 #include "absl/status/status.h"
41 #include "absl/status/statusor.h"
42 #include "absl/strings/string_view.h"
43 #include "google/protobuf/message_lite.h"
44 #include "sandboxed_api/util/fileops.h"
45 
46 namespace proto2 {
47 class Message;
48 }  // namespace proto2
49 
50 namespace sandbox2 {
51 
52 class Client;
53 class ListeningComms;
54 
55 class Comms {
56  public:
57   struct DefaultConnectionTag {};
58 
59   // Default tags, custom tags should be <0x80000000.
60   static constexpr uint32_t kTagBool = 0x80000001;
61   static constexpr uint32_t kTagInt8 = 0x80000002;
62   static constexpr uint32_t kTagUint8 = 0x80000003;
63   static constexpr uint32_t kTagInt16 = 0x80000004;
64   static constexpr uint32_t kTagUint16 = 0x80000005;
65   static constexpr uint32_t kTagInt32 = 0x80000006;
66   static constexpr uint32_t kTagUint32 = 0x80000007;
67   static constexpr uint32_t kTagInt64 = 0x80000008;
68   static constexpr uint32_t kTagUint64 = 0x80000009;
69   static constexpr uint32_t kTagString = 0x80000100;
70   static constexpr uint32_t kTagBytes = 0x80000101;
71   static constexpr uint32_t kTagProto2 = 0x80000102;
72   static constexpr uint32_t kTagFd = 0X80000201;
73 
74   // Any payload size above this limit will LOG(WARNING).
75   static constexpr size_t kWarnMsgSize = (256ULL << 20);
76 
77   // A high file descriptor number to be used with certain fork server request
78   // modes to map the target executable. This is considered to be an
79   // implementation detail.
80   // This number is chosen so that low FD numbers are not interfered with.
81   static constexpr int kSandbox2TargetExecFD = 1022;
82 
83   // Sandbox2-specific convention where FD=1023 is always passed to the
84   // sandboxed process as a communication channel (encapsulated in the
85   // sandbox2::Comms object at the server-side).
86   static constexpr int kSandbox2ClientCommsFD = 1023;
87 
88   // Within SendTLV, a stack-allocated buffer is created to contiguously store
89   // the TLV in order to perform one call to Send.
90   // If the TLV is larger than the size below, two calls to Send are
91   // used.
92   static constexpr size_t kSendTLVTempBufferSize = 1024;
93 
94   static constexpr DefaultConnectionTag kDefaultConnection = {};
95 
96   static constexpr const char* kSandbox2CommsFDEnvVar = "SANDBOX2_COMMS_FD";
97 
98   static absl::StatusOr<Comms> Connect(const std::string& socket_name,
99                                        bool abstract_uds = true);
100 
Comms(Comms && other)101   Comms(Comms&& other) { *this = std::move(other); }
102   Comms& operator=(Comms&& other) {
103     if (this != &other) {
104       using std::swap;
105       swap(*this, other);
106       other.Terminate();
107     }
108     return *this;
109   }
110 
111   Comms(const Comms&) = delete;
112   Comms& operator=(const Comms&) = delete;
113 
114   // Instantiates a pre-connected object.
115   // Takes ownership over fd, which will be closed on object's destruction.
116   explicit Comms(int fd, absl::string_view name = "");
117 
118   // Instantiates a pre-connected object using the default connection params.
119   explicit Comms(DefaultConnectionTag);
120 
121   ~Comms();
122 
123   // Terminates all underlying file descriptors, and sets the status of the
124   // Comms object to TERMINATED.
125   void Terminate();
126 
127   // Returns the already connected FD.
128   int GetConnectionFD() const;
129 
IsConnected()130   bool IsConnected() const { return state_ == State::kConnected; }
IsTerminated()131   bool IsTerminated() const { return state_ == State::kTerminated; }
132 
133   // Returns the maximum size of a message that can be send over the comms
134   // channel.
135   // Note: The actual size is "unlimited", although the Buffer API is more
136   // efficient for large transfers. There is an arbitrary limit to ~2GiB to
137   // avoid protobuf serialization issues.
GetMaxMsgSize()138   size_t GetMaxMsgSize() const { return std::numeric_limits<int32_t>::max(); }
139 
140   bool SendTLV(uint32_t tag, size_t length, const void* value);
141   // Receive a TLV structure, the memory for the value will be allocated
142   // by std::vector.
143   bool RecvTLV(uint32_t* tag, std::vector<uint8_t>* value);
144   // Receive a TLV structure, the memory for the value will be allocated
145   // by std::string.
146   bool RecvTLV(uint32_t* tag, std::string* value);
147   // Receives a TLV value into a specified buffer without allocating memory.
148   bool RecvTLV(uint32_t* tag, size_t* length, void* buffer, size_t buffer_size,
149                std::optional<uint32_t> expected_tag = std::nullopt);
150 
151   // Sends/receives various types of data.
RecvUint8(uint8_t * v)152   bool RecvUint8(uint8_t* v) { return RecvIntGeneric(v, kTagUint8); }
SendUint8(uint8_t v)153   bool SendUint8(uint8_t v) { return SendGeneric(v, kTagUint8); }
RecvInt8(int8_t * v)154   bool RecvInt8(int8_t* v) { return RecvIntGeneric(v, kTagInt8); }
SendInt8(int8_t v)155   bool SendInt8(int8_t v) { return SendGeneric(v, kTagInt8); }
RecvUint16(uint16_t * v)156   bool RecvUint16(uint16_t* v) { return RecvIntGeneric(v, kTagUint16); }
SendUint16(uint16_t v)157   bool SendUint16(uint16_t v) { return SendGeneric(v, kTagUint16); }
RecvInt16(int16_t * v)158   bool RecvInt16(int16_t* v) { return RecvIntGeneric(v, kTagInt16); }
SendInt16(int16_t v)159   bool SendInt16(int16_t v) { return SendGeneric(v, kTagInt16); }
RecvUint32(uint32_t * v)160   bool RecvUint32(uint32_t* v) { return RecvIntGeneric(v, kTagUint32); }
SendUint32(uint32_t v)161   bool SendUint32(uint32_t v) { return SendGeneric(v, kTagUint32); }
RecvInt32(int32_t * v)162   bool RecvInt32(int32_t* v) { return RecvIntGeneric(v, kTagInt32); }
SendInt32(int32_t v)163   bool SendInt32(int32_t v) { return SendGeneric(v, kTagInt32); }
RecvUint64(uint64_t * v)164   bool RecvUint64(uint64_t* v) { return RecvIntGeneric(v, kTagUint64); }
SendUint64(uint64_t v)165   bool SendUint64(uint64_t v) { return SendGeneric(v, kTagUint64); }
RecvInt64(int64_t * v)166   bool RecvInt64(int64_t* v) { return RecvIntGeneric(v, kTagInt64); }
SendInt64(int64_t v)167   bool SendInt64(int64_t v) { return SendGeneric(v, kTagInt64); }
RecvBool(bool * v)168   bool RecvBool(bool* v) { return RecvIntGeneric(v, kTagBool); }
SendBool(bool v)169   bool SendBool(bool v) { return SendGeneric(v, kTagBool); }
170   bool RecvString(std::string* v);
171   bool SendString(const std::string& v);
172 
173   bool RecvBytes(std::vector<uint8_t>* buffer);
174   bool SendBytes(const uint8_t* v, size_t len);
175   bool SendBytes(const std::vector<uint8_t>& buffer);
176 
177   // Receives remote process credentials.
178   bool RecvCreds(pid_t* pid, uid_t* uid, gid_t* gid);
179 
180   // Receives/sends file descriptors.
181   bool RecvFD(int* fd);
182   bool SendFD(int fd);
183 
184   // Receives/sends protobufs.
185   bool RecvProtoBuf(google::protobuf::MessageLite* message);
186   bool SendProtoBuf(const google::protobuf::MessageLite& message);
187 
188   // Receives/sends Status objects.
189   bool RecvStatus(absl::Status* status);
190   bool SendStatus(const absl::Status& status);
191 
Swap(Comms & other)192   void Swap(Comms& other) {
193     if (this == &other) {
194       return;
195     }
196     using std::swap;
197     swap(name_, other.name_);
198     swap(abstract_uds_, other.abstract_uds_);
199     swap(raw_comms_, other.raw_comms_);
200     swap(state_, other.state_);
201     swap(listening_comms_, other.listening_comms_);
202   }
203 
swap(Comms & x,Comms & y)204   friend void swap(Comms& x, Comms& y) { return x.Swap(y); }
205 
206  protected:
207   class RawComms {
208    public:
~RawComms()209     virtual ~RawComms() {};
210     virtual int GetConnectionFD() const = 0;
211     virtual void MoveToAnotherFd() = 0;
212     virtual ssize_t RawSend(const void* data, size_t len) = 0;
213     virtual ssize_t RawRecv(void* data, size_t len) = 0;
214     virtual ssize_t RawSendMsg(const void* msg) = 0;
215     virtual ssize_t RawRecvMsg(void* msg) = 0;
216   };
217 
218   class RawCommsFdImpl : public RawComms {
219    public:
RawCommsFdImpl(int fd)220     RawCommsFdImpl(int fd) : connection_fd_(fd) {}
221     int GetConnectionFD() const override;
222     void MoveToAnotherFd() override;
223     ssize_t RawSend(const void* data, size_t len) override;
224     ssize_t RawRecv(void* data, size_t len) override;
225     ssize_t RawSendMsg(const void* msg) override;
226     ssize_t RawRecvMsg(void* msg) override;
227 
228    private:
229     sapi::file_util::fileops::FDCloser connection_fd_;
230   };
231 
232  private:
233   friend class Client;
234 
235   // State of the channel
236   enum class State {
237     kUnconnected = 0,
238     kConnected,
239     kTerminated,
240   };
241 
242   // Connection parameters.
243   std::string name_;
244   bool abstract_uds_ = true;
245   std::variant<std::unique_ptr<RawComms>, RawCommsFdImpl> raw_comms_;
246 
247   std::unique_ptr<ListeningComms> listening_comms_;
248 
249   // State of the channel (enum), socket will have to be connected later on.
250   State state_ = State::kUnconnected;
251 
252   // Special struct for passing credentials or FDs.
253   // When passing credentials or FDs, it inlines the value. This is important as
254   // the data is transmitted using sendmsg/recvmsg instead of send/recv.
255   // It is also used when sending/receiving through SendTLV/RecvTLV to reduce
256   // writes/reads, although the value is written/read separately.
257   struct ABSL_ATTRIBUTE_PACKED InternalTLV {
258     uint32_t tag;
259     size_t len;
260   };
261 
Comms(std::unique_ptr<RawComms> raw_comms)262   Comms(std::unique_ptr<RawComms> raw_comms)
263       : raw_comms_(std::move(raw_comms)) {
264     state_ = State::kConnected;
265   }
266 
GetRawComms()267   RawComms* GetRawComms() {
268     RawComms* raw_comms = std::get_if<RawCommsFdImpl>(&raw_comms_);
269     if (!raw_comms) {
270       raw_comms = std::get<std::unique_ptr<RawComms>>(raw_comms_).get();
271     }
272     return raw_comms;
273   }
274 
GetRawComms()275   const RawComms* GetRawComms() const {
276     const RawComms* raw_comms = std::get_if<RawCommsFdImpl>(&raw_comms_);
277     if (!raw_comms) {
278       raw_comms = std::get<std::unique_ptr<RawComms>>(raw_comms_).get();
279     }
280     return raw_comms;
281   }
282 
283   // Moves the comms fd to an other free file descriptor.
284   void MoveToAnotherFd();
285 
286   // Support for EINTR and size completion.
287   bool Send(const void* data, size_t len);
288   bool Recv(void* data, size_t len);
289 
290   // Receives tag and length.
291   bool RecvTL(uint32_t* tag, size_t* length);
292 
293   // T has to be a ContiguousContainer
294   template <typename T>
295   bool RecvTLVGeneric(uint32_t* tag, T* value);
296 
297   // Receives arbitrary integers.
298   bool RecvInt(void* buffer, size_t len, uint32_t tag);
299 
300   template <typename T>
RecvIntGeneric(T * output,uint32_t tag)301   bool RecvIntGeneric(T* output, uint32_t tag) {
302     return RecvInt(output, sizeof(T), tag);
303   }
304 
305   template <typename T>
SendGeneric(T value,uint32_t tag)306   bool SendGeneric(T value, uint32_t tag) {
307     return SendTLV(tag, sizeof(T), &value);
308   }
309 };
310 
311 class ListeningComms {
312  public:
313   static absl::StatusOr<ListeningComms> Create(absl::string_view socket_name,
314                                                bool abstract_uds = true);
315 
316   ListeningComms(ListeningComms&& other) = default;
317   ListeningComms& operator=(ListeningComms&& other) = default;
318   ~ListeningComms() = default;
319   absl::StatusOr<Comms> Accept();
320 
321  private:
ListeningComms(absl::string_view socket_name,bool abstract_uds)322   ListeningComms(absl::string_view socket_name, bool abstract_uds)
323       : socket_name_(socket_name), abstract_uds_(abstract_uds), bind_fd_(-1) {}
324   absl::Status Listen();
325 
326   std::string socket_name_;
327   bool abstract_uds_;
328   sapi::file_util::fileops::FDCloser bind_fd_;
329 };
330 
331 }  // namespace sandbox2
332 
333 #endif  // SANDBOXED_API_SANDBOX2_COMMS_H_
334