2/7/2022 7:17 PM, Willem de Bruijn пишет:
If bind() function has already been restricted so the following
listen() function is automatically banned. I agree with Mickaёl about
the usecase here. Why do we need new-bound socket with restricted listening?
The intended use-case is for a privileged process to open a connection
(i.e., bound and connected socket) and pass that to a restricted
process. The intent is for that process to only be allowed to
communicate over this pre-established channel.
In practice, it is able to disconnect (while staying bound) and
elevate its privileges to that of a listening server:
static void child_process(int fd)
{
struct sockaddr addr = { .sa_family = AF_UNSPEC };
int client_fd;
if (listen(fd, 1) == 0)
error(1, 0, "listen succeeded while connected");
if (connect(fd, &addr, sizeof(addr)))
error(1, errno, "disconnect");
if (listen(fd, 1))
error(1, errno, "listen");
client_fd = accept(fd, NULL, NULL);
if (client_fd == -1)
error(1, errno, "accept");
if (close(client_fd))
error(1, errno, "close client");
}
int main(int argc, char **argv)
{
struct sockaddr_in6 addr = { 0 };
pid_t pid;
int fd;
fd = socket(PF_INET6, SOCK_STREAM, 0);
if (fd == -1)
error(1, errno, "socket");
addr.sin6_family = AF_INET6;
addr.sin6_addr = in6addr_loopback;
addr.sin6_port = htons(8001);
if (bind(fd, (void *)&addr, sizeof(addr)))
error(1, errno, "bind");
addr.sin6_port = htons(8000);
if (connect(fd, (void *)&addr, sizeof(addr)))
error(1, errno, "connect");
pid = fork();
if (pid == -1)
error(1, errno, "fork");
if (pid)
wait(NULL);
else
child_process(fd);
if (close(fd))
error(1, errno, "close");
return 0;
}
It's fine to not address this case in this patch series directly, of
course. But we should be aware of the AF_UNSPEC loophole.
I did just notice that with autobind (i.e., without the explicit call
to bind), the socket gets a different local port after listen. So
internally a bind call may be made, which may or may not be correctly
handled by the current landlock implementation already:
Thanks again. I will check it out.
+static void show_local_port(int fd)
+{
+ char addr_str[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 addr = { 0 };
+ socklen_t alen;
+
+ alen = sizeof(addr);
+ if (getsockname(fd, (void *)&addr, &alen))
+ error(1, errno, "getsockname");
+
+ if (addr.sin6_family != AF_INET6)
+ error(1, 0, "getsockname: family");
+
+ if (!inet_ntop(AF_INET6, &addr.sin6_addr, addr_str, sizeof(addr_str)))
+ error(1, errno, "inet_ntop");
+ fprintf(stderr, "server: %s:%hu\n", addr_str, ntohs(addr.sin6_port));
+
+}
+
@@ -23,6 +42,8 @@ static void child_process(int fd)
if (connect(fd, &addr, sizeof(addr)))
error(1, errno, "disconnect");
+ show_local_port(fd);
+
if (listen(fd, 1))
error(1, errno, "listen");
+ show_local_port(fd);
+
@@ -47,10 +68,6 @@ int main(int argc, char **argv)
addr.sin6_family = AF_INET6;
addr.sin6_addr = in6addr_loopback;
- addr.sin6_port = htons(8001);
- if (bind(fd, (void *)&addr, sizeof(addr)))
- error(1, errno, "bind");
-
addr.sin6_port = htons(8000);
if (connect(fd, (void *)&addr, sizeof(addr)))
error(1, errno, "connect");
.