diff mbox series

[3/3] selinux: Add SELinux GTP support

Message ID 20200930094934.32144-4-richard_c_haines@btinternet.com (mailing list archive)
State Changes Requested
Delegated to: Paul Moore
Headers show
Series Add LSM/SELinux support for GPRS Tunneling Protocol (GTP) | expand

Commit Message

Richard Haines Sept. 30, 2020, 9:49 a.m. UTC
The SELinux GTP implementation is explained in:
Documentation/security/GTP.rst

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
---
 Documentation/security/GTP.rst      | 61 ++++++++++++++++++++++++++
 security/selinux/hooks.c            | 66 +++++++++++++++++++++++++++++
 security/selinux/include/classmap.h |  2 +
 security/selinux/include/objsec.h   |  4 ++
 4 files changed, 133 insertions(+)

Comments

Harald Welte Sept. 30, 2020, 11:01 a.m. UTC | #1
Hi Richard,

I don't fully understand in which context you need / use those SELinux GTP hooks,
however one comment from the point of view of somebody who is working on GGSN/P-GW
software using the GTP kernel module:

On Wed, Sep 30, 2020 at 10:49:34AM +0100, Richard Haines wrote:
> +selinux_gtp_dev_cmd()
> +~~~~~~~~~~~~~~~~~~~~~
> +Validate if the caller (current SID) and the GTP device SID have the required
> +permission to perform the operation. The GTP/SELinux permission map is
> +as follow::
> +
> +    GTP_CMD_NEWPDP = gtp { add }
> +    GTP_CMD_DELPDP = gtp { del }
> +    GTP_CMD_GETPDP = gtp { get }

Wouldn't it make sense to differentiate between:

a) add/del/get on the GTP netdev
b) add/del/get on the indivudual PDP wihin the GTP netdev

'a' is typically only created once at startup of a GGSN/P-GW software, or is
done even at system stat-up time.

'b' is performed frequently during runtime as the GGSN/P-GW function runs, as
subscribers attach to / detach from the cellular network.

By differentiating between those two, one could further constrain the permissions
required at runtime.
Richard Haines Sept. 30, 2020, 12:25 p.m. UTC | #2
On Wed, 2020-09-30 at 13:01 +0200, Harald Welte wrote:
> Hi Richard,
> 
> I don't fully understand in which context you need / use those
> SELinux GTP hooks,

As in the reply to Pablo, I did it for no particular reason other than
idle curiosity, and given the attempted move to Open 5G I thought
adding MAC support might be useful somewhere along the line.

> however one comment from the point of view of somebody who is working
> on GGSN/P-GW
> software using the GTP kernel module:
> 
> On Wed, Sep 30, 2020 at 10:49:34AM +0100, Richard Haines wrote:
> > +selinux_gtp_dev_cmd()
> > +~~~~~~~~~~~~~~~~~~~~~
> > +Validate if the caller (current SID) and the GTP device SID have
> > the required
> > +permission to perform the operation. The GTP/SELinux permission
> > map is
> > +as follow::
> > +
> > +    GTP_CMD_NEWPDP = gtp { add }
> > +    GTP_CMD_DELPDP = gtp { del }
> > +    GTP_CMD_GETPDP = gtp { get }
> 
> Wouldn't it make sense to differentiate between:
> 
> a) add/del/get on the GTP netdev
> b) add/del/get on the indivudual PDP wihin the GTP netdev
> 
> 'a' is typically only created once at startup of a GGSN/P-GW
> software, or is
> done even at system stat-up time.
> 
> 'b' is performed frequently during runtime as the GGSN/P-GW function
> runs, as
> subscribers attach to / detach from the cellular network.
> 
> By differentiating between those two, one could further constrain the
> permissions
> required at runtime.

