From d4195672b7571e105cd9fb3891334d8c734ae775 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 24 Jun 2020 22:57:37 +0200 Subject: [PATCH] Kernel+LibC: Add sys$recvfd() and sys$sendfd() for fd passing These new syscalls allow you to send and receive file descriptors over a local domain socket. This will enable various privilege separation techniques and other good stuff. :^) --- Kernel/Net/LocalSocket.cpp | 50 ++++++++++++++++++++++++++++++++++- Kernel/Net/LocalSocket.h | 8 ++++++ Kernel/Process.cpp | 49 ++++++++++++++++++++++++++++++++++ Kernel/Process.h | 4 +++ Kernel/Syscall.h | 4 ++- Libraries/LibC/sys/socket.cpp | 12 +++++++++ Libraries/LibC/sys/socket.h | 2 ++ 7 files changed, 127 insertions(+), 2 deletions(-) diff --git a/Kernel/Net/LocalSocket.cpp b/Kernel/Net/LocalSocket.cpp index 0b074813cea..fee7b67d025 100644 --- a/Kernel/Net/LocalSocket.cpp +++ b/Kernel/Net/LocalSocket.cpp @@ -29,8 +29,8 @@ #include #include #include -#include #include +#include #include //#define DEBUG_LOCAL_SOCKET @@ -397,4 +397,52 @@ KResult LocalSocket::chown(FileDescription&, uid_t uid, gid_t gid) return KSuccess; } +NonnullRefPtrVector& LocalSocket::recvfd_queue_for(FileDescription& description) +{ + auto role = this->role(description); + if (role == Role::Connected) + return m_fds_for_client; + if (role == Role::Accepted) + return m_fds_for_server; + ASSERT_NOT_REACHED(); +} + +NonnullRefPtrVector& LocalSocket::sendfd_queue_for(FileDescription& description) +{ + auto role = this->role(description); + if (role == Role::Connected) + return m_fds_for_server; + if (role == Role::Accepted) + return m_fds_for_client; + ASSERT_NOT_REACHED(); +} + +KResult LocalSocket::sendfd(FileDescription& socket_description, NonnullRefPtr passing_description) +{ + LOCKER(lock()); + auto role = this->role(socket_description); + if (role != Role::Connected && role != Role::Accepted) + return KResult(-EINVAL); + auto& queue = sendfd_queue_for(socket_description); + // FIXME: Figure out how we should limit this properly. + if (queue.size() > 16) + return KResult(-EBUSY); + queue.append(move(passing_description)); + return KSuccess; +} + +KResultOr> LocalSocket::recvfd(FileDescription& socket_description) +{ + LOCKER(lock()); + auto role = this->role(socket_description); + if (role != Role::Connected && role != Role::Accepted) + return KResult(-EINVAL); + auto& queue = recvfd_queue_for(socket_description); + if (queue.is_empty()) { + // FIXME: Figure out the perfect error code for this. + return KResult(-EAGAIN); + } + return queue.take_first(); +} + } diff --git a/Kernel/Net/LocalSocket.h b/Kernel/Net/LocalSocket.h index 8435480d535..0d50b77bdda 100644 --- a/Kernel/Net/LocalSocket.h +++ b/Kernel/Net/LocalSocket.h @@ -42,6 +42,9 @@ public: static KResultOr> create(int type); virtual ~LocalSocket() override; + KResult sendfd(FileDescription& socket_description, NonnullRefPtr passing_description); + KResultOr> recvfd(FileDescription& socket_description); + static void for_each(Function); StringView socket_path() const; @@ -71,6 +74,8 @@ private: static Lockable>& all_sockets(); DoubleBuffer& receive_buffer_for(FileDescription&); DoubleBuffer& send_buffer_for(FileDescription&); + NonnullRefPtrVector& sendfd_queue_for(FileDescription&); + NonnullRefPtrVector& recvfd_queue_for(FileDescription&); // An open socket file on the filesystem. RefPtr m_file; @@ -100,6 +105,9 @@ private: DoubleBuffer m_for_client; DoubleBuffer m_for_server; + NonnullRefPtrVector m_fds_for_client; + NonnullRefPtrVector m_fds_for_server; + // for InlineLinkedList LocalSocket* m_prev { nullptr }; LocalSocket* m_next { nullptr }; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 80ea2e8de6f..687aa25025c 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -5178,4 +5179,52 @@ KResult Process::poke_user_data(u32* address, u32 data) return KResult(KSuccess); } +int Process::sys$sendfd(int sockfd, int fd) +{ + REQUIRE_PROMISE(sendfd); + auto socket_description = file_description(sockfd); + if (!socket_description) + return -EBADF; + if (!socket_description->is_socket()) + return -ENOTSOCK; + auto& socket = *socket_description->socket(); + if (!socket.is_local()) + return -EAFNOSUPPORT; + if (!socket.is_connected()) + return -ENOTCONN; + + auto passing_descriptor = file_description(fd); + if (!passing_descriptor) + return -EBADF; + + auto& local_socket = static_cast(socket); + return local_socket.sendfd(*socket_description, *passing_descriptor); +} + +int Process::sys$recvfd(int sockfd) +{ + REQUIRE_PROMISE(recvfd); + auto socket_description = file_description(sockfd); + if (!socket_description) + return -EBADF; + if (!socket_description->is_socket()) + return -ENOTSOCK; + auto& socket = *socket_description->socket(); + if (!socket.is_local()) + return -EAFNOSUPPORT; + + int new_fd = alloc_fd(); + if (new_fd < 0) + return new_fd; + + auto& local_socket = static_cast(socket); + auto received_descriptor_or_error = local_socket.recvfd(*socket_description); + + if (received_descriptor_or_error.is_error()) + return received_descriptor_or_error.error(); + + m_fds[new_fd].set(*received_descriptor_or_error.value(), 0); + return new_fd; +} + } diff --git a/Kernel/Process.h b/Kernel/Process.h index 0dfc939343f..cdebf6dc671 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -65,6 +65,8 @@ extern VirtualAddress g_return_to_ring3_from_signal_trampoline; __ENUMERATE_PLEDGE_PROMISE(proc) \ __ENUMERATE_PLEDGE_PROMISE(exec) \ __ENUMERATE_PLEDGE_PROMISE(unix) \ + __ENUMERATE_PLEDGE_PROMISE(recvfd) \ + __ENUMERATE_PLEDGE_PROMISE(sendfd) \ __ENUMERATE_PLEDGE_PROMISE(fattr) \ __ENUMERATE_PLEDGE_PROMISE(tty) \ __ENUMERATE_PLEDGE_PROMISE(chown) \ @@ -316,6 +318,8 @@ public: int sys$perf_event(int type, FlatPtr arg1, FlatPtr arg2); int sys$get_stack_bounds(FlatPtr* stack_base, size_t* stack_size); int sys$ptrace(const Syscall::SC_ptrace_params*); + int sys$sendfd(int sockfd, int fd); + int sys$recvfd(int sockfd); template int get_sock_or_peer_name(const Params&); diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index c07fdffe70e..8c11349c168 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -189,7 +189,9 @@ namespace Kernel { __ENUMERATE_SYSCALL(shutdown) \ __ENUMERATE_SYSCALL(get_stack_bounds) \ __ENUMERATE_SYSCALL(ptrace) \ - __ENUMERATE_SYSCALL(minherit) + __ENUMERATE_SYSCALL(minherit) \ + __ENUMERATE_SYSCALL(sendfd) \ + __ENUMERATE_SYSCALL(recvfd) namespace Syscall { diff --git a/Libraries/LibC/sys/socket.cpp b/Libraries/LibC/sys/socket.cpp index 81e83aaed75..62e27a20f82 100644 --- a/Libraries/LibC/sys/socket.cpp +++ b/Libraries/LibC/sys/socket.cpp @@ -119,4 +119,16 @@ int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen) int rc = syscall(SC_getpeername, ¶ms); __RETURN_WITH_ERRNO(rc, rc, -1); } + +int sendfd(int sockfd, int fd) +{ + int rc = syscall(SC_sendfd, sockfd, fd); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int recvfd(int sockfd) +{ + int rc = syscall(SC_recvfd, sockfd); + __RETURN_WITH_ERRNO(rc, rc, -1); +} } diff --git a/Libraries/LibC/sys/socket.h b/Libraries/LibC/sys/socket.h index 2e98f478655..269158beb10 100644 --- a/Libraries/LibC/sys/socket.h +++ b/Libraries/LibC/sys/socket.h @@ -100,5 +100,7 @@ int getsockopt(int sockfd, int level, int option, void*, socklen_t*); int setsockopt(int sockfd, int level, int option, const void*, socklen_t); int getsockname(int sockfd, struct sockaddr*, socklen_t*); int getpeername(int sockfd, struct sockaddr*, socklen_t*); +int sendfd(int sockfd, int fd); +int recvfd(int sockfd); __END_DECLS