net.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Landlock LSM - Network management and hooks
  4. *
  5. * Copyright © 2022-2023 Huawei Tech. Co., Ltd.
  6. * Copyright © 2022-2023 Microsoft Corporation
  7. */
  8. #include <linux/in.h>
  9. #include <linux/net.h>
  10. #include <linux/socket.h>
  11. #include <net/ipv6.h>
  12. #include "common.h"
  13. #include "cred.h"
  14. #include "limits.h"
  15. #include "net.h"
  16. #include "ruleset.h"
  17. int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
  18. const u16 port, access_mask_t access_rights)
  19. {
  20. int err;
  21. const struct landlock_id id = {
  22. .key.data = (__force uintptr_t)htons(port),
  23. .type = LANDLOCK_KEY_NET_PORT,
  24. };
  25. BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
  26. /* Transforms relative access rights to absolute ones. */
  27. access_rights |= LANDLOCK_MASK_ACCESS_NET &
  28. ~landlock_get_net_access_mask(ruleset, 0);
  29. mutex_lock(&ruleset->lock);
  30. err = landlock_insert_rule(ruleset, id, access_rights);
  31. mutex_unlock(&ruleset->lock);
  32. return err;
  33. }
  34. static const struct access_masks any_net = {
  35. .net = ~0,
  36. };
  37. static int current_check_access_socket(struct socket *const sock,
  38. struct sockaddr *const address,
  39. const int addrlen,
  40. access_mask_t access_request)
  41. {
  42. __be16 port;
  43. layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
  44. const struct landlock_rule *rule;
  45. struct landlock_id id = {
  46. .type = LANDLOCK_KEY_NET_PORT,
  47. };
  48. const struct landlock_ruleset *const dom =
  49. landlock_get_applicable_domain(landlock_get_current_domain(),
  50. any_net);
  51. if (!dom)
  52. return 0;
  53. if (WARN_ON_ONCE(dom->num_layers < 1))
  54. return -EACCES;
  55. /* Checks if it's a (potential) TCP socket. */
  56. if (sock->type != SOCK_STREAM)
  57. return 0;
  58. /* Checks for minimal header length to safely read sa_family. */
  59. if (addrlen < offsetofend(typeof(*address), sa_family))
  60. return -EINVAL;
  61. switch (address->sa_family) {
  62. case AF_UNSPEC:
  63. case AF_INET:
  64. if (addrlen < sizeof(struct sockaddr_in))
  65. return -EINVAL;
  66. port = ((struct sockaddr_in *)address)->sin_port;
  67. break;
  68. #if IS_ENABLED(CONFIG_IPV6)
  69. case AF_INET6:
  70. if (addrlen < SIN6_LEN_RFC2133)
  71. return -EINVAL;
  72. port = ((struct sockaddr_in6 *)address)->sin6_port;
  73. break;
  74. #endif /* IS_ENABLED(CONFIG_IPV6) */
  75. default:
  76. return 0;
  77. }
  78. /* Specific AF_UNSPEC handling. */
  79. if (address->sa_family == AF_UNSPEC) {
  80. /*
  81. * Connecting to an address with AF_UNSPEC dissolves the TCP
  82. * association, which have the same effect as closing the
  83. * connection while retaining the socket object (i.e., the file
  84. * descriptor). As for dropping privileges, closing
  85. * connections is always allowed.
  86. *
  87. * For a TCP access control system, this request is legitimate.
  88. * Let the network stack handle potential inconsistencies and
  89. * return -EINVAL if needed.
  90. */
  91. if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP)
  92. return 0;
  93. /*
  94. * For compatibility reason, accept AF_UNSPEC for bind
  95. * accesses (mapped to AF_INET) only if the address is
  96. * INADDR_ANY (cf. __inet_bind). Checking the address is
  97. * required to not wrongfully return -EACCES instead of
  98. * -EAFNOSUPPORT.
  99. *
  100. * We could return 0 and let the network stack handle these
  101. * checks, but it is safer to return a proper error and test
  102. * consistency thanks to kselftest.
  103. */
  104. if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
  105. /* addrlen has already been checked for AF_UNSPEC. */
  106. const struct sockaddr_in *const sockaddr =
  107. (struct sockaddr_in *)address;
  108. if (sock->sk->__sk_common.skc_family != AF_INET)
  109. return -EINVAL;
  110. if (sockaddr->sin_addr.s_addr != htonl(INADDR_ANY))
  111. return -EAFNOSUPPORT;
  112. }
  113. } else {
  114. /*
  115. * Checks sa_family consistency to not wrongfully return
  116. * -EACCES instead of -EINVAL. Valid sa_family changes are
  117. * only (from AF_INET or AF_INET6) to AF_UNSPEC.
  118. *
  119. * We could return 0 and let the network stack handle this
  120. * check, but it is safer to return a proper error and test
  121. * consistency thanks to kselftest.
  122. */
  123. if (address->sa_family != sock->sk->__sk_common.skc_family)
  124. return -EINVAL;
  125. }
  126. id.key.data = (__force uintptr_t)port;
  127. BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
  128. rule = landlock_find_rule(dom, id);
  129. access_request = landlock_init_layer_masks(
  130. dom, access_request, &layer_masks, LANDLOCK_KEY_NET_PORT);
  131. if (landlock_unmask_layers(rule, access_request, &layer_masks,
  132. ARRAY_SIZE(layer_masks)))
  133. return 0;
  134. return -EACCES;
  135. }
  136. static int hook_socket_bind(struct socket *const sock,
  137. struct sockaddr *const address, const int addrlen)
  138. {
  139. return current_check_access_socket(sock, address, addrlen,
  140. LANDLOCK_ACCESS_NET_BIND_TCP);
  141. }
  142. static int hook_socket_connect(struct socket *const sock,
  143. struct sockaddr *const address,
  144. const int addrlen)
  145. {
  146. return current_check_access_socket(sock, address, addrlen,
  147. LANDLOCK_ACCESS_NET_CONNECT_TCP);
  148. }
  149. static struct security_hook_list landlock_hooks[] __ro_after_init = {
  150. LSM_HOOK_INIT(socket_bind, hook_socket_bind),
  151. LSM_HOOK_INIT(socket_connect, hook_socket_connect),
  152. };
  153. __init void landlock_add_net_hooks(void)
  154. {
  155. security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
  156. &landlock_lsmid);
  157. }