Yes, at first I did separate them (add_dev, del_dev, add_pkt, del_pkt,
get_pkt), so if this patchset goes anywhere then I can change it no
problem. I guess the '*_pkt' permissions would cover PDP for 3G and PDR
& FAR for 5G ?.
I didn't implement 'get_dev' but thought it could be useful for
retrieving the security context of a device, but that requires passing
it back via netlink so thought I would leave it until later.

>
Harald Welte Sept. 30, 2020, 1:38 p.m. UTC | #3
Hi Richard,

On Wed, Sep 30, 2020 at 01:25:27PM +0100, Richard Haines wrote:

> As in the reply to Pablo, I did it for no particular reason other than
> idle curiosity, and given the attempted move to Open 5G I thought
> adding MAC support might be useful somewhere along the line.

thanks, I only saw your related mail earlier today.

Unfortunately there's a lot of talk about "open source" in the context of 5G
but as far as I can tell (and I'm involved in open source cellular full-time
for a decade now) it's mostly marketing.  And if something is relased, it's
some shared source license that doesn't pass the OSI OSD nor DFSG, ...

In any case, this is off-topic here.

I think it would not be the best idea to merge SELinux support patches for the
GTP kernel driver without thoroughly understanding the use case, and/or having
some actual userspace implementations that make use of them.  In the end, we may
be introducing code that nobody uses, and which only turns out to be insufficient
for what later actual users may want.

So like Pablo suggested, it would probably be best to focus on
submitting / merging features for things that are either well-defined (e.g.
specified in a standerd), and/or have existing userspace implementations.

> I guess the '*_pkt' permissions would cover PDP for 3G and PDR
> & FAR for 5G ?.

The permissions would probably cover those two items, yes.  As you
probably know, we currently don't have any ability in the kernel GTP
driver to map "external" IP traffic to TEID based on anything except the
destination IP address.  This is sufficient for all 2G and 3G use cases,
and should also cover many 4G use cases.  However, if you want to go for
different dedicated bearers and QoS classes, for sure you need something
more advanced in terms of classification of packets.

Regards,
	Harald
Paul Moore Oct. 12, 2020, 2:09 a.m. UTC | #4
On Wed, Sep 30, 2020 at 9:39 AM Harald Welte <laforge@gnumonks.org> wrote:
> Hi Richard,
>
> On Wed, Sep 30, 2020 at 01:25:27PM +0100, Richard Haines wrote:
>
> > As in the reply to Pablo, I did it for no particular reason other than
> > idle curiosity, and given the attempted move to Open 5G I thought
> > adding MAC support might be useful somewhere along the line.
>
> ...
>
> I think it would not be the best idea to merge SELinux support patches for the
> GTP kernel driver without thoroughly understanding the use case, and/or having
> some actual userspace implementations that make use of them.  In the end, we may
> be introducing code that nobody uses, and which only turns out to be insufficient
> for what later actual users may want.
>
> So like Pablo suggested, it would probably be best to focus on
> submitting / merging features for things that are either well-defined (e.g.
> specified in a standerd), and/or have existing userspace implementations.

Having a solid use case or two is also helpful for those of us who
don't have a GTP/GPRS background.  I did spend some time reading a few
things on GTP, but I don't feel like I've made much of a dent on
understanding how it is actually used.

Harald, Pablo - I know you both suggested taking a slow iterative
approach to merging functionality, perhaps you could also help those
of us on the SELinux side better understand some of the common GTP use
cases?
Harald Welte Oct. 12, 2020, 9:38 a.m. UTC | #5
Hi Paul,

On Sun, Oct 11, 2020 at 10:09:11PM -0400, Paul Moore wrote:
> Harald, Pablo - I know you both suggested taking a slow iterative
> approach to merging functionality, perhaps you could also help those
> of us on the SELinux side better understand some of the common GTP use
> cases?

There really only is one use case for this code:  The GGSN or P-GW function
in the 3GPP network architecture.  There are open source implementations
like OsmoGGSN and (at least older/previos versions) of ergw, but also
non-FOSS-but-shared-source (like OpenAirInterface) and proprietary
software that use the existing kernel GTP for that.

