summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-06-24 22:57:37 +0200
committerAndreas Kling <kling@serenityos.org>2020-06-24 23:08:09 +0200
commitd4195672b7571e105cd9fb3891334d8c734ae775 (patch)
tree6c2797a945aa3fd8f88d31fb9ea9bbb443bb89a4
parentcd02144a06d4b648effdb744e00b664a871bf52b (diff)
downloadserenity-d4195672b7571e105cd9fb3891334d8c734ae775.zip
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. :^)
-rw-r--r--Kernel/Net/LocalSocket.cpp50
-rw-r--r--Kernel/Net/LocalSocket.h8
-rw-r--r--Kernel/Process.cpp49
-rw-r--r--Kernel/Process.h4
-rw-r--r--Kernel/Syscall.h4
-rw-r--r--Libraries/LibC/sys/socket.cpp12
-rw-r--r--Libraries/LibC/sys/socket.h2
7 files changed, 127 insertions, 2 deletions
diff --git a/Kernel/Net/LocalSocket.cpp b/Kernel/Net/LocalSocket.cpp
index 0b074813ce..fee7b67d02 100644
--- a/Kernel/Net/LocalSocket.cpp
+++ b/Kernel/Net/LocalSocket.cpp
@@ -29,8 +29,8 @@
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Net/LocalSocket.h>
#include <Kernel/Process.h>
-#include <Kernel/UnixTypes.h>
#include <Kernel/StdLib.h>
+#include <Kernel/UnixTypes.h>
#include <LibC/errno_numbers.h>
//#define DEBUG_LOCAL_SOCKET
@@ -397,4 +397,52 @@ KResult LocalSocket::chown(FileDescription&, uid_t uid, gid_t gid)
return KSuccess;
}
+NonnullRefPtrVector<FileDescription>& 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<FileDescription>& 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<FileDescription> 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<NonnullRefPtr<FileDescription>> 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 8435480d53..0d50b77bdd 100644
--- a/Kernel/Net/LocalSocket.h
+++ b/Kernel/Net/LocalSocket.h
@@ -42,6 +42,9 @@ public:
static KResultOr<NonnullRefPtr<Socket>> create(int type);
virtual ~LocalSocket() override;
+ KResult sendfd(FileDescription& socket_description, NonnullRefPtr<FileDescription> passing_description);
+ KResultOr<NonnullRefPtr<FileDescription>> recvfd(FileDescription& socket_description);
+
static void for_each(Function<void(const LocalSocket&)>);
StringView socket_path() const;
@@ -71,6 +74,8 @@ private:
static Lockable<InlineLinkedList<LocalSocket>>& all_sockets();
DoubleBuffer& receive_buffer_for(FileDescription&);
DoubleBuffer& send_buffer_for(FileDescription&);
+ NonnullRefPtrVector<FileDescription>& sendfd_queue_for(FileDescription&);
+ NonnullRefPtrVector<FileDescription>& recvfd_queue_for(FileDescription&);
// An open socket file on the filesystem.
RefPtr<FileDescription> m_file;
@@ -100,6 +105,9 @@ private:
DoubleBuffer m_for_client;
DoubleBuffer m_for_server;
+ NonnullRefPtrVector<FileDescription> m_fds_for_client;
+ NonnullRefPtrVector<FileDescription> 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 80ea2e8de6..687aa25025 100644
--- a/Kernel/Process.cpp
+++ b/Kernel/Process.cpp
@@ -55,6 +55,7 @@
#include <Kernel/KSyms.h>
#include <Kernel/Module.h>
#include <Kernel/Multiboot.h>
+#include <Kernel/Net/LocalSocket.h>
#include <Kernel/Net/Socket.h>
#include <Kernel/PerformanceEventBuffer.h>
#include <Kernel/Process.h>
@@ -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<LocalSocket&>(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<LocalSocket&>(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 0dfc939343..cdebf6dc67 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<bool sockname, typename Params>
int get_sock_or_peer_name(const Params&);
diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h
index c07fdffe70..8c11349c16 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 81e83aaed7..62e27a20f8 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, &params);
__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 2e98f47865..269158beb1 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