blob: 2671e4414ad5b1f48d7eceb3dd55619fe667d29e [file] [log] [blame]
// Copyright 2016 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "broker_service.h"
#include <errno.h>
#include <fcntl.h>
#include <limits.h> // PATH_MAX
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <memory>
#include <string>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include <pthread.h>
namespace broker_service {
BrokerService::BrokerService(DBus::Connection& conn, const char* path,
const char* name)
: DBus::ObjectProxy(conn, path, name) {}
void BrokerService::RemoveConnection(int fd) {
active_requests.erase(fd);
close(fd);
}
void BrokerService::HandleRequest(const std::unique_ptr<Connection>& conn,
int sockfd) {
cmsghdr* cmsg;
msghdr msg = {0};
iovec iov = {0};
char dummy = '!';
LOG(INFO) << __func__ << ": Requesting file descriptor to '" << conn->path
<< "' from permission broker ...";
// Request permission from permission broker.
DBus::FileDescriptor fd;
try {
fd = this->OpenPath(std::string(conn->path));
} catch (...) {
// DBus throws an exception when it the path is denied.
// Don't break off here, we have to return -1 to the asking process.
fd = -1;
}
LOG(INFO) << __func__ << ": Received file descriptor '" << fd.get()
<< "' from permission broker.";
iov.iov_base = &dummy;
iov.iov_len = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char msg_buf[CMSG_SPACE(sizeof(int))] = {0};
msg.msg_control = msg_buf;
msg.msg_controllen = sizeof(msg_buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = msg.msg_controllen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
int* fdptr = reinterpret_cast<int*>(CMSG_DATA(cmsg));
if (fd.get() < 0) {
msg.msg_control = NULL;
msg.msg_controllen = 0;
} else {
fdptr[0] = fd.get();
}
int rc = sendmsg(sockfd, &msg, 0);
if (rc < 0) {
PLOG(INFO) << __func__ << ": sendmsg";
return;
}
LOG(INFO) << __func__ << ": Sent file descriptor to client.";
if (fd.get() != -1) {
close(fd.get());
}
}
void BrokerService::RunService(const char* sockname, int socknamelen) {
sockaddr_un addr = {0};
int maxpath = sizeof(addr.sun_path);
int pathlen = socknamelen;
if (pathlen >= maxpath) {
LOG(FATAL) << __func__ << ": pathlen(" << pathlen << ") >= maxpath("
<< maxpath << ")";
}
// Upstart event should be "socket".
const char* env_var = getenv("UPSTART_EVENTS");
if (!env_var) {
LOG(FATAL) << "UPSTART_EVENTS : not called by upstart.";
}
if (strcasecmp(env_var, "socket")) {
// Not a socket event.
LOG(FATAL) << "UPSTART_EVENTS(" << env_var
<< "): called by upstart, but not a socket event. ";
}
// Upstart listens to the socket and gives a good to go socket fd on
// which we can accept(...).
env_var = getenv("UPSTART_FDS");
if (!env_var) {
LOG(FATAL) << "UPSTART_FDS: cannot get legit socket";
}
int upstart_sockfd = atoi(env_var);
if (upstart_sockfd < 0) {
LOG(FATAL) << "UPSTART_FDS(" << upstart_sockfd << "): bad socket";
}
char recv_buf[256];
int rc;
while (true) {
LOG(INFO) << "Setting up fds for poll(...) ...";
int num_clients = active_requests.size();
pollfd fds[num_clients + 1];
fds[0].fd = upstart_sockfd;
fds[0].events = POLLIN;
int i = 1;
for (auto it = active_requests.begin(); it != active_requests.end();
++it, ++i) {
fds[i].fd = it->first;
fds[i].events = POLLIN;
}
int active_sockets =
poll(fds, num_clients + 1, active_requests.empty() ? 15000 : -1);
if (active_sockets <= 0) {
PLOG(ERROR) << "select failed, exiting cleanly";
break;
}
if (fds[0].revents & POLLIN) {
int sockfd = accept4(upstart_sockfd, NULL, NULL, SOCK_NONBLOCK);
if (sockfd < 0) {
PLOG(ERROR) << __func__ << ": accept";
continue;
}
LOG(INFO) << "Got a new client (sockfd = " << sockfd << ")";
active_requests[sockfd] = std::unique_ptr<Connection>(new Connection());
}
for (int i = 1; i <= num_clients; ++i) {
int fd = fds[i].fd;
if ((fds[i].revents & POLLIN) && !(active_requests[fd]->PathOk())) {
rc = recv(fd, recv_buf, sizeof(recv_buf), 0);
if (rc < 0) {
// Some other error occurred.
PLOG(ERROR) << __func__ << ": recv";
RemoveConnection(fd);
continue;
}
if (rc + active_requests[fd]->path_len <= PATH_MAX) {
memcpy(&active_requests[fd]->path[active_requests[fd]->path_len],
recv_buf, rc);
active_requests[fd]->path_len += rc;
if (active_requests[fd]->PathOk()) {
HandleRequest(active_requests[fd], fd);
RemoveConnection(fd);
}
continue;
}
// Data is too large to fit in path.
RemoveConnection(fd);
}
}
}
close(upstart_sockfd);
}
} // namespace broker_service
using base::FilePath;
using broker_service::BrokerService;
using broker_service::kPermissionBrokerPath;
using broker_service::kPermissionBrokerName;
int main(int argc, char** argv) {
brillo::FlagHelper::Init(argc, argv, "broker_service");
brillo::OpenLog("broker_service", true);
brillo::InitLog(brillo::kLogToSyslog);
DBus::BusDispatcher dispatcher;
DBus::default_dispatcher = &dispatcher;
DBus::Connection conn = DBus::Connection::SystemBus();
const char kBrokerSocketPath[] = "/run/broker_service/adb";
constexpr size_t kBrokerSocketPathLen = sizeof(kBrokerSocketPath) - 1;
BrokerService service(conn, kPermissionBrokerPath, kPermissionBrokerName);
service.RunService(kBrokerSocketPath, kBrokerSocketPathLen);
return 0;
}