In a GGSN (2G/3G) or P-GW (4G), you have a control plane instance
talking a control plane protocol GTP-C (GTPv1-C or GTPv2-C) with other
elements in the network, such as the SGSN (2G/3G) or S-GW (4G)

This control plane instance will then talk to the user plane instance
(which is, in the case of the kernel GTP module, netlink) to crate PDP
contexts (2G/3G) or "barers" (4G).

Those PDP contexts / bearers are constantly being created, modified and
torn down.  Creation happens at the time your modern phone attaches to
the network (once you have "mobile data" enabled).  Modification happens
when you start roaming around the country / coverage area as you change
between larger regions.  Deletion happens once you disable "mobile data'
or go into airplane mode.

The kernel GTP "tun" device typically represents an "APN", i.e. one
specific IP network your phones/modems want to attach to.  There could be
multiple in parallel (e.g. public internet, private operator network for
VoLTE, company-private networks).

So you have one tun device per APN which is mostly static configuration,
stable for months, years, ...  and then you have the hightly volatile
PDP contexts / bearers within each of those APN.  That's why I was
hinting from a security policy point of view, it makes sense to separate
those two, as one only happens at system boot / application start time,
and the other happens at any point during runtime of your GGSN/PGW.

If you want to see how that looks on the wire, check the OsmoGGSN
automatic test suite, where every test case generates a pcap file:
overview at https://jenkins.osmocom.org/jenkins/view/TTCN3/job/ttcn3-ggsn-test/
actual artefacts including per-test pcap files:
https://jenkins.osmocom.org/jenkins/view/TTCN3/job/ttcn3-ggsn-test/lastSuccessfulBuild/artifact/logs/ggsn-tester/
specifically, if you open
https://jenkins.osmocom.org/jenkins/view/TTCN3/job/ttcn3-ggsn-test/lastSuccessfulBuild/artifact/logs/ggsn-tester/GGSN_Tests.TC_pdp6_act_deact_gtpu_access.pcap.gz
in wirehark and filter on 'gtp', you will see the GTP-C traffic on udp
port 2123, and the GTP-U traffic on UDP port 2152.  This second part is
where you would use the kernel-GTP-U implementation to avoid the
kernel-userspace-kernel roundtrip for every user IP packet.

There are many other use cases for GTP in general in other network
elements such as a S-GW (which basically acts as a proxy for GTP-U), but
those are not implemented in the existing Linux kernel GTP module.

I think OsmoGGSN is about the most simple to understand user out there,
due to its very limited code size.  Check
https://git.osmocom.org/osmo-ggsn/tree/lib/gtp-kernel.c for the libgtpnl
interface and https://git.osmocom.org/osmo-ggsn/tree/ggsn/ggsn.c for the
hearth of the logic, including the calls to the gtp_kernel_tunnel_*() API.

Hope this helps,
	Harald
Paul Moore Oct. 13, 2020, 1:55 p.m. UTC | #6
On Mon, Oct 12, 2020 at 5:40 AM Harald Welte <laforge@gnumonks.org> wrote:
>
> Hi Paul,
>
> On Sun, Oct 11, 2020 at 10:09:11PM -0400, Paul Moore wrote:
> > Harald, Pablo - I know you both suggested taking a slow iterative
> > approach to merging functionality, perhaps you could also help those
> > of us on the SELinux side better understand some of the common GTP use
> > cases?
>
> There really only is one use case for this code:  The GGSN or P-GW function
> in the 3GPP network architecture ...
>
> Hope this helps,
>         Harald

It does, thank you.

It looks like this patchset is not really a candidate for merging in
its current form, but I didn't want to lose this information (both the
patches and Harald's comments) so I created a GH issue to track this
at the URL below.

