diff options
author | Andreas Kling <kling@serenityos.org> | 2020-06-24 22:57:37 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-06-24 23:08:09 +0200 |
commit | d4195672b7571e105cd9fb3891334d8c734ae775 (patch) | |
tree | 6c2797a945aa3fd8f88d31fb9ea9bbb443bb89a4 | |
parent | cd02144a06d4b648effdb744e00b664a871bf52b (diff) | |
download | serenity-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.cpp | 50 | ||||
-rw-r--r-- | Kernel/Net/LocalSocket.h | 8 | ||||
-rw-r--r-- | Kernel/Process.cpp | 49 | ||||
-rw-r--r-- | Kernel/Process.h | 4 | ||||
-rw-r--r-- | Kernel/Syscall.h | 4 | ||||
-rw-r--r-- | Libraries/LibC/sys/socket.cpp | 12 | ||||
-rw-r--r-- | 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 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, ¶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 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 |