[PATCH v10 09/13] landlock: Add network rules and TCP hooks support

Mickaël Salaün mic at digikod.net
Wed Apr 26 14:15:28 UTC 2023


On 21/04/2023 11:39, Konstantin Meskhidze (A) wrote:
> 
> 
> 4/16/2023 7:11 PM, Mickaël Salaün пишет:
>>
>> On 23/03/2023 09:52, Konstantin Meskhidze wrote:
>>> This commit adds network rules support in the ruleset management
>>> helpers and the landlock_create_ruleset syscall.
>>> Refactor user space API to support network actions. Add new network
>>> access flags, network rule and network attributes. Increment Landlock
>>> ABI version. Expand access_masks_t to u32 to be sure network access
>>> rights can be stored. Implement socket_bind() and socket_connect()
>>> LSM hooks, which enable to restrict TCP socket binding and connection
>>
>> "which enables to"
>>
>      Got it.
>>
>>> to specific ports.
>>>
>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze at huawei.com>
>>> ---

[...]

>>> +static u16 get_port(const struct sockaddr *const address)
>>> +{
>>> +	/* Gets port value in host byte order. */
>>> +	switch (address->sa_family) {
>>> +	case AF_UNSPEC:
>>> +	case AF_INET: {
>>> +		const struct sockaddr_in *const sockaddr =
>>> +			(struct sockaddr_in *)address;
>>> +		return ntohs(sockaddr->sin_port);
>>> +	}
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> +	case AF_INET6: {
>>> +		const struct sockaddr_in6 *const sockaddr_ip6 =
>>> +			(struct sockaddr_in6 *)address;
>>> +		return ntohs(sockaddr_ip6->sin6_port);
>>> +	}
>>> +#endif
>>> +	}
>>> +	WARN_ON_ONCE(1);
>>> +	return 0;
>>> +}
>>> +
>>> +static int check_socket_access(struct socket *sock, struct sockaddr *address, int addrlen, u16 port,
>>> +			       access_mask_t access_request)
>>> +{
>>> +	int ret;
>>> +	bool allowed = false;
>>> +	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
>>> +	const struct landlock_rule *rule;
>>> +	access_mask_t handled_access;
>>> +	const struct landlock_id id = {
>>> +		.key.data = port,
>>> +		.type = LANDLOCK_KEY_NET_PORT,
>>> +	};
>>> +	const struct landlock_ruleset *const domain = get_current_net_domain();
>>> +
>>> +	if (WARN_ON_ONCE(!domain))
>>> +		return 0;
>>> +	if (WARN_ON_ONCE(domain->num_layers < 1))
>>> +		return -EACCES;
>>> +	/* Check if it's a TCP socket. */
>>> +	if (sock->type != SOCK_STREAM)
>>> +		return 0;
>>> +
>>> +	ret = check_addrlen(address, addrlen);
>>> +	if (ret)
>>> +		return ret;
>>
>> As explained above, this should be replaced with:
>>
>> if (addrlen < offsetofend(struct sockaddr, sa_family))
>> 	return -EINVAL;
>>
>     Ok.
>>
>>> +
>>> +	switch (address->sa_family) {
>>
>>
>> This below block should be moved after the generic switch statement
>> (i.e. once port is checked).
>>
>     Do you mean checking address family after a port has been checked??


These specific AF_UNSPEC checks should be in an `if (address->sa_family 
== AF_UNSPEC)` block after the generic AF_UNSPEC, AF_INET, and AF_INET6 
checks in the address->sa_family switch/case, because the checks and 
errors order must be consistent whatever the sa_family. The AF_UNSPEC 
checks are really an exception to the AF_INET ones, and should then come 
after.

This may look like this:

switch (address->sa_family) {
case AF_UNSPEC:
case AF_INET:
	port = ...;
	break;
#if IS_ENABLED(CONFIG_IPV6)
	case AF_INET6:
	port = ...;
	break;
#endif
default:
	return 0;
}

/* Specific AF_UNSPEC handling. */
if (address->sa_family == AF_UNSPEC) {
	...
	if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP)
		return 0;
	...
}

id.key.data = (__force uintptr_t)port;
BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));

rule = landlock_find_rule(domain, id);
handled_access = landlock_init_layer_masks(
	domain, access_request, &layer_masks,
	LANDLOCK_KEY_NET_PORT);
if (landlock_unmask_layers(rule, handled_access,
				 &layer_masks,
				 ARRAY_SIZE(layer_masks)))
	return 0;

return -EACCES;





> 
>>
>>
>>> +	case AF_UNSPEC:
>>> +		/*
>>> +		 * Connecting to an address with AF_UNSPEC dissolves the TCP
>>> +		 * association, which have the same effect as closing the
>>> +		 * connection while retaining the socket object (i.e., the file
>>> +		 * descriptor).  As for dropping privileges, closing
>>> +		 * connections is always allowed.
>>> +		 */
>>> +		if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP)
>>> +			return 0;
>>> +
>>> +		/*
>>> +		 * For compatibility reason, accept AF_UNSPEC for bind
>>> +		 * accesses (mapped to AF_INET) only if the address is
>>> +		 * INADDR_ANY (cf. __inet_bind).  Checking the address is
>>> +		 * required to not wrongfully return -EACCES instead of
>>> +		 * -EAFNOSUPPORT.
>>> +		 */
>>> +		if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
>>> +			const struct sockaddr_in *const sockaddr =
>>> +				(struct sockaddr_in *)address;
>>> +
>>> +			if (sockaddr->sin_addr.s_addr != htonl(INADDR_ANY))
>>> +				return -EAFNOSUPPORT;
>>> +		}
>>> +
>>> +		fallthrough;
>>
>>
>>
>> case AF_UNSPEC:
>>
>>> +	case AF_INET:
>>
>> if (addrlen < sizeof(struct sockaddr_in))
>> 	return -EINVAL;
>>
>> port = ((struct sockaddr_in *)address)->sin_port;
>> break;
>>
>>
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> +	case AF_INET6:
>>
>> if (addrlen < SIN6_LEN_RFC2133)
>> 	return -EINVAL;
>>
>> port = ((struct sockaddr_in6 *)address)->sin6_port;
>> break;
>>
>>
>>> +#endif
>>
>> /* Allows unhandled protocols. */
>> default:
>> 	return 0;
>> }
>>
>> if (address->sa_family == AF_UNSPEC) {
>>
>> // Add here the above AF_UNSPEC checks to be consistent with the
>> EINVAL/EAFNOSUPPORT return ordering.
>>
>> }
>>
>> id.key.data = (__force uintprt_t)port;
>> BUID_BUG_ON(...);
>>
>     Will be refactored. Thanks.



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