* [PATCH v2 net-next 01/15] arm64: dts: mediatek: mt7986: introduce ethernet nodes
From: Lorenzo Bianconi @ 2022-05-16 16:06 UTC (permalink / raw)
To: netdev
Cc: nbd, john, sean.wang, Mark-MC.Lee, davem, edumazet, kuba, pabeni,
Sam.Shih, linux-mediatek, devicetree, robh, lorenzo.bianconi
In-Reply-To: <cover.1652716741.git.lorenzo@kernel.org>
Introduce ethernet nodes in mt7986 bindings in order to
enable mt7986a/mt7986b ethernet support.
Co-developed-by: Sam Shih <sam.shih@mediatek.com>
Signed-off-by: Sam Shih <sam.shih@mediatek.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts | 74 ++++++++++++++++++++
arch/arm64/boot/dts/mediatek/mt7986a.dtsi | 39 +++++++++++
arch/arm64/boot/dts/mediatek/mt7986b-rfb.dts | 70 ++++++++++++++++++
3 files changed, 183 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts b/arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts
index 21e420829572..882277a52b69 100644
--- a/arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts
@@ -25,6 +25,80 @@ memory@40000000 {
};
};
+ð {
+ status = "okay";
+
+ gmac0: mac@0 {
+ compatible = "mediatek,eth-mac";
+ reg = <0>;
+ phy-mode = "2500base-x";
+
+ fixed-link {
+ speed = <2500>;
+ full-duplex;
+ pause;
+ };
+ };
+
+ mdio: mdio-bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+};
+
+&mdio {
+ switch: switch@0 {
+ compatible = "mediatek,mt7531";
+ reg = <31>;
+ reset-gpios = <&pio 5 0>;
+ };
+};
+
+&switch {
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ label = "lan0";
+ };
+
+ port@1 {
+ reg = <1>;
+ label = "lan1";
+ };
+
+ port@2 {
+ reg = <2>;
+ label = "lan2";
+ };
+
+ port@3 {
+ reg = <3>;
+ label = "lan3";
+ };
+
+ port@4 {
+ reg = <4>;
+ label = "lan4";
+ };
+
+ port@6 {
+ reg = <6>;
+ label = "cpu";
+ ethernet = <&gmac0>;
+ phy-mode = "2500base-x";
+
+ fixed-link {
+ speed = <2500>;
+ full-duplex;
+ pause;
+ };
+ };
+ };
+};
+
&uart0 {
status = "okay";
};
diff --git a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi
index 694acf8f5b70..d2636a0ed152 100644
--- a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi
@@ -222,6 +222,45 @@ ethsys: syscon@15000000 {
#reset-cells = <1>;
};
+ eth: ethernet@15100000 {
+ compatible = "mediatek,mt7986-eth";
+ reg = <0 0x15100000 0 0x80000>;
+ interrupts = <GIC_SPI 196 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 197 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 198 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 199 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <ðsys CLK_ETH_FE_EN>,
+ <ðsys CLK_ETH_GP2_EN>,
+ <ðsys CLK_ETH_GP1_EN>,
+ <ðsys CLK_ETH_WOCPU1_EN>,
+ <ðsys CLK_ETH_WOCPU0_EN>,
+ <&sgmiisys0 CLK_SGMII0_TX250M_EN>,
+ <&sgmiisys0 CLK_SGMII0_RX250M_EN>,
+ <&sgmiisys0 CLK_SGMII0_CDR_REF>,
+ <&sgmiisys0 CLK_SGMII0_CDR_FB>,
+ <&sgmiisys1 CLK_SGMII1_TX250M_EN>,
+ <&sgmiisys1 CLK_SGMII1_RX250M_EN>,
+ <&sgmiisys1 CLK_SGMII1_CDR_REF>,
+ <&sgmiisys1 CLK_SGMII1_CDR_FB>,
+ <&topckgen CLK_TOP_NETSYS_SEL>,
+ <&topckgen CLK_TOP_NETSYS_500M_SEL>;
+ clock-names = "fe", "gp2", "gp1", "wocpu1", "wocpu0",
+ "sgmii_tx250m", "sgmii_rx250m",
+ "sgmii_cdr_ref", "sgmii_cdr_fb",
+ "sgmii2_tx250m", "sgmii2_rx250m",
+ "sgmii2_cdr_ref", "sgmii2_cdr_fb",
+ "netsys0", "netsys1";
+ assigned-clocks = <&topckgen CLK_TOP_NETSYS_2X_SEL>,
+ <&topckgen CLK_TOP_SGM_325M_SEL>;
+ assigned-clock-parents = <&apmixedsys CLK_APMIXED_NET2PLL>,
+ <&apmixedsys CLK_APMIXED_SGMPLL>;
+ mediatek,ethsys = <ðsys>;
+ mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>;
+ #reset-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm64/boot/dts/mediatek/mt7986b-rfb.dts b/arch/arm64/boot/dts/mediatek/mt7986b-rfb.dts
index d73467ea3641..0f49d5764ff3 100644
--- a/arch/arm64/boot/dts/mediatek/mt7986b-rfb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt7986b-rfb.dts
@@ -28,3 +28,73 @@ memory@40000000 {
&uart0 {
status = "okay";
};
+
+ð {
+ status = "okay";
+
+ gmac0: mac@0 {
+ compatible = "mediatek,eth-mac";
+ reg = <0>;
+ phy-mode = "2500base-x";
+
+ fixed-link {
+ speed = <2500>;
+ full-duplex;
+ pause;
+ };
+ };
+
+ mdio: mdio-bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ switch@0 {
+ compatible = "mediatek,mt7531";
+ reg = <31>;
+ reset-gpios = <&pio 5 0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ label = "lan0";
+ };
+
+ port@1 {
+ reg = <1>;
+ label = "lan1";
+ };
+
+ port@2 {
+ reg = <2>;
+ label = "lan2";
+ };
+
+ port@3 {
+ reg = <3>;
+ label = "lan3";
+ };
+
+ port@4 {
+ reg = <4>;
+ label = "lan4";
+ };
+
+ port@6 {
+ reg = <6>;
+ label = "cpu";
+ ethernet = <&gmac0>;
+ phy-mode = "2500base-x";
+
+ fixed-link {
+ speed = <2500>;
+ full-duplex;
+ pause;
+ };
+ };
+ };
+ };
+ };
+};
--
2.35.3
^ permalink raw reply related
* [PATCH v2 net-next 00/15] introduce mt7986 ethernet support
From: Lorenzo Bianconi @ 2022-05-16 16:06 UTC (permalink / raw)
To: netdev
Cc: nbd, john, sean.wang, Mark-MC.Lee, davem, edumazet, kuba, pabeni,
Sam.Shih, linux-mediatek, devicetree, robh, lorenzo.bianconi
Add support for mt7986-eth driver available on mt7986 soc.
Changes since v1:
- drop SRAM option
- convert ring->dma to void
- convert scratch_ring to void
- enable port4
- fix irq dts bindings
- drop gmac1 support from mt7986a-rfb dts for the moment
Lorenzo Bianconi (15):
arm64: dts: mediatek: mt7986: introduce ethernet nodes
dt-bindings: net: mediatek,net: add mt7986-eth binding
net: ethernet: mtk_eth_soc: move tx dma desc configuration in
mtk_tx_set_dma_desc
net: ethernet: mtk_eth_soc: add txd_size to mtk_soc_data
net: ethernet: mtk_eth_soc: rely on txd_size in
mtk_tx_alloc/mtk_tx_clean
net: ethernet: mtk_eth_soc: rely on txd_size in mtk_desc_to_tx_buf
net: ethernet: mtk_eth_soc: rely on txd_size in txd_to_idx
net: ethernet: mtk_eth_soc: add rxd_size to mtk_soc_data
net: ethernet: mtk_eth_soc: rely on txd_size field in
mtk_poll_tx/mtk_poll_rx
net: ethernet: mtk_eth_soc: rely on rxd_size field in
mtk_rx_alloc/mtk_rx_clean
net: ethernet: mtk_eth_soc: introduce device register map
net: ethernet: mtk_eth_soc: introduce MTK_NETSYS_V2 support
net: ethernet: mtk_eth_soc: convert ring dma pointer to void
net: ethernet: mtk_eth_soc: convert scratch_ring pointer to void
net: ethernet: mtk_eth_soc: introduce support for mt7986 chipset
.../devicetree/bindings/net/mediatek,net.yaml | 141 +++-
arch/arm64/boot/dts/mediatek/mt7986a-rfb.dts | 74 ++
arch/arm64/boot/dts/mediatek/mt7986a.dtsi | 39 ++
arch/arm64/boot/dts/mediatek/mt7986b-rfb.dts | 70 ++
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 630 +++++++++++++-----
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 328 +++++++--
6 files changed, 1037 insertions(+), 245 deletions(-)
--
2.35.3
^ permalink raw reply
* Re: [PATCH 21/30] panic: Introduce the panic pre-reboot notifier list
From: Guilherme G. Piccoli @ 2022-05-16 16:05 UTC (permalink / raw)
To: Petr Mladek, Tony Luck, Dinh Nguyen
Cc: akpm, bhe, kexec, linux-kernel, bcm-kernel-feedback-list,
linuxppc-dev, linux-alpha, linux-edac, linux-hyperv, linux-leds,
linux-mips, linux-parisc, linux-pm, linux-remoteproc, linux-s390,
linux-tegra, linux-um, linux-xtensa, netdev, openipmi-developer,
rcu, sparclinux, xen-devel, x86, kernel-dev, kernel, halves,
fabiomirmar, alejandro.j.jimenez, andriy.shevchenko, arnd, bp,
corbet, d.hatayama, dave.hansen, dyoung, feng.tang, gregkh,
mikelley, hidehiro.kawai.ez, jgross, john.ogness, keescook, luto,
mhiramat, mingo, paulmck, peterz, rostedt, senozhatsky, stern,
tglx, vgoyal, vkuznets, will, Alex Elder, Alexander Gordeev,
Anton Ivanov, Benjamin Herrenschmidt, Bjorn Andersson,
Boris Ostrovsky, Chris Zankel, Christian Borntraeger,
Corey Minyard, Dexuan Cui, H. Peter Anvin, Haiyang Zhang,
Heiko Carstens, Helge Deller, Ivan Kokshaysky,
James E.J. Bottomley, James Morse, Johannes Berg,
K. Y. Srinivasan, Mathieu Poirier, Matt Turner,
Mauro Carvalho Chehab, Max Filippov, Michael Ellerman,
Paul Mackerras, Pavel Machek, Richard Weinberger, Robert Richter,
Stefano Stabellini, Stephen Hemminger, Sven Schnelle,
Vasily Gorbik, Wei Liu
In-Reply-To: <YoJgcC8c6LaKADZV@alley>
Thanks again for the review! Comments inline below:
On 16/05/2022 11:33, Petr Mladek wrote:
> [...]
>> --- a/drivers/edac/altera_edac.c
>> +++ b/drivers/edac/altera_edac.c
>> @@ -2163,7 +2162,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
>> int dberror, err_addr;
>>
>> edac->panic_notifier.notifier_call = s10_edac_dberr_handler;
>> - atomic_notifier_chain_register(&panic_notifier_list,
>> + atomic_notifier_chain_register(&panic_pre_reboot_list,
>
> My understanding is that this notifier first prints info about ECC
> errors and then triggers reboot. It might make sense to split it
> into two notifiers.
I disagree here - looping the maintainers for comments (CCing Dinh /
Tony). BTW, sorry for not having you on CC already Dinh, it was my mistake.
So, my reasoning here is: this notifier should fit the info list,
definitely! But...it's very high risk for kdump. It deep dives into the
regmap API (there are locks in such code) plus there is an (MM)IO write
to the device and an ARM firmware call. So, despite the nature of this
notifier _fits the informational list_, the _code is risky_ so we should
avoid running it before a kdump.
Now, we indeed have a chicken/egg problem: want to avoid it before
kdump, BUT in case kdump is not set, kmsg_dump() (and console flushing,
after your suggestion Petr) will run before it and not save collected
information from EDAC PoV.
My idea: I could call a second kmsg_dump() or at least a panic console
flush for within such notifier. Let me know what you think Petr (also
Dinh / Tony and all interested parties).
> [...]
>> --- a/drivers/leds/trigger/ledtrig-panic.c
>> +++ b/drivers/leds/trigger/ledtrig-panic.c
>> @@ -64,7 +63,7 @@ static long led_panic_blink(int state)
>>
>> static int __init ledtrig_panic_init(void)
>> {
>> - atomic_notifier_chain_register(&panic_notifier_list,
>> + atomic_notifier_chain_register(&panic_pre_reboot_list,
>> &led_trigger_panic_nb);
>
> Blinking => should go to the last "post_reboot/loop" list.
> [...]
>> --- a/drivers/misc/ibmasm/heartbeat.c
>> +++ b/drivers/misc/ibmasm/heartbeat.c
>> @@ -32,20 +31,23 @@ static int suspend_heartbeats = 0;
>> static int panic_happened(struct notifier_block *n, unsigned long val, void *v)
>> {
>> suspend_heartbeats = 1;
>> - return 0;
>> + return NOTIFY_DONE;
>> }
>>
>> -static struct notifier_block panic_notifier = { panic_happened, NULL, 1 };
>> +static struct notifier_block panic_notifier = {
>> + .notifier_call = panic_happened,
>> +};
>>
>> void ibmasm_register_panic_notifier(void)
>> {
>> - atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier);
>> + atomic_notifier_chain_register(&panic_pre_reboot_list,
>> + &panic_notifier);
>
> Same here. Blinking => should go to the last "post_reboot/loop" list.
Ack on both.
IBMasm is not blinking IIUC, but still fits properly the loop list. This
notifier would make a heartbeat mechanism stop, and once it's stopped,
service processor is allowed to reboot - that's my understanding.
Cheers,
Guilherme
^ permalink raw reply
* Re: [RFC PATCH 2/3] dt-bindings: can: ctucanfd: add properties for HW timestamping
From: Rob Herring @ 2022-05-16 16:02 UTC (permalink / raw)
To: Matej Vasilevski
Cc: linux-can, mkl, pisa, devicetree, netdev, ondrej.ille,
martin.jerabek01
In-Reply-To: <20220512232706.24575-3-matej.vasilevski@seznam.cz>
On Fri, May 13, 2022 at 01:27:06AM +0200, Matej Vasilevski wrote:
> Extend dt-bindings for CTU CAN-FD IP core with necessary properties
> to enable HW timestamping for platform devices. Since the timestamping
> counter is provided by the system integrator usign those IP cores in
> their FPGA design, we need to have the properties specified in device tree.
>
> Signed-off-by: Matej Vasilevski <matej.vasilevski@seznam.cz>
> ---
> .../bindings/net/can/ctu,ctucanfd.yaml | 34 +++++++++++++++++--
> 1 file changed, 31 insertions(+), 3 deletions(-)
What's the base for this patch? Doesn't apply for me.
>
> diff --git a/Documentation/devicetree/bindings/net/can/ctu,ctucanfd.yaml b/Documentation/devicetree/bindings/net/can/ctu,ctucanfd.yaml
> index fb34d971dcb3..c3693dadbcd8 100644
> --- a/Documentation/devicetree/bindings/net/can/ctu,ctucanfd.yaml
> +++ b/Documentation/devicetree/bindings/net/can/ctu,ctucanfd.yaml
> @@ -41,9 +41,35 @@ properties:
>
> clocks:
> description: |
> - phandle of reference clock (100 MHz is appropriate
> - for FPGA implementation on Zynq-7000 system).
> + Phandle of reference clock (100 MHz is appropriate for FPGA
> + implementation on Zynq-7000 system). If you wish to use timestamps
> + from the core, add a second phandle with the clock used for timestamping
> + (can be the same as the first clock).
> + maxItems: 2
With more than 1, you have to define what each entry is. IOW, use
'items'.
> +
> + clock-names:
> + description: |
> + Specify clock names for the "clocks" property. The first clock name
> + doesn't matter, the second has to be "ts_clk". Timestamping frequency
> + is then obtained from the "ts_clk" clock. This takes precedence over
> + the ts-frequency property.
> + You can omit this property if you don't need timestamps.
> + maxItems: 2
You must define what the names are as a schema.
> +
> + ts-used-bits:
> + description: width of the timestamping counter
> + maxItems: 1
> + items:
Not an array, so you don't need maxItems nor items.
> + minimum: 8
> + maximum: 64
> +
> + ts-frequency:
Use a standard unit suffix.
> + description: |
> + Frequency of the timestamping counter. Set this if you want to get
> + timestamps, but you didn't set the timestamping clock in clocks property.
> maxItems: 1
> + items:
Not an array.
Is timestamping a common feature for CAN or is this specific to this
controller? In the latter case, you need vendor prefixes on these
properties. In the former case, you need to define them in a common
schema.
> + minimum: 1
>
> required:
> - compatible
> @@ -58,6 +84,8 @@ examples:
> ctu_can_fd_0: can@43c30000 {
> compatible = "ctu,ctucanfd";
> interrupts = <0 30 4>;
> - clocks = <&clkc 15>;
> + clocks = <&clkc 15>, <&clkc 15>;
> + clock-names = "can_clk", "ts_clk";
> reg = <0x43c30000 0x10000>;
> + ts-used-bits = <64>;
> };
> --
> 2.25.1
>
>
^ permalink raw reply
* [PATCH iproute2] iplink: remove GSO_MAX_SIZE definition
From: Eric Dumazet @ 2022-05-16 15:34 UTC (permalink / raw)
To: David Ahern, Stephen Hemminger; +Cc: netdev, Eric Dumazet, Eric Dumazet
From: Eric Dumazet <edumazet@google.com>
David removed the check using GSO_MAX_SIZE
in commit f1d18e2e6ec5 ("Update kernel headers").
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
ip/iplink.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/ip/iplink.c b/ip/iplink.c
index fbdf542ae92bf1b9a7cb60f6916347870709cb48..c64721bc6bceb57ede26209269e79853fb997aa1 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -35,9 +35,6 @@
#define IPLINK_IOCTL_COMPAT 1
-#ifndef GSO_MAX_SIZE
-#define GSO_MAX_SIZE 65536
-#endif
#ifndef GSO_MAX_SEGS
#define GSO_MAX_SEGS 65535
#endif
--
2.36.0.550.gb090851708-goog
^ permalink raw reply related
* Re: [PATCH net-next 8/9] net: tcp: add skb drop reasons to tcp tw code path
From: kernel test robot @ 2022-05-16 15:30 UTC (permalink / raw)
To: menglong8.dong, edumazet
Cc: llvm, kbuild-all, rostedt, mingo, davem, yoshfuji, dsahern, kuba,
pabeni, imagedong, kafai, talalahmad, keescook, dongli.zhang,
linux-kernel, netdev
In-Reply-To: <20220516034519.184876-9-imagedong@tencent.com>
Hi,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on net-next/master]
url: https://github.com/intel-lab-lkp/linux/commits/menglong8-dong-gmail-com/net-tcp-add-skb-drop-reasons-to-tcp-state-change/20220516-114934
base: https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git d9713088158b23973266e07fdc85ff7d68791a8c
config: mips-mtx1_defconfig (https://download.01.org/0day-ci/archive/20220516/202205162352.OThc1nAw-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project 853fa8ee225edf2d0de94b0dcbd31bea916e825e)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install mips cross compiling tool for clang build
# apt-get install binutils-mips-linux-gnu
# https://github.com/intel-lab-lkp/linux/commit/6a657e07d2943a7df8277769f29624ea28599e09
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review menglong8-dong-gmail-com/net-tcp-add-skb-drop-reasons-to-tcp-state-change/20220516-114934
git checkout 6a657e07d2943a7df8277769f29624ea28599e09
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=mips SHELL=/bin/bash net/ipv4/ net/ipv6/
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All warnings (new ones prefixed by >>):
>> net/ipv4/tcp_ipv4.c:2161:7: warning: variable 'ret' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
if (drop_reason)
^~~~~~~~~~~
net/ipv4/tcp_ipv4.c:2092:9: note: uninitialized use occurs here
return ret;
^~~
net/ipv4/tcp_ipv4.c:2161:3: note: remove the 'if' if its condition is always true
if (drop_reason)
^~~~~~~~~~~~~~~~
net/ipv4/tcp_ipv4.c:1926:9: note: initialize the variable 'ret' to silence this warning
int ret;
^
= 0
1 warning generated.
--
>> net/ipv6/tcp_ipv6.c:1825:7: warning: variable 'ret' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
if (drop_reason)
^~~~~~~~~~~
net/ipv6/tcp_ipv6.c:1753:9: note: uninitialized use occurs here
return ret ? -1 : 0;
^~~
net/ipv6/tcp_ipv6.c:1825:3: note: remove the 'if' if its condition is always true
if (drop_reason)
^~~~~~~~~~~~~~~~
net/ipv6/tcp_ipv6.c:1594:9: note: initialize the variable 'ret' to silence this warning
int ret;
^
= 0
1 warning generated.
vim +2161 net/ipv4/tcp_ipv4.c
1911
1912 /*
1913 * From tcp_input.c
1914 */
1915
1916 int tcp_v4_rcv(struct sk_buff *skb)
1917 {
1918 struct net *net = dev_net(skb->dev);
1919 enum skb_drop_reason drop_reason;
1920 int sdif = inet_sdif(skb);
1921 int dif = inet_iif(skb);
1922 const struct iphdr *iph;
1923 const struct tcphdr *th;
1924 bool refcounted;
1925 struct sock *sk;
1926 int ret;
1927
1928 drop_reason = SKB_DROP_REASON_NOT_SPECIFIED;
1929 if (skb->pkt_type != PACKET_HOST)
1930 goto discard_it;
1931
1932 /* Count it even if it's bad */
1933 __TCP_INC_STATS(net, TCP_MIB_INSEGS);
1934
1935 if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
1936 goto discard_it;
1937
1938 th = (const struct tcphdr *)skb->data;
1939
1940 if (unlikely(th->doff < sizeof(struct tcphdr) / 4)) {
1941 drop_reason = SKB_DROP_REASON_PKT_TOO_SMALL;
1942 goto bad_packet;
1943 }
1944 if (!pskb_may_pull(skb, th->doff * 4))
1945 goto discard_it;
1946
1947 /* An explanation is required here, I think.
1948 * Packet length and doff are validated by header prediction,
1949 * provided case of th->doff==0 is eliminated.
1950 * So, we defer the checks. */
1951
1952 if (skb_checksum_init(skb, IPPROTO_TCP, inet_compute_pseudo))
1953 goto csum_error;
1954
1955 th = (const struct tcphdr *)skb->data;
1956 iph = ip_hdr(skb);
1957 lookup:
1958 sk = __inet_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th), th->source,
1959 th->dest, sdif, &refcounted);
1960 if (!sk)
1961 goto no_tcp_socket;
1962
1963 process:
1964 if (sk->sk_state == TCP_TIME_WAIT)
1965 goto do_time_wait;
1966
1967 if (sk->sk_state == TCP_NEW_SYN_RECV) {
1968 struct request_sock *req = inet_reqsk(sk);
1969 bool req_stolen = false;
1970 struct sock *nsk;
1971
1972 sk = req->rsk_listener;
1973 drop_reason = tcp_inbound_md5_hash(sk, skb,
1974 &iph->saddr, &iph->daddr,
1975 AF_INET, dif, sdif);
1976 if (unlikely(drop_reason)) {
1977 sk_drops_add(sk, skb);
1978 reqsk_put(req);
1979 goto discard_it;
1980 }
1981 if (tcp_checksum_complete(skb)) {
1982 reqsk_put(req);
1983 goto csum_error;
1984 }
1985 if (unlikely(sk->sk_state != TCP_LISTEN)) {
1986 nsk = reuseport_migrate_sock(sk, req_to_sk(req), skb);
1987 if (!nsk) {
1988 inet_csk_reqsk_queue_drop_and_put(sk, req);
1989 goto lookup;
1990 }
1991 sk = nsk;
1992 /* reuseport_migrate_sock() has already held one sk_refcnt
1993 * before returning.
1994 */
1995 } else {
1996 /* We own a reference on the listener, increase it again
1997 * as we might lose it too soon.
1998 */
1999 sock_hold(sk);
2000 }
2001 refcounted = true;
2002 nsk = NULL;
2003 if (!tcp_filter(sk, skb)) {
2004 th = (const struct tcphdr *)skb->data;
2005 iph = ip_hdr(skb);
2006 tcp_v4_fill_cb(skb, iph, th);
2007 nsk = tcp_check_req(sk, skb, req, false, &req_stolen);
2008 } else {
2009 drop_reason = SKB_DROP_REASON_SOCKET_FILTER;
2010 }
2011 if (!nsk) {
2012 reqsk_put(req);
2013 if (req_stolen) {
2014 /* Another cpu got exclusive access to req
2015 * and created a full blown socket.
2016 * Try to feed this packet to this socket
2017 * instead of discarding it.
2018 */
2019 tcp_v4_restore_cb(skb);
2020 sock_put(sk);
2021 goto lookup;
2022 }
2023 goto discard_and_relse;
2024 }
2025 if (nsk == sk) {
2026 reqsk_put(req);
2027 tcp_v4_restore_cb(skb);
2028 } else {
2029 drop_reason = tcp_child_process(sk, nsk, skb);
2030 if (drop_reason) {
2031 tcp_v4_send_reset(nsk, skb);
2032 goto discard_and_relse;
2033 } else {
2034 sock_put(sk);
2035 return 0;
2036 }
2037 }
2038 }
2039
2040 if (static_branch_unlikely(&ip4_min_ttl)) {
2041 /* min_ttl can be changed concurrently from do_ip_setsockopt() */
2042 if (unlikely(iph->ttl < READ_ONCE(inet_sk(sk)->min_ttl))) {
2043 __NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP);
2044 goto discard_and_relse;
2045 }
2046 }
2047
2048 if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) {
2049 drop_reason = SKB_DROP_REASON_XFRM_POLICY;
2050 goto discard_and_relse;
2051 }
2052
2053 drop_reason = tcp_inbound_md5_hash(sk, skb, &iph->saddr,
2054 &iph->daddr, AF_INET, dif, sdif);
2055 if (drop_reason)
2056 goto discard_and_relse;
2057
2058 nf_reset_ct(skb);
2059
2060 if (tcp_filter(sk, skb)) {
2061 drop_reason = SKB_DROP_REASON_SOCKET_FILTER;
2062 goto discard_and_relse;
2063 }
2064 th = (const struct tcphdr *)skb->data;
2065 iph = ip_hdr(skb);
2066 tcp_v4_fill_cb(skb, iph, th);
2067
2068 skb->dev = NULL;
2069
2070 if (sk->sk_state == TCP_LISTEN) {
2071 ret = tcp_v4_do_rcv(sk, skb);
2072 goto put_and_return;
2073 }
2074
2075 sk_incoming_cpu_update(sk);
2076
2077 bh_lock_sock_nested(sk);
2078 tcp_segs_in(tcp_sk(sk), skb);
2079 ret = 0;
2080 if (!sock_owned_by_user(sk)) {
2081 ret = tcp_v4_do_rcv(sk, skb);
2082 } else {
2083 if (tcp_add_backlog(sk, skb, &drop_reason))
2084 goto discard_and_relse;
2085 }
2086 bh_unlock_sock(sk);
2087
2088 put_and_return:
2089 if (refcounted)
2090 sock_put(sk);
2091
2092 return ret;
2093
2094 no_tcp_socket:
2095 drop_reason = SKB_DROP_REASON_NO_SOCKET;
2096 if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
2097 goto discard_it;
2098
2099 tcp_v4_fill_cb(skb, iph, th);
2100
2101 if (tcp_checksum_complete(skb)) {
2102 csum_error:
2103 drop_reason = SKB_DROP_REASON_TCP_CSUM;
2104 trace_tcp_bad_csum(skb);
2105 __TCP_INC_STATS(net, TCP_MIB_CSUMERRORS);
2106 bad_packet:
2107 __TCP_INC_STATS(net, TCP_MIB_INERRS);
2108 } else {
2109 tcp_v4_send_reset(NULL, skb);
2110 }
2111
2112 discard_it:
2113 /* Discard frame. */
2114 kfree_skb_reason(skb, drop_reason);
2115 return 0;
2116
2117 discard_and_relse:
2118 sk_drops_add(sk, skb);
2119 if (refcounted)
2120 sock_put(sk);
2121 goto discard_it;
2122
2123 do_time_wait:
2124 if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
2125 drop_reason = SKB_DROP_REASON_XFRM_POLICY;
2126 inet_twsk_put(inet_twsk(sk));
2127 goto discard_it;
2128 }
2129
2130 tcp_v4_fill_cb(skb, iph, th);
2131
2132 if (tcp_checksum_complete(skb)) {
2133 inet_twsk_put(inet_twsk(sk));
2134 goto csum_error;
2135 }
2136 switch (tcp_timewait_state_process(inet_twsk(sk), skb, th,
2137 &drop_reason)) {
2138 case TCP_TW_SYN: {
2139 struct sock *sk2 = inet_lookup_listener(dev_net(skb->dev),
2140 &tcp_hashinfo, skb,
2141 __tcp_hdrlen(th),
2142 iph->saddr, th->source,
2143 iph->daddr, th->dest,
2144 inet_iif(skb),
2145 sdif);
2146 if (sk2) {
2147 inet_twsk_deschedule_put(inet_twsk(sk));
2148 sk = sk2;
2149 tcp_v4_restore_cb(skb);
2150 refcounted = false;
2151 goto process;
2152 }
2153 /* TCP_FLAGS or NO_SOCKET? */
2154 SKB_DR_SET(drop_reason, TCP_FLAGS);
2155 }
2156 /* to ACK */
2157 fallthrough;
2158 case TCP_TW_ACK:
2159 tcp_v4_timewait_ack(sk, skb);
2160 refcounted = false;
> 2161 if (drop_reason)
2162 goto discard_it;
2163 else
2164 goto put_and_return;
2165 case TCP_TW_RST:
2166 tcp_v4_send_reset(sk, skb);
2167 inet_twsk_deschedule_put(inet_twsk(sk));
2168 goto discard_it;
2169 case TCP_TW_SUCCESS:;
2170 }
2171 goto discard_it;
2172 }
2173
--
0-DAY CI Kernel Test Service
https://01.org/lkp
^ permalink raw reply
* [PATCH v5 05/15] landlock: landlock_add_rule syscall refactoring
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
Landlock_add_rule syscall was refactored to support new
rule types in future Landlock versions. Add_rule_path_beneath()
helper was added to support current filesystem rules. It is called
by the switch case.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Split commit.
* Refactoring landlock_add_rule syscall.
Changes since v4:
* Refactoring add_rule_path_beneath() and landlock_add_rule() functions
to optimize code usage.
* Refactoring base_test.c seltest: adds LANDLOCK_RULE_PATH_BENEATH
rule type in landlock_add_rule() call.
---
security/landlock/syscalls.c | 105 ++++++++++---------
tools/testing/selftests/landlock/base_test.c | 4 +-
2 files changed, 59 insertions(+), 50 deletions(-)
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 1db799d1a50b..412ced6c512f 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -274,67 +274,23 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
return err;
}
-/**
- * sys_landlock_add_rule - Add a new rule to a ruleset
- *
- * @ruleset_fd: File descriptor tied to the ruleset that should be extended
- * with the new rule.
- * @rule_type: Identify the structure type pointed to by @rule_attr (only
- * LANDLOCK_RULE_PATH_BENEATH for now).
- * @rule_attr: Pointer to a rule (only of type &struct
- * landlock_path_beneath_attr for now).
- * @flags: Must be 0.
- *
- * This system call enables to define a new rule and add it to an existing
- * ruleset.
- *
- * Possible returned errors are:
- *
- * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
- * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
- * &landlock_path_beneath_attr.allowed_access is not a subset of the
- * ruleset handled accesses);
- * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
- * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
- * member of @rule_attr is not a file descriptor as expected;
- * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
- * @rule_attr is not the expected file descriptor type;
- * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
- * - EFAULT: @rule_attr inconsistency.
- */
-SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
- const enum landlock_rule_type, rule_type,
- const void __user *const, rule_attr, const __u32, flags)
+static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_attr)
{
struct landlock_path_beneath_attr path_beneath_attr;
struct path path;
struct landlock_ruleset *ruleset;
int res, err;
- if (!landlock_initialized)
- return -EOPNOTSUPP;
-
- /* No flag for now. */
- if (flags)
- return -EINVAL;
-
/* Gets and checks the ruleset. */
ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
if (IS_ERR(ruleset))
return PTR_ERR(ruleset);
- if (rule_type != LANDLOCK_RULE_PATH_BENEATH) {
- err = -EINVAL;
- goto out_put_ruleset;
- }
-
/* Copies raw user space buffer, only one type for now. */
res = copy_from_user(&path_beneath_attr, rule_attr,
- sizeof(path_beneath_attr));
- if (res) {
- err = -EFAULT;
- goto out_put_ruleset;
- }
+ sizeof(path_beneath_attr));
+ if (res)
+ return -EFAULT;
/*
* Informs about useless rule: empty allowed_access (i.e. deny rules)
@@ -370,6 +326,59 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
return err;
}
+/**
+ * sys_landlock_add_rule - Add a new rule to a ruleset
+ *
+ * @ruleset_fd: File descriptor tied to the ruleset that should be extended
+ * with the new rule.
+ * @rule_type: Identify the structure type pointed to by @rule_attr (only
+ * LANDLOCK_RULE_PATH_BENEATH for now).
+ * @rule_attr: Pointer to a rule (only of type &struct
+ * landlock_path_beneath_attr for now).
+ * @flags: Must be 0.
+ *
+ * This system call enables to define a new rule and add it to an existing
+ * ruleset.
+ *
+ * Possible returned errors are:
+ *
+ * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
+ * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
+ * &landlock_path_beneath_attr.allowed_access is not a subset of the rule's
+ * accesses);
+ * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
+ * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
+ * member of @rule_attr is not a file descriptor as expected;
+ * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
+ * @rule_attr is not the expected file descriptor type (e.g. file open
+ * without O_PATH);
+ * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
+ * - EFAULT: @rule_attr inconsistency.
+ */
+SYSCALL_DEFINE4(landlock_add_rule,
+ const int, ruleset_fd, const enum landlock_rule_type, rule_type,
+ const void __user *const, rule_attr, const __u32, flags)
+{
+ int err;
+
+ if (!landlock_initialized)
+ return -EOPNOTSUPP;
+
+ /* No flag for now. */
+ if (flags)
+ return -EINVAL;
+
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ err = add_rule_path_beneath(ruleset_fd, rule_attr);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
/* Enforcement */
/**
diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index da9290817866..0c4c3a538d54 100644
--- a/tools/testing/selftests/landlock/base_test.c
+++ b/tools/testing/selftests/landlock/base_test.c
@@ -156,11 +156,11 @@ TEST(add_rule_checks_ordering)
ASSERT_LE(0, ruleset_fd);
/* Checks invalid flags. */
- ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 1));
+ ASSERT_EQ(-1, landlock_add_rule(-1, LANDLOCK_RULE_PATH_BENEATH, NULL, 1));
ASSERT_EQ(EINVAL, errno);
/* Checks invalid ruleset FD. */
- ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 0));
+ ASSERT_EQ(-1, landlock_add_rule(-1, LANDLOCK_RULE_PATH_BENEATH, NULL, 0));
ASSERT_EQ(EBADF, errno);
/* Checks invalid rule type. */
--
2.25.1
^ permalink raw reply related
* [PATCH v5 15/15] samples/landlock: adds network demo
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
This commit adds network demo. It's possible to
allow a sandoxer to bind/connect to a list of
particular ports restricting networks actions to
the rest of ports.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v4:
* Adds ENV_TCP_BIND_NAME "LL_TCP_BIND" and
ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT" variables
to insert TCP ports.
* Renames populate_ruleset() to populate_ruleset_fs().
* Adds populate_ruleset_net() and parse_port_num() helpers.
* Refactoring main() to support network sandboxing.
---
samples/landlock/sandboxer.c | 105 +++++++++++++++++++++++++++++++----
security/landlock/ruleset.h | 4 +-
2 files changed, 95 insertions(+), 14 deletions(-)
diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index 3e404e51ec64..4006c42eec1c 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -51,6 +51,8 @@ static inline int landlock_restrict_self(const int ruleset_fd,
#define ENV_FS_RO_NAME "LL_FS_RO"
#define ENV_FS_RW_NAME "LL_FS_RW"
+#define ENV_TCP_BIND_NAME "LL_TCP_BIND"
+#define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT"
#define ENV_PATH_TOKEN ":"
static int parse_path(char *env_path, const char ***const path_list)
@@ -71,6 +73,20 @@ static int parse_path(char *env_path, const char ***const path_list)
return num_paths;
}
+static int parse_port_num(char *env_port)
+{
+ int i, num_ports = 0;
+
+ if (env_port) {
+ num_ports++;
+ for (i = 0; env_port[i]; i++) {
+ if (env_port[i] == ENV_PATH_TOKEN[0])
+ num_ports++;
+ }
+ }
+ return num_ports;
+}
+
/* clang-format off */
#define ACCESS_FILE ( \
@@ -80,7 +96,7 @@ static int parse_path(char *env_path, const char ***const path_list)
/* clang-format on */
-static int populate_ruleset(const char *const env_var, const int ruleset_fd,
+static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
const __u64 allowed_access)
{
int num_paths, i, ret = 1;
@@ -142,6 +158,49 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd,
return ret;
}
+static int populate_ruleset_net(const char *const env_var,
+ const int ruleset_fd,
+ const __u64 allowed_access)
+{
+ int num_ports, i, ret = 1;
+ char *env_port_name;
+ struct landlock_net_service_attr net_service = {
+ .allowed_access = 0,
+ .port = 0,
+ };
+
+ env_port_name = getenv(env_var);
+ if (!env_port_name) {
+ /* Prevents users to forget a setting. */
+ fprintf(stderr, "Missing environment variable %s\n", env_var);
+ return 1;
+ }
+ env_port_name = strdup(env_port_name);
+ unsetenv(env_var);
+ num_ports = parse_port_num(env_port_name);
+
+ if (num_ports == 1 && (strtok(env_port_name, ENV_PATH_TOKEN) == NULL)) {
+ ret = 0;
+ goto out_free_name;
+ }
+
+ for (i = 0; i < num_ports; i++) {
+ net_service.allowed_access = allowed_access;
+ net_service.port = atoi(strsep(&env_port_name, ENV_PATH_TOKEN));
+ if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service, 0)) {
+ fprintf(stderr, "Failed to update the ruleset with port \"%d\": %s\n",
+ net_service.port, strerror(errno));
+ goto out_free_name;
+ }
+ }
+ ret = 0;
+
+out_free_name:
+ free(env_port_name);
+ return ret;
+}
+
/* clang-format off */
#define ACCESS_FS_ROUGHLY_READ ( \
@@ -173,19 +232,24 @@ int main(const int argc, char *const argv[], char *const *const envp)
char *const *cmd_argv;
int ruleset_fd, abi;
__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ,
- access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE;
+ access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE,
+ access_net_tcp = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP;
struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = access_fs_rw,
+ .handled_access_net = access_net_tcp,
};
if (argc < 2) {
fprintf(stderr,
- "usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n",
- ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
+ "usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s "
+ "<cmd> [args]...\n\n", ENV_FS_RO_NAME, ENV_FS_RW_NAME,
+ ENV_TCP_BIND_NAME, ENV_TCP_CONNECT_NAME, argv[0]);
fprintf(stderr,
"Launch a command in a restricted environment.\n\n");
- fprintf(stderr, "Environment variables containing paths, "
- "each separated by a colon:\n");
+ fprintf(stderr,
+ "Environment variables containing paths and ports "
+ "each separated by a colon:\n");
fprintf(stderr,
"* %s: list of paths allowed to be used in a read-only way.\n",
ENV_FS_RO_NAME);
@@ -193,11 +257,19 @@ int main(const int argc, char *const argv[], char *const *const envp)
"* %s: list of paths allowed to be used in a read-write way.\n",
ENV_FS_RW_NAME);
fprintf(stderr,
- "\nexample:\n"
+ "* %s: list of ports allowed to bind (server).\n",
+ ENV_TCP_BIND_NAME);
+ fprintf(stderr,
+ "* %s: list of ports allowed to connect (client).\n",
+ ENV_TCP_CONNECT_NAME);
+ fprintf(stderr, "\nexample:\n"
"%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" "
"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
+ "%s=\"15000:16000\" "
+ "%s=\"10000:12000\" "
"%s bash -i\n",
- ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
+ ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
+ ENV_TCP_CONNECT_NAME, argv[0]);
return 1;
}
@@ -234,16 +306,25 @@ int main(const int argc, char *const argv[], char *const *const envp)
ruleset_fd =
landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+
if (ruleset_fd < 0) {
perror("Failed to create a ruleset");
return 1;
}
- if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) {
+ if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro))
goto err_close_ruleset;
- }
- if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
+
+ if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw))
goto err_close_ruleset;
- }
+
+ if (populate_ruleset_net(ENV_TCP_BIND_NAME, ruleset_fd,
+ LANDLOCK_ACCESS_NET_BIND_TCP))
+ goto err_close_ruleset;
+
+ if (populate_ruleset_net(ENV_TCP_CONNECT_NAME, ruleset_fd,
+ LANDLOCK_ACCESS_NET_CONNECT_TCP))
+ goto err_close_ruleset;
+
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
perror("Failed to restrict privileges");
goto err_close_ruleset;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 916b30b31c06..e1ff40f238a6 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -19,7 +19,7 @@
#include "limits.h"
#include "object.h"
-typedef u16 access_mask_t;
+typedef u32 access_mask_t;
/* Makes sure all filesystem access rights can be stored. */
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
@@ -157,7 +157,7 @@ struct landlock_ruleset {
* layers are set once and never changed for the
* lifetime of the ruleset.
*/
- u32 access_masks[];
+ access_mask_t access_masks[];
};
};
};
--
2.25.1
^ permalink raw reply related
* [PATCH v5 12/15] seltests/landlock: rules overlapping test
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
This patch adds overlapping rules for one port.
First rule adds just bind() access right for a port.
The second one adds both bind() and connect()
access rights for the same port.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Add ruleset_overlap test.
Changes since v4:
* Refactoring code with self->port, self->addr4 variables.
---
tools/testing/selftests/landlock/net_test.c | 51 +++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index bf8e49466d1d..1d8c9dfdbd48 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -677,4 +677,55 @@ TEST_F_FORK(socket_test, connect_afunspec_with_restictions) {
ASSERT_EQ(1, WIFEXITED(status));
ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
}
+
+TEST_F_FORK(socket_test, ruleset_overlap) {
+
+ int sockfd;
+
+ struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ };
+ struct landlock_net_service_attr net_service_1 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+
+ .port = self->port[0],
+ };
+
+ struct landlock_net_service_attr net_service_2 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+
+ .port = self->port[0],
+ };
+
+ const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+ sizeof(ruleset_attr), 0);
+ ASSERT_LE(0, ruleset_fd);
+
+ /* Allows bind operations to the port[0] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_1, 0));
+ /* Allows connect and bind operations to the port[0] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_2, 0));
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd);
+
+ /* Creates a server socket */
+ sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd);
+
+ /* Binds the socket to address with port[0] */
+ ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&self->addr4[0], sizeof(self->addr4[0])));
+
+ /* Makes connection to socket with port[0] */
+ ASSERT_EQ(0, connect(sockfd, (struct sockaddr *)&self->addr4[0],
+ sizeof(self->addr4[0])));
+
+ /* Closes socket */
+ ASSERT_EQ(0, close(sockfd));
+}
+
TEST_HARNESS_MAIN
--
2.25.1
^ permalink raw reply related
* [PATCH v5 14/15] seltests/landlock: invalid user input data test
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
This patch adds rules with invalid user space
supplied data:
- unhandled allowed access;
- zero port value;
- zero access value;
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Add inval test.
Changes since v4:
* Refactoring code with self->port variable.
---
tools/testing/selftests/landlock/net_test.c | 52 +++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index b1639a55a898..ca4b7a7256e8 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -880,4 +880,56 @@ TEST_F_FORK(socket_test, ruleset_expanding) {
/* Closes socket 1 */
ASSERT_EQ(0, close(sockfd_1));
}
+
+TEST_F_FORK(socket_test, inval) {
+
+ struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP
+ };
+ struct landlock_net_service_attr net_service_1 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ .port = self->port[0],
+ };
+ struct landlock_net_service_attr net_service_2 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+ .port = 0,
+ };
+ struct landlock_net_service_attr net_service_3 = {
+ .allowed_access = 0,
+ .port = self->port[1],
+ };
+ struct landlock_net_service_attr net_service_4 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+ .port = self->port[2],
+ };
+
+ /* Gets ruleset. */
+ const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+ sizeof(ruleset_attr), 0);
+ ASSERT_LE(0, ruleset_fd);
+
+ /* Checks unhandled allowed_access. */
+ ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_1, 0));
+ ASSERT_EQ(EINVAL, errno);
+
+ /* Checks zero port value. */
+ ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_2, 0));
+ ASSERT_EQ(EINVAL, errno);
+
+ /* Checks zero access value. */
+ ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_3, 0));
+ ASSERT_EQ(ENOMSG, errno);
+
+ /* Adds with legitimate values. */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_4, 0));
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd);
+ ASSERT_EQ(0, close(ruleset_fd));
+}
TEST_HARNESS_MAIN
--
2.25.1
^ permalink raw reply related
* [PATCH v5 13/15] seltests/landlock: ruleset expanding test
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
This patch adds expanding rulesets in which
rules are gradually added one by one, restricting
sockets' connections.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Add ruleset_expanding test.
Changes since v4:
* Refactoring code with self->port, self->addr4 variables.
---
tools/testing/selftests/landlock/net_test.c | 152 ++++++++++++++++++++
1 file changed, 152 insertions(+)
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index 1d8c9dfdbd48..b1639a55a898 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -728,4 +728,156 @@ TEST_F_FORK(socket_test, ruleset_overlap) {
ASSERT_EQ(0, close(sockfd));
}
+TEST_F_FORK(socket_test, ruleset_expanding) {
+
+ int sockfd_1, sockfd_2;
+
+ struct landlock_ruleset_attr ruleset_attr_1 = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP,
+ };
+ struct landlock_net_service_attr net_service_1 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+
+ .port = self->port[0],
+ };
+
+ const int ruleset_fd_1 = landlock_create_ruleset(&ruleset_attr_1,
+ sizeof(ruleset_attr_1), 0);
+ ASSERT_LE(0, ruleset_fd_1);
+
+ /* Adds rule to port[0] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd_1, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_1, 0));
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd_1);
+ ASSERT_EQ(0, close(ruleset_fd_1));
+
+ /* Creates a socket 1 */
+ sockfd_1 = create_socket(_metadata, false, true);
+ ASSERT_LE(0, sockfd_1);
+
+ /* Binds the socket 1 to address with port[0] */
+ ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr *)&self->addr4[0], sizeof(self->addr4[0])));
+
+ /* Makes connection to socket 1 with port[0] */
+ ASSERT_EQ(0, connect(sockfd_1, (struct sockaddr *)&self->addr4[0],
+ sizeof(self->addr4[0])));
+
+ /* Closes socket 1 */
+ ASSERT_EQ(0, close(sockfd_1));
+
+ /* Creates a socket 2 */
+ sockfd_2 = create_socket(_metadata, false, true);
+ ASSERT_LE(0, sockfd_2);
+
+ /*
+ * Forbids to bind the socket 2 to address with port[1],
+ * cause there is no rule with bind() access for port[1].
+ */
+ ASSERT_EQ(-1, bind(sockfd_2, (struct sockaddr *)&self->addr4[1], sizeof(self->addr4[1])));
+ ASSERT_EQ(EACCES, errno);
+
+ /* Expands network mask */
+ struct landlock_ruleset_attr ruleset_attr_2 = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ };
+
+ /* Adds connect() access to port[0] */
+ struct landlock_net_service_attr net_service_2 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+
+ .port = self->port[0],
+ };
+ /* Adds bind() access to port[1] */
+ struct landlock_net_service_attr net_service_3 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+
+ .port = self->port[1],
+ };
+
+ const int ruleset_fd_2 = landlock_create_ruleset(&ruleset_attr_2,
+ sizeof(ruleset_attr_2), 0);
+ ASSERT_LE(0, ruleset_fd_2);
+
+ /* Adds rule to port[0] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd_2, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_2, 0));
+ /* Adds rule to port[1] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd_2, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_3, 0));
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd_2);
+ ASSERT_EQ(0, close(ruleset_fd_2));
+
+ /* Creates a socket 1 */
+ sockfd_1 = create_socket(_metadata, false, true);
+ ASSERT_LE(0, sockfd_1);
+
+ /* Binds the socket 1 to address with port[0] */
+ ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr *)&self->addr4[0], sizeof(self->addr4[0])));
+
+ /* Makes connection to socket 1 with port[0] */
+ ASSERT_EQ(0, connect(sockfd_1, (struct sockaddr *)&self->addr4[0],
+ sizeof(self->addr4[0])));
+ /* Closes socket 1 */
+ ASSERT_EQ(0, close(sockfd_1));
+
+ /* Creates a socket 2 */
+ sockfd_2 = create_socket(_metadata, false, true);
+ ASSERT_LE(0, sockfd_2);
+
+ /*
+ * Forbids to bind the socket 2 to address with port[1],
+ * cause just one layer has bind() access rule.
+ */
+ ASSERT_EQ(-1, bind(sockfd_2, (struct sockaddr *)&self->addr4[1], sizeof(self->addr4[1])));
+ ASSERT_EQ(EACCES, errno);
+
+ /* Expands network mask */
+ struct landlock_ruleset_attr ruleset_attr_3 = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ };
+
+ /* Restricts connect() access to port[0] */
+ struct landlock_net_service_attr net_service_4 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+
+ .port = self->port[0],
+ };
+
+ const int ruleset_fd_3 = landlock_create_ruleset(&ruleset_attr_3,
+ sizeof(ruleset_attr_3), 0);
+ ASSERT_LE(0, ruleset_fd_3);
+
+ /* Adds rule to port[0] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd_3, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_4, 0));
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd_3);
+ ASSERT_EQ(0, close(ruleset_fd_3));
+
+ /* Creates a socket 1 */
+ sockfd_1 = create_socket(_metadata, false, true);
+ ASSERT_LE(0, sockfd_1);
+
+ /* Binds the socket 1 to address with port[0] */
+ ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr *)&self->addr4[0], sizeof(self->addr4[0])));
+
+ /*
+ * Forbids to bind the socket 1 to address with port[0],
+ * cause just one layer has connect() access rule.
+ */
+ ASSERT_EQ(-1, connect(sockfd_1, (struct sockaddr *)&self->addr4[0],
+ sizeof(self->addr4[0])));
+ ASSERT_EQ(EACCES, errno);
+
+ /* Closes socket 1 */
+ ASSERT_EQ(0, close(sockfd_1));
+}
TEST_HARNESS_MAIN
--
2.25.1
^ permalink raw reply related
* [PATCH v5 09/15] seltests/landlock: add tests for bind() hooks
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
Adds selftests for bind socket action.
The first is with no landlock restrictions:
- bind_no_restrictions_ip4;
- bind_no_restrictions_ip6;
The second ones is with mixed landlock rules:
- bind_with_restrictions_ip4;
- bind_with_restrictions_ip6;
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Split commit.
* Add helper create_socket.
* Add FIXTURE_SETUP.
Changes since v4:
* Adds port[MAX_SOCKET_NUM], struct sockaddr_in addr4
and struct sockaddr_in addr6 in FIXTURE.
* Refactoring FIXTURE_SETUP:
- initializing self->port, self->addr4 and self->addr6.
- adding network namespace.
* Refactoring code with self->port, self->addr4 and
self->addr6 variables.
* Adds selftests for IP6 family:
- bind_no_restrictions_ip6.
- bind_with_restrictions_ip6.
* Refactoring selftests/landlock/config
* Moves enforce_ruleset() into common.h
---
tools/testing/selftests/landlock/common.h | 9 +
tools/testing/selftests/landlock/config | 5 +-
tools/testing/selftests/landlock/fs_test.c | 10 -
tools/testing/selftests/landlock/net_test.c | 237 ++++++++++++++++++++
4 files changed, 250 insertions(+), 11 deletions(-)
create mode 100644 tools/testing/selftests/landlock/net_test.c
diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
index 7ba18eb23783..c5381e641dfd 100644
--- a/tools/testing/selftests/landlock/common.h
+++ b/tools/testing/selftests/landlock/common.h
@@ -102,6 +102,15 @@ static inline int landlock_restrict_self(const int ruleset_fd,
}
#endif
+static void enforce_ruleset(struct __test_metadata *const _metadata,
+ const int ruleset_fd)
+{
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
+ ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) {
+ TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
+ }
+}
+
static void _init_caps(struct __test_metadata *const _metadata, bool drop_all)
{
cap_t cap_p;
diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config
index 0f0a65287bac..b56f3274d3f5 100644
--- a/tools/testing/selftests/landlock/config
+++ b/tools/testing/selftests/landlock/config
@@ -1,7 +1,10 @@
+CONFIG_INET=y
+CONFIG_IPV6=y
+CONFIG_NET=y
CONFIG_OVERLAY_FS=y
CONFIG_SECURITY_LANDLOCK=y
CONFIG_SECURITY_PATH=y
CONFIG_SECURITY=y
CONFIG_SHMEM=y
CONFIG_TMPFS_XATTR=y
-CONFIG_TMPFS=y
+CONFIG_TMPFS=y
\ No newline at end of file
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index 21a2ce8fa739..036dd6f8f9ea 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -551,16 +551,6 @@ static int create_ruleset(struct __test_metadata *const _metadata,
return ruleset_fd;
}
-static void enforce_ruleset(struct __test_metadata *const _metadata,
- const int ruleset_fd)
-{
- ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
- ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0))
- {
- TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
- }
-}
-
TEST_F_FORK(layout1, proc_nsfs)
{
const struct rule rules[] = {
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
new file mode 100644
index 000000000000..478ef2eff559
--- /dev/null
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock tests - Network
+ *
+ * Copyright (C) 2022 Huawei Tech. Co., Ltd.
+ */
+
+#define _GNU_SOURCE
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/landlock.h>
+#include <netinet/in.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "common.h"
+
+#define MAX_SOCKET_NUM 10
+
+#define SOCK_PORT_START 3470
+#define SOCK_PORT_ADD 10
+
+#define IP_ADDRESS "127.0.0.1"
+
+/* Number pending connections queue to be hold */
+#define BACKLOG 10
+
+static int create_socket(struct __test_metadata *const _metadata,
+ bool ip6, bool reuse_addr)
+{
+ int sockfd;
+ int one = 1;
+
+ if (ip6)
+ sockfd = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ else
+ sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+
+ ASSERT_LE(0, sockfd);
+ /* Allows to reuse of local address */
+ if (reuse_addr)
+ ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET,
+ SO_REUSEADDR, &one, sizeof(one)));
+ return sockfd;
+}
+
+FIXTURE(socket_test) {
+ uint port[MAX_SOCKET_NUM];
+ struct sockaddr_in addr4[MAX_SOCKET_NUM];
+ struct sockaddr_in6 addr6[MAX_SOCKET_NUM];
+};
+
+FIXTURE_SETUP(socket_test)
+{
+ int i;
+ /* Creates IP4 socket addresses */
+ for (i = 0; i < MAX_SOCKET_NUM; i++) {
+ self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD*i;
+ self->addr4[i].sin_family = AF_INET;
+ self->addr4[i].sin_port = htons(self->port[i]);
+ self->addr4[i].sin_addr.s_addr = htonl(INADDR_ANY);
+ memset(&(self->addr4[i].sin_zero), '\0', 8);
+ }
+
+ /* Creates IP6 socket addresses */
+ for (i = 0; i < MAX_SOCKET_NUM; i++) {
+ self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD*i;
+ self->addr6[i].sin6_family = AF_INET6;
+ self->addr6[i].sin6_port = htons(self->port[i]);
+ self->addr6[i].sin6_addr = in6addr_any;
+ }
+
+ set_cap(_metadata, CAP_SYS_ADMIN);
+ ASSERT_EQ(0, unshare(CLONE_NEWNET));
+ ASSERT_EQ(0, system("ip link set dev lo up"));
+ clear_cap(_metadata, CAP_SYS_ADMIN);
+}
+
+FIXTURE_TEARDOWN(socket_test)
+{ }
+
+TEST_F_FORK(socket_test, bind_no_restrictions_ip4) {
+
+ int sockfd;
+
+ sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd);
+
+ /* Binds a socket to port[0] */
+ ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&self->addr4[0], sizeof(self->addr4[0])));
+
+ ASSERT_EQ(0, close(sockfd));
+}
+
+TEST_F_FORK(socket_test, bind_no_restrictions_ip6) {
+
+ int sockfd;
+
+ sockfd = create_socket(_metadata, true, false);
+ ASSERT_LE(0, sockfd);
+
+ /* Binds a socket to port[0] */
+ ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&self->addr6[0], sizeof(self->addr6[0])));
+
+ ASSERT_EQ(0, close(sockfd));
+}
+
+TEST_F_FORK(socket_test, bind_with_restrictions_ip4) {
+
+ int sockfd;
+
+ struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ };
+ struct landlock_net_service_attr net_service_1 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ .port = self->port[0],
+ };
+ struct landlock_net_service_attr net_service_2 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ .port = self->port[1],
+ };
+ struct landlock_net_service_attr net_service_3 = {
+ .allowed_access = 0,
+ .port = self->port[2],
+ };
+
+ const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+ sizeof(ruleset_attr), 0);
+ ASSERT_LE(0, ruleset_fd);
+
+ /* Allows connect and bind operations to the port[0] socket. */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_1, 0));
+ /* Allows connect and deny bind operations to the port[1] socket. */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_2, 0));
+ /* Empty allowed_access (i.e. deny rules) are ignored in network actions
+ * for port[2] socket.
+ */
+ ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_3, 0));
+ ASSERT_EQ(ENOMSG, errno);
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd);
+
+ sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd);
+ /* Binds a socket to port[0] */
+ ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&self->addr4[0], sizeof(self->addr4[0])));
+
+ /* Close bounded socket*/
+ ASSERT_EQ(0, close(sockfd));
+
+ sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd);
+ /* Binds a socket to port[1] */
+ ASSERT_EQ(-1, bind(sockfd, (struct sockaddr *)&self->addr4[1], sizeof(self->addr4[1])));
+ ASSERT_EQ(EACCES, errno);
+
+ sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd);
+ /* Binds a socket to port[2] */
+ ASSERT_EQ(-1, bind(sockfd, (struct sockaddr *)&self->addr4[2], sizeof(self->addr4[2])));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F_FORK(socket_test, bind_with_restrictions_ip6) {
+
+ int sockfd;
+
+ struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ };
+ struct landlock_net_service_attr net_service_1 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ .port = self->port[0],
+ };
+ struct landlock_net_service_attr net_service_2 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ .port = self->port[1],
+ };
+ struct landlock_net_service_attr net_service_3 = {
+ .allowed_access = 0,
+ .port = self->port[2],
+ };
+
+ const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+ sizeof(ruleset_attr), 0);
+ ASSERT_LE(0, ruleset_fd);
+
+ /* Allows connect and bind operations to the port[0] socket. */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_1, 0));
+ /* Allows connect and deny bind operations to the port[1] socket. */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_2, 0));
+ /* Empty allowed_access (i.e. deny rules) are ignored in network actions
+ * for port[2] socket.
+ */
+ ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_3, 0));
+ ASSERT_EQ(ENOMSG, errno);
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd);
+
+ sockfd = create_socket(_metadata, true, false);
+ ASSERT_LE(0, sockfd);
+ /* Binds a socket to port[0] */
+ ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&self->addr6[0], sizeof(self->addr6[0])));
+
+ /* Close bounded socket*/
+ ASSERT_EQ(0, close(sockfd));
+
+ sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd);
+ /* Binds a socket to port[1] */
+ ASSERT_EQ(-1, bind(sockfd, (struct sockaddr *)&self->addr6[1], sizeof(self->addr6[1])));
+ ASSERT_EQ(EACCES, errno);
+
+ sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd);
+ /* Binds a socket to port[2] */
+ ASSERT_EQ(-1, bind(sockfd, (struct sockaddr *)&self->addr6[2], sizeof(self->addr6[2])));
+ ASSERT_EQ(EACCES, errno);
+}
+TEST_HARNESS_MAIN
--
2.25.1
^ permalink raw reply related
* [PATCH v5 10/15] seltests/landlock: add tests for connect() hooks
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
Adds selftests for connect socket action.
The first are with no landlock restrictions:
- connect_no_restrictions_ip4;
- connect_no_restrictions_ip6;
The second ones are with mixed landlock rules:
- connect_with_restrictions_ip4;
- connect_with_restrictions_ip6;
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Split commit.
Changes since v4:
* Adds selftests for IP6 family:
- connect_no_restrictions_ip6.
- connect_with_restrictions_ip6.
* Refactoring code with self->port, self->addr4 and
self->addr6 variables.
---
tools/testing/selftests/landlock/net_test.c | 322 ++++++++++++++++++++
1 file changed, 322 insertions(+)
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index 478ef2eff559..cf914d311eb3 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -234,4 +234,326 @@ TEST_F_FORK(socket_test, bind_with_restrictions_ip6) {
ASSERT_EQ(-1, bind(sockfd, (struct sockaddr *)&self->addr6[2], sizeof(self->addr6[2])));
ASSERT_EQ(EACCES, errno);
}
+
+TEST_F_FORK(socket_test, connect_no_restrictions_ip4) {
+
+ int sockfd, new_fd;
+ pid_t child;
+ int status;
+
+ /* Creates a server socket */
+ sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd);
+
+ /* Binds a socket to port[0] */
+ ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&self->addr4[0], sizeof(self->addr4[0])));
+
+ /* Makes listening socket */
+ ASSERT_EQ(0, listen(sockfd, BACKLOG));
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (child == 0) {
+ int child_sockfd;
+
+ /* Closes listening socket for the child */
+ ASSERT_EQ(0, close(sockfd));
+ /* Create a stream client socket */
+ child_sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, child_sockfd);
+
+ /* Makes connection to the listening socket */
+ ASSERT_EQ(0, connect(child_sockfd, (struct sockaddr *)&self->addr4[0],
+ sizeof(self->addr4[0])));
+ _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+ return;
+ }
+ /* Accepts connection from the child */
+ new_fd = accept(sockfd, NULL, 0);
+ ASSERT_LE(0, new_fd);
+
+ /* Closes connection */
+ ASSERT_EQ(0, close(new_fd));
+
+ /* Closes listening socket for the parent*/
+ ASSERT_EQ(0, close(sockfd));
+
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ ASSERT_EQ(1, WIFEXITED(status));
+ ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+}
+
+TEST_F_FORK(socket_test, connect_no_restrictions_ip6) {
+
+ int sockfd, new_fd;
+ pid_t child;
+ int status;
+
+ /* Creates a server socket */
+ sockfd = create_socket(_metadata, true, false);
+ ASSERT_LE(0, sockfd);
+
+ /* Binds a socket to port[0] */
+ ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&self->addr6[0], sizeof(self->addr6[0])));
+
+ /* Makes listening socket */
+ ASSERT_EQ(0, listen(sockfd, BACKLOG));
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (child == 0) {
+ int child_sockfd;
+
+ /* Closes listening socket for the child */
+ ASSERT_EQ(0, close(sockfd));
+ /* Create a stream client socket */
+ child_sockfd = create_socket(_metadata, true, false);
+ ASSERT_LE(0, child_sockfd);
+
+ /* Makes connection to the listening socket */
+ ASSERT_EQ(0, connect(child_sockfd, (struct sockaddr *)&self->addr6[0],
+ sizeof(self->addr6[0])));
+ _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+ return;
+ }
+ /* Accepts connection from the child */
+ new_fd = accept(sockfd, NULL, 0);
+ ASSERT_LE(0, new_fd);
+
+ /* Closes connection */
+ ASSERT_EQ(0, close(new_fd));
+
+ /* Closes listening socket for the parent*/
+ ASSERT_EQ(0, close(sockfd));
+
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ ASSERT_EQ(1, WIFEXITED(status));
+ ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+}
+
+TEST_F_FORK(socket_test, connect_with_restrictions_ip4) {
+
+ int new_fd;
+ int sockfd_1, sockfd_2;
+ pid_t child_1, child_2;
+ int status;
+
+ struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ };
+ struct landlock_net_service_attr net_service_1 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ .port = self->port[0],
+ };
+ struct landlock_net_service_attr net_service_2 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+ .port = self->port[1],
+ };
+
+ const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+ sizeof(ruleset_attr), 0);
+ ASSERT_LE(0, ruleset_fd);
+
+ /* Allows connect and bind operations to the port[0] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_1, 0));
+ /* Allows connect and deny bind operations to the port[1] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_2, 0));
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd);
+
+ /* Creates a server socket 1 */
+ sockfd_1 = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd_1);
+
+ /* Binds the socket 1 to address with port[0] */
+ ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr *)&self->addr4[0], sizeof(self->addr4[0])));
+
+ /* Makes listening socket 1 */
+ ASSERT_EQ(0, listen(sockfd_1, BACKLOG));
+
+ child_1 = fork();
+ ASSERT_LE(0, child_1);
+ if (child_1 == 0) {
+ int child_sockfd;
+
+ /* Closes listening socket for the child */
+ ASSERT_EQ(0, close(sockfd_1));
+ /* Creates a stream client socket */
+ child_sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, child_sockfd);
+
+ /* Makes connection to the listening socket */
+ ASSERT_EQ(0, connect(child_sockfd, (struct sockaddr *)&self->addr4[0],
+ sizeof(self->addr4[0])));
+ _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+ return;
+ }
+ /* Accepts connection from the child 1 */
+ new_fd = accept(sockfd_1, NULL, 0);
+ ASSERT_LE(0, new_fd);
+
+ /* Closes connection */
+ ASSERT_EQ(0, close(new_fd));
+
+ /* Closes listening socket 1 for the parent*/
+ ASSERT_EQ(0, close(sockfd_1));
+
+ ASSERT_EQ(child_1, waitpid(child_1, &status, 0));
+ ASSERT_EQ(1, WIFEXITED(status));
+ ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+ /* Creates a server socket 2 */
+ sockfd_2 = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd_2);
+
+ /* Binds the socket 2 to address with port[1] */
+ ASSERT_EQ(0, bind(sockfd_2, (struct sockaddr *)&self->addr4[1], sizeof(self->addr4[1])));
+
+ /* Makes listening socket 2 */
+ ASSERT_EQ(0, listen(sockfd_2, BACKLOG));
+
+ child_2 = fork();
+ ASSERT_LE(0, child_2);
+ if (child_2 == 0) {
+ int child_sockfd;
+
+ /* Closes listening socket for the child */
+ ASSERT_EQ(0, close(sockfd_2));
+ /* Creates a stream client socket */
+ child_sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, child_sockfd);
+
+ /* Makes connection to the listening socket */
+ ASSERT_EQ(-1, connect(child_sockfd, (struct sockaddr *)&self->addr4[1],
+ sizeof(self->addr4[1])));
+ ASSERT_EQ(EACCES, errno);
+ _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+ return;
+ }
+
+ /* Closes listening socket 2 for the parent*/
+ ASSERT_EQ(0, close(sockfd_2));
+
+ ASSERT_EQ(child_2, waitpid(child_2, &status, 0));
+ ASSERT_EQ(1, WIFEXITED(status));
+ ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+}
+
+TEST_F_FORK(socket_test, connect_with_restrictions_ip6) {
+
+ int new_fd;
+ int sockfd_1, sockfd_2;
+ pid_t child_1, child_2;
+ int status;
+
+ struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ };
+ struct landlock_net_service_attr net_service_1 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ .port = self->port[0],
+ };
+ struct landlock_net_service_attr net_service_2 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+ .port = self->port[1],
+ };
+
+ const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+ sizeof(ruleset_attr), 0);
+ ASSERT_LE(0, ruleset_fd);
+
+ /* Allows connect and bind operations to the port[0] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_1, 0));
+ /* Allows connect and deny bind operations to the port[1] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_2, 0));
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd);
+
+ /* Creates a server socket 1 */
+ sockfd_1 = create_socket(_metadata, true, false);
+ ASSERT_LE(0, sockfd_1);
+
+ /* Binds the socket 1 to address with port[0] */
+ ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr *)&self->addr6[0], sizeof(self->addr6[0])));
+
+ /* Makes listening socket 1 */
+ ASSERT_EQ(0, listen(sockfd_1, BACKLOG));
+
+ child_1 = fork();
+ ASSERT_LE(0, child_1);
+ if (child_1 == 0) {
+ int child_sockfd;
+
+ /* Closes listening socket for the child */
+ ASSERT_EQ(0, close(sockfd_1));
+ /* Creates a stream client socket */
+ child_sockfd = create_socket(_metadata, true, false);
+ ASSERT_LE(0, child_sockfd);
+
+ /* Makes connection to the listening socket */
+ ASSERT_EQ(0, connect(child_sockfd, (struct sockaddr *)&self->addr6[0],
+ sizeof(self->addr6[0])));
+ _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+ return;
+ }
+ /* Accepts connection from the child 1 */
+ new_fd = accept(sockfd_1, NULL, 0);
+ ASSERT_LE(0, new_fd);
+
+ /* Closes connection */
+ ASSERT_EQ(0, close(new_fd));
+
+ /* Closes listening socket 1 for the parent*/
+ ASSERT_EQ(0, close(sockfd_1));
+
+ ASSERT_EQ(child_1, waitpid(child_1, &status, 0));
+ ASSERT_EQ(1, WIFEXITED(status));
+ ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+ /* Creates a server socket 2 */
+ sockfd_2 = create_socket(_metadata, true, false);
+ ASSERT_LE(0, sockfd_2);
+
+ /* Binds the socket 2 to address with port[1] */
+ ASSERT_EQ(0, bind(sockfd_2, (struct sockaddr *)&self->addr6[1], sizeof(self->addr6[1])));
+
+ /* Makes listening socket 2 */
+ ASSERT_EQ(0, listen(sockfd_2, BACKLOG));
+
+ child_2 = fork();
+ ASSERT_LE(0, child_2);
+ if (child_2 == 0) {
+ int child_sockfd;
+
+ /* Closes listening socket for the child */
+ ASSERT_EQ(0, close(sockfd_2));
+ /* Creates a stream client socket */
+ child_sockfd = create_socket(_metadata, true, false);
+ ASSERT_LE(0, child_sockfd);
+
+ /* Makes connection to the listening socket */
+ ASSERT_EQ(-1, connect(child_sockfd, (struct sockaddr *)&self->addr6[1],
+ sizeof(self->addr6[1])));
+ ASSERT_EQ(EACCES, errno);
+ _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+ return;
+ }
+
+ /* Closes listening socket 2 for the parent*/
+ ASSERT_EQ(0, close(sockfd_2));
+
+ ASSERT_EQ(child_2, waitpid(child_2, &status, 0));
+ ASSERT_EQ(1, WIFEXITED(status));
+ ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+}
TEST_HARNESS_MAIN
--
2.25.1
^ permalink raw reply related
* [PATCH v5 11/15] seltests/landlock: connect() with AF_UNSPEC tests
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
Adds two selftests for connect() action with
AF_UNSPEC family flag.
The one is with no landlock restrictions
allows to disconnect already conneted socket
with connect(..., AF_UNSPEC, ...):
- connect_afunspec_no_restictions;
The second one refuses landlocked process
to disconnect already connected socket:
- connect_afunspec_with_restictions;
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Add connect_afunspec_no_restictions test.
* Add connect_afunspec_with_restictions test.
Changes since v4:
* Refactoring code with self->port, self->addr4 variables.
* Adds bind() hook check for with AF_UNSPEC family.
---
tools/testing/selftests/landlock/net_test.c | 121 ++++++++++++++++++++
1 file changed, 121 insertions(+)
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index cf914d311eb3..bf8e49466d1d 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -449,6 +449,7 @@ TEST_F_FORK(socket_test, connect_with_restrictions_ip6) {
int new_fd;
int sockfd_1, sockfd_2;
pid_t child_1, child_2;
+
int status;
struct landlock_ruleset_attr ruleset_attr = {
@@ -467,10 +468,12 @@ TEST_F_FORK(socket_test, connect_with_restrictions_ip6) {
const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
sizeof(ruleset_attr), 0);
+
ASSERT_LE(0, ruleset_fd);
/* Allows connect and bind operations to the port[0] socket */
ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+
&net_service_1, 0));
/* Allows connect and deny bind operations to the port[1] socket */
ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
@@ -480,6 +483,7 @@ TEST_F_FORK(socket_test, connect_with_restrictions_ip6) {
enforce_ruleset(_metadata, ruleset_fd);
/* Creates a server socket 1 */
+
sockfd_1 = create_socket(_metadata, true, false);
ASSERT_LE(0, sockfd_1);
@@ -556,4 +560,121 @@ TEST_F_FORK(socket_test, connect_with_restrictions_ip6) {
ASSERT_EQ(1, WIFEXITED(status));
ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
}
+
+TEST_F_FORK(socket_test, connect_afunspec_no_restictions) {
+
+ int sockfd;
+ pid_t child;
+ int status;
+
+ /* Creates a server socket 1 */
+ sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd);
+
+ /* Binds the socket 1 to address with port[0] with AF_UNSPEC family */
+ self->addr4[0].sin_family = AF_UNSPEC;
+ ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&self->addr4[0], sizeof(self->addr4[0])));
+
+ /* Makes connection to socket with port[0] */
+ ASSERT_EQ(0, connect(sockfd, (struct sockaddr *)&self->addr4[0],
+ sizeof(self->addr4[0])));
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (child == 0) {
+ struct sockaddr addr_unspec = {.sa_family = AF_UNSPEC};
+
+ /* Child tries to disconnect already connected socket */
+ ASSERT_EQ(0, connect(sockfd, (struct sockaddr *)&addr_unspec,
+ sizeof(addr_unspec)));
+ _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+ return;
+ }
+ /* Closes listening socket 1 for the parent*/
+ ASSERT_EQ(0, close(sockfd));
+
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ ASSERT_EQ(1, WIFEXITED(status));
+ ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+}
+
+TEST_F_FORK(socket_test, connect_afunspec_with_restictions) {
+
+ int sockfd;
+ pid_t child;
+ int status;
+
+ struct landlock_ruleset_attr ruleset_attr_1 = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP,
+ };
+ struct landlock_net_service_attr net_service_1 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+
+ .port = self->port[0],
+ };
+
+ struct landlock_ruleset_attr ruleset_attr_2 = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ };
+ struct landlock_net_service_attr net_service_2 = {
+ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+ LANDLOCK_ACCESS_NET_CONNECT_TCP,
+
+ .port = self->port[0],
+ };
+
+ const int ruleset_fd_1 = landlock_create_ruleset(&ruleset_attr_1,
+ sizeof(ruleset_attr_1), 0);
+ ASSERT_LE(0, ruleset_fd_1);
+
+ /* Allows bind operations to the port[0] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd_1, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_1, 0));
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd_1);
+
+ /* Creates a server socket 1 */
+ sockfd = create_socket(_metadata, false, false);
+ ASSERT_LE(0, sockfd);
+
+ /* Binds the socket 1 to address with port[0] with AF_UNSPEC family */
+ self->addr4[0].sin_family = AF_UNSPEC;
+ ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&self->addr4[0], sizeof(self->addr4[0])));
+
+ /* Makes connection to socket with port[0] */
+ ASSERT_EQ(0, connect(sockfd, (struct sockaddr *)&self->addr4[0],
+ sizeof(self->addr4[0])));
+
+ const int ruleset_fd_2 = landlock_create_ruleset(&ruleset_attr_2,
+ sizeof(ruleset_attr_2), 0);
+ ASSERT_LE(0, ruleset_fd_2);
+
+ /* Allows connect and bind operations to the port[0] socket */
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd_2, LANDLOCK_RULE_NET_SERVICE,
+ &net_service_2, 0));
+
+ /* Enforces the ruleset. */
+ enforce_ruleset(_metadata, ruleset_fd_2);
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (child == 0) {
+ struct sockaddr addr_unspec = {.sa_family = AF_UNSPEC};
+
+ /* Child tries to disconnect already connected socket */
+ ASSERT_EQ(-1, connect(sockfd, (struct sockaddr *)&addr_unspec,
+ sizeof(addr_unspec)));
+ ASSERT_EQ(EACCES, errno);
+ _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+ return;
+ }
+ /* Closes listening socket 1 for the parent*/
+ ASSERT_EQ(0, close(sockfd));
+
+ ASSERT_EQ(child, waitpid(child, &status, 0));
+ ASSERT_EQ(1, WIFEXITED(status));
+ ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+}
TEST_HARNESS_MAIN
--
2.25.1
^ permalink raw reply related
* [PATCH v5 07/15] landlock: add support network rules
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
This modification adds network rules support
in internal landlock functions (presented in ruleset.c)
and landlock_create_ruleset syscall.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Split commit.
* Add network rule support for internal landlock functions.
* Add set_mask and get_mask for network.
* Add rb_root root_net_port.
Changes since v4:
* Refactoring landlock_create_ruleset() - splits ruleset and
masks checks.
* Refactoring landlock_create_ruleset() and landlock mask
setters/getters to support two rule types.
* Refactoring landlock_add_rule syscall add_rule_path_beneath
function by factoring out get_ruleset_from_fd() and
landlock_put_ruleset().
---
security/landlock/limits.h | 8 +++-
security/landlock/ruleset.c | 82 +++++++++++++++++++++++++++++++-----
security/landlock/ruleset.h | 34 +++++++++++++--
security/landlock/syscalls.c | 45 +++++++++++---------
4 files changed, 132 insertions(+), 37 deletions(-)
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index b54184ab9439..23694bf05cb7 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -22,6 +22,12 @@
#define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
-/* clang-format on */
+#define LANDLOCK_LAST_ACCESS_NET LANDLOCK_ACCESS_NET_CONNECT_TCP
+#define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
+#define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
+#define LANDLOCK_MASK_SHIFT_NET 16
+
+#define LANDLOCK_RULE_TYPE_NUM LANDLOCK_RULE_NET_SERVICE
+/* clang-format on */
#endif /* _SECURITY_LANDLOCK_LIMITS_H */
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index c4ed783d655b..ea9ecb3f471a 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -36,6 +36,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
refcount_set(&new_ruleset->usage, 1);
mutex_init(&new_ruleset->lock);
new_ruleset->root_inode = RB_ROOT;
+ new_ruleset->root_net_port = RB_ROOT;
new_ruleset->num_layers = num_layers;
/*
* hierarchy = NULL
@@ -46,17 +47,21 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
}
struct landlock_ruleset *landlock_create_ruleset(
- const access_mask_t access_mask)
+ const access_mask_t access_mask_fs,
+ const access_mask_t access_mask_net)
{
struct landlock_ruleset *new_ruleset;
/* Informs about useless ruleset. */
- if (!access_mask)
+ if (!access_mask_fs && !access_mask_net)
return ERR_PTR(-ENOMSG);
new_ruleset = create_ruleset(1);
- if (!IS_ERR(new_ruleset))
- landlock_set_fs_access_mask(new_ruleset, access_mask, 0);
-
+ if (IS_ERR(new_ruleset))
+ return new_ruleset;
+ if (access_mask_fs)
+ landlock_set_fs_access_mask(new_ruleset, access_mask_fs, 0);
+ if (access_mask_net)
+ landlock_set_net_access_mask(new_ruleset, access_mask_net, 0);
return new_ruleset;
}
@@ -94,9 +99,11 @@ static struct landlock_rule *create_rule(
return ERR_PTR(-ENOMEM);
RB_CLEAR_NODE(&new_rule->node);
- if (object_ptr) {
+ if (object_ptr && !object_data) {
landlock_get_object(object_ptr);
new_rule->object.ptr = object_ptr;
+ } else if (object_data && !object_ptr) {
+ new_rule->object.data = object_data;
} else if (object_ptr && object_data) {
WARN_ON_ONCE(1);
return ERR_PTR(-EINVAL);
@@ -132,10 +139,12 @@ static void build_check_ruleset(void)
.num_layers = ~0,
};
typeof(ruleset.access_masks[0]) fs_access_mask = ~0;
+ typeof(ruleset.access_masks[0]) net_access_mask = ~0;
BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
BUILD_BUG_ON(fs_access_mask < LANDLOCK_MASK_ACCESS_FS);
+ BUILD_BUG_ON(net_access_mask < LANDLOCK_MASK_ACCESS_NET);
}
/**
@@ -183,6 +192,11 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
object_data = (uintptr_t)object_ptr;
root = &ruleset->root_inode;
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ if (WARN_ON_ONCE(object_ptr))
+ return -EINVAL;
+ root = &ruleset->root_net_port;
+ break;
default:
WARN_ON_ONCE(1);
return -EINVAL;
@@ -237,6 +251,16 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
&ruleset->root_inode);
free_rule(this, rule_type);
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ new_rule = create_rule(NULL, object_data,
+ &this->layers, this->num_layers,
+ &(*layers)[0]);
+ if (IS_ERR(new_rule))
+ return PTR_ERR(new_rule);
+ rb_replace_node(&this->node, &new_rule->node,
+ &ruleset->root_net_port);
+ free_rule(this, rule_type);
+ break;
}
return 0;
}
@@ -254,6 +278,15 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
rb_link_node(&new_rule->node, parent_node, walker_node);
rb_insert_color(&new_rule->node, &ruleset->root_inode);
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ new_rule = create_rule(NULL, object_data, layers,
+ num_layers, NULL);
+ if (IS_ERR(new_rule))
+ return PTR_ERR(new_rule);
+ rb_link_node(&new_rule->node, parent_node, walker_node);
+ rb_insert_color(&new_rule->node, &ruleset->root_net_port);
+ ruleset->num_rules++;
+ break;
}
return 0;
}
@@ -315,6 +348,9 @@ static int tree_merge(struct landlock_ruleset *const src,
case LANDLOCK_RULE_PATH_BENEATH:
src_root = &src->root_inode;
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ src_root = &src->root_net_port;
+ break;
default:
return -EINVAL;
}
@@ -341,6 +377,11 @@ static int tree_merge(struct landlock_ruleset *const src,
rule_type, &layers,
ARRAY_SIZE(layers));
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ err = insert_rule(dst, NULL, walker_rule->object.data,
+ rule_type, &layers,
+ ARRAY_SIZE(layers));
+ break;
}
if (err)
return err;
@@ -376,6 +417,10 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
err = tree_merge(src, dst, LANDLOCK_RULE_PATH_BENEATH);
if (err)
goto out_unlock;
+ /* Merges the @src network tree. */
+ err = tree_merge(src, dst, LANDLOCK_RULE_NET_SERVICE);
+ if (err)
+ goto out_unlock;
out_unlock:
mutex_unlock(&src->lock);
@@ -395,6 +440,9 @@ static int tree_copy(struct landlock_ruleset *const parent,
case LANDLOCK_RULE_PATH_BENEATH:
parent_root = &parent->root_inode;
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ parent_root = &parent->root_net_port;
+ break;
default:
return -EINVAL;
}
@@ -407,6 +455,12 @@ static int tree_copy(struct landlock_ruleset *const parent,
rule_type, &walker_rule->layers,
walker_rule->num_layers);
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ err = insert_rule(child, NULL,
+ walker_rule->object.data, rule_type,
+ &walker_rule->layers,
+ walker_rule->num_layers);
+ break;
}
if (err)
return err;
@@ -429,6 +483,10 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
/* Copies the @parent inode tree. */
err = tree_copy(parent, child, LANDLOCK_RULE_PATH_BENEATH);
+ if (err)
+ goto out_unlock;
+ /* Copies the @parent inode tree. */
+ err = tree_copy(parent, child, LANDLOCK_RULE_NET_SERVICE);
if (err)
goto out_unlock;
@@ -463,9 +521,11 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
might_sleep();
rbtree_postorder_for_each_entry_safe(freeme, next,
- &ruleset->root_inode,
- node)
+ &ruleset->root_inode, node)
free_rule(freeme, LANDLOCK_RULE_PATH_BENEATH);
+ rbtree_postorder_for_each_entry_safe(freeme, next,
+ &ruleset->root_net_port, node)
+ free_rule(freeme, LANDLOCK_RULE_NET_SERVICE);
put_hierarchy(ruleset->hierarchy);
kfree(ruleset);
}
@@ -560,13 +620,13 @@ const struct landlock_rule *landlock_find_rule(
{
const struct rb_node *node;
- if (!object_data)
- return NULL;
-
switch (rule_type) {
case LANDLOCK_RULE_PATH_BENEATH:
node = ruleset->root_inode.rb_node;
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ node = ruleset->root_net_port.rb_node;
+ break;
default:
WARN_ON_ONCE(1);
return NULL;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index f3cd890d0348..916b30b31c06 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -102,6 +102,12 @@ struct landlock_ruleset {
* tree is immutable until @usage reaches zero.
*/
struct rb_root root_inode;
+ /**
+ * @root_net_port: Root of a red-black tree containing object nodes
+ * for network port. Once a ruleset is tied to a process (i.e. as a domain),
+ * this tree is immutable until @usage reaches zero.
+ */
+ struct rb_root root_net_port;
/**
* @hierarchy: Enables hierarchy identification even when a parent
* domain vanishes. This is needed for the ptrace protection.
@@ -157,7 +163,8 @@ struct landlock_ruleset {
};
struct landlock_ruleset *landlock_create_ruleset(
- const access_mask_t access_mask);
+ const access_mask_t access_mask_fs,
+ const access_mask_t access_mask_net);
void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
@@ -183,11 +190,12 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
}
/* A helper function to set a filesystem mask */
-static inline void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
- const access_mask_t access_maskset,
+static inline void landlock_set_fs_access_mask(
+ struct landlock_ruleset *ruleset,
+ const access_mask_t access_mask_fs,
u16 mask_level)
{
- ruleset->access_masks[mask_level] = access_maskset;
+ ruleset->access_masks[mask_level] = access_mask_fs;
}
/* A helper function to get a filesystem mask */
@@ -198,6 +206,24 @@ static inline u32 landlock_get_fs_access_mask(
return (ruleset->access_masks[mask_level] & LANDLOCK_MASK_ACCESS_FS);
}
+/* A helper function to set a network mask */
+static inline void landlock_set_net_access_mask(
+ struct landlock_ruleset *ruleset,
+ const access_mask_t access_mask_net,
+ u16 mask_level)
+{
+ ruleset->access_masks[mask_level] |= (access_mask_net <<
+ LANDLOCK_MASK_SHIFT_NET);
+}
+
+/* A helper function to get a network mask */
+static inline u32 landlock_get_net_access_mask(
+ const struct landlock_ruleset *ruleset,
+ u16 mask_level)
+{
+ return (ruleset->access_masks[mask_level] >> LANDLOCK_MASK_SHIFT_NET);
+}
+
access_mask_t get_handled_accesses(
const struct landlock_ruleset *const domain,
u16 rule_type, u16 num_access);
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 31f9facec123..812541f4e155 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -189,8 +189,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
LANDLOCK_MASK_ACCESS_FS)
return -EINVAL;
+ /* Checks network content (and 32-bits cast). */
+ if ((ruleset_attr.handled_access_net | LANDLOCK_MASK_ACCESS_NET) !=
+ LANDLOCK_MASK_ACCESS_NET)
+ return -EINVAL;
+
/* Checks arguments and transforms to kernel struct. */
- ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
+ ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
+ ruleset_attr.handled_access_net);
if (IS_ERR(ruleset))
return PTR_ERR(ruleset);
@@ -275,21 +281,17 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
return err;
}
-static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_attr)
+static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
+ const void *const rule_attr)
{
struct landlock_path_beneath_attr path_beneath_attr;
struct path path;
- struct landlock_ruleset *ruleset;
int res, err;
-
- /* Gets and checks the ruleset. */
- ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
- if (IS_ERR(ruleset))
- return PTR_ERR(ruleset);
+ u32 mask;
/* Copies raw user space buffer, only one type for now. */
res = copy_from_user(&path_beneath_attr, rule_attr,
- sizeof(path_beneath_attr));
+ sizeof(path_beneath_attr));
if (res)
return -EFAULT;
@@ -298,32 +300,26 @@ static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_at
* are ignored in path walks.
*/
if (!path_beneath_attr.allowed_access) {
- err = -ENOMSG;
- goto out_put_ruleset;
+ return -ENOMSG;
}
/*
* Checks that allowed_access matches the @ruleset constraints
* (ruleset->access_masks[0] is automatically upgraded to 64-bits).
*/
- if ((path_beneath_attr.allowed_access |
- landlock_get_fs_access_mask(ruleset, 0)) !=
- landlock_get_fs_access_mask(ruleset, 0)) {
- err = -EINVAL;
- goto out_put_ruleset;
- }
+ mask = landlock_get_fs_access_mask(ruleset, 0);
+ if ((path_beneath_attr.allowed_access | mask) != mask)
+ return -EINVAL;
/* Gets and checks the new rule. */
err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
if (err)
- goto out_put_ruleset;
+ return err;
/* Imports the new rule. */
err = landlock_append_fs_rule(ruleset, &path,
path_beneath_attr.allowed_access);
path_put(&path);
-out_put_ruleset:
- landlock_put_ruleset(ruleset);
return err;
}
@@ -360,6 +356,7 @@ SYSCALL_DEFINE4(landlock_add_rule,
const int, ruleset_fd, const enum landlock_rule_type, rule_type,
const void __user *const, rule_attr, const __u32, flags)
{
+ struct landlock_ruleset *ruleset;
int err;
if (!landlock_initialized)
@@ -369,14 +366,20 @@ SYSCALL_DEFINE4(landlock_add_rule,
if (flags)
return -EINVAL;
+ /* Gets and checks the ruleset. */
+ ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
+ if (IS_ERR(ruleset))
+ return PTR_ERR(ruleset);
+
switch (rule_type) {
case LANDLOCK_RULE_PATH_BENEATH:
- err = add_rule_path_beneath(ruleset_fd, rule_attr);
+ err = add_rule_path_beneath(ruleset, rule_attr);
break;
default:
err = -EINVAL;
break;
}
+ landlock_put_ruleset(ruleset);
return err;
}
--
2.25.1
^ permalink raw reply related
* [PATCH v5 08/15] landlock: TCP network hooks implementation
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
Support of socket_bind() and socket_connect() hooks.
Its possible to restrict binding and connecting of TCP
types of sockets to particular ports. Its just basic idea
how Landlock could support network confinement.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Split commit.
* Add SECURITY_NETWORK in config.
* Add IS_ENABLED(CONFIG_INET) if a kernel has no INET configuration.
* Add hook_socket_bind and hook_socket_connect hooks.
Changes since v4:
* Factors out CONFIG_INET into make file.
* Refactoring check_socket_access().
* Adds helper get_port().
* Adds CONFIG_IPV6 in get_port(), hook_socket_bind/connect
functions to support AF_INET6 family.
* Adds AF_UNSPEC family support in hook_socket_bind/connect
functions.
* Refactoring add_rule_net_service() and landlock_add_rule
syscall to support network rule inserting.
* Refactoring init_layer_masks() to support network rules.
---
security/landlock/Kconfig | 1 +
security/landlock/Makefile | 2 +
security/landlock/net.c | 159 +++++++++++++++++++++++++++++++++++
security/landlock/net.h | 25 ++++++
security/landlock/ruleset.c | 15 +++-
security/landlock/setup.c | 2 +
security/landlock/syscalls.c | 63 ++++++++++++--
7 files changed, 261 insertions(+), 6 deletions(-)
create mode 100644 security/landlock/net.c
create mode 100644 security/landlock/net.h
diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig
index 8e33c4e8ffb8..10c099097533 100644
--- a/security/landlock/Kconfig
+++ b/security/landlock/Kconfig
@@ -3,6 +3,7 @@
config SECURITY_LANDLOCK
bool "Landlock support"
depends on SECURITY && !ARCH_EPHEMERAL_INODES
+ select SECURITY_NETWORK
select SECURITY_PATH
help
Landlock is a sandboxing mechanism that enables processes to restrict
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index 7bbd2f413b3e..53d3c92ae22e 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -2,3 +2,5 @@ obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
landlock-y := setup.o syscalls.o object.o ruleset.o \
cred.o ptrace.o fs.o
+
+landlock-$(CONFIG_INET) += net.o
\ No newline at end of file
diff --git a/security/landlock/net.c b/security/landlock/net.c
new file mode 100644
index 000000000000..9302e5891991
--- /dev/null
+++ b/security/landlock/net.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock LSM - Network management and hooks
+ *
+ * Copyright (C) 2022 Huawei Tech. Co., Ltd.
+ */
+
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <net/ipv6.h>
+
+#include "cred.h"
+#include "limits.h"
+#include "net.h"
+
+int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
+ u16 port, u32 access_rights)
+{
+ int err;
+
+ /* Transforms relative access rights to absolute ones. */
+ access_rights |= LANDLOCK_MASK_ACCESS_NET &
+ ~landlock_get_net_access_mask(ruleset, 0);
+
+ BUILD_BUG_ON(sizeof(port) > sizeof(uintptr_t));
+ mutex_lock(&ruleset->lock);
+ err = landlock_insert_rule(ruleset, NULL, port,
+ access_rights, LANDLOCK_RULE_NET_SERVICE);
+ mutex_unlock(&ruleset->lock);
+
+ return err;
+}
+
+static int check_socket_access(const struct landlock_ruleset *const domain,
+ u16 port, access_mask_t access_request)
+{
+ bool allowed = false;
+ layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
+ const struct landlock_rule *rule;
+ access_mask_t handled_access;
+
+ if (WARN_ON_ONCE(!domain))
+ return 0;
+ if (WARN_ON_ONCE(domain->num_layers < 1))
+ return -EACCES;
+
+ rule = landlock_find_rule(domain, port,
+ LANDLOCK_RULE_NET_SERVICE);
+
+ handled_access = init_layer_masks(domain, access_request,
+ &layer_masks, sizeof(layer_masks),
+ LANDLOCK_RULE_NET_SERVICE);
+ allowed = unmask_layers(rule, handled_access,
+ &layer_masks, ARRAY_SIZE(layer_masks));
+
+ return allowed ? 0 : -EACCES;
+}
+
+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
+ }
+ return 0;
+}
+
+static int hook_socket_bind(struct socket *sock, struct sockaddr *address,
+ int addrlen)
+{
+ const struct landlock_ruleset *const dom =
+ landlock_get_current_domain();
+
+ if (!dom)
+ return 0;
+
+ /* Check if it's a TCP socket */
+ if (sock->type != SOCK_STREAM)
+ return 0;
+
+ /* Get port value in host byte order */
+ switch (address->sa_family) {
+ case AF_UNSPEC:
+ case AF_INET:
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+#endif
+ return check_socket_access(dom, get_port(address),
+ LANDLOCK_ACCESS_NET_BIND_TCP);
+ default:
+ return 0;
+ }
+}
+
+static int hook_socket_connect(struct socket *sock, struct sockaddr *address,
+ int addrlen)
+{
+ const struct landlock_ruleset *const dom =
+ landlock_get_current_domain();
+
+ if (!dom)
+ return 0;
+
+ /* Check if it's a TCP socket */
+ if (sock->type != SOCK_STREAM)
+ return 0;
+
+ /* Get port value in host byte order */
+ switch (address->sa_family) {
+ case AF_INET:
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+#endif
+ return check_socket_access(dom, get_port(address),
+ LANDLOCK_ACCESS_NET_CONNECT_TCP);
+ case AF_UNSPEC:
+ {
+ u16 i;
+ /*
+ * If just in a layer a mask supports connect access,
+ * the socket_connect() hook with AF_UNSPEC family flag
+ * must be banned. This prevents from disconnecting already
+ * connected sockets.
+ */
+ for (i = 0; i < dom->num_layers; i++) {
+ if (landlock_get_net_access_mask(dom, i) &
+ LANDLOCK_ACCESS_NET_CONNECT_TCP)
+ return -EACCES;
+ }
+ }
+ }
+ return 0;
+}
+
+static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = {
+ LSM_HOOK_INIT(socket_bind, hook_socket_bind),
+ LSM_HOOK_INIT(socket_connect, hook_socket_connect),
+};
+
+__init void landlock_add_net_hooks(void)
+{
+ security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
+ LANDLOCK_NAME);
+}
diff --git a/security/landlock/net.h b/security/landlock/net.h
new file mode 100644
index 000000000000..da5ce8fa04cc
--- /dev/null
+++ b/security/landlock/net.h
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock LSM - Network management and hooks
+ *
+ * Copyright (C) 2022 Huawei Tech. Co., Ltd.
+ */
+
+#ifndef _SECURITY_LANDLOCK_NET_H
+#define _SECURITY_LANDLOCK_NET_H
+
+#include "common.h"
+#include "ruleset.h"
+#include "setup.h"
+
+#if IS_ENABLED(CONFIG_INET)
+__init void landlock_add_net_hooks(void);
+
+int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
+ u16 port, u32 access_hierarchy);
+#else /* IS_ENABLED(CONFIG_INET) */
+static inline void landlock_add_net_hooks(void)
+{}
+#endif /* IS_ENABLED(CONFIG_INET) */
+
+#endif /* _SECURITY_LANDLOCK_NET_H */
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index ea9ecb3f471a..317cf98890f6 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -671,7 +671,7 @@ access_mask_t get_handled_accesses(
}
break;
default:
- break;
+ return 0;
}
return access_dom;
}
@@ -763,6 +763,19 @@ access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
}
}
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ for_each_set_bit(access_bit, &access_req,
+ LANDLOCK_NUM_ACCESS_NET) {
+ if (landlock_get_net_access_mask(domain,
+ layer_level) &
+ BIT_ULL(access_bit)) {
+ (*layer_masks)[access_bit] |=
+ BIT_ULL(layer_level);
+ handled_accesses |=
+ BIT_ULL(access_bit);
+ }
+ }
+ break;
default:
return 0;
}
diff --git a/security/landlock/setup.c b/security/landlock/setup.c
index f8e8e980454c..8059dc0b47d3 100644
--- a/security/landlock/setup.c
+++ b/security/landlock/setup.c
@@ -14,6 +14,7 @@
#include "fs.h"
#include "ptrace.h"
#include "setup.h"
+#include "net.h"
bool landlock_initialized __lsm_ro_after_init = false;
@@ -28,6 +29,7 @@ static int __init landlock_init(void)
landlock_add_cred_hooks();
landlock_add_ptrace_hooks();
landlock_add_fs_hooks();
+ landlock_add_net_hooks();
landlock_initialized = true;
pr_info("Up and running.\n");
return 0;
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 812541f4e155..9454c6361011 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -29,6 +29,7 @@
#include "cred.h"
#include "fs.h"
#include "limits.h"
+#include "net.h"
#include "ruleset.h"
#include "setup.h"
@@ -74,7 +75,8 @@ static void build_check_abi(void)
{
struct landlock_ruleset_attr ruleset_attr;
struct landlock_path_beneath_attr path_beneath_attr;
- size_t ruleset_size, path_beneath_size;
+ struct landlock_net_service_attr net_service_attr;
+ size_t ruleset_size, path_beneath_size, net_service_size;
/*
* For each user space ABI structures, first checks that there is no
@@ -90,6 +92,11 @@ static void build_check_abi(void)
path_beneath_size += sizeof(path_beneath_attr.parent_fd);
BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size);
BUILD_BUG_ON(sizeof(path_beneath_attr) != 12);
+
+ net_service_size = sizeof(net_service_attr.allowed_access);
+ net_service_size += sizeof(net_service_attr.port);
+ BUILD_BUG_ON(sizeof(net_service_attr) != net_service_size);
+ BUILD_BUG_ON(sizeof(net_service_attr) != 10);
}
/* Ruleset handling */
@@ -299,9 +306,9 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
* Informs about useless rule: empty allowed_access (i.e. deny rules)
* are ignored in path walks.
*/
- if (!path_beneath_attr.allowed_access) {
+ if (!path_beneath_attr.allowed_access)
return -ENOMSG;
- }
+
/*
* Checks that allowed_access matches the @ruleset constraints
* (ruleset->access_masks[0] is automatically upgraded to 64-bits).
@@ -323,13 +330,54 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
return err;
}
+static int add_rule_net_service(struct landlock_ruleset *ruleset,
+ const void *const rule_attr)
+{
+#if IS_ENABLED(CONFIG_INET)
+ struct landlock_net_service_attr net_service_attr;
+ int res;
+ u32 mask;
+
+ /* Copies raw user space buffer, only one type for now. */
+ res = copy_from_user(&net_service_attr, rule_attr,
+ sizeof(net_service_attr));
+ if (res)
+ return -EFAULT;
+
+ /*
+ * Informs about useless rule: empty allowed_access (i.e. deny rules)
+ * are ignored by network actions
+ */
+ if (!net_service_attr.allowed_access)
+ return -ENOMSG;
+
+ /*
+ * Checks that allowed_access matches the @ruleset constraints
+ * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
+ */
+ mask = landlock_get_net_access_mask(ruleset, 0);
+ if ((net_service_attr.allowed_access | mask) != mask)
+ return -EINVAL;
+
+ /* Denies inserting a rule with port 0 */
+ if (net_service_attr.port == 0)
+ return -EINVAL;
+
+ /* Imports the new rule. */
+ return landlock_append_net_rule(ruleset, net_service_attr.port,
+ net_service_attr.allowed_access);
+#else /* IS_ENABLED(CONFIG_INET) */
+ return -EAFNOSUPPORT;
+#endif /* IS_ENABLED(CONFIG_INET) */
+}
+
/**
* sys_landlock_add_rule - Add a new rule to a ruleset
*
* @ruleset_fd: File descriptor tied to the ruleset that should be extended
* with the new rule.
- * @rule_type: Identify the structure type pointed to by @rule_attr (only
- * LANDLOCK_RULE_PATH_BENEATH for now).
+ * @rule_type: Identify the structure type pointed to by @rule_attr:
+ * LANDLOCK_RULE_PATH_BENEATH or LANDLOCK_RULE_NET_SERVICE.
* @rule_attr: Pointer to a rule (only of type &struct
* landlock_path_beneath_attr for now).
* @flags: Must be 0.
@@ -340,6 +388,8 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
* Possible returned errors are:
*
* - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
+ * - EAFNOSUPPORT: @rule_type is LANDLOCK_RULE_NET_SERVICE but TCP/IP is not
+ * supported by the running kernel;
* - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
* &landlock_path_beneath_attr.allowed_access is not a subset of the rule's
* accesses);
@@ -375,6 +425,9 @@ SYSCALL_DEFINE4(landlock_add_rule,
case LANDLOCK_RULE_PATH_BENEATH:
err = add_rule_path_beneath(ruleset, rule_attr);
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ err = add_rule_net_service(ruleset, rule_attr);
+ break;
default:
err = -EINVAL;
break;
--
2.25.1
^ permalink raw reply related
* [PATCH v5 06/15] landlock: user space API network support
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
User space API was refactored to support
network actions. New network access flags,
network rule and network attributes were
added.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Split commit.
* Refactoring User API for network rule type.
Changes since v4:
* None
---
include/uapi/linux/landlock.h | 48 +++++++++++++++++++++++++++++++++++
security/landlock/syscalls.c | 3 ++-
2 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index 23df4e0e8ace..91d6cb359bf8 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -31,6 +31,13 @@ struct landlock_ruleset_attr {
* this access right.
*/
__u64 handled_access_fs;
+
+ /**
+ * @handled_access_net: Bitmask of actions (cf. `Network flags`_)
+ * that is handled by this ruleset and should then be forbidden if no
+ * rule explicitly allow them.
+ */
+ __u64 handled_access_net;
};
/*
@@ -54,6 +61,11 @@ enum landlock_rule_type {
* landlock_path_beneath_attr .
*/
LANDLOCK_RULE_PATH_BENEATH = 1,
+ /**
+ * @LANDLOCK_RULE_NET_SERVICE: Type of a &struct
+ * landlock_net_service_attr .
+ */
+ LANDLOCK_RULE_NET_SERVICE = 2,
};
/**
@@ -79,6 +91,24 @@ struct landlock_path_beneath_attr {
*/
} __attribute__((packed));
+/**
+ * struct landlock_net_service_attr - TCP subnet definition
+ *
+ * Argument of sys_landlock_add_rule().
+ */
+struct landlock_net_service_attr {
+ /**
+ * @allowed_access: Bitmask of allowed access network for services
+ * (cf. `Network flags`_).
+ */
+ __u64 allowed_access;
+ /**
+ * @port: Network port
+ */
+ __u16 port;
+
+} __attribute__((packed));
+
/**
* DOC: fs_access
*
@@ -162,4 +192,22 @@ struct landlock_path_beneath_attr {
#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
/* clang-format on */
+/**
+ * DOC: net_access
+ *
+ * Network flags
+ * ~~~~~~~~~~~~~~~~
+ *
+ * These flags enable to restrict a sandboxed process to a set of network
+ * actions.
+ *
+ * TCP sockets with allowed actions:
+ *
+ * - %LANDLOCK_ACCESS_NET_BIND_TCP: Bind a TCP socket to a local port.
+ * - %LANDLOCK_ACCESS_NET_CONNECT_TCP: Connect an active TCP socket to
+ * a remote port.
+ */
+#define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0)
+#define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1)
+
#endif /* _UAPI_LINUX_LANDLOCK_H */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 412ced6c512f..31f9facec123 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -82,8 +82,9 @@ static void build_check_abi(void)
* struct size.
*/
ruleset_size = sizeof(ruleset_attr.handled_access_fs);
+ ruleset_size += sizeof(ruleset_attr.handled_access_net);
BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
- BUILD_BUG_ON(sizeof(ruleset_attr) != 8);
+ BUILD_BUG_ON(sizeof(ruleset_attr) != 16);
path_beneath_size = sizeof(path_beneath_attr.allowed_access);
path_beneath_size += sizeof(path_beneath_attr.parent_fd);
--
2.25.1
^ permalink raw reply related
* [PATCH v5 03/15] landlock: merge and inherit function refactoring
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
Merge_ruleset() and inherit_ruleset() functions were
refactored to support new rule types. This patch adds
tree_merge() and tree_copy() helpers. Each has
rule_type argument to choose a particular rb_tree
structure in a ruleset.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Split commit.
* Refactoring functions:
-insert_rule.
-merge_ruleset.
-tree_merge.
-inherit_ruleset.
-tree_copy.
-free_rule.
Changes since v4:
* None
---
security/landlock/ruleset.c | 144 ++++++++++++++++++++++++------------
1 file changed, 98 insertions(+), 46 deletions(-)
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index f079a2a320f1..4b4c9953bb32 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -112,12 +112,16 @@ static struct landlock_rule *create_rule(
return new_rule;
}
-static void free_rule(struct landlock_rule *const rule)
+static void free_rule(struct landlock_rule *const rule, const u16 rule_type)
{
might_sleep();
if (!rule)
return;
- landlock_put_object(rule->object.ptr);
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ landlock_put_object(rule->object.ptr);
+ break;
+ }
kfree(rule);
}
@@ -227,12 +231,12 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
new_rule = create_rule(object_ptr, 0, &this->layers,
this->num_layers,
&(*layers)[0]);
+ if (IS_ERR(new_rule))
+ return PTR_ERR(new_rule);
+ rb_replace_node(&this->node, &new_rule->node, &ruleset->root_inode);
+ free_rule(this, rule_type);
break;
}
- if (IS_ERR(new_rule))
- return PTR_ERR(new_rule);
- rb_replace_node(&this->node, &new_rule->node, &ruleset->root_inode);
- free_rule(this);
return 0;
}
@@ -243,13 +247,12 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
switch (rule_type) {
case LANDLOCK_RULE_PATH_BENEATH:
new_rule = create_rule(object_ptr, 0, layers, num_layers, NULL);
+ if (IS_ERR(new_rule))
+ return PTR_ERR(new_rule);
+ rb_link_node(&new_rule->node, parent_node, walker_node);
+ rb_insert_color(&new_rule->node, &ruleset->root_inode);
break;
}
- if (IS_ERR(new_rule))
- return PTR_ERR(new_rule);
- rb_link_node(&new_rule->node, parent_node, walker_node);
- rb_insert_color(&new_rule->node, &ruleset->root_inode);
- ruleset->num_rules++;
return 0;
}
@@ -298,10 +301,53 @@ static void put_hierarchy(struct landlock_hierarchy *hierarchy)
}
}
+static int tree_merge(struct landlock_ruleset *const src,
+ struct landlock_ruleset *const dst, u16 rule_type)
+{
+ struct landlock_rule *walker_rule, *next_rule;
+ struct rb_root *src_root;
+ int err = 0;
+
+ /* Choose rb_tree structure depending on a rule type */
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ src_root = &src->root_inode;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* Merges the @src tree. */
+ rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
+ src_root, node) {
+ struct landlock_layer layers[] = {{
+ .level = dst->num_layers,
+ }};
+
+ if (WARN_ON_ONCE(walker_rule->num_layers != 1)) {
+ err = -EINVAL;
+ return err;
+ }
+ if (WARN_ON_ONCE(walker_rule->layers[0].level != 0)) {
+ err = -EINVAL;
+ return err;
+ }
+ layers[0].access = walker_rule->layers[0].access;
+
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ err = insert_rule(dst, walker_rule->object.ptr, 0, rule_type,
+ &layers, ARRAY_SIZE(layers));
+ break;
+ }
+ if (err)
+ return err;
+ }
+ return err;
+}
+
static int merge_ruleset(struct landlock_ruleset *const dst,
struct landlock_ruleset *const src)
{
- struct landlock_rule *walker_rule, *next_rule;
int err = 0;
might_sleep();
@@ -323,29 +369,10 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
}
dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
- /* Merges the @src tree. */
- rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
- &src->root_inode, node) {
- struct landlock_layer layers[] = {{
- .level = dst->num_layers,
- } };
-
- if (WARN_ON_ONCE(walker_rule->num_layers != 1)) {
- err = -EINVAL;
- goto out_unlock;
- }
- if (WARN_ON_ONCE(walker_rule->layers[0].level != 0)) {
- err = -EINVAL;
- goto out_unlock;
- }
- layers[0].access = walker_rule->layers[0].access;
-
- err = insert_rule(dst, walker_rule->object.ptr, 0,
- LANDLOCK_RULE_PATH_BENEATH, &layers,
- ARRAY_SIZE(layers));
- if (err)
- goto out_unlock;
- }
+ /* Merges the @src inode tree. */
+ err = tree_merge(src, dst, LANDLOCK_RULE_PATH_BENEATH);
+ if (err)
+ goto out_unlock;
out_unlock:
mutex_unlock(&src->lock);
@@ -353,10 +380,40 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
return err;
}
+static int tree_copy(struct landlock_ruleset *const parent,
+ struct landlock_ruleset *const child, u16 rule_type)
+{
+ struct landlock_rule *walker_rule, *next_rule;
+ struct rb_root *parent_root;
+ int err = 0;
+
+ /* Choose rb_tree structure depending on a rule type */
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ parent_root = &parent->root_inode;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* Copies the @parent inode tree. */
+ rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
+ parent_root, node) {
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ err = insert_rule(child, walker_rule->object.ptr, 0,
+ rule_type, &walker_rule->layers,
+ walker_rule->num_layers);
+ break;
+ }
+ if (err)
+ return err;
+ }
+ return err;
+}
+
static int inherit_ruleset(struct landlock_ruleset *const parent,
struct landlock_ruleset *const child)
{
- struct landlock_rule *walker_rule, *next_rule;
int err = 0;
might_sleep();
@@ -367,15 +424,10 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
mutex_lock(&child->lock);
mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);
- /* Copies the @parent tree. */
- rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
- &parent->root_inode, node) {
- err = insert_rule(child, walker_rule->object.ptr, 0,
- LANDLOCK_RULE_PATH_BENEATH, &walker_rule->layers,
- walker_rule->num_layers);
- if (err)
- goto out_unlock;
- }
+ /* Copies the @parent inode tree. */
+ err = tree_copy(parent, child, LANDLOCK_RULE_PATH_BENEATH);
+ if (err)
+ goto out_unlock;
if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
err = -EINVAL;
@@ -405,7 +457,7 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
might_sleep();
rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root_inode,
node)
- free_rule(freeme);
+ free_rule(freeme, LANDLOCK_RULE_PATH_BENEATH);
put_hierarchy(ruleset->hierarchy);
kfree(ruleset);
}
--
2.25.1
^ permalink raw reply related
* [PATCH v5 04/15] landlock: helper functions refactoring
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
Unmask_layers(), init_layer_masks() and
get_handled_accesses() helper functions move to
ruleset.c and rule_type argument is added.
This modification supports implementing new rule
types into next landlock versions.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Splits commit.
* Refactoring landlock_unmask_layers functions.
Changes since v4:
* Refactoring init_layer_masks(), get_handled_accesses()
and unmask_layers() functions to support multiple rule types.
* Refactoring landlock_get_fs_access_mask() function with
LANDLOCK_MASK_ACCESS_FS mask.
---
security/landlock/fs.c | 158 ++++++++----------------------------
security/landlock/ruleset.c | 152 +++++++++++++++++++++++++++++++---
security/landlock/ruleset.h | 17 +++-
3 files changed, 192 insertions(+), 135 deletions(-)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 5de24d4dd74c..3506e182b23e 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -211,60 +211,6 @@ find_rule(const struct landlock_ruleset *const domain,
return rule;
}
-/*
- * @layer_masks is read and may be updated according to the access request and
- * the matching rule.
- *
- * Returns true if the request is allowed (i.e. relevant layer masks for the
- * request are empty).
- */
-static inline bool
-unmask_layers(const struct landlock_rule *const rule,
- const access_mask_t access_request,
- layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
-{
- size_t layer_level;
-
- if (!access_request || !layer_masks)
- return true;
- if (!rule)
- return false;
-
- /*
- * An access is granted if, for each policy layer, at least one rule
- * encountered on the pathwalk grants the requested access,
- * regardless of its position in the layer stack. We must then check
- * the remaining layers for each inode, from the first added layer to
- * the last one. When there is multiple requested accesses, for each
- * policy layer, the full set of requested accesses may not be granted
- * by only one rule, but by the union (binary OR) of multiple rules.
- * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
- */
- for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
- const struct landlock_layer *const layer =
- &rule->layers[layer_level];
- const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
- const unsigned long access_req = access_request;
- unsigned long access_bit;
- bool is_empty;
-
- /*
- * Records in @layer_masks which layer grants access to each
- * requested access.
- */
- is_empty = true;
- for_each_set_bit(access_bit, &access_req,
- ARRAY_SIZE(*layer_masks)) {
- if (layer->access & BIT_ULL(access_bit))
- (*layer_masks)[access_bit] &= ~layer_bit;
- is_empty = is_empty && !(*layer_masks)[access_bit];
- }
- if (is_empty)
- return true;
- }
- return false;
-}
-
/*
* Allows access to pseudo filesystems that will never be mountable (e.g.
* sockfs, pipefs), but can still be reachable through
@@ -277,59 +223,6 @@ static inline bool is_nouser_or_private(const struct dentry *dentry)
unlikely(IS_PRIVATE(d_backing_inode(dentry))));
}
-static inline access_mask_t
-get_handled_accesses(const struct landlock_ruleset *const domain)
-{
- access_mask_t access_dom = 0;
- unsigned long access_bit;
-
- for (access_bit = 0; access_bit < LANDLOCK_NUM_ACCESS_FS;
- access_bit++) {
- size_t layer_level;
-
- for (layer_level = 0; layer_level < domain->num_layers;
- layer_level++) {
- if (landlock_get_fs_access_mask(domain, layer_level) &
- BIT_ULL(access_bit)) {
- access_dom |= BIT_ULL(access_bit);
- break;
- }
- }
- }
- return access_dom;
-}
-
-static inline access_mask_t
-init_layer_masks(const struct landlock_ruleset *const domain,
- const access_mask_t access_request,
- layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
-{
- access_mask_t handled_accesses = 0;
- size_t layer_level;
-
- memset(layer_masks, 0, sizeof(*layer_masks));
- /* An empty access request can happen because of O_WRONLY | O_RDWR. */
- if (!access_request)
- return 0;
-
- /* Saves all handled accesses per layer. */
- for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
- const unsigned long access_req = access_request;
- unsigned long access_bit;
-
- for_each_set_bit(access_bit, &access_req,
- ARRAY_SIZE(*layer_masks)) {
- if (landlock_get_fs_access_mask(domain, layer_level) &
- BIT_ULL(access_bit)) {
- (*layer_masks)[access_bit] |=
- BIT_ULL(layer_level);
- handled_accesses |= BIT_ULL(access_bit);
- }
- }
- }
- return handled_accesses;
-}
-
/*
* Check that a destination file hierarchy has more restrictions than a source
* file hierarchy. This is only used for link and rename actions.
@@ -506,7 +399,8 @@ static int check_access_path_dual(
* a superset of the meaningful requested accesses).
*/
access_masked_parent1 = access_masked_parent2 =
- get_handled_accesses(domain);
+ get_handled_accesses(domain, LANDLOCK_RULE_PATH_BENEATH,
+ LANDLOCK_NUM_ACCESS_FS);
is_dom_check = true;
} else {
if (WARN_ON_ONCE(dentry_child1 || dentry_child2))
@@ -519,17 +413,25 @@ static int check_access_path_dual(
if (unlikely(dentry_child1)) {
unmask_layers(find_rule(domain, dentry_child1),
- init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
- &_layer_masks_child1),
- &_layer_masks_child1);
+ init_layer_masks(domain,
+ LANDLOCK_MASK_ACCESS_FS,
+ &_layer_masks_child1,
+ sizeof(_layer_masks_child1),
+ LANDLOCK_RULE_PATH_BENEATH),
+ &_layer_masks_child1,
+ ARRAY_SIZE(_layer_masks_child1));
layer_masks_child1 = &_layer_masks_child1;
child1_is_directory = d_is_dir(dentry_child1);
}
if (unlikely(dentry_child2)) {
unmask_layers(find_rule(domain, dentry_child2),
- init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
- &_layer_masks_child2),
- &_layer_masks_child2);
+ init_layer_masks(domain,
+ LANDLOCK_MASK_ACCESS_FS,
+ &_layer_masks_child2,
+ sizeof(_layer_masks_child2),
+ LANDLOCK_RULE_PATH_BENEATH),
+ &_layer_masks_child2,
+ ARRAY_SIZE(_layer_masks_child2));
layer_masks_child2 = &_layer_masks_child2;
child2_is_directory = d_is_dir(dentry_child2);
}
@@ -582,14 +484,15 @@ static int check_access_path_dual(
rule = find_rule(domain, walker_path.dentry);
allowed_parent1 = unmask_layers(rule, access_masked_parent1,
- layer_masks_parent1);
+ layer_masks_parent1,
+ ARRAY_SIZE(*layer_masks_parent1));
allowed_parent2 = unmask_layers(rule, access_masked_parent2,
- layer_masks_parent2);
+ layer_masks_parent2,
+ ARRAY_SIZE(*layer_masks_parent2));
/* Stops when a rule from each layer grants access. */
if (allowed_parent1 && allowed_parent2)
break;
-
jump_up:
if (walker_path.dentry == walker_path.mnt->mnt_root) {
if (follow_up(&walker_path)) {
@@ -645,7 +548,9 @@ static inline int check_access_path(const struct landlock_ruleset *const domain,
{
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
- access_request = init_layer_masks(domain, access_request, &layer_masks);
+ access_request = init_layer_masks(domain, access_request,
+ &layer_masks, sizeof(layer_masks),
+ LANDLOCK_RULE_PATH_BENEATH);
return check_access_path_dual(domain, path, access_request,
&layer_masks, NULL, 0, NULL, NULL);
}
@@ -729,7 +634,8 @@ static bool collect_domain_accesses(
return true;
access_dom = init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
- layer_masks_dom);
+ layer_masks_dom, sizeof(*layer_masks_dom),
+ LANDLOCK_RULE_PATH_BENEATH);
dget(dir);
while (true) {
@@ -737,7 +643,8 @@ static bool collect_domain_accesses(
/* Gets all layers allowing all domain accesses. */
if (unmask_layers(find_rule(domain, dir), access_dom,
- layer_masks_dom)) {
+ layer_masks_dom,
+ ARRAY_SIZE(*layer_masks_dom))) {
/*
* Stops when all handled accesses are allowed by at
* least one rule in each layer.
@@ -851,9 +758,10 @@ static int current_check_refer_path(struct dentry *const old_dentry,
* The LANDLOCK_ACCESS_FS_REFER access right is not required
* for same-directory referer (i.e. no reparenting).
*/
- access_request_parent1 = init_layer_masks(
- dom, access_request_parent1 | access_request_parent2,
- &layer_masks_parent1);
+ access_request_parent1 = init_layer_masks(dom,
+ access_request_parent1 | access_request_parent2,
+ &layer_masks_parent1, sizeof(layer_masks_parent1),
+ LANDLOCK_RULE_PATH_BENEATH);
return check_access_path_dual(dom, new_dir,
access_request_parent1,
&layer_masks_parent1, NULL, 0,
@@ -861,7 +769,9 @@ static int current_check_refer_path(struct dentry *const old_dentry,
}
/* Backward compatibility: no reparenting support. */
- if (!(get_handled_accesses(dom) & LANDLOCK_ACCESS_FS_REFER))
+ if (!(get_handled_accesses(dom, LANDLOCK_RULE_PATH_BENEATH,
+ LANDLOCK_NUM_ACCESS_FS) &
+ LANDLOCK_ACCESS_FS_REFER))
return -EXDEV;
access_request_parent1 |= LANDLOCK_ACCESS_FS_REFER;
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 4b4c9953bb32..c4ed783d655b 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -233,7 +233,8 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
&(*layers)[0]);
if (IS_ERR(new_rule))
return PTR_ERR(new_rule);
- rb_replace_node(&this->node, &new_rule->node, &ruleset->root_inode);
+ rb_replace_node(&this->node, &new_rule->node,
+ &ruleset->root_inode);
free_rule(this, rule_type);
break;
}
@@ -246,7 +247,8 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
return -E2BIG;
switch (rule_type) {
case LANDLOCK_RULE_PATH_BENEATH:
- new_rule = create_rule(object_ptr, 0, layers, num_layers, NULL);
+ new_rule = create_rule(object_ptr, 0, layers,
+ num_layers, NULL);
if (IS_ERR(new_rule))
return PTR_ERR(new_rule);
rb_link_node(&new_rule->node, parent_node, walker_node);
@@ -281,8 +283,8 @@ int landlock_insert_rule(struct landlock_ruleset *const ruleset,
} };
build_check_layer();
- return insert_rule(ruleset, object_ptr, object_data, rule_type, &layers,
- ARRAY_SIZE(layers));
+ return insert_rule(ruleset, object_ptr, object_data, rule_type,
+ &layers, ARRAY_SIZE(layers));
}
static inline void get_hierarchy(struct landlock_hierarchy *const hierarchy)
@@ -335,8 +337,9 @@ static int tree_merge(struct landlock_ruleset *const src,
switch (rule_type) {
case LANDLOCK_RULE_PATH_BENEATH:
- err = insert_rule(dst, walker_rule->object.ptr, 0, rule_type,
- &layers, ARRAY_SIZE(layers));
+ err = insert_rule(dst, walker_rule->object.ptr, 0,
+ rule_type, &layers,
+ ARRAY_SIZE(layers));
break;
}
if (err)
@@ -433,9 +436,13 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
err = -EINVAL;
goto out_unlock;
}
- /* Copies the parent layer stack and leaves a space for the new layer. */
+ /*
+ * Copies the parent layer stack and leaves a space
+ * for the new layer.
+ */
memcpy(child->access_masks, parent->access_masks,
- flex_array_size(parent, access_masks, parent->num_layers));
+ flex_array_size(parent, access_masks,
+ parent->num_layers));
if (WARN_ON_ONCE(!parent->hierarchy)) {
err = -EINVAL;
@@ -455,8 +462,9 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
struct landlock_rule *freeme, *next;
might_sleep();
- rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root_inode,
- node)
+ rbtree_postorder_for_each_entry_safe(freeme, next,
+ &ruleset->root_inode,
+ node)
free_rule(freeme, LANDLOCK_RULE_PATH_BENEATH);
put_hierarchy(ruleset->hierarchy);
kfree(ruleset);
@@ -577,3 +585,127 @@ const struct landlock_rule *landlock_find_rule(
}
return NULL;
}
+
+access_mask_t get_handled_accesses(
+ const struct landlock_ruleset *const domain,
+ u16 rule_type, u16 num_access)
+{
+ access_mask_t access_dom = 0;
+ unsigned long access_bit;
+
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ for (access_bit = 0; access_bit < LANDLOCK_NUM_ACCESS_FS;
+ access_bit++) {
+ size_t layer_level;
+
+ for (layer_level = 0; layer_level < domain->num_layers;
+ layer_level++) {
+ if (landlock_get_fs_access_mask(domain,
+ layer_level) &
+ BIT_ULL(access_bit)) {
+ access_dom |= BIT_ULL(access_bit);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return access_dom;
+}
+
+/*
+ * @layer_masks is read and may be updated according to the access request and
+ * the matching rule.
+ *
+ * Returns true if the request is allowed (i.e. relevant layer masks for the
+ * request are empty).
+ */
+bool unmask_layers(const struct landlock_rule *const rule,
+ const access_mask_t access_request,
+ layer_mask_t (*const layer_masks)[], size_t masks_array_size)
+{
+ size_t layer_level;
+
+ if (!access_request || !layer_masks)
+ return true;
+ if (!rule)
+ return false;
+
+ /*
+ * An access is granted if, for each policy layer, at least one rule
+ * encountered on the pathwalk grants the requested access,
+ * regardless of its position in the layer stack. We must then check
+ * the remaining layers for each inode, from the first added layer to
+ * the last one. When there is multiple requested accesses, for each
+ * policy layer, the full set of requested accesses may not be granted
+ * by only one rule, but by the union (binary OR) of multiple rules.
+ * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
+ */
+ for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
+ const struct landlock_layer *const layer =
+ &rule->layers[layer_level];
+ const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
+ const unsigned long access_req = access_request;
+ unsigned long access_bit;
+ bool is_empty;
+
+ /*
+ * Records in @layer_masks which layer grants access to each
+ * requested access.
+ */
+ is_empty = true;
+ for_each_set_bit(access_bit, &access_req, masks_array_size) {
+ if (layer->access & BIT_ULL(access_bit))
+ (*layer_masks)[access_bit] &= ~layer_bit;
+ is_empty = is_empty && !(*layer_masks)[access_bit];
+ }
+ if (is_empty)
+ return true;
+ }
+ return false;
+}
+
+access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
+ const access_mask_t access_request,
+ layer_mask_t (*const layer_masks)[],
+ size_t masks_size,
+ u16 rule_type)
+{
+ access_mask_t handled_accesses = 0;
+ size_t layer_level;
+
+ memset(layer_masks, 0, masks_size);
+
+ /* An empty access request can happen because of O_WRONLY | O_RDWR. */
+ if (!access_request)
+ return 0;
+
+ /* Saves all handled accesses per layer. */
+ for (layer_level = 0; layer_level < domain->num_layers;
+ layer_level++) {
+ const unsigned long access_req = access_request;
+ unsigned long access_bit;
+
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ for_each_set_bit(access_bit, &access_req,
+ LANDLOCK_NUM_ACCESS_FS) {
+ if (landlock_get_fs_access_mask(domain,
+ layer_level) &
+ BIT_ULL(access_bit)) {
+ (*layer_masks)[access_bit] |=
+ BIT_ULL(layer_level);
+ handled_accesses |=
+ BIT_ULL(access_bit);
+ }
+ }
+ break;
+ default:
+ return 0;
+ }
+ }
+ return handled_accesses;
+}
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 3066e5d7180c..f3cd890d0348 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -195,7 +195,22 @@ static inline u32 landlock_get_fs_access_mask(
const struct landlock_ruleset *ruleset,
u16 mask_level)
{
- return ruleset->access_masks[mask_level];
+ return (ruleset->access_masks[mask_level] & LANDLOCK_MASK_ACCESS_FS);
}
+access_mask_t get_handled_accesses(
+ const struct landlock_ruleset *const domain,
+ u16 rule_type, u16 num_access);
+
+bool unmask_layers(const struct landlock_rule *const rule,
+ const access_mask_t access_request,
+ layer_mask_t (*const layer_masks)[],
+ size_t masks_array_size);
+
+access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
+ const access_mask_t access_request,
+ layer_mask_t (*const layer_masks)[],
+ size_t masks_size,
+ u16 rule_type);
+
#endif /* _SECURITY_LANDLOCK_RULESET_H */
--
2.25.1
^ permalink raw reply related
* [PATCH v5 02/15] landlock: landlock_find/insert_rule refactoring
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
A new object union added to support a socket port
rule type. To support it landlock_insert_rule() and
landlock_find_rule() were refactored. Now adding
or searching a rule in a ruleset depends on a
rule_type argument provided in refactored
functions mentioned above.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Split commit.
* Refactoring landlock_insert_rule and landlock_find_rule functions.
* Rename new_ruleset->root_inode.
Changes since v4:
* Refactoring insert_rule() and create_rule() functions by deleting
rule_type from their arguments list, it helps to reduce useless code.
---
security/landlock/fs.c | 8 ++-
security/landlock/ruleset.c | 129 +++++++++++++++++++++++++-----------
security/landlock/ruleset.h | 32 +++++----
3 files changed, 113 insertions(+), 56 deletions(-)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 8eea52e5a3a4..5de24d4dd74c 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -173,7 +173,8 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
if (IS_ERR(object))
return PTR_ERR(object);
mutex_lock(&ruleset->lock);
- err = landlock_insert_rule(ruleset, object, access_rights);
+ err = landlock_insert_rule(ruleset, object, 0, access_rights,
+ LANDLOCK_RULE_PATH_BENEATH);
mutex_unlock(&ruleset->lock);
/*
* No need to check for an error because landlock_insert_rule()
@@ -203,8 +204,9 @@ find_rule(const struct landlock_ruleset *const domain,
inode = d_backing_inode(dentry);
rcu_read_lock();
- rule = landlock_find_rule(
- domain, rcu_dereference(landlock_inode(inode)->object));
+ rule = landlock_find_rule(domain,
+ (uintptr_t)rcu_dereference(landlock_inode(inode)->object),
+ LANDLOCK_RULE_PATH_BENEATH);
rcu_read_unlock();
return rule;
}
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index b8917f6a8050..f079a2a320f1 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -35,7 +35,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
return ERR_PTR(-ENOMEM);
refcount_set(&new_ruleset->usage, 1);
mutex_init(&new_ruleset->lock);
- new_ruleset->root = RB_ROOT;
+ new_ruleset->root_inode = RB_ROOT;
new_ruleset->num_layers = num_layers;
/*
* hierarchy = NULL
@@ -69,10 +69,12 @@ static void build_check_rule(void)
BUILD_BUG_ON(rule.num_layers < LANDLOCK_MAX_NUM_LAYERS);
}
-static struct landlock_rule *
-create_rule(struct landlock_object *const object,
- const struct landlock_layer (*const layers)[], const u32 num_layers,
- const struct landlock_layer *const new_layer)
+static struct landlock_rule *create_rule(
+ struct landlock_object *const object_ptr,
+ const uintptr_t object_data,
+ const struct landlock_layer (*const layers)[],
+ const u32 num_layers,
+ const struct landlock_layer *const new_layer)
{
struct landlock_rule *new_rule;
u32 new_num_layers;
@@ -91,8 +93,15 @@ create_rule(struct landlock_object *const object,
if (!new_rule)
return ERR_PTR(-ENOMEM);
RB_CLEAR_NODE(&new_rule->node);
- landlock_get_object(object);
- new_rule->object = object;
+
+ if (object_ptr) {
+ landlock_get_object(object_ptr);
+ new_rule->object.ptr = object_ptr;
+ } else if (object_ptr && object_data) {
+ WARN_ON_ONCE(1);
+ return ERR_PTR(-EINVAL);
+ }
+
new_rule->num_layers = new_num_layers;
/* Copies the original layer stack. */
memcpy(new_rule->layers, layers,
@@ -108,7 +117,7 @@ static void free_rule(struct landlock_rule *const rule)
might_sleep();
if (!rule)
return;
- landlock_put_object(rule->object);
+ landlock_put_object(rule->object.ptr);
kfree(rule);
}
@@ -144,26 +153,44 @@ static void build_check_ruleset(void)
* access rights.
*/
static int insert_rule(struct landlock_ruleset *const ruleset,
- struct landlock_object *const object,
- const struct landlock_layer (*const layers)[],
- size_t num_layers)
+ struct landlock_object *const object_ptr,
+ uintptr_t object_data, u16 rule_type,
+ const struct landlock_layer (*const layers)[],
+ size_t num_layers)
{
struct rb_node **walker_node;
struct rb_node *parent_node = NULL;
struct landlock_rule *new_rule;
+ struct rb_root *root;
might_sleep();
lockdep_assert_held(&ruleset->lock);
- if (WARN_ON_ONCE(!object || !layers))
+ /* Choose rb_tree structure depending on a rule type */
+
+ if (WARN_ON_ONCE(!layers))
return -ENOENT;
- walker_node = &(ruleset->root.rb_node);
+ if (WARN_ON_ONCE(object_ptr && object_data))
+ return -EINVAL;
+
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ if (WARN_ON_ONCE(!object_ptr))
+ return -ENOENT;
+ object_data = (uintptr_t)object_ptr;
+ root = &ruleset->root_inode;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+ walker_node = &root->rb_node;
while (*walker_node) {
struct landlock_rule *const this =
rb_entry(*walker_node, struct landlock_rule, node);
- if (this->object != object) {
+ if (this->object.data != object_data) {
parent_node = *walker_node;
- if (this->object < object)
+ if (this->object.data < object_data)
walker_node = &((*walker_node)->rb_right);
else
walker_node = &((*walker_node)->rb_left);
@@ -195,11 +222,16 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
* Intersects access rights when it is a merge between a
* ruleset and a domain.
*/
- new_rule = create_rule(object, &this->layers, this->num_layers,
- &(*layers)[0]);
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ new_rule = create_rule(object_ptr, 0, &this->layers,
+ this->num_layers,
+ &(*layers)[0]);
+ break;
+ }
if (IS_ERR(new_rule))
return PTR_ERR(new_rule);
- rb_replace_node(&this->node, &new_rule->node, &ruleset->root);
+ rb_replace_node(&this->node, &new_rule->node, &ruleset->root_inode);
free_rule(this);
return 0;
}
@@ -208,11 +240,15 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
build_check_ruleset();
if (ruleset->num_rules >= LANDLOCK_MAX_NUM_RULES)
return -E2BIG;
- new_rule = create_rule(object, layers, num_layers, NULL);
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ new_rule = create_rule(object_ptr, 0, layers, num_layers, NULL);
+ break;
+ }
if (IS_ERR(new_rule))
return PTR_ERR(new_rule);
rb_link_node(&new_rule->node, parent_node, walker_node);
- rb_insert_color(&new_rule->node, &ruleset->root);
+ rb_insert_color(&new_rule->node, &ruleset->root_inode);
ruleset->num_rules++;
return 0;
}
@@ -230,8 +266,10 @@ static void build_check_layer(void)
/* @ruleset must be locked by the caller. */
int landlock_insert_rule(struct landlock_ruleset *const ruleset,
- struct landlock_object *const object,
- const access_mask_t access)
+ struct landlock_object *const object_ptr,
+ const uintptr_t object_data,
+ const access_mask_t access,
+ const u16 rule_type)
{
struct landlock_layer layers[] = { {
.access = access,
@@ -240,7 +278,8 @@ int landlock_insert_rule(struct landlock_ruleset *const ruleset,
} };
build_check_layer();
- return insert_rule(ruleset, object, &layers, ARRAY_SIZE(layers));
+ return insert_rule(ruleset, object_ptr, object_data, rule_type, &layers,
+ ARRAY_SIZE(layers));
}
static inline void get_hierarchy(struct landlock_hierarchy *const hierarchy)
@@ -285,9 +324,9 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
/* Merges the @src tree. */
- rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, &src->root,
- node) {
- struct landlock_layer layers[] = { {
+ rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
+ &src->root_inode, node) {
+ struct landlock_layer layers[] = {{
.level = dst->num_layers,
} };
@@ -300,7 +339,9 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
goto out_unlock;
}
layers[0].access = walker_rule->layers[0].access;
- err = insert_rule(dst, walker_rule->object, &layers,
+
+ err = insert_rule(dst, walker_rule->object.ptr, 0,
+ LANDLOCK_RULE_PATH_BENEATH, &layers,
ARRAY_SIZE(layers));
if (err)
goto out_unlock;
@@ -328,10 +369,10 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
/* Copies the @parent tree. */
rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
- &parent->root, node) {
- err = insert_rule(child, walker_rule->object,
- &walker_rule->layers,
- walker_rule->num_layers);
+ &parent->root_inode, node) {
+ err = insert_rule(child, walker_rule->object.ptr, 0,
+ LANDLOCK_RULE_PATH_BENEATH, &walker_rule->layers,
+ walker_rule->num_layers);
if (err)
goto out_unlock;
}
@@ -362,7 +403,8 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
struct landlock_rule *freeme, *next;
might_sleep();
- rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root, node)
+ rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root_inode,
+ node)
free_rule(freeme);
put_hierarchy(ruleset->hierarchy);
kfree(ruleset);
@@ -452,22 +494,31 @@ landlock_merge_ruleset(struct landlock_ruleset *const parent,
/*
* The returned access has the same lifetime as @ruleset.
*/
-const struct landlock_rule *
-landlock_find_rule(const struct landlock_ruleset *const ruleset,
- const struct landlock_object *const object)
+const struct landlock_rule *landlock_find_rule(
+ const struct landlock_ruleset *const ruleset,
+ const uintptr_t object_data, const u16 rule_type)
{
const struct rb_node *node;
- if (!object)
+ if (!object_data)
return NULL;
- node = ruleset->root.rb_node;
+
+ switch (rule_type) {
+ case LANDLOCK_RULE_PATH_BENEATH:
+ node = ruleset->root_inode.rb_node;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return NULL;
+ }
+
while (node) {
struct landlock_rule *this =
rb_entry(node, struct landlock_rule, node);
- if (this->object == object)
+ if (this->object.data == object_data)
return this;
- if (this->object < object)
+ if (this->object.data < object_data)
node = node->rb_right;
else
node = node->rb_left;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index f27a79624962..3066e5d7180c 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -54,15 +54,17 @@ struct landlock_rule {
*/
struct rb_node node;
/**
- * @object: Pointer to identify a kernel object (e.g. an inode). This
- * is used as a key for this ruleset element. This pointer is set once
- * and never modified. It always points to an allocated object because
- * each rule increments the refcount of its object.
- */
- struct landlock_object *object;
- /**
- * @num_layers: Number of entries in @layers.
+ * @object: A union to identify either a kernel object (e.g. an inode) or
+ * a raw data value (e.g. a network socket port). This is used as a key
+ * for this ruleset element. This pointer/@object.ptr/ is set once and
+ * never modified. It always points to an allocated object because each
+ * rule increments the refcount of its object (for inodes);
*/
+ union {
+ struct landlock_object *ptr;
+ uintptr_t data;
+ } object;
+
u32 num_layers;
/**
* @layers: Stack of layers, from the latest to the newest, implemented
@@ -99,7 +101,7 @@ struct landlock_ruleset {
* nodes. Once a ruleset is tied to a process (i.e. as a domain), this
* tree is immutable until @usage reaches zero.
*/
- struct rb_root root;
+ struct rb_root root_inode;
/**
* @hierarchy: Enables hierarchy identification even when a parent
* domain vanishes. This is needed for the ptrace protection.
@@ -161,16 +163,18 @@ void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
int landlock_insert_rule(struct landlock_ruleset *const ruleset,
- struct landlock_object *const object,
- const access_mask_t access);
+ struct landlock_object *const object_ptr,
+ const uintptr_t object_data,
+ const access_mask_t access,
+ const u16 rule_type);
struct landlock_ruleset *
landlock_merge_ruleset(struct landlock_ruleset *const parent,
struct landlock_ruleset *const ruleset);
-const struct landlock_rule *
-landlock_find_rule(const struct landlock_ruleset *const ruleset,
- const struct landlock_object *const object);
+const struct landlock_rule *landlock_find_rule(
+ const struct landlock_ruleset *const ruleset,
+ const uintptr_t object_data, const u16 rule_type);
static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
{
--
2.25.1
^ permalink raw reply related
* [PATCH v5 01/15] landlock: access mask renaming
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
In-Reply-To: <20220516152038.39594-1-konstantin.meskhidze@huawei.com>
Currently Landlock supports filesystem
restrictions. To support network type rules,
this modification extends and renames
ruleset's access masks.
This patch adds filesystem helper functions
to set and get filesystem mask. Also the modification
adds a helper structure landlock_access_mask to
support managing multiple access mask.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---
Changes since v3:
* Splits commit.
* Adds get_mask, set_mask helpers for filesystem.
* Adds new struct landlock_access_mask.
Changes since v4:
* Deletes struct landlock_access_mask.
---
security/landlock/fs.c | 15 ++++++++-------
security/landlock/ruleset.c | 25 +++++++++++++------------
security/landlock/ruleset.h | 27 ++++++++++++++++++++++-----
security/landlock/syscalls.c | 7 ++++---
4 files changed, 47 insertions(+), 27 deletions(-)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index ec5a6247cd3e..8eea52e5a3a4 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -167,7 +167,8 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
return -EINVAL;
/* Transforms relative access rights to absolute ones. */
- access_rights |= LANDLOCK_MASK_ACCESS_FS & ~ruleset->fs_access_masks[0];
+ access_rights |= LANDLOCK_MASK_ACCESS_FS &
+ ~landlock_get_fs_access_mask(ruleset, 0);
object = get_inode_object(d_backing_inode(path->dentry));
if (IS_ERR(object))
return PTR_ERR(object);
@@ -285,9 +286,9 @@ get_handled_accesses(const struct landlock_ruleset *const domain)
size_t layer_level;
for (layer_level = 0; layer_level < domain->num_layers;
- layer_level++) {
- if (domain->fs_access_masks[layer_level] &
- BIT_ULL(access_bit)) {
+ layer_level++) {
+ if (landlock_get_fs_access_mask(domain, layer_level) &
+ BIT_ULL(access_bit)) {
access_dom |= BIT_ULL(access_bit);
break;
}
@@ -315,9 +316,9 @@ init_layer_masks(const struct landlock_ruleset *const domain,
unsigned long access_bit;
for_each_set_bit(access_bit, &access_req,
- ARRAY_SIZE(*layer_masks)) {
- if (domain->fs_access_masks[layer_level] &
- BIT_ULL(access_bit)) {
+ ARRAY_SIZE(*layer_masks)) {
+ if (landlock_get_fs_access_mask(domain, layer_level) &
+ BIT_ULL(access_bit)) {
(*layer_masks)[access_bit] |=
BIT_ULL(layer_level);
handled_accesses |= BIT_ULL(access_bit);
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 996484f98bfd..b8917f6a8050 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -28,9 +28,9 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
{
struct landlock_ruleset *new_ruleset;
- new_ruleset =
- kzalloc(struct_size(new_ruleset, fs_access_masks, num_layers),
- GFP_KERNEL_ACCOUNT);
+ new_ruleset = kzalloc(struct_size(new_ruleset, access_masks,
+ num_layers), GFP_KERNEL_ACCOUNT);
+
if (!new_ruleset)
return ERR_PTR(-ENOMEM);
refcount_set(&new_ruleset->usage, 1);
@@ -40,22 +40,23 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
/*
* hierarchy = NULL
* num_rules = 0
- * fs_access_masks[] = 0
+ * access_masks[] = 0
*/
return new_ruleset;
}
-struct landlock_ruleset *
-landlock_create_ruleset(const access_mask_t fs_access_mask)
+struct landlock_ruleset *landlock_create_ruleset(
+ const access_mask_t access_mask)
{
struct landlock_ruleset *new_ruleset;
/* Informs about useless ruleset. */
- if (!fs_access_mask)
+ if (!access_mask)
return ERR_PTR(-ENOMSG);
new_ruleset = create_ruleset(1);
if (!IS_ERR(new_ruleset))
- new_ruleset->fs_access_masks[0] = fs_access_mask;
+ landlock_set_fs_access_mask(new_ruleset, access_mask, 0);
+
return new_ruleset;
}
@@ -117,7 +118,7 @@ static void build_check_ruleset(void)
.num_rules = ~0,
.num_layers = ~0,
};
- typeof(ruleset.fs_access_masks[0]) fs_access_mask = ~0;
+ typeof(ruleset.access_masks[0]) fs_access_mask = ~0;
BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
@@ -281,7 +282,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
err = -EINVAL;
goto out_unlock;
}
- dst->fs_access_masks[dst->num_layers - 1] = src->fs_access_masks[0];
+ dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
/* Merges the @src tree. */
rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, &src->root,
@@ -340,8 +341,8 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
goto out_unlock;
}
/* Copies the parent layer stack and leaves a space for the new layer. */
- memcpy(child->fs_access_masks, parent->fs_access_masks,
- flex_array_size(parent, fs_access_masks, parent->num_layers));
+ memcpy(child->access_masks, parent->access_masks,
+ flex_array_size(parent, access_masks, parent->num_layers));
if (WARN_ON_ONCE(!parent->hierarchy)) {
err = -EINVAL;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index d43231b783e4..f27a79624962 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -20,6 +20,7 @@
#include "object.h"
typedef u16 access_mask_t;
+
/* Makes sure all filesystem access rights can be stored. */
static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
/* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
@@ -110,7 +111,7 @@ struct landlock_ruleset {
* section. This is only used by
* landlock_put_ruleset_deferred() when @usage reaches zero.
* The fields @lock, @usage, @num_rules, @num_layers and
- * @fs_access_masks are then unused.
+ * @access_masks are then unused.
*/
struct work_struct work_free;
struct {
@@ -137,7 +138,7 @@ struct landlock_ruleset {
*/
u32 num_layers;
/**
- * @fs_access_masks: Contains the subset of filesystem
+ * @access_masks: Contains the subset of filesystem
* actions that are restricted by a ruleset. A domain
* saves all layers of merged rulesets in a stack
* (FAM), starting from the first layer to the last
@@ -148,13 +149,13 @@ struct landlock_ruleset {
* layers are set once and never changed for the
* lifetime of the ruleset.
*/
- access_mask_t fs_access_masks[];
+ u32 access_masks[];
};
};
};
-struct landlock_ruleset *
-landlock_create_ruleset(const access_mask_t fs_access_mask);
+struct landlock_ruleset *landlock_create_ruleset(
+ const access_mask_t access_mask);
void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
@@ -177,4 +178,20 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
refcount_inc(&ruleset->usage);
}
+/* A helper function to set a filesystem mask */
+static inline void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
+ const access_mask_t access_maskset,
+ u16 mask_level)
+{
+ ruleset->access_masks[mask_level] = access_maskset;
+}
+
+/* A helper function to get a filesystem mask */
+static inline u32 landlock_get_fs_access_mask(
+ const struct landlock_ruleset *ruleset,
+ u16 mask_level)
+{
+ return ruleset->access_masks[mask_level];
+}
+
#endif /* _SECURITY_LANDLOCK_RULESET_H */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 735a0865ea11..1db799d1a50b 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -346,10 +346,11 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
}
/*
* Checks that allowed_access matches the @ruleset constraints
- * (ruleset->fs_access_masks[0] is automatically upgraded to 64-bits).
+ * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
*/
- if ((path_beneath_attr.allowed_access | ruleset->fs_access_masks[0]) !=
- ruleset->fs_access_masks[0]) {
+ if ((path_beneath_attr.allowed_access |
+ landlock_get_fs_access_mask(ruleset, 0)) !=
+ landlock_get_fs_access_mask(ruleset, 0)) {
err = -EINVAL;
goto out_put_ruleset;
}
--
2.25.1
^ permalink raw reply related
* [PATCH v5 00/15] Network support for Landlock
From: Konstantin Meskhidze @ 2022-05-16 15:20 UTC (permalink / raw)
To: mic
Cc: willemdebruijn.kernel, linux-security-module, netdev,
netfilter-devel, yusongping, anton.sirazetdinov
Hi,
This is a new V5 patch related to Landlock LSM network confinement.
It is based on the latest landlock-wip branch on top of v5.18-rc5:
https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git/log/?h=landlock-wip
It brings refactoring of previous patch version V4.
Added additional selftests for IP6 network families and network namespace.
Added TCP sockets confinement support in sandboxer demo.
All test were run in QEMU evironment and compiled with
-static flag.
1. network_test: 13/13 tests passed.
2. base_test: 7/7 tests passed.
3. fs_test: 59/59 tests passed.
4. ptrace_test: 8/8 tests passed.
Still have issue with base_test were compiled without -static flag
(landlock-wip branch without network support)
1. base_test: 6/7 tests passed.
Error:
# RUN global.inconsistent_attr ...
# base_test.c:54:inconsistent_attr:Expected ENOMSG (42) == errno (22)
# inconsistent_attr: Test terminated by assertion
# FAIL global.inconsistent_attr
not ok 1 global.inconsistent_attr
LCOV - code coverage report:
Hit Total Coverage
Lines: 952 1010 94.3 %
Functions: 79 82 96.3 %
Previous versions:
v4: https://lore.kernel.org/linux-security-module/20220309134459.6448-1-konstantin.meskhidze@huawei.com/
v3: https://lore.kernel.org/linux-security-module/20220124080215.265538-1-konstantin.meskhidze@huawei.com/
v2: https://lore.kernel.org/linux-security-module/20211228115212.703084-1-konstantin.meskhidze@huawei.com/
v1: https://lore.kernel.org/linux-security-module/20211210072123.386713-1-konstantin.meskhidze@huawei.com/
Konstantin Meskhidze (15):
landlock: access mask renaming
landlock: landlock_find/insert_rule refactoring
landlock: merge and inherit function refactoring
landlock: helper functions refactoring
landlock: landlock_add_rule syscall refactoring
landlock: user space API network support
landlock: add support network rules
landlock: TCP network hooks implementation
seltests/landlock: add tests for bind() hooks
seltests/landlock: add tests for connect() hooks
seltests/landlock: connect() with AF_UNSPEC tests
seltests/landlock: rules overlapping test
seltests/landlock: ruleset expanding test
seltests/landlock: invalid user input data test
samples/landlock: adds network demo
include/uapi/linux/landlock.h | 48 +
samples/landlock/sandboxer.c | 105 ++-
security/landlock/Kconfig | 1 +
security/landlock/Makefile | 2 +
security/landlock/fs.c | 169 +---
security/landlock/limits.h | 8 +-
security/landlock/net.c | 159 ++++
security/landlock/net.h | 25 +
security/landlock/ruleset.c | 481 ++++++++--
security/landlock/ruleset.h | 102 +-
security/landlock/setup.c | 2 +
security/landlock/syscalls.c | 173 ++--
tools/testing/selftests/landlock/base_test.c | 4 +-
tools/testing/selftests/landlock/common.h | 9 +
tools/testing/selftests/landlock/config | 5 +-
tools/testing/selftests/landlock/fs_test.c | 10 -
tools/testing/selftests/landlock/net_test.c | 935 +++++++++++++++++++
17 files changed, 1925 insertions(+), 313 deletions(-)
create mode 100644 security/landlock/net.c
create mode 100644 security/landlock/net.h
create mode 100644 tools/testing/selftests/landlock/net_test.c
--
2.25.1
^ permalink raw reply
* Re: sockets staying in FIN-WAIT-1, CLOSING, or LAST-ACK state till reboot
From: Eric Dumazet @ 2022-05-16 15:12 UTC (permalink / raw)
To: Sami Farin, Linux Networking Mailing List, Eric Dumazet
In-Reply-To: <20220515140001.cws3pgz4iaaanpjo@m.mifar.in>
On 5/15/22 07:04, Sami Farin wrote:
> Hello,
>
> with 5.15.37, ss -K -t does not properly kill the TCP sockets.
> [ Disclaimer: this is my guess of the culprit. I also use wireguard. ]
>
> tcp FIN-WAIT-1 0 1 80.220.8.55:22384
> 91.198.174.208:443 ino:0 sk:510b cgroup:unreachable:6c46 ---
> skmem:(r0,rb87380,t0,tb130560,f0,w0,o0,bl0,d0) ts sack ecn
> ecnseen bbr wscale:9,10 rto:3744 rtt:33.364/18.714 ato:40 mss:36
> pmtu:68 rcvmss:536 advmss:1448 cwnd:1 bytes_sent:701 bytes_acked:702
> bytes_received:254 segs_out:6 segs_in:3 data_segs_out:2 data_segs_in:1
> bbr:(bw:10176bps,mrtt:27.924,pacing_gain:2.88672,cwnd_gain:2.88672)
> send 8632bps lastsnd:601107881 lastrcv:601107884 lastack:601107810
> pacing_rate 11658680bps delivery_rate 10192bps delivered:3 app_limited
> busy:23371222ms lost:1 rcv_space:14480 rcv_ssthresh:42242 minrtt:27.924
>
> $ nc -l -p 22384
> Ncat: bind to 0.0.0.0:22384: Address already in use. QUITTING.
> $ nc -l 127.0.0.1 22384
> ^C
> $
>
> These zombie sockets all have Send-Q > 0.
That is right.
tcp_abort() currently supports established and SYN_RECV sockets only.
Adding support for TIMEWAIT should not be difficult.
^ permalink raw reply
* Re: [PATCH 19/30] panic: Add the panic hypervisor notifier list
From: Guilherme G. Piccoli @ 2022-05-16 15:06 UTC (permalink / raw)
To: Petr Mladek, David Gow, Evan Green, Julius Werner, Scott Branden,
bcm-kernel-feedback-list, Sebastian Reichel, linux-pm,
Florian Fainelli
Cc: akpm, bhe, kexec, linux-kernel, linuxppc-dev, linux-alpha,
linux-arm-kernel, linux-edac, linux-hyperv, linux-leds,
linux-mips, linux-parisc, linux-remoteproc, linux-s390,
linux-tegra, linux-um, linux-xtensa, netdev, openipmi-developer,
rcu, sparclinux, xen-devel, x86, kernel-dev, kernel, halves,
fabiomirmar, alejandro.j.jimenez, andriy.shevchenko, arnd, bp,
corbet, d.hatayama, dave.hansen, dyoung, feng.tang, gregkh,
mikelley, hidehiro.kawai.ez, jgross, john.ogness, keescook, luto,
mhiramat, mingo, paulmck, peterz, rostedt, senozhatsky, stern,
tglx, vgoyal, vkuznets, will, Alexander Gordeev, Andrea Parri,
Ard Biesheuvel, Benjamin Herrenschmidt, Brian Norris,
Christian Borntraeger, Christophe JAILLET, David S. Miller,
Dexuan Cui, Doug Berger, Haiyang Zhang, Hari Bathini,
Heiko Carstens, Justin Chen, K. Y. Srinivasan, Lee Jones,
Markus Mayer, Michael Ellerman, Mihai Carabas, Nicholas Piggin,
Paul Mackerras, Pavel Machek, Shile Zhang, Stephen Hemminger,
Sven Schnelle, Thomas Bogendoerfer, Tianyu Lan, Vasily Gorbik,
Wang ShaoBo, Wei Liu, zhenwei pi
In-Reply-To: <YoJZVZl/MH0KiE/J@alley>
Thanks for the review!
I agree with the blinking stuff, I can rework and add all LED/blinking
stuff into the loop list, it does make sense. I'll comment a bit in the
others below...
On 16/05/2022 11:01, Petr Mladek wrote:
> [...]
>> --- a/arch/mips/sgi-ip22/ip22-reset.c
>> +++ b/arch/mips/sgi-ip22/ip22-reset.c
>> @@ -195,7 +195,7 @@ static int __init reboot_setup(void)
>> }
>>
>> timer_setup(&blink_timer, blink_timeout, 0);
>> - atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
>> + atomic_notifier_chain_register(&panic_hypervisor_list, &panic_block);
>
> This notifier enables blinking. It is not much safe. It calls
> mod_timer() that takes a lock internally.
>
> This kind of functionality should go into the last list called
> before panic() enters the infinite loop. IMHO, all the blinking
> stuff should go there.
> [...]
>> --- a/arch/mips/sgi-ip32/ip32-reset.c
>> +++ b/arch/mips/sgi-ip32/ip32-reset.c
>> @@ -145,7 +144,7 @@ static __init int ip32_reboot_setup(void)
>> pm_power_off = ip32_machine_halt;
>>
>> timer_setup(&blink_timer, blink_timeout, 0);
>> - atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
>> + atomic_notifier_chain_register(&panic_hypervisor_list, &panic_block);
>
> Same here. Should be done only before the "loop".
> [...]
Ack.
>> --- a/drivers/firmware/google/gsmi.c
>> +++ b/drivers/firmware/google/gsmi.c
>> @@ -1034,7 +1034,7 @@ static __init int gsmi_init(void)
>>
>> register_reboot_notifier(&gsmi_reboot_notifier);
>> register_die_notifier(&gsmi_die_notifier);
>> - atomic_notifier_chain_register(&panic_notifier_list,
>> + atomic_notifier_chain_register(&panic_hypervisor_list,
>> &gsmi_panic_notifier);
>
> I am not sure about this one. It looks like some logging or
> pre_reboot stuff.
>
Disagree here. I'm looping Google maintainers, so they can comment.
(CCed Evan, David, Julius)
This notifier is clearly a hypervisor notification mechanism. I've fixed
a locking stuff there (in previous patch), I feel it's low-risk but even
if it's mid-risk, the class of such callback remains a perfect fit with
the hypervisor list IMHO.
> [...]
>> --- a/drivers/leds/trigger/ledtrig-activity.c
>> +++ b/drivers/leds/trigger/ledtrig-activity.c
>> @@ -247,7 +247,7 @@ static int __init activity_init(void)
>> int rc = led_trigger_register(&activity_led_trigger);
>>
>> if (!rc) {
>> - atomic_notifier_chain_register(&panic_notifier_list,
>> + atomic_notifier_chain_register(&panic_hypervisor_list,
>> &activity_panic_nb);
>
> The notifier is trivial. It just sets a variable.
>
> But still, it is about blinking and should be done
> in the last "loop" list.
>
>
>> register_reboot_notifier(&activity_reboot_nb);
>> }
>> --- a/drivers/leds/trigger/ledtrig-heartbeat.c
>> +++ b/drivers/leds/trigger/ledtrig-heartbeat.c
>> @@ -190,7 +190,7 @@ static int __init heartbeat_trig_init(void)
>> int rc = led_trigger_register(&heartbeat_led_trigger);
>>
>> if (!rc) {
>> - atomic_notifier_chain_register(&panic_notifier_list,
>> + atomic_notifier_chain_register(&panic_hypervisor_list,
>> &heartbeat_panic_nb);
>
> Same here. Blinking => loop list.
Ack.
>> [...]
>> diff --git a/drivers/misc/bcm-vk/bcm_vk_dev.c b/drivers/misc/bcm-vk/bcm_vk_dev.c
>> index a16b99bdaa13..d9d5199cdb2b 100644
>> --- a/drivers/misc/bcm-vk/bcm_vk_dev.c
>> +++ b/drivers/misc/bcm-vk/bcm_vk_dev.c
>> @@ -1446,7 +1446,7 @@ static int bcm_vk_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>>
>> /* register for panic notifier */
>> vk->panic_nb.notifier_call = bcm_vk_on_panic;
>> - err = atomic_notifier_chain_register(&panic_notifier_list,
>> + err = atomic_notifier_chain_register(&panic_hypervisor_list,
>> &vk->panic_nb);
>
> It seems to reset some hardware or so. IMHO, it should go into the
> pre-reboot list.
Mixed feelings here, I'm looping Broadcom maintainers to comment.
(CC Scott and Broadcom list)
I'm afraid it breaks kdump if this device is not reset beforehand - it's
a doorbell write, so not high risk I think...
But in case the not-reset device can be probed normally in kdump kernel,
then I'm fine in moving this to the reboot list! I don't have the HW to
test myself.
> [...]
>> --- a/drivers/power/reset/ltc2952-poweroff.c
>> +++ b/drivers/power/reset/ltc2952-poweroff.c
>> @@ -279,7 +279,7 @@ static int ltc2952_poweroff_probe(struct platform_device *pdev)
>> pm_power_off = ltc2952_poweroff_kill;
>>
>> data->panic_notifier.notifier_call = ltc2952_poweroff_notify_panic;
>> - atomic_notifier_chain_register(&panic_notifier_list,
>> + atomic_notifier_chain_register(&panic_hypervisor_list,
>> &data->panic_notifier);
>
> I looks like this somehow triggers the reboot. IMHO, it should go
> into the pre_reboot list.
Mixed feeling again here - CCing the maintainers for comments (Sebastian
/ PM folks).
This is setting a variable only, and once it's set (data->kernel_panic
is the bool's name), it just bails out the IRQ handler and a timer
setting - this timer seems kinda tricky, so bailing out ASAP makes sense
IMHO.
But my mixed feeling comes from the fact this notifier really is not a
fit to any list - it's just a "watchdog"/device quiesce in some form.
Since it's very low-risk (IIUC), I've put it here.
> [...]
>> --- a/drivers/soc/bcm/brcmstb/pm/pm-arm.c
>> +++ b/drivers/soc/bcm/brcmstb/pm/pm-arm.c
>> @@ -814,7 +814,7 @@ static int brcmstb_pm_probe(struct platform_device *pdev)
>> goto out;
>> }
>>
>> - atomic_notifier_chain_register(&panic_notifier_list,
>> + atomic_notifier_chain_register(&panic_hypervisor_list,
>> &brcmstb_pm_panic_nb);
>
> I am not sure about this one. It instruct some HW to preserve DRAM.
> IMHO, it better fits into pre_reboot category but I do not have
> strong opinion.
Disagree here, I'm CCing Florian for information.
This notifier preserves RAM so it's *very interesting* if we have
kmsg_dump() for example, but maybe might be also relevant in case kdump
kernel is configured to store something in a persistent RAM (then,
without this notifier, after kdump reboots the system data would be lost).
Cheers,
Guilherme
^ permalink raw reply
* Re: [PATCH 25/30] panic, printk: Add console flush parameter and convert panic_print to a notifier
From: Petr Mladek @ 2022-05-16 14:56 UTC (permalink / raw)
To: Guilherme G. Piccoli
Cc: akpm, bhe, kexec, linux-kernel, bcm-kernel-feedback-list,
coresight, linuxppc-dev, linux-alpha, linux-arm-kernel,
linux-edac, linux-hyperv, linux-leds, linux-mips, linux-parisc,
linux-pm, linux-remoteproc, linux-s390, linux-tegra, linux-um,
linux-xtensa, netdev, openipmi-developer, rcu, sparclinux,
xen-devel, x86, kernel-dev, kernel, halves, fabiomirmar,
alejandro.j.jimenez, andriy.shevchenko, arnd, bp, corbet,
d.hatayama, dave.hansen, dyoung, feng.tang, gregkh, mikelley,
hidehiro.kawai.ez, jgross, john.ogness, keescook, luto, mhiramat,
mingo, paulmck, peterz, rostedt, senozhatsky, stern, tglx, vgoyal,
vkuznets, will
In-Reply-To: <20220427224924.592546-26-gpiccoli@igalia.com>
On Wed 2022-04-27 19:49:19, Guilherme G. Piccoli wrote:
> Currently the parameter "panic_print" relies in a function called
> directly on panic path; one of the flags the users can set for
> panic_print triggers a console replay mechanism, to show the
> entire kernel log buffer (from the beginning) in a panic event.
>
> Two problems with that: the dual nature of the panic_print
> isn't really appropriate, the function was originally meant
> to allow users dumping system information on panic events,
> and was "overridden" to also force a console flush of the full
> kernel log buffer. It also turns the code a bit more complex
> and duplicate than it needs to be.
>
> This patch proposes 2 changes: first, we decouple panic_print
> from the console flushing mechanism, in the form of a new kernel
> core parameter (panic_console_replay); we kept the functionality
> on panic_print to avoid userspace breakage, although we comment
> in both code and documentation that this panic_print usage is
> deprecated.
>
> We converted panic_print function to a panic notifier too, adding
> it on the panic informational notifier list, executed as the final
> callback. This allows a more clear code and makes sense, as
> panic_print_sys_info() is really a panic-time only function.
> We also moved its code to kernel/printk.c, it seems to make more
> sense given it's related to printing stuff.
I really like both changes. Just please split it them into two
patchset. I mean one patch for the new "panic_console_replay"
parameter and 2nd that moves "printk_info" into the notifier.
Best Regards,
Petr
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox