xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sandbox2/comms.h (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
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 <functional>
32 #include <limits>
33 #include <memory>
34 #include <string>
35 #include <utility>
36 #include <vector>
37 
38 #include "absl/base/attributes.h"
39 #include "absl/log/die_if_null.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 
150   // Sends/receives various types of data.
RecvUint8(uint8_t * v)151   bool RecvUint8(uint8_t* v) { return RecvIntGeneric(v, kTagUint8); }
SendUint8(uint8_t v)152   bool SendUint8(uint8_t v) { return SendGeneric(v, kTagUint8); }
RecvInt8(int8_t * v)153   bool RecvInt8(int8_t* v) { return RecvIntGeneric(v, kTagInt8); }
SendInt8(int8_t v)154   bool SendInt8(int8_t v) { return SendGeneric(v, kTagInt8); }
RecvUint16(uint16_t * v)155   bool RecvUint16(uint16_t* v) { return RecvIntGeneric(v, kTagUint16); }
SendUint16(uint16_t v)156   bool SendUint16(uint16_t v) { return SendGeneric(v, kTagUint16); }
RecvInt16(int16_t * v)157   bool RecvInt16(int16_t* v) { return RecvIntGeneric(v, kTagInt16); }
SendInt16(int16_t v)158   bool SendInt16(int16_t v) { return SendGeneric(v, kTagInt16); }
RecvUint32(uint32_t * v)159   bool RecvUint32(uint32_t* v) { return RecvIntGeneric(v, kTagUint32); }
SendUint32(uint32_t v)160   bool SendUint32(uint32_t v) { return SendGeneric(v, kTagUint32); }
RecvInt32(int32_t * v)161   bool RecvInt32(int32_t* v) { return RecvIntGeneric(v, kTagInt32); }
SendInt32(int32_t v)162   bool SendInt32(int32_t v) { return SendGeneric(v, kTagInt32); }
RecvUint64(uint64_t * v)163   bool RecvUint64(uint64_t* v) { return RecvIntGeneric(v, kTagUint64); }
SendUint64(uint64_t v)164   bool SendUint64(uint64_t v) { return SendGeneric(v, kTagUint64); }
RecvInt64(int64_t * v)165   bool RecvInt64(int64_t* v) { return RecvIntGeneric(v, kTagInt64); }
SendInt64(int64_t v)166   bool SendInt64(int64_t v) { return SendGeneric(v, kTagInt64); }
RecvBool(bool * v)167   bool RecvBool(bool* v) { return RecvIntGeneric(v, kTagBool); }
SendBool(bool v)168   bool SendBool(bool v) { return SendGeneric(v, kTagBool); }
169   bool RecvString(std::string* v);
170   bool SendString(const std::string& v);
171 
172   bool RecvBytes(std::vector<uint8_t>* buffer);
173   bool SendBytes(const uint8_t* v, size_t len);
174   bool SendBytes(const std::vector<uint8_t>& buffer);
175 
176   // Receives remote process credentials.
177   bool RecvCreds(pid_t* pid, uid_t* uid, gid_t* gid);
178 
179   // Receives/sends file descriptors.
180   bool RecvFD(int* fd);
181   bool SendFD(int fd);
182 
183   // Receives/sends protobufs.
184   bool RecvProtoBuf(google::protobuf::MessageLite* message);
185   bool SendProtoBuf(const google::protobuf::MessageLite& message);
186 
187   // Receives/sends Status objects.
188   bool RecvStatus(absl::Status* status);
189   bool SendStatus(const absl::Status& status);
190 
Swap(Comms & other)191   void Swap(Comms& other) {
192     if (this == &other) {
193       return;
194     }
195     using std::swap;
196     swap(name_, other.name_);
197     swap(abstract_uds_, other.abstract_uds_);
198     swap(connection_fd_, other.connection_fd_);
199     swap(state_, other.state_);
200     swap(listening_comms_, other.listening_comms_);
201   }
202 
swap(Comms & x,Comms & y)203   friend void swap(Comms& x, Comms& y) { return x.Swap(y); }
204 
205  private:
206   friend class Client;
207 
208   // State of the channel
209   enum class State {
210     kUnconnected = 0,
211     kConnected,
212     kTerminated,
213   };
214 
215   // Connection parameters.
216   std::string name_;
217   bool abstract_uds_ = true;
218   sapi::file_util::fileops::FDCloser connection_fd_;
219 
220   std::unique_ptr<ListeningComms> listening_comms_;
221 
222   // State of the channel (enum), socket will have to be connected later on.
223   State state_ = State::kUnconnected;
224 
225   // Special struct for passing credentials or FDs.
226   // When passing credentials or FDs, it inlines the value. This is important as
227   // the data is transmitted using sendmsg/recvmsg instead of send/recv.
228   // It is also used when sending/receiving through SendTLV/RecvTLV to reduce
229   // writes/reads, although the value is written/read separately.
230   struct ABSL_ATTRIBUTE_PACKED InternalTLV {
231     uint32_t tag;
232     size_t len;
233   };
234 
235   // Moves the comms fd to an other free file descriptor.
236   void MoveToAnotherFd();
237 
238   // Support for EINTR and size completion.
239   bool Send(const void* data, size_t len);
240   bool Recv(void* data, size_t len);
241 
242   // Receives tag and length.
243   bool RecvTL(uint32_t* tag, size_t* length);
244 
245   // T has to be a ContiguousContainer
246   template <typename T>
247   bool RecvTLVGeneric(uint32_t* tag, T* value);
248 
249   // Receives arbitrary integers.
250   bool RecvInt(void* buffer, size_t len, uint32_t tag);
251 
252   template <typename T>
RecvIntGeneric(T * output,uint32_t tag)253   bool RecvIntGeneric(T* output, uint32_t tag) {
254     return RecvInt(output, sizeof(T), tag);
255   }
256 
257   template <typename T>
SendGeneric(T value,uint32_t tag)258   bool SendGeneric(T value, uint32_t tag) {
259     return SendTLV(tag, sizeof(T), &value);
260   }
261 };
262 
263 class ListeningComms {
264  public:
265   static absl::StatusOr<ListeningComms> Create(absl::string_view socket_name,
266                                                bool abstract_uds = true);
267 
268   ListeningComms(ListeningComms&& other) = default;
269   ListeningComms& operator=(ListeningComms&& other) = default;
270   ~ListeningComms() = default;
271   absl::StatusOr<Comms> Accept();
272 
273  private:
ListeningComms(absl::string_view socket_name,bool abstract_uds)274   ListeningComms(absl::string_view socket_name, bool abstract_uds)
275       : socket_name_(socket_name), abstract_uds_(abstract_uds), bind_fd_(-1) {}
276   absl::Status Listen();
277 
278   std::string socket_name_;
279   bool abstract_uds_;
280   sapi::file_util::fileops::FDCloser bind_fd_;
281 };
282 
283 }  // namespace sandbox2
284 
285 #endif  // SANDBOXED_API_SANDBOX2_COMMS_H_
286