| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "debug.h" | |
| 6 #include "sandbox_impl.h" | |
| 7 | |
| 8 namespace playground { | |
| 9 | |
| 10 #if defined(__NR_socket) | |
| 11 | |
| 12 ssize_t Sandbox::sandbox_recvfrom(int sockfd, void* buf, size_t len, int flags, | |
| 13 void* from, socklen_t* fromlen) { | |
| 14 long long tm; | |
| 15 Debug::syscall(&tm, __NR_recvfrom, "Executing handler"); | |
| 16 | |
| 17 SysCalls sys; | |
| 18 if (!from && !flags) { | |
| 19 // recv() with a NULL sender and no flags is the same as read(), which | |
| 20 // is unrestricted in seccomp mode. | |
| 21 Debug::message("Replaced recv() with call to read()"); | |
| 22 ssize_t rc = sys.read(sockfd, buf, len); | |
| 23 if (rc < 0) { | |
| 24 Debug::elapsed(tm, __NR_recvfrom); | |
| 25 return -sys.my_errno; | |
| 26 } else { | |
| 27 Debug::elapsed(tm, __NR_recvfrom); | |
| 28 return rc; | |
| 29 } | |
| 30 } | |
| 31 | |
| 32 struct { | |
| 33 int sysnum; | |
| 34 long long cookie; | |
| 35 RecvFrom recvfrom_req; | |
| 36 } __attribute__((packed)) request; | |
| 37 request.sysnum = __NR_recvfrom; | |
| 38 request.cookie = cookie(); | |
| 39 request.recvfrom_req.sockfd = sockfd; | |
| 40 request.recvfrom_req.buf = buf; | |
| 41 request.recvfrom_req.len = len; | |
| 42 request.recvfrom_req.flags = flags; | |
| 43 request.recvfrom_req.from = from; | |
| 44 request.recvfrom_req.fromlen = fromlen; | |
| 45 | |
| 46 long rc; | |
| 47 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
| 48 sizeof(request) || | |
| 49 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
| 50 die("Failed to forward recvfrom() request [sandbox]"); | |
| 51 } | |
| 52 Debug::elapsed(tm, __NR_recvfrom); | |
| 53 return static_cast<ssize_t>(rc); | |
| 54 } | |
| 55 | |
| 56 ssize_t Sandbox::sandbox_recvmsg(int sockfd, struct msghdr* msg, int flags) { | |
| 57 long long tm; | |
| 58 Debug::syscall(&tm, __NR_recvmsg, "Executing handler"); | |
| 59 | |
| 60 // We cannot simplify recvmsg() to recvfrom(), recv() or read(), as we do | |
| 61 // not know whether the caller needs us to set msg->msg_flags. | |
| 62 struct { | |
| 63 int sysnum; | |
| 64 long long cookie; | |
| 65 RecvMsg recvmsg_req; | |
| 66 } __attribute__((packed)) request; | |
| 67 request.sysnum = __NR_recvmsg; | |
| 68 request.cookie = cookie(); | |
| 69 request.recvmsg_req.sockfd = sockfd; | |
| 70 request.recvmsg_req.msg = msg; | |
| 71 request.recvmsg_req.flags = flags; | |
| 72 | |
| 73 long rc; | |
| 74 SysCalls sys; | |
| 75 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
| 76 sizeof(request) || | |
| 77 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
| 78 die("Failed to forward recvmsg() request [sandbox]"); | |
| 79 } | |
| 80 Debug::elapsed(tm, __NR_recvmsg); | |
| 81 return static_cast<ssize_t>(rc); | |
| 82 } | |
| 83 | |
| 84 size_t Sandbox::sandbox_sendmsg(int sockfd, const struct msghdr* msg, | |
| 85 int flags) { | |
| 86 long long tm; | |
| 87 Debug::syscall(&tm, __NR_sendmsg, "Executing handler"); | |
| 88 | |
| 89 if (msg->msg_iovlen == 1 && msg->msg_controllen == 0) { | |
| 90 // sendmsg() can sometimes be simplified as sendto() | |
| 91 return sandbox_sendto(sockfd, msg->msg_iov, msg->msg_iovlen, | |
| 92 flags, msg->msg_name, msg->msg_namelen); | |
| 93 } | |
| 94 | |
| 95 struct Request { | |
| 96 int sysnum; | |
| 97 long long cookie; | |
| 98 SendMsg sendmsg_req; | |
| 99 struct msghdr msg; | |
| 100 } __attribute__((packed)); | |
| 101 char data[sizeof(struct Request) + msg->msg_namelen + msg->msg_controllen]; | |
| 102 struct Request *request = reinterpret_cast<struct Request *>(data); | |
| 103 request->sysnum = __NR_sendmsg; | |
| 104 request->cookie = cookie(); | |
| 105 request->sendmsg_req.sockfd = sockfd; | |
| 106 request->sendmsg_req.msg = msg; | |
| 107 request->sendmsg_req.flags = flags; | |
| 108 request->msg = *msg; | |
| 109 memcpy(reinterpret_cast<char *>( | |
| 110 memcpy(request + 1, msg->msg_name, msg->msg_namelen)) + | |
| 111 msg->msg_namelen, | |
| 112 msg->msg_control, msg->msg_controllen); | |
| 113 | |
| 114 long rc; | |
| 115 SysCalls sys; | |
| 116 if (write(sys, processFdPub(), &data, sizeof(data)) != | |
| 117 (ssize_t)sizeof(data) || | |
| 118 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
| 119 die("Failed to forward sendmsg() request [sandbox]"); | |
| 120 } | |
| 121 Debug::elapsed(tm, __NR_sendmsg); | |
| 122 return static_cast<ssize_t>(rc); | |
| 123 } | |
| 124 | |
| 125 ssize_t Sandbox::sandbox_sendto(int sockfd, const void* buf, size_t len, | |
| 126 int flags, const void* to, socklen_t tolen) { | |
| 127 long long tm; | |
| 128 Debug::syscall(&tm, __NR_sendto, "Executing handler"); | |
| 129 | |
| 130 SysCalls sys; | |
| 131 if (!to && !flags) { | |
| 132 // sendto() with a NULL recipient and no flags is the same as write(), | |
| 133 // which is unrestricted in seccomp mode. | |
| 134 Debug::message("Replaced sendto() with call to write()"); | |
| 135 ssize_t rc = sys.write(sockfd, buf, len); | |
| 136 if (rc < 0) { | |
| 137 Debug::elapsed(tm, __NR_sendto); | |
| 138 return -sys.my_errno; | |
| 139 } else { | |
| 140 Debug::elapsed(tm, __NR_sendto); | |
| 141 return rc; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 struct { | |
| 146 int sysnum; | |
| 147 long long cookie; | |
| 148 SendTo sendto_req; | |
| 149 } __attribute__((packed)) request; | |
| 150 request.sysnum = __NR_sendto; | |
| 151 request.cookie = cookie(); | |
| 152 request.sendto_req.sockfd = sockfd; | |
| 153 request.sendto_req.buf = buf; | |
| 154 request.sendto_req.len = len; | |
| 155 request.sendto_req.flags = flags; | |
| 156 request.sendto_req.to = to; | |
| 157 request.sendto_req.tolen = tolen; | |
| 158 | |
| 159 long rc; | |
| 160 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
| 161 sizeof(request) || | |
| 162 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
| 163 die("Failed to forward sendto() request [sandbox]"); | |
| 164 } | |
| 165 Debug::elapsed(tm, __NR_sendto); | |
| 166 return static_cast<ssize_t>(rc); | |
| 167 } | |
| 168 | |
| 169 long Sandbox::sandbox_setsockopt(int sockfd, int level, int optname, | |
| 170 const void* optval, socklen_t optlen) { | |
| 171 long long tm; | |
| 172 Debug::syscall(&tm, __NR_setsockopt, "Executing handler"); | |
| 173 | |
| 174 struct { | |
| 175 int sysnum; | |
| 176 long long cookie; | |
| 177 SetSockOpt setsockopt_req; | |
| 178 } __attribute__((packed)) request; | |
| 179 request.sysnum = __NR_setsockopt; | |
| 180 request.cookie = cookie(); | |
| 181 request.setsockopt_req.sockfd = sockfd; | |
| 182 request.setsockopt_req.level = level; | |
| 183 request.setsockopt_req.optname = optname; | |
| 184 request.setsockopt_req.optval = optval; | |
| 185 request.setsockopt_req.optlen = optlen; | |
| 186 | |
| 187 long rc; | |
| 188 SysCalls sys; | |
| 189 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
| 190 sizeof(request) || | |
| 191 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
| 192 die("Failed to forward setsockopt() request [sandbox]"); | |
| 193 } | |
| 194 Debug::elapsed(tm, __NR_setsockopt); | |
| 195 return rc; | |
| 196 } | |
| 197 | |
| 198 long Sandbox::sandbox_getsockopt(int sockfd, int level, int optname, | |
| 199 void* optval, socklen_t* optlen) { | |
| 200 long long tm; | |
| 201 Debug::syscall(&tm, __NR_getsockopt, "Executing handler"); | |
| 202 | |
| 203 struct { | |
| 204 int sysnum; | |
| 205 long long cookie; | |
| 206 GetSockOpt getsockopt_req; | |
| 207 } __attribute__((packed)) request; | |
| 208 request.sysnum = __NR_getsockopt; | |
| 209 request.cookie = cookie(); | |
| 210 request.getsockopt_req.sockfd = sockfd; | |
| 211 request.getsockopt_req.level = level; | |
| 212 request.getsockopt_req.optname = optname; | |
| 213 request.getsockopt_req.optval = optval; | |
| 214 request.getsockopt_req.optlen = optlen; | |
| 215 | |
| 216 long rc; | |
| 217 SysCalls sys; | |
| 218 if (write(sys, processFdPub(), &request, sizeof(request)) != | |
| 219 sizeof(request) || | |
| 220 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
| 221 die("Failed to forward getsockopt() request [sandbox]"); | |
| 222 } | |
| 223 Debug::elapsed(tm, __NR_getsockopt); | |
| 224 return rc; | |
| 225 } | |
| 226 | |
| 227 bool Sandbox::process_recvfrom(int parentMapsFd, int sandboxFd, | |
| 228 int threadFdPub, int threadFd, | |
| 229 SecureMem::Args* mem) { | |
| 230 // Read request | |
| 231 RecvFrom recvfrom_req; | |
| 232 SysCalls sys; | |
| 233 if (read(sys, sandboxFd, &recvfrom_req, sizeof(recvfrom_req)) != | |
| 234 sizeof(recvfrom_req)) { | |
| 235 die("Failed to read parameters for recvfrom() [process]"); | |
| 236 } | |
| 237 | |
| 238 // Unsupported flag encountered. Deny the call. | |
| 239 if (recvfrom_req.flags & | |
| 240 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | |
| 241 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
| 242 return false; | |
| 243 } | |
| 244 | |
| 245 // While we do not anticipate any particular need to receive data on | |
| 246 // unconnected sockets, there is no particular risk in doing so. | |
| 247 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
| 248 __NR_recvfrom, recvfrom_req.sockfd, | |
| 249 recvfrom_req.buf, recvfrom_req.len, | |
| 250 recvfrom_req.flags, recvfrom_req.from, | |
| 251 recvfrom_req.fromlen); | |
| 252 return true; | |
| 253 } | |
| 254 | |
| 255 bool Sandbox::process_recvmsg(int parentMapsFd, int sandboxFd, int threadFdPub, | |
| 256 int threadFd, SecureMem::Args* mem) { | |
| 257 // Read request | |
| 258 RecvMsg recvmsg_req; | |
| 259 SysCalls sys; | |
| 260 if (read(sys, sandboxFd, &recvmsg_req, sizeof(recvmsg_req)) != | |
| 261 sizeof(recvmsg_req)) { | |
| 262 die("Failed to read parameters for recvmsg() [process]"); | |
| 263 } | |
| 264 | |
| 265 // Unsupported flag encountered. Deny the call. | |
| 266 if (recvmsg_req.flags & | |
| 267 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | |
| 268 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
| 269 return false; | |
| 270 } | |
| 271 | |
| 272 // Receiving messages is general not security critical. | |
| 273 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
| 274 __NR_recvmsg, recvmsg_req.sockfd, | |
| 275 recvmsg_req.msg, recvmsg_req.flags); | |
| 276 return true; | |
| 277 } | |
| 278 | |
| 279 bool Sandbox::process_sendmsg(int parentMapsFd, int sandboxFd, int threadFdPub, | |
| 280 int threadFd, SecureMem::Args* mem) { | |
| 281 // Read request | |
| 282 struct { | |
| 283 SendMsg sendmsg_req; | |
| 284 struct msghdr msg; | |
| 285 } __attribute__((packed)) data; | |
| 286 SysCalls sys; | |
| 287 if (read(sys, sandboxFd, &data, sizeof(data)) != sizeof(data)) { | |
| 288 die("Failed to read parameters for sendmsg() [process]"); | |
| 289 } | |
| 290 | |
| 291 if (data.msg.msg_namelen > 4096 || data.msg.msg_controllen > 4096) { | |
| 292 die("Unexpected size for socketcall() payload [process]"); | |
| 293 } | |
| 294 char extra[data.msg.msg_namelen + data.msg.msg_controllen]; | |
| 295 if (read(sys, sandboxFd, &extra, sizeof(extra)) != (ssize_t)sizeof(extra)) { | |
| 296 die("Failed to read parameters for sendmsg() [process]"); | |
| 297 } | |
| 298 if (sizeof(struct msghdr) + sizeof(extra) > sizeof(mem->pathname)) { | |
| 299 goto deny; | |
| 300 } | |
| 301 | |
| 302 if (data.msg.msg_namelen || | |
| 303 (data.sendmsg_req.flags & | |
| 304 ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB))) { | |
| 305 deny: | |
| 306 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
| 307 return false; | |
| 308 } | |
| 309 | |
| 310 // The trusted process receives file handles when a new untrusted thread | |
| 311 // gets created. We have security checks in place that prevent any | |
| 312 // critical information from being tampered with during thread creation. | |
| 313 // But if we disallowed passing of file handles, this would add an extra | |
| 314 // hurdle for an attacker. | |
| 315 // Unfortunately, for now, this is not possible as Chrome's | |
| 316 // base::SendRecvMsg() needs the ability to pass file handles. | |
| 317 if (data.msg.msg_controllen) { | |
| 318 data.msg.msg_control = extra + data.msg.msg_namelen; | |
| 319 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&data.msg); | |
| 320 do { | |
| 321 if (cmsg->cmsg_level != SOL_SOCKET || | |
| 322 cmsg->cmsg_type != SCM_RIGHTS) { | |
| 323 goto deny; | |
| 324 } | |
| 325 } while ((cmsg = CMSG_NXTHDR(&data.msg, cmsg)) != NULL); | |
| 326 } | |
| 327 | |
| 328 // This must be a locked system call, because we have to ensure that the | |
| 329 // untrusted code does not tamper with the msghdr after we have examined it. | |
| 330 SecureMem::lockSystemCall(parentMapsFd, mem); | |
| 331 if (sizeof(extra) > 0) { | |
| 332 if (data.msg.msg_namelen > 0) { | |
| 333 data.msg.msg_name = mem->pathname + sizeof(struct msghdr); | |
| 334 } | |
| 335 if (data.msg.msg_controllen > 0) { | |
| 336 data.msg.msg_control = mem->pathname + sizeof(struct msghdr) + | |
| 337 data.msg.msg_namelen; | |
| 338 } | |
| 339 memcpy(mem->pathname + sizeof(struct msghdr), extra, sizeof(extra)); | |
| 340 } | |
| 341 memcpy(mem->pathname, &data.msg, sizeof(struct msghdr)); | |
| 342 SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, | |
| 343 __NR_sendmsg, data.sendmsg_req.sockfd, | |
| 344 mem->pathname - (char*)mem + (char*)mem->self, | |
| 345 data.sendmsg_req.flags); | |
| 346 return true; | |
| 347 } | |
| 348 | |
| 349 bool Sandbox::process_sendto(int parentMapsFd, int sandboxFd, int threadFdPub, | |
| 350 int threadFd, SecureMem::Args* mem) { | |
| 351 // Read request | |
| 352 SendTo sendto_req; | |
| 353 SysCalls sys; | |
| 354 if (read(sys, sandboxFd, &sendto_req, sizeof(sendto_req)) != | |
| 355 sizeof(sendto_req)) { | |
| 356 die("Failed to read parameters for sendto() [process]"); | |
| 357 } | |
| 358 | |
| 359 // The sandbox does not allow sending to arbitrary addresses. | |
| 360 if (sendto_req.to) { | |
| 361 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
| 362 return false; | |
| 363 } | |
| 364 | |
| 365 // Unsupported flag encountered. Deny the call. | |
| 366 if (sendto_req.flags & | |
| 367 ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB)) { | |
| 368 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
| 369 return false; | |
| 370 } | |
| 371 | |
| 372 // Sending data on a connected socket is similar to calling write(). | |
| 373 // Allow it. | |
| 374 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
| 375 __NR_sendto, sendto_req.sockfd, | |
| 376 sendto_req.buf, sendto_req.len, | |
| 377 sendto_req.flags, sendto_req.to, | |
| 378 sendto_req.tolen); | |
| 379 return true; | |
| 380 } | |
| 381 | |
| 382 bool Sandbox::process_setsockopt(int parentMapsFd, int sandboxFd, | |
| 383 int threadFdPub, int threadFd, | |
| 384 SecureMem::Args* mem) { | |
| 385 // Read request | |
| 386 SetSockOpt setsockopt_req; | |
| 387 SysCalls sys; | |
| 388 if (read(sys, sandboxFd, &setsockopt_req, sizeof(setsockopt_req)) != | |
| 389 sizeof(setsockopt_req)) { | |
| 390 die("Failed to read parameters for setsockopt() [process]"); | |
| 391 } | |
| 392 | |
| 393 switch (setsockopt_req.level) { | |
| 394 case SOL_SOCKET: | |
| 395 switch (setsockopt_req.optname) { | |
| 396 case SO_KEEPALIVE: | |
| 397 case SO_LINGER: | |
| 398 case SO_OOBINLINE: | |
| 399 case SO_RCVBUF: | |
| 400 case SO_RCVLOWAT: | |
| 401 case SO_SNDLOWAT: | |
| 402 case SO_RCVTIMEO: | |
| 403 case SO_SNDTIMEO: | |
| 404 case SO_REUSEADDR: | |
| 405 case SO_SNDBUF: | |
| 406 case SO_TIMESTAMP: | |
| 407 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
| 408 __NR_setsockopt, setsockopt_req.sockfd, | |
| 409 setsockopt_req.level, setsockopt_req.optname, | |
| 410 setsockopt_req.optval, setsockopt_req.optlen); | |
| 411 return true; | |
| 412 default: | |
| 413 break; | |
| 414 } | |
| 415 break; | |
| 416 case IPPROTO_TCP: | |
| 417 switch (setsockopt_req.optname) { | |
| 418 case TCP_CORK: | |
| 419 case TCP_DEFER_ACCEPT: | |
| 420 case TCP_INFO: | |
| 421 case TCP_KEEPCNT: | |
| 422 case TCP_KEEPIDLE: | |
| 423 case TCP_KEEPINTVL: | |
| 424 case TCP_LINGER2: | |
| 425 case TCP_MAXSEG: | |
| 426 case TCP_NODELAY: | |
| 427 case TCP_QUICKACK: | |
| 428 case TCP_SYNCNT: | |
| 429 case TCP_WINDOW_CLAMP: | |
| 430 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
| 431 __NR_setsockopt, setsockopt_req.sockfd, | |
| 432 setsockopt_req.level, setsockopt_req.optname, | |
| 433 setsockopt_req.optval, setsockopt_req.optlen); | |
| 434 return true; | |
| 435 default: | |
| 436 break; | |
| 437 } | |
| 438 break; | |
| 439 default: | |
| 440 break; | |
| 441 } | |
| 442 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
| 443 return false; | |
| 444 } | |
| 445 | |
| 446 bool Sandbox::process_getsockopt(int parentMapsFd, int sandboxFd, | |
| 447 int threadFdPub, int threadFd, | |
| 448 SecureMem::Args* mem) { | |
| 449 // Read request | |
| 450 GetSockOpt getsockopt_req; | |
| 451 SysCalls sys; | |
| 452 if (read(sys, sandboxFd, &getsockopt_req, sizeof(getsockopt_req)) != | |
| 453 sizeof(getsockopt_req)) { | |
| 454 die("Failed to read parameters for getsockopt() [process]"); | |
| 455 } | |
| 456 | |
| 457 switch (getsockopt_req.level) { | |
| 458 case SOL_SOCKET: | |
| 459 switch (getsockopt_req.optname) { | |
| 460 case SO_ACCEPTCONN: | |
| 461 case SO_ERROR: | |
| 462 case SO_KEEPALIVE: | |
| 463 case SO_LINGER: | |
| 464 case SO_OOBINLINE: | |
| 465 case SO_RCVBUF: | |
| 466 case SO_RCVLOWAT: | |
| 467 case SO_SNDLOWAT: | |
| 468 case SO_RCVTIMEO: | |
| 469 case SO_SNDTIMEO: | |
| 470 case SO_REUSEADDR: | |
| 471 case SO_SNDBUF: | |
| 472 case SO_TIMESTAMP: | |
| 473 case SO_TYPE: | |
| 474 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
| 475 __NR_getsockopt, getsockopt_req.sockfd, | |
| 476 getsockopt_req.level, getsockopt_req.optname, | |
| 477 getsockopt_req.optval, getsockopt_req.optlen); | |
| 478 return true; | |
| 479 default: | |
| 480 break; | |
| 481 } | |
| 482 break; | |
| 483 case IPPROTO_TCP: | |
| 484 switch (getsockopt_req.optname) { | |
| 485 case TCP_CORK: | |
| 486 case TCP_DEFER_ACCEPT: | |
| 487 case TCP_INFO: | |
| 488 case TCP_KEEPCNT: | |
| 489 case TCP_KEEPIDLE: | |
| 490 case TCP_KEEPINTVL: | |
| 491 case TCP_LINGER2: | |
| 492 case TCP_MAXSEG: | |
| 493 case TCP_NODELAY: | |
| 494 case TCP_QUICKACK: | |
| 495 case TCP_SYNCNT: | |
| 496 case TCP_WINDOW_CLAMP: | |
| 497 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | |
| 498 __NR_getsockopt, getsockopt_req.sockfd, | |
| 499 getsockopt_req.level, getsockopt_req.optname, | |
| 500 getsockopt_req.optval, getsockopt_req.optlen); | |
| 501 return true; | |
| 502 default: | |
| 503 break; | |
| 504 } | |
| 505 break; | |
| 506 default: | |
| 507 break; | |
| 508 } | |
| 509 SecureMem::abandonSystemCall(threadFd, -EINVAL); | |
| 510 return false; | |
| 511 } | |
| 512 | |
| 513 #endif | |
| 514 #if defined(__NR_socketcall) | |
| 515 | |
| 516 enum { | |
| 517 SYS_SOCKET = 1, | |
| 518 SYS_BIND = 2, | |
| 519 SYS_CONNECT = 3, | |
| 520 SYS_LISTEN = 4, | |
| 521 SYS_ACCEPT = 5, | |
| 522 SYS_GETSOCKNAME = 6, | |
| 523 SYS_GETPEERNAME = 7, | |
| 524 SYS_SOCKETPAIR = 8, | |
| 525 SYS_SEND = 9, | |
| 526 SYS_RECV = 10, | |
| 527 SYS_SENDTO = 11, | |
| 528 SYS_RECVFROM = 12, | |
| 529 SYS_SHUTDOWN = 13, | |
| 530 SYS_SETSOCKOPT = 14, | |
| 531 SYS_GETSOCKOPT = 15, | |
| 532 SYS_SENDMSG = 16, | |
| 533 SYS_RECVMSG = 17, | |
| 534 SYS_ACCEPT4 = 18 | |
| 535 }; | |
| 536 | |
| 537 struct Sandbox::SocketCallArgInfo { | |
| 538 size_t len; | |
| 539 off_t addrOff; | |
| 540 off_t lengthOff; | |
| 541 }; | |
| 542 const struct Sandbox::SocketCallArgInfo Sandbox::socketCallArgInfo[] = { | |
| 543 #define STRUCT(s) reinterpret_cast<SocketCall *>(0)->args.s | |
| 544 #define SIZE(s) sizeof(STRUCT(s)) | |
| 545 #define OFF(s, f) offsetof(typeof STRUCT(s), f) | |
| 546 { 0 }, | |
| 547 { SIZE(socket) }, | |
| 548 { SIZE(bind), OFF(bind, addr), OFF(bind, addrlen) }, | |
| 549 { SIZE(connect), OFF(connect, addr), OFF(connect, addrlen) }, | |
| 550 { SIZE(listen) }, | |
| 551 { SIZE(accept) }, | |
| 552 { SIZE(getsockname) }, | |
| 553 { SIZE(getpeername) }, | |
| 554 { SIZE(socketpair) }, | |
| 555 { SIZE(send) }, | |
| 556 { SIZE(recv) }, | |
| 557 { SIZE(sendto), OFF(sendto, to), OFF(sendto, tolen) }, | |
| 558 { SIZE(recvfrom) }, | |
| 559 { SIZE(shutdown) }, | |
| 560 { SIZE(setsockopt), OFF(setsockopt, optval), OFF(setsockopt, optlen) }, | |
| 561 { SIZE(getsockopt) }, | |
| 562 { SIZE(sendmsg) }, | |
| 563 { SIZE(recvmsg) }, | |
| 564 { SIZE(accept4) } | |
| 565 #undef STRUCT | |
| 566 #undef SIZE | |
| 567 #undef OFF | |
| 568 }; | |
| 569 | |
| 570 long Sandbox::sandbox_socketcall(int call, void* args) { | |
| 571 long long tm; | |
| 572 Debug::syscall(&tm, __NR_socketcall, "Executing handler", call); | |
| 573 | |
| 574 // When demultiplexing socketcall(), only accept calls that have a valid | |
| 575 // "call" opcode. | |
| 576 if (call < SYS_SOCKET || call > SYS_ACCEPT4) { | |
| 577 Debug::elapsed(tm, __NR_socketcall, call); | |
| 578 return -ENOSYS; | |
| 579 } | |
| 580 | |
| 581 // Some type of calls include a pointer to an address or name, which cannot | |
| 582 // be accessed by the trusted process, as it lives in a separate address | |
| 583 // space. For these calls, append the extra data to the serialized request. | |
| 584 // This requires some copying of data, as we have to make sure there is | |
| 585 // only a single atomic call to write(). | |
| 586 socklen_t numExtraData = 0; | |
| 587 const void* extraDataAddr = NULL; | |
| 588 if (socketCallArgInfo[call].lengthOff) { | |
| 589 memcpy(&numExtraData, | |
| 590 reinterpret_cast<char *>(args) + socketCallArgInfo[call].lengthOff, | |
| 591 sizeof(socklen_t)); | |
| 592 extraDataAddr = reinterpret_cast<char *>(args) + | |
| 593 socketCallArgInfo[call].addrOff; | |
| 594 } | |
| 595 | |
| 596 // sendmsg() and recvmsg() have more complicated requirements for computing | |
| 597 // the amount of extra data that needs to be sent to the trusted process. | |
| 598 if (call == SYS_SENDMSG) { | |
| 599 SendMsg *sendmsg_args = reinterpret_cast<SendMsg *>(args); | |
| 600 if (sendmsg_args->msg->msg_iovlen == 1 && | |
| 601 !sendmsg_args->msg->msg_control) { | |
| 602 // Further down in the code, this sendmsg() call will be simplified to | |
| 603 // a sendto() call. Make sure we already compute the correct value for | |
| 604 // numExtraData, as it is needed when we allocate "data[]" on the stack. | |
| 605 numExtraData = sendmsg_args->msg->msg_namelen; | |
| 606 extraDataAddr = sendmsg_args->msg->msg_name; | |
| 607 } else { | |
| 608 // sendmsg() needs to include some of the extra data so that we can | |
| 609 // inspect it in process_socketcall() | |
| 610 numExtraData = sizeof(*sendmsg_args->msg) + | |
| 611 sendmsg_args->msg->msg_namelen + | |
| 612 sendmsg_args->msg->msg_controllen; | |
| 613 extraDataAddr = NULL; | |
| 614 } | |
| 615 } | |
| 616 if (call == SYS_RECVMSG) { | |
| 617 RecvMsg *recvmsg_args = reinterpret_cast<RecvMsg *>(args); | |
| 618 numExtraData = sizeof(*recvmsg_args->msg); | |
| 619 extraDataAddr = recvmsg_args->msg; | |
| 620 } | |
| 621 | |
| 622 // Set up storage for the request header and copy the data from "args" | |
| 623 // into it. | |
| 624 struct Request { | |
| 625 int sysnum; | |
| 626 long long cookie; | |
| 627 SocketCall socketcall_req; | |
| 628 } __attribute__((packed)) *request; | |
| 629 char data[sizeof(struct Request) + numExtraData]; | |
| 630 request = reinterpret_cast<struct Request *>(data); | |
| 631 memcpy(&request->socketcall_req.args, args, socketCallArgInfo[call].len); | |
| 632 | |
| 633 // Simplify send(), sendto() and sendmsg(), if there are simpler equivalent | |
| 634 // calls. This allows us to occasionally replace them with calls to write(), | |
| 635 // which don't have to be forwarded to the trusted process. | |
| 636 SysCalls sys; | |
| 637 if (call == SYS_SENDMSG && | |
| 638 request->socketcall_req.args.sendmsg.msg->msg_iovlen == 1 && | |
| 639 !request->socketcall_req.args.sendmsg.msg->msg_control) { | |
| 640 // Ordering of these assignments is important, as we are reshuffling | |
| 641 // fields inside of a union. | |
| 642 call = SYS_SENDTO; | |
| 643 request->socketcall_req.args.sendto.flags = | |
| 644 request->socketcall_req.args.sendmsg.flags; | |
| 645 request->socketcall_req.args.sendto.to = | |
| 646 request->socketcall_req.args.sendmsg.msg->msg_name; | |
| 647 request->socketcall_req.args.sendto.tolen = | |
| 648 request->socketcall_req.args.sendmsg.msg->msg_namelen; | |
| 649 request->socketcall_req.args.sendto.len = | |
| 650 request->socketcall_req.args.sendmsg.msg->msg_iov->iov_len; | |
| 651 request->socketcall_req.args.sendto.buf = | |
| 652 request->socketcall_req.args.sendmsg.msg->msg_iov->iov_base; | |
| 653 } | |
| 654 if (call == SYS_SENDTO && !request->socketcall_req.args.sendto.to) { | |
| 655 // sendto() with a NULL address is the same as send() | |
| 656 call = SYS_SEND; | |
| 657 numExtraData = 0; | |
| 658 } | |
| 659 if (call == SYS_SEND && !request->socketcall_req.args.send.flags) { | |
| 660 // send() with no flags is the same as write(), which is unrestricted | |
| 661 // in seccomp mode. | |
| 662 Debug::message("Replaced socketcall() with call to write()"); | |
| 663 ssize_t rc = sys.write(request->socketcall_req.args.send.sockfd, | |
| 664 request->socketcall_req.args.send.buf, | |
| 665 request->socketcall_req.args.send.len); | |
| 666 if (rc < 0) { | |
| 667 Debug::elapsed(tm, __NR_socketcall, call); | |
| 668 return -sys.my_errno; | |
| 669 } else { | |
| 670 Debug::elapsed(tm, __NR_socketcall, call); | |
| 671 return rc; | |
| 672 } | |
| 673 } | |
| 674 | |
| 675 // Simplify recv(), and recvfrom(), if there are simpler equivalent calls. | |
| 676 // This allows us to occasionally replace them with calls to read(), which | |
| 677 // don't have to be forwarded to the trusted process. | |
| 678 // We cannot simplify recvmsg() to recvfrom(), recv() or read(), as we do | |
| 679 // not know whether the caller needs us to set msg->msg_flags. | |
| 680 if (call == SYS_RECVFROM && !request->socketcall_req.args.recvfrom.from) { | |
| 681 // recvfrom() with a NULL address buffer is the same as recv() | |
| 682 call = SYS_RECV; | |
| 683 } | |
| 684 if (call == SYS_RECV && !request->socketcall_req.args.recv.flags) { | |
| 685 // recv() with no flags is the same as read(), which is unrestricted | |
| 686 // in seccomp mode. | |
| 687 Debug::message("Replaced socketcall() with call to read()"); | |
| 688 ssize_t rc = sys.read(request->socketcall_req.args.recv.sockfd, | |
| 689 request->socketcall_req.args.recv.buf, | |
| 690 request->socketcall_req.args.recv.len); | |
| 691 if (rc < 0) { | |
| 692 Debug::elapsed(tm, __NR_socketcall, call); | |
| 693 return -sys.my_errno; | |
| 694 } else { | |
| 695 Debug::elapsed(tm, __NR_socketcall, call); | |
| 696 return rc; | |
| 697 } | |
| 698 } | |
| 699 | |
| 700 // Fill in the rest of the request header. | |
| 701 request->sysnum = __NR_socketcall; | |
| 702 request->cookie = cookie(); | |
| 703 request->socketcall_req.call = call; | |
| 704 request->socketcall_req.arg_ptr = args; | |
| 705 int padding = sizeof(request->socketcall_req.args) - | |
| 706 socketCallArgInfo[call].len; | |
| 707 if (padding > 0) { | |
| 708 memset((char *)(&request->socketcall_req.args + 1) - padding, 0, padding); | |
| 709 } | |
| 710 if (call == SYS_SENDMSG) { | |
| 711 // for sendmsg() we include the (optional) destination address, and the | |
| 712 // (optional) control data in the payload. | |
| 713 SendMsg *sendmsg_args = reinterpret_cast<SendMsg *>(args); | |
| 714 memcpy(reinterpret_cast<char *>( | |
| 715 memcpy(reinterpret_cast<char *>( | |
| 716 memcpy(request + 1, sendmsg_args->msg, sizeof(*sendmsg_args->msg))) + | |
| 717 sizeof(*sendmsg_args->msg), | |
| 718 sendmsg_args->msg->msg_name, sendmsg_args->msg->msg_namelen)) + | |
| 719 sendmsg_args->msg->msg_namelen, | |
| 720 sendmsg_args->msg->msg_control, sendmsg_args->msg->msg_controllen); | |
| 721 } else if (extraDataAddr) { | |
| 722 memcpy(request + 1, extraDataAddr, numExtraData); | |
| 723 } | |
| 724 | |
| 725 // Send request to trusted process and collect response from trusted thread. | |
| 726 long rc; | |
| 727 ssize_t len = sizeof(struct Request) + numExtraData; | |
| 728 if (write(sys, processFdPub(), data, len) != len || | |
| 729 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | |
| 730 die("Failed to forward socketcall() request [sandbox]"); | |
| 731 } | |
| 732 Debug::elapsed(tm, __NR_socketcall, call); | |
| 733 return rc; | |
| 734 } | |
| 735 | |
| 736 bool Sandbox::process_socketcall(int parentMapsFd, int sandboxFd, | |
| 737 int threadFdPub, int threadFd, | |
| 738 SecureMem::Args* mem) { | |
| 739 // Read request | |
| 740 SocketCall socketcall_req; | |
| 741 SysCalls sys; | |
| 742 if (read(sys, sandboxFd, &socketcall_req, sizeof(socketcall_req)) != | |
| 743 sizeof(socketcall_req)) { | |
| 744 die("Failed to read parameters for socketcall() [process]"); | |
| 745 } | |
| 746 | |
| 747 // sandbox_socketcall() should never send us an unexpected "call" opcode. | |
| 748 // If it did, something went very wrong and we better terminate the process. | |
| 749 if (socketcall_req.call < SYS_SOCKET || socketcall_req.call > SYS_ACCEPT4) { | |
| 750 die("Unexpected socketcall() [process]"); | |
| 751 } | |
| 752 | |
| 753 // Check if this particular operation carries an extra payload. | |
| 754 socklen_t numExtraData = 0; | |
| 755 if (socketCallArgInfo[socketcall_req.call].lengthOff) { | |
| 756 memcpy(&numExtraData, | |
| 757 reinterpret_cast<char *>(&socketcall_req) + | |
| 758 socketCallArgInfo[socketcall_req.call].lengthOff, | |
| 759 sizeof(socklen_t)); | |
| 760 } else if (socketcall_req.call == SYS_SENDMSG) { | |
| 761 numExtraData = sizeof(*socketcall_req.args.sendmsg.msg); | |
| 762 } else if (socketcall_req.call == SYS_RECVMSG) { | |
| 763 numExtraData = sizeof(*socketcall_req.args.recvmsg.msg); | |
| 764 } | |
| 765 | |
| 766 // Verify that the length for the payload is reasonable. We don't want to | |
| 767 // blow up our stack, and excessive (or negative) buffer sizes are almost | |
| 768 // certainly a bug. | |
| 769 if (numExtraData > 4096) { | |
| 770 die("Unexpected size for socketcall() payload [process]"); | |
| 771 } | |
| 772 | |
| 773 // Read the extra payload, if any. | |
| 774 char extra[numExtraData]; | |
| 775 if (numExtraData) { | |
| 776 if (read(sys, sandboxFd, extra, numExtraData) != (ssize_t)numExtraData) { | |
| 777 die("Failed to read socketcall() payload [process]"); | |
| 778 } | |
| 779 } | |
| 780 | |
| 781 // sendmsg() has another level of indirection and can carry even more payload | |
| 782 ssize_t numSendmsgExtra = 0; | |
| 783 if (socketcall_req.call == SYS_SENDMSG) { | |
| 784 struct msghdr* msg = reinterpret_cast<struct msghdr*>(extra); | |
| 785 if (msg->msg_namelen > 4096 || msg->msg_controllen > 4096) { | |
| 786 die("Unexpected size for socketcall() payload [process]"); | |
| 787 } | |
| 788 numSendmsgExtra = msg->msg_namelen + msg->msg_controllen; | |
| 789 } | |
| 790 char sendmsgExtra[numSendmsgExtra]; | |
| 791 if (numSendmsgExtra) { | |
| 792 if (read(sys, sandboxFd, sendmsgExtra, numSendmsgExtra) != | |
| 793 numSendmsgExtra) { | |
| 794 die("Failed to read socketcall() payload [process]"); | |
| 795 } | |
| 796 } | |
| 797 | |
| 798 int rc = -EINVAL; | |
| 799 switch (socketcall_req.call) { | |
| 800 case SYS_SOCKET: | |
| 801 // The sandbox does not allow creation of any new sockets. | |
| 802 goto deny; | |
| 803 case SYS_BIND: | |
| 804 // The sandbox does not allow binding an address to a socket. | |
| 805 goto deny; | |
| 806 case SYS_CONNECT: | |
| 807 // The sandbox does not allow connecting a socket. | |
| 808 goto deny; | |
| 809 case SYS_LISTEN: | |
| 810 // The sandbox does not allow a socket to enter listening state. | |
| 811 goto deny; | |
| 812 case SYS_ACCEPT4: | |
| 813 case SYS_ACCEPT: | |
| 814 // If the sandbox obtained a socket that is already in the listening | |
| 815 // state (e.g. because somebody sent it a suitable file descriptor), it | |
| 816 // is permissible to call accept(). | |
| 817 | |
| 818 accept_simple: | |
| 819 // None of the parameters need to be checked, so it is OK to refer | |
| 820 // to the parameter block created by the untrusted code. | |
| 821 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, __NR_socketcall, | |
| 822 socketcall_req.call, socketcall_req.arg_ptr); | |
| 823 return true; | |
| 824 case SYS_GETSOCKNAME: | |
| 825 case SYS_GETPEERNAME: | |
| 826 // Querying the local and the remote name is not considered security | |
| 827 // sensitive for the purposes of the sandbox. | |
| 828 goto accept_simple; | |
| 829 case SYS_SOCKETPAIR: | |
| 830 // Socket pairs are connected to each other and not considered | |
| 831 // security sensitive. | |
| 832 goto accept_simple; | |
| 833 case SYS_SENDTO: | |
| 834 if (socketcall_req.args.sendto.to) { | |
| 835 // The sandbox does not allow sending to arbitrary addresses. | |
| 836 goto deny; | |
| 837 } | |
| 838 // Fall through | |
| 839 case SYS_SEND: | |
| 840 if (socketcall_req.args.send.flags & | |
| 841 ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB)) { | |
| 842 // Unsupported flag encountered. Deny the call. | |
| 843 goto deny; | |
| 844 } | |
| 845 // Sending data on a connected socket is similar to calling write(). | |
| 846 // Allow it. | |
| 847 | |
| 848 accept_complex: | |
| 849 // The parameter block contains potentially security critical information | |
| 850 // that should not be tampered with after it has been inspected. Copy it | |
| 851 // into the write-protected securely shared memory before telling the | |
| 852 // trusted thread to execute the socket call. | |
| 853 SecureMem::lockSystemCall(parentMapsFd, mem); | |
| 854 memcpy(mem->pathname, &socketcall_req.args, sizeof(socketcall_req.args)); | |
| 855 SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, | |
| 856 __NR_socketcall, socketcall_req.call, | |
| 857 mem->pathname - (char*)mem + (char*)mem->self); | |
| 858 return true; | |
| 859 case SYS_RECVFROM: | |
| 860 // While we do not anticipate any particular need to receive data on | |
| 861 // unconnected sockets, there is no particular risk in doing so. | |
| 862 // Fall through | |
| 863 case SYS_RECV: | |
| 864 if (socketcall_req.args.recv.flags & | |
| 865 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | |
| 866 // Unsupported flag encountered. Deny the call. | |
| 867 goto deny; | |
| 868 } | |
| 869 // Receiving data on a connected socket is similar to calling read(). | |
| 870 // Allow it. | |
| 871 goto accept_complex; | |
| 872 case SYS_SHUTDOWN: | |
| 873 // Shutting down a socket is always OK. | |
| 874 goto accept_simple; | |
| 875 case SYS_SETSOCKOPT: | |
| 876 switch (socketcall_req.args.setsockopt.level) { | |
| 877 case SOL_SOCKET: | |
| 878 switch (socketcall_req.args.setsockopt.optname) { | |
| 879 case SO_KEEPALIVE: | |
| 880 case SO_LINGER: | |
| 881 case SO_OOBINLINE: | |
| 882 case SO_RCVBUF: | |
| 883 case SO_RCVLOWAT: | |
| 884 case SO_SNDLOWAT: | |
| 885 case SO_RCVTIMEO: | |
| 886 case SO_SNDTIMEO: | |
| 887 case SO_REUSEADDR: | |
| 888 case SO_SNDBUF: | |
| 889 case SO_TIMESTAMP: | |
| 890 goto accept_complex; | |
| 891 default: | |
| 892 break; | |
| 893 } | |
| 894 break; | |
| 895 case IPPROTO_TCP: | |
| 896 switch (socketcall_req.args.setsockopt.optname) { | |
| 897 case TCP_CORK: | |
| 898 case TCP_DEFER_ACCEPT: | |
| 899 case TCP_INFO: | |
| 900 case TCP_KEEPCNT: | |
| 901 case TCP_KEEPIDLE: | |
| 902 case TCP_KEEPINTVL: | |
| 903 case TCP_LINGER2: | |
| 904 case TCP_MAXSEG: | |
| 905 case TCP_NODELAY: | |
| 906 case TCP_QUICKACK: | |
| 907 case TCP_SYNCNT: | |
| 908 case TCP_WINDOW_CLAMP: | |
| 909 goto accept_complex; | |
| 910 default: | |
| 911 break; | |
| 912 } | |
| 913 break; | |
| 914 default: | |
| 915 break; | |
| 916 } | |
| 917 goto deny; | |
| 918 case SYS_GETSOCKOPT: | |
| 919 switch (socketcall_req.args.getsockopt.level) { | |
| 920 case SOL_SOCKET: | |
| 921 switch (socketcall_req.args.getsockopt.optname) { | |
| 922 case SO_ACCEPTCONN: | |
| 923 case SO_ERROR: | |
| 924 case SO_KEEPALIVE: | |
| 925 case SO_LINGER: | |
| 926 case SO_OOBINLINE: | |
| 927 case SO_RCVBUF: | |
| 928 case SO_RCVLOWAT: | |
| 929 case SO_SNDLOWAT: | |
| 930 case SO_RCVTIMEO: | |
| 931 case SO_SNDTIMEO: | |
| 932 case SO_REUSEADDR: | |
| 933 case SO_SNDBUF: | |
| 934 case SO_TIMESTAMP: | |
| 935 case SO_TYPE: | |
| 936 goto accept_complex; | |
| 937 default: | |
| 938 break; | |
| 939 } | |
| 940 break; | |
| 941 case IPPROTO_TCP: | |
| 942 switch (socketcall_req.args.getsockopt.optname) { | |
| 943 case TCP_CORK: | |
| 944 case TCP_DEFER_ACCEPT: | |
| 945 case TCP_INFO: | |
| 946 case TCP_KEEPCNT: | |
| 947 case TCP_KEEPIDLE: | |
| 948 case TCP_KEEPINTVL: | |
| 949 case TCP_LINGER2: | |
| 950 case TCP_MAXSEG: | |
| 951 case TCP_NODELAY: | |
| 952 case TCP_QUICKACK: | |
| 953 case TCP_SYNCNT: | |
| 954 case TCP_WINDOW_CLAMP: | |
| 955 goto accept_complex; | |
| 956 default: | |
| 957 break; | |
| 958 } | |
| 959 break; | |
| 960 default: | |
| 961 break; | |
| 962 } | |
| 963 goto deny; | |
| 964 case SYS_SENDMSG: { | |
| 965 struct msghdr* msg = reinterpret_cast<struct msghdr*>(extra); | |
| 966 | |
| 967 if (sizeof(socketcall_req.args) + sizeof(*msg) + numSendmsgExtra > | |
| 968 sizeof(mem->pathname)) { | |
| 969 goto deny; | |
| 970 } | |
| 971 | |
| 972 if (msg->msg_namelen || | |
| 973 (socketcall_req.args.sendmsg.flags & | |
| 974 ~(MSG_CONFIRM|MSG_DONTWAIT|MSG_EOR|MSG_MORE|MSG_NOSIGNAL|MSG_OOB))){ | |
| 975 goto deny; | |
| 976 } | |
| 977 | |
| 978 // The trusted process receives file handles when a new untrusted thread | |
| 979 // gets created. We have security checks in place that prevent any | |
| 980 // critical information from being tampered with during thread creation. | |
| 981 // But if we disallowed passing of file handles, this would add an extra | |
| 982 // hurdle for an attacker. | |
| 983 // Unfortunately, for now, this is not possible as Chrome's | |
| 984 // base::SendRecvMsg() needs the ability to pass file handles. | |
| 985 if (msg->msg_controllen) { | |
| 986 msg->msg_control = sendmsgExtra + msg->msg_namelen; | |
| 987 struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); | |
| 988 do { | |
| 989 if (cmsg->cmsg_level != SOL_SOCKET || | |
| 990 cmsg->cmsg_type != SCM_RIGHTS) { | |
| 991 goto deny; | |
| 992 } | |
| 993 } while ((cmsg = CMSG_NXTHDR(msg, cmsg)) != NULL); | |
| 994 } | |
| 995 | |
| 996 // This must be a locked system call, because we have to ensure that | |
| 997 // the untrusted code does not tamper with the msghdr after we have | |
| 998 // examined it. | |
| 999 SecureMem::lockSystemCall(parentMapsFd, mem); | |
| 1000 socketcall_req.args.sendmsg.msg = | |
| 1001 reinterpret_cast<struct msghdr*>(mem->pathname + | |
| 1002 sizeof(socketcall_req.args) - | |
| 1003 (char*)mem + (char*)mem->self); | |
| 1004 memcpy(mem->pathname, &socketcall_req.args, sizeof(socketcall_req.args)); | |
| 1005 if (numSendmsgExtra) { | |
| 1006 if (msg->msg_namelen > 0) { | |
| 1007 msg->msg_name = const_cast<struct msghdr*>( | |
| 1008 socketcall_req.args.sendmsg.msg) + 1; | |
| 1009 } | |
| 1010 if (msg->msg_controllen > 0) { | |
| 1011 msg->msg_control = (char *)( | |
| 1012 socketcall_req.args.sendmsg.msg + 1) + msg->msg_namelen; | |
| 1013 } | |
| 1014 memcpy(mem->pathname + sizeof(socketcall_req.args) + sizeof(*msg), | |
| 1015 sendmsgExtra, numSendmsgExtra); | |
| 1016 } | |
| 1017 memcpy(mem->pathname + sizeof(socketcall_req.args), msg, sizeof(*msg)); | |
| 1018 SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, | |
| 1019 __NR_socketcall, socketcall_req.call, | |
| 1020 mem->pathname - (char*)mem + (char*)mem->self); | |
| 1021 return true; | |
| 1022 } | |
| 1023 case SYS_RECVMSG: | |
| 1024 // Receiving messages is general not security critical. | |
| 1025 if (socketcall_req.args.recvmsg.flags & | |
| 1026 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | |
| 1027 goto deny; | |
| 1028 } | |
| 1029 goto accept_complex; | |
| 1030 default: | |
| 1031 deny: | |
| 1032 SecureMem::abandonSystemCall(threadFd, rc); | |
| 1033 return false; | |
| 1034 } | |
| 1035 } | |
| 1036 | |
| 1037 #endif | |
| 1038 | |
| 1039 } // namespace | |
| OLD | NEW |