[PATCH 1/2] landlock: Add hook on socket_listen()

Mickaël Salaün mic at digikod.net
Fri May 17 15:22:45 UTC 2024


On Mon, May 13, 2024 at 03:15:50PM +0300, Ivanov Mikhail wrote:
> 
> 
> 4/30/2024 4:36 PM, Mickaël Salaün wrote:
> > On Mon, Apr 08, 2024 at 05:47:46PM +0800, Ivanov Mikhail wrote:
> > > Make hook for socket_listen(). It will check that the socket protocol is
> > > TCP, and if the socket's local port number is 0 (which means,
> > > that listen(2) was called without any previous bind(2) call),
> > > then listen(2) call will be legitimate only if there is a rule for bind(2)
> > > allowing binding to port 0 (or if LANDLOCK_ACCESS_NET_BIND_TCP is not
> > > supported by the sandbox).
> > 
> > Thanks for this patch and sorry for the late full review.  The code is
> > good overall.
> > 
> > We should either consider this patch as a fix or add a new flag/access
> > right to Landlock syscalls for compatibility reason.  I think this
> > should be a fix.  Calling listen(2) without a previous call to bind(2)
> > is a corner case that we should properly handle.  The commit message
> > should make that explicit and highlight the goal of the patch: first
> > explain why, and then how.
> 
> Yeap, this is fix-patch. I have covered motivation and proposed solution
> in cover letter. Do you have any suggestions on how i can improve this?

You can start this commit message with the same description as in the
cover letter.

[...]

> > 
> > > +	if (!((1 << cur_sk_state) & (TCPF_CLOSE | TCPF_LISTEN)))
> > > +		return -EINVAL;
> > > +
> > > +	/* Sockets can listen only if ULP control hook has clone method. */
> > 
> > What is ULP?
> 
> ULP (Upper Layer Protocol) stands for protocols which are higher than
> transport protocol in OSI model. Linux has an infrastructure that
> allows TCP sockets to support logic of some ULP (e.g. TLS ULP). [1]

OK, you can extend the comment with this information, but no need for
the links.

> 
> There is a patch that prevents ULP sockets from listening
> if corresponding ULP implementation in linux doesn't have a clone
> method. [2]
> 
> Landlock shouldn't return EACCES for ULP sockets that cannot listen
> due to some ULP restrictions.

Looks good.

> 
> [1]
> https://lore.kernel.org/all/20170524162646.GA24128@davejwatson-mba.local/
> [2] https://lore.kernel.org/all/4b80c3d1dbe3d0ab072f80450c202d9bc88b4b03.1672740602.git.pabeni@redhat.com/
> 
> > 
> > > +	icsk = inet_csk(sk);
> > > +	if (icsk->icsk_ulp_ops && !icsk->icsk_ulp_ops->clone)
> > > +		return -EINVAL;
> > 
> > Can you please add tests covering all these error cases?
> 
> Yeap, i'll add a test for first check.
> 
> I have not found a way to trigger the second check from userspace.
> Since socket wasn't binded to any port, this means that it cannot
> be part of a TCP connection in any state, so it has to be in TCPF_CLOSE

If you're sure this cannot be triggered from user space, you can wrap
the test with WARN_ON_ONCE(), but we need to be careful.  I'd like to
get the point of view of kernel network expert though.

Eric, is this assumption correct?

> state. Nevertheless i think that this check is required:
> 
> * for consistency with inet stack (see __inet_listen_sk())
> 
> * i have not found any restrictions connected with sock locking
>   for TCP-like protocols, so listen(2) can be called after
>   sk->sk_prot->connect() method will change sock state in
>   __inet_stream_connect() (e.g. to TCP_SYN_SENT). In that case this
>   check may be required.
> 
> What do you think?

This looks good, but we need to explain this rationale in comments, with
explicit mention of network stack functions.

> Btw this hook on socket_listen() should be fixed in
> order to not check socket that is already in TCP_LISTEN state. Calling
> listen(2) only changes backlog value, so landlock shouldn't do anything
> in this case.
> 
> I'm not sure about ULP checking. I thought of adding test that creates
> espintcp ULP (net/xfrm/expintcp.c) socket and tries to listen on it.
> Since espintcp doesn't have clone method ULP check will be triggered.
> Problem is that kernel doesnt support this ULP module by default and it
> should be configured with CONFIG_XFRM_ESPINTCP option enabled. I think
> that selftests shouldn't depend on specific kernel configuration to be
> fully executed, so probably we should just skip this. What do you think?

Testing with espintcp makes sense for this clone case.  I hope it would
not require too much boilerplate code though.  We can and should add all
the required kernel option in tools/testing/selftests/landlock/config,
and we should not restrict tests to default kernel options, quite the
contrary if it makes sense.

[...]

> > 
> > > +	family = sock->sk->__sk_common.skc_family;
> > > +
> > > +	if (family == AF_INET || family == AF_INET6) {
> > 
> > This would make the code simpler:
> > 
> > if (family != AF_INET && family != AF_INET6)
> > 	return 0;
> 
> indeed, will be fixed.
> 
> > 
> > 
> > What would be the effect of listen() on an AF_UNSPEC socket?
> 
> AF_UNSPEC is a family type that only addresses can use.
> Socket itself can only be AF_INET or AF_INET6 in TCP.

Indeed, it is worth mentioning in a comment.



More information about the Linux-security-module-archive mailing list