1 // Copyright 2014 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 "sandbox/linux/syscall_broker/broker_client.h"
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <sys/socket.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <utility>
15 
16 #include "base/logging.h"
17 #include "base/pickle.h"
18 #include "base/posix/unix_domain_socket_linux.h"
19 #include "build/build_config.h"
20 #include "sandbox/linux/syscall_broker/broker_channel.h"
21 #include "sandbox/linux/syscall_broker/broker_common.h"
22 #include "sandbox/linux/syscall_broker/broker_policy.h"
23 
24 #if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
25 #define MSG_CMSG_CLOEXEC 0x40000000
26 #endif
27 
28 namespace sandbox {
29 
30 namespace syscall_broker {
31 
32 // Make a remote system call over IPC for syscalls that take a path and flags
33 // as arguments, currently open() and access().
34 // Will return -errno like a real system call.
35 // This function needs to be async signal safe.
PathAndFlagsSyscall(IPCCommand syscall_type,const char * pathname,int flags) const36 int BrokerClient::PathAndFlagsSyscall(IPCCommand syscall_type,
37                                       const char* pathname,
38                                       int flags) const {
39   int recvmsg_flags = 0;
40   RAW_CHECK(syscall_type == COMMAND_OPEN || syscall_type == COMMAND_ACCESS);
41   if (!pathname)
42     return -EFAULT;
43 
44   // For this "remote system call" to work, we need to handle any flag that
45   // cannot be sent over a Unix socket in a special way.
46   // See the comments around kCurrentProcessOpenFlagsMask.
47   if (syscall_type == COMMAND_OPEN && (flags & kCurrentProcessOpenFlagsMask)) {
48     // This implementation only knows about O_CLOEXEC, someone needs to look at
49     // this code if other flags are added.
50     RAW_CHECK(kCurrentProcessOpenFlagsMask == O_CLOEXEC);
51     recvmsg_flags |= MSG_CMSG_CLOEXEC;
52     flags &= ~O_CLOEXEC;
53   }
54 
55   // There is no point in forwarding a request that we know will be denied.
56   // Of course, the real security check needs to be on the other side of the
57   // IPC.
58   if (fast_check_in_client_) {
59     if (syscall_type == COMMAND_OPEN &&
60         !broker_policy_.GetFileNameIfAllowedToOpen(
61             pathname, flags, NULL /* file_to_open */,
62             NULL /* unlink_after_open */)) {
63       return -broker_policy_.denied_errno();
64     }
65     if (syscall_type == COMMAND_ACCESS &&
66         !broker_policy_.GetFileNameIfAllowedToAccess(pathname, flags, NULL)) {
67       return -broker_policy_.denied_errno();
68     }
69   }
70 
71   base::Pickle write_pickle;
72   write_pickle.WriteInt(syscall_type);
73   write_pickle.WriteString(pathname);
74   write_pickle.WriteInt(flags);
75   RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
76 
77   int returned_fd = -1;
78   uint8_t reply_buf[kMaxMessageLength];
79 
80   // Send a request (in write_pickle) as well that will include a new
81   // temporary socketpair (created internally by SendRecvMsg()).
82   // Then read the reply on this new socketpair in reply_buf and put an
83   // eventual attached file descriptor in |returned_fd|.
84   ssize_t msg_len = base::UnixDomainSocket::SendRecvMsgWithFlags(
85       ipc_channel_.get(), reply_buf, sizeof(reply_buf), recvmsg_flags,
86       &returned_fd, write_pickle);
87   if (msg_len <= 0) {
88     if (!quiet_failures_for_tests_)
89       RAW_LOG(ERROR, "Could not make request to broker process");
90     return -ENOMEM;
91   }
92 
93   base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
94   base::PickleIterator iter(read_pickle);
95   int return_value = -1;
96   // Now deserialize the return value and eventually return the file
97   // descriptor.
98   if (iter.ReadInt(&return_value)) {
99     switch (syscall_type) {
100       case COMMAND_ACCESS:
101         // We should never have a fd to return.
102         RAW_CHECK(returned_fd == -1);
103         return return_value;
104       case COMMAND_OPEN:
105         if (return_value < 0) {
106           RAW_CHECK(returned_fd == -1);
107           return return_value;
108         } else {
109           // We have a real file descriptor to return.
110           RAW_CHECK(returned_fd >= 0);
111           return returned_fd;
112         }
113       default:
114         RAW_LOG(ERROR, "Unsupported command");
115         return -ENOSYS;
116     }
117   } else {
118     RAW_LOG(ERROR, "Could not read pickle");
119     NOTREACHED();
120     return -ENOMEM;
121   }
122 }
123 
BrokerClient(const BrokerPolicy & broker_policy,BrokerChannel::EndPoint ipc_channel,bool fast_check_in_client,bool quiet_failures_for_tests)124 BrokerClient::BrokerClient(const BrokerPolicy& broker_policy,
125                            BrokerChannel::EndPoint ipc_channel,
126                            bool fast_check_in_client,
127                            bool quiet_failures_for_tests)
128     : broker_policy_(broker_policy),
129       ipc_channel_(std::move(ipc_channel)),
130       fast_check_in_client_(fast_check_in_client),
131       quiet_failures_for_tests_(quiet_failures_for_tests) {}
132 
~BrokerClient()133 BrokerClient::~BrokerClient() {
134 }
135 
Access(const char * pathname,int mode) const136 int BrokerClient::Access(const char* pathname, int mode) const {
137   return PathAndFlagsSyscall(COMMAND_ACCESS, pathname, mode);
138 }
139 
Open(const char * pathname,int flags) const140 int BrokerClient::Open(const char* pathname, int flags) const {
141   return PathAndFlagsSyscall(COMMAND_OPEN, pathname, flags);
142 }
143 
144 }  // namespace syscall_broker
145 
146 }  // namespace sandbox
147