* https://github.com/SELinuxProject/selinux-kernel/issues/54
Richard Haines Oct. 13, 2020, 4:38 p.m. UTC | #7
On Tue, 2020-10-13 at 09:55 -0400, Paul Moore wrote:
> On Mon, Oct 12, 2020 at 5:40 AM Harald Welte <laforge@gnumonks.org>
> wrote:
> > Hi Paul,
> > 
> > On Sun, Oct 11, 2020 at 10:09:11PM -0400, Paul Moore wrote:
> > > Harald, Pablo - I know you both suggested taking a slow iterative
> > > approach to merging functionality, perhaps you could also help
> > > those
> > > of us on the SELinux side better understand some of the common
> > > GTP use
> > > cases?
> > 
> > There really only is one use case for this code:  The GGSN or P-GW
> > function
> > in the 3GPP network architecture ...
> > 
> > Hope this helps,
> >         Harald
> 
> It does, thank you.
> 
> It looks like this patchset is not really a candidate for merging in
> its current form, but I didn't want to lose this information (both
> the
> patches and Harald's comments) so I created a GH issue to track this
> at the URL below.
> 
> * https://github.com/SELinuxProject/selinux-kernel/issues/54
> 

While I was not expecting these patches to be excepted for the current
version, the main aim was to see what LSM security services could be
implemented on possible 5G components, bearing in mind the DARPA Open
Programmable Secure 5G (OPS-5G) initiative (probably 'jumping the gun'
here a bit though). 

There is in development a 5G version of GTP at [1]. I have added the
enhanced hooks to this (plus retrieve contexts via call-backs etc.),
and have it running on 5.9, passing their tests. I'm not sure how far
this development will go, but a starter ??.

The other component that seems to be widely used in these systems is
SCTP that I added hooks to a few years ago, also TCP/UDP etc. that are
already well catered for. Also there would be a large amount of
userspace code ....

Anyway food for thought.

[1] https://github.com/PrinzOwO/gtp5g
Harald Welte Oct. 13, 2020, 8:42 p.m. UTC | #8
Hi Richard and list[s],

On Tue, Oct 13, 2020 at 05:38:16PM +0100, Richard Haines wrote:
> There is in development a 5G version of GTP at [1]. 

Please note that there is no such thing as "5G version of GTP".  The GTP-U
(user plane) did not change between 2G, 3G, 4G or even 5G:  IT is still the
same protocol version (GTPv1-U), which you can see from looking at
3GPP TS 29.281 even in its latest release (Rel 15), which is what the authors
of the "gtp5g" github repository reference.

What has changed over time is how the protocol is used, and what kind of
QoS/classification features are added in order to use different GTP
tunnels for different traffic (to the same subscriber / IP address) in
order to subject it to different QoS within the 3GPP network.  This
functionality, by the way, can also be used in 4G networks, and even in
3G/2G networks that follow some of the later releases.

The "gtp5g" module hence should in my point not be a separate module,
but it should be broken down in incremental feature enhancements to the
existing in-kernel GTP user plane module.  The netlink interface should
also obviously be extended in a backwards-compatible way.

My most active kernel years are long gone, but I still think we never
have two implementations of the same protocol (GTPv1U in this case) in
the Kernel.

One could of course also consider to switch to a completely new
implementation / rewrite, but only if it is backwards compatible in
terms of use cases as well as the netlink interface (and hence existing
users of the GTPv1U kernel support).

> The other component that seems to be widely used in these systems is
> SCTP that I added hooks to a few years ago, [...]

indeed, SCTP is extremely heavily used in all cellular systems, from 2G
to 4G (with a peak in 4G), but still used on some 5G interfaces.

Unfortunately it is the tradition (until today) that none of the
industry players that need and use those protocols (GTP, SCTP) seem
to be participating in the development and maintenance effort of related
implementation.  So rather than Nokia, Ericsson or others improving the
in-kernel SCTP, their Linux based devices tend to roll their own
[userspace] SCTP implementations.

