net.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. if (!sk_is_tcp(sock->sk))
  56. return 0;
  57. /* Checks for minimal header length to safely read sa_family. */
  58. if (addrlen < offsetofend(typeof(*address), sa_family))
  59. return -EINVAL;
  60. switch (address->sa_family) {
  61. case AF_UNSPEC:
  62. case AF_INET:
  63. if (addrlen < sizeof(struct sockaddr_in))
  64. return -EINVAL;
  65. port = ((struct sockaddr_in *)address)->sin_port;
  66. break;
  67. #if IS_ENABLED(CONFIG_IPV6)
  68. case AF_INET6:
  69. if (addrlen < SIN6_LEN_RFC2133)
  70. return -EINVAL;
  71. port = ((struct sockaddr_in6 *)address)->sin6_port;
  72. break;
  73. #endif /* IS_ENABLED(CONFIG_IPV6) */
  74. default:
  75. return 0;
  76. }
  77. /* Specific AF_UNSPEC handling. */
  78. if (address->sa_family == AF_UNSPEC) {
  79. /*
  80. * Connecting to an address with AF_UNSPEC dissolves the TCP
  81. * association, which have the same effect as closing the
  82. * connection while retaining the socket object (i.e., the file
  83. * descriptor). As for dropping privileges, closing
  84. * connections is always allowed.
  85. *
  86. * For a TCP access control system, this request is legitimate.
  87. * Let the network stack handle potential inconsistencies and
  88. * return -EINVAL if needed.
  89. */
  90. if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP)
  91. return 0;
  92. /*
  93. * For compatibility reason, accept AF_UNSPEC for bind
  94. * accesses (mapped to AF_INET) only if the address is
  95. * INADDR_ANY (cf. __inet_bind). Checking the address is
  96. * required to not wrongfully return -EACCES instead of
  97. * -EAFNOSUPPORT.
  98. *
  99. * We could return 0 and let the network stack handle these
  100. * checks, but it is safer to return a proper error and test
  101. * consistency thanks to kselftest.
  102. */
  103. if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
  104. /* addrlen has already been checked for AF_UNSPEC. */
  105. const struct sockaddr_in *const sockaddr =
  106. (struct sockaddr_in *)address;
  107. if (sock->sk->__sk_common.skc_family != AF_INET)
  108. return -EINVAL;
  109. if (sockaddr->sin_addr.s_addr != htonl(INADDR_ANY))
  110. return -EAFNOSUPPORT;
  111. }
  112. } else {
  113. /*
  114. * Checks sa_family consistency to not wrongfully return
  115. * -EACCES instead of -EINVAL. Valid sa_family changes are
  116. * only (from AF_INET or AF_INET6) to AF_UNSPEC.
  117. *
  118. * We could return 0 and let the network stack handle this
  119. * check, but it is safer to return a proper error and test
  120. * consistency thanks to kselftest.
  121. */
  122. if (address->sa_family != sock->sk->__sk_common.skc_family)
  123. return -EINVAL;
  124. }
  125. id.key.data = (__force uintptr_t)port;
  126. BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
  127. rule = landlock_find_rule(dom, id);
  128. access_request = landlock_init_layer_masks(
  129. dom, access_request, &layer_masks, LANDLOCK_KEY_NET_PORT);
  130. if (landlock_unmask_layers(rule, access_request, &layer_masks,
  131. ARRAY_SIZE(layer_masks)))
  132. return 0;
  133. return -EACCES;
  134. }
  135. static int hook_socket_bind(struct socket *const sock,
  136. struct sockaddr *const address, const int addrlen)
  137. {
  138. return current_check_access_socket(sock, address, addrlen,
  139. LANDLOCK_ACCESS_NET_BIND_TCP);
  140. }
  141. static int hook_socket_connect(struct socket *const sock,
  142. struct sockaddr *const address,
  143. const int addrlen)
  144. {
  145. return current_check_access_socket(sock, address, addrlen,
  146. LANDLOCK_ACCESS_NET_CONNECT_TCP);
  147. }
  148. static struct security_hook_list landlock_hooks[] __ro_after_init = {
  149. LSM_HOOK_INIT(socket_bind, hook_socket_bind),
  150. LSM_HOOK_INIT(socket_connect, hook_socket_connect),
  151. };
  152. __init void landlock_add_net_hooks(void)
  153. {
  154. security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
  155. &landlock_lsmid);
  156. }