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