Even while in 2020 everybody in "marketing land" speaks about "open
source" in the context of cellular/5G, it is not happening.  It is only
open-washing in order to appear attractive.  In reality, anyone in this
industry derives a *massive* revenue from their patent royalty
collection and they would do anything but release or contribute to code
that comes with an explicit or implicit patent license grant.

So here we are, in 2020, where every single cellular equipment maker
uses Linux, but the most relevant real open source projects in the industry
are run by small enthusiast or very small players...

Regards,
	Harald
diff mbox series

Patch

diff --git a/Documentation/security/GTP.rst b/Documentation/security/GTP.rst
index c748587ec..433fcb688 100644
--- a/Documentation/security/GTP.rst
+++ b/Documentation/security/GTP.rst
@@ -15,6 +15,9 @@  For security module support, three GTP specific hooks have been implemented::
     security_gtp_dev_free()
     security_gtp_dev_cmd()
 
+The usage of these hooks are described below with the SELinux implementation
+described in the `GTP SELinux Support`_ chapter.
+
 
 security_gtp_dev_alloc()
 ~~~~~~~~~~~~~~~~~~~~~~
@@ -37,3 +40,61 @@  zero on success, negative values on failure. The commands are based on values
 from ``include/uapi/linux/gtp.h`` as follows::
 
 ``enum gtp_genl_cmds { GTP_CMD_NEWPDP, GTP_CMD_DELPDP, GTP_CMD_GETPDP };``
+
+
+GTP SELinux Support
+===================
+
+Policy Statements
+-----------------
+The following class and permissions to support GTP are available within the
+kernel::
+
+    class gtp { add del get }
+
+The permissions are described in the sections that follow.
+
+
+Security Hooks
+--------------
+
+The `GTP LSM Support`_ chapter above describes the following GTP security
+hooks with the SELinux specifics expanded below::
+
+    security_gtp_dev_alloc -> selinux_gtp_dev_alloc_security(gtp)
+    security_gtp_dev_free  -> selinux_gtp_dev_free_security(gtp)
+    security_gtp_dev_cmd   -> selinux_gtp_dev_cmd(gtp, cmd)
+
+
+selinux_gtp_dev_alloc_security()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Allocates a security structure for a GTP device provided the caller has the
+``gtp { add }`` permission. Can return errors ``-ENOMEM`` or ``-EACCES``.
+Returns zero if the security structure has been allocated.
+
+
+selinux_gtp_dev_free_security()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Frees a security structure for a GTP device provided the caller has the
+``gtp { del }`` permission. Can return error ``-EACCES``. Returns zero if the
+security structure has been freed.
+
+
+selinux_gtp_dev_cmd()
+~~~~~~~~~~~~~~~~~~~~~
+Validate if the caller (current SID) and the GTP device SID have the required
+permission to perform the operation. The GTP/SELinux permission map is
+as follow::
+
+    GTP_CMD_NEWPDP = gtp { add }
+    GTP_CMD_DELPDP = gtp { del }
+    GTP_CMD_GETPDP = gtp { get }
+
+Returns ``-EACCES`` if denied or zero if allowed.
+
+NOTES::
+   1) If the GTP device has the ``{ add }`` permission it can add device and
+      also add PDP's (packet data protocol).
+
+   2) If the GTP device has the ``{ del }`` permission it can delete a device
+      and also delete PDP's.
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d6b182c11..5229a4f20 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -91,6 +91,7 @@ 
 #include <uapi/linux/mount.h>
 #include <linux/fsnotify.h>
 #include <linux/fanotify.h>
+#include <net/gtp.h>
 
 #include "avc.h"
 #include "objsec.h"
@@ -5520,6 +5521,68 @@  static int selinux_tun_dev_open(void *security)
 	return 0;
 }
 
+static int selinux_gtp_dev_alloc_security(struct gtp_dev *gtp)
+{
+	struct gtp_security_struct *gtpsec;
+	u32 sid = current_sid();
+	int err;
+
+	err = avc_has_perm(&selinux_state, sid, sid,
+			   SECCLASS_GTP, GTP__ADD, NULL);
+	if (err < 0)
+		return err;
+
+	gtpsec = kzalloc(sizeof(*gtpsec), GFP_KERNEL);
+	if (!gtpsec)
+		return -ENOMEM;
+
+	gtpsec->sid = sid;
+	gtp->security = gtpsec;
+
+	return 0;
+}
+
+static int selinux_gtp_dev_free_security(struct gtp_dev *gtp)
+{
+	struct gtp_security_struct *gtpsec = gtp->security;
+	u32 sid = current_sid();
+	int err;
+
+	err = avc_has_perm(&selinux_state, sid, gtpsec->sid,
+			   SECCLASS_GTP, GTP__DEL, NULL);
+	if (err < 0)
+		return err;
+
+	gtp->security = NULL;
+	kfree(gtpsec);
+
+	return 0;
+}
+
+static int selinux_gtp_dev_cmd(struct gtp_dev *gtp, enum gtp_genl_cmds cmd)
+{
+	struct gtp_security_struct *gtpsec = gtp->security;
+	u32 perm, sid = current_sid();
+
+	switch (cmd) {
+	case GTP_CMD_NEWPDP:
+		perm = GTP__ADD;
+		break;
+	case GTP_CMD_DELPDP:
+		perm = GTP__DEL;
+		break;
+	case GTP_CMD_GETPDP:
+		perm = GTP__GET;
+		break;
+	default:
+		WARN_ON(1);
+		return -EPERM;
+	}
+
+	return avc_has_perm(&selinux_state, sid, gtpsec->sid,
+			    SECCLASS_GTP, perm, NULL);
+}
+
 #ifdef CONFIG_NETFILTER
 
 static unsigned int selinux_ip_forward(struct sk_buff *skb,
@@ -7130,6 +7193,8 @@  static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(tun_dev_attach_queue, selinux_tun_dev_attach_queue),
 	LSM_HOOK_INIT(tun_dev_attach, selinux_tun_dev_attach),
 	LSM_HOOK_INIT(tun_dev_open, selinux_tun_dev_open),
+	LSM_HOOK_INIT(gtp_dev_free_security, selinux_gtp_dev_free_security),
+	LSM_HOOK_INIT(gtp_dev_cmd, selinux_gtp_dev_cmd),
 #ifdef CONFIG_SECURITY_INFINIBAND
 	LSM_HOOK_INIT(ib_pkey_access, selinux_ib_pkey_access),
 	LSM_HOOK_INIT(ib_endport_manage_subnet,
@@ -7204,6 +7269,7 @@  static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx),
 	LSM_HOOK_INIT(sk_alloc_security, selinux_sk_alloc_security),
 	LSM_HOOK_INIT(tun_dev_alloc_security, selinux_tun_dev_alloc_security),
+	LSM_HOOK_INIT(gtp_dev_alloc_security, selinux_gtp_dev_alloc_security),
 #ifdef CONFIG_SECURITY_INFINIBAND
 	LSM_HOOK_INIT(ib_alloc_security, selinux_ib_alloc_security),
 #endif
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 40cebde62..3865a4549 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -249,6 +249,8 @@  struct security_class_mapping secclass_map[] = {
 	  {"open", "cpu", "kernel", "tracepoint", "read", "write"} },
 	{ "lockdown",
 	  { "integrity", "confidentiality", NULL } },
+	{ "gtp",
+	  { "add", "del", "get", NULL } },
 	{ NULL }
   };
 
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 330b7b6d4..311ffb6ea 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -148,6 +148,10 @@  struct perf_event_security_struct {
 	u32 sid;  /* SID of perf_event obj creator */
 };
 
+struct gtp_security_struct {
+	u32 sid;  /* SID of gtp obj creator */
+};
+
 extern struct lsm_blob_sizes selinux_blob_sizes;
 static inline struct task_security_struct *selinux_cred(const struct cred *cred)
 {