openbsc[master]: Add regexp authorization policy for IMSI
diff mbox

Message ID gerrit.1464093748635.I525f4b80676de47d1d422686da2ca012301b0129@gerrit.osmocom.org
State New
Headers show

Commit Message

gerrit-no-reply@lists.osmocom.org May 24, 2016, 12:42 p.m. UTC
Review at  https://gerrit.osmocom.org/104

Add regexp authorization policy for IMSI

* extend "auth policy" vty command with new option "regexp"
* add vty command "authorized-regexp" for setting arbitrary POSIX
  regular expression
* add basic vty test
* add optional "regexp" argument to subscriber-create-on-demand vty
  command

With those in place we can now set the regexp against which MS's IMSI
will be matched.

If IMSI match the regexp than MS is allowed to access the network. If
subscriber is already marked as authorized in HLR than it'll be allowed
regardless of IMSI matching.

The same way we can decide whether to create subscribers on-demand
basesd on IMSI regexp match. Similar to authorization this restriction
can be overridden by manually creating subscriber via vty, ctrl
interface or directly in HLR.

Change-Id: I525f4b80676de47d1d422686da2ca012301b0129
Fixes: OS#1647
---
M openbsc/include/openbsc/gsm_data.h
M openbsc/src/libbsc/bsc_vty.c
M openbsc/src/libbsc/net_init.c
M openbsc/src/libcommon/gsm_data.c
M openbsc/src/libmsc/gsm_04_08.c
M openbsc/src/libmsc/vty_interface_layer3.c
M openbsc/tests/vty_test_runner.py
7 files changed, 87 insertions(+), 11 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/openbsc refs/changes/04/104/1

Comments

gerrit-no-reply@lists.osmocom.org May 24, 2016, 1:54 p.m. UTC | #1
Patch Set 1: Code-Review+1
gerrit-no-reply@lists.osmocom.org May 25, 2016, 7:52 p.m. UTC | #2
Patch Set 1: Code-Review-1

(4 comments)

https://gerrit.osmocom.org/#/c/104/1/openbsc/src/libbsc/bsc_vty.c
File openbsc/src/libbsc/bsc_vty.c:

Line 1411:       "Use regular expression for IMSI authorization decision\n"
tabs vs. spaces. Yes the rest is inconsistent but please use tabs here.


https://gerrit.osmocom.org/#/c/104/1/openbsc/src/libbsc/net_init.c
File openbsc/src/libbsc/net_init.c:

Line 52: 	net->authorized_reg_str = NULL;
you can omit it. We rely on talloc_zero for many of these things. You don't need to omit it though.


https://gerrit.osmocom.org/#/c/104/1/openbsc/src/libmsc/gsm_04_08.c
File openbsc/src/libmsc/gsm_04_08.c:

Line 255: 	if (regexec(&net->authorized_regexp, imsi, 0, NULL, 0) != REG_NOMATCH)
just return it?


Line 538: 	return subscr_create_subscriber(net->subscr_group, imsi);
Do you want to have authorized = 1; here as well or rely on the regexp being called every time?
gerrit-no-reply@lists.osmocom.org June 2, 2016, 11:40 a.m. UTC | #3
Patch Set 2: Code-Review+2

Patch
diff mbox

diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index a08938c..31a4ed5 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -2,6 +2,8 @@ 
 #define _GSM_DATA_H
 
 #include <stdint.h>
+#include <regex.h>
+#include <sys/types.h>
 
 #include <osmocom/core/timer.h>
 #include <osmocom/core/select.h>
@@ -22,6 +24,7 @@ 
 enum gsm_subscr_creation_mode {
 	GSM_SUBSCR_DONT_CREATE = 0,
 	GSM_SUBSCR_CREAT_W_RAND_EXT = 1,
+	GSM_SUBSCR_CREAT_W_REGEXP = 2,
 };
 
 enum gsm_security_event {
@@ -205,6 +208,7 @@ 
 	GSM_AUTH_POLICY_CLOSED, /* only subscribers authorized in DB */
 	GSM_AUTH_POLICY_ACCEPT_ALL, /* accept everyone, even if not authorized in DB */
 	GSM_AUTH_POLICY_TOKEN, /* accept first, send token per sms, then revoke authorization */
+	GSM_AUTH_POLICY_REGEXP, /* accept IMSIs matching given regexp */
 };
 
 #define GSM_T3101_DEFAULT 10
@@ -219,6 +223,8 @@ 
 	char *name_long;
 	char *name_short;
 	enum gsm_auth_policy auth_policy;
+	regex_t authorized_regexp;
+	char *authorized_reg_str;
 	enum gsm48_reject_value reject_cause;
 	int a5_encryption;
 	int neci;
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index 46ad457..66c9107 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -190,8 +190,11 @@ 
 		net->name_long, VTY_NEWLINE);
 	vty_out(vty, "  Short network name: '%s'%s",
 		net->name_short, VTY_NEWLINE);
-	vty_out(vty, "  Authentication policy: %s%s",
-		gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE);
+	vty_out(vty, "  Authentication policy: %s",
+		gsm_auth_policy_name(net->auth_policy));
+	if (net->authorized_reg_str)
+		vty_out(vty, ", authorized regexp: %s", net->authorized_reg_str);
+	vty_out(vty, "%s", VTY_NEWLINE);
 	vty_out(vty, "  Location updating reject cause: %u%s",
 		net->reject_cause, VTY_NEWLINE);
 	vty_out(vty, "  Encryption: A5/%u%s", net->a5_encryption,
@@ -791,6 +794,8 @@ 
 	vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE);
 	vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE);
 	vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
+	if (gsmnet->authorized_reg_str)
+		vty_out(vty, " authorized-regexp %s%s", gsmnet->authorized_reg_str, VTY_NEWLINE);
 	vty_out(vty, " location updating reject cause %u%s",
 		gsmnet->reject_cause, VTY_NEWLINE);
 	vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
@@ -1398,17 +1403,34 @@ 
 
 DEFUN(cfg_net_auth_policy,
       cfg_net_auth_policy_cmd,
-      "auth policy (closed|accept-all|token)",
+      "auth policy (closed|accept-all|regexp|token)",
 	"Authentication (not cryptographic)\n"
 	"Set the GSM network authentication policy\n"
 	"Require the MS to be activated in HLR\n"
 	"Accept all MS, whether in HLR or not\n"
+      "Use regular expression for IMSI authorization decision\n"
 	"Use SMS-token based authentication\n")
 {
 	enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]);
 	struct gsm_network *gsmnet = gsmnet_from_vty(vty);
 
 	gsmnet->auth_policy = policy;
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_authorize_regexp, cfg_net_authorize_regexp_cmd,
+      "authorized-regexp REGEXP",
+      "Set regexp for IMSI which will be used for authorization decision\n"
+      "Regular expression, IMSIs matching it are allowed to use the network\n")
+{
+	struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+	if (gsm_parse_reg(gsmnet, &gsmnet->authorized_regexp,
+			  &gsmnet->authorized_reg_str, argc, argv) != 0) {
+		vty_out(vty, "%%Failed to parse the authorized-regexp: '%s'%s",
+			argv[0], VTY_NEWLINE);
+		return CMD_WARNING;
+	}
 
 	return CMD_SUCCESS;
 }
@@ -3973,6 +3995,7 @@ 
 	install_element(GSMNET_NODE, &cfg_net_name_short_cmd);
 	install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
 	install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
+	install_element(GSMNET_NODE, &cfg_net_authorize_regexp_cmd);
 	install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd);
 	install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
 	install_element(GSMNET_NODE, &cfg_net_neci_cmd);
diff --git a/openbsc/src/libbsc/net_init.c b/openbsc/src/libbsc/net_init.c
index afcaaf3..caf134a 100644
--- a/openbsc/src/libbsc/net_init.c
+++ b/openbsc/src/libbsc/net_init.c
@@ -49,7 +49,7 @@ 
 
 	net->subscr_group->net = net;
 	net->subscr_creation_mode = GSM_SUBSCR_CREAT_W_RAND_EXT;
-
+	net->authorized_reg_str = NULL;
 	net->country_code = country_code;
 	net->network_code = network_code;
 	net->num_bts = 0;
diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c
index 4e235fd..9d794ee 100644
--- a/openbsc/src/libcommon/gsm_data.c
+++ b/openbsc/src/libcommon/gsm_data.c
@@ -162,6 +162,7 @@ 
 	{ GSM_AUTH_POLICY_CLOSED,	"closed" },
 	{ GSM_AUTH_POLICY_ACCEPT_ALL,	"accept-all" },
 	{ GSM_AUTH_POLICY_TOKEN,	"token" },
+	{ GSM_AUTH_POLICY_REGEXP,	"regexp" },
 	{ 0,				NULL }
 };
 
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index 74da34b..1f948b2 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -25,9 +25,12 @@ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdbool.h>
 #include <errno.h>
 #include <time.h>
 #include <netinet/in.h>
+#include <regex.h>
+#include <sys/types.h>
 
 #include "bscconfig.h"
 
@@ -244,6 +247,17 @@ 
 	return -EINVAL; /* not reached */
 }
 
+static bool subscr_regexp_check(const struct gsm_network *net, const char *imsi)
+{
+	if (!net->authorized_reg_str)
+		return false;
+
+	if (regexec(&net->authorized_regexp, imsi, 0, NULL, 0) != REG_NOMATCH)
+		return true;
+
+	return false;
+}
+
 static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
 				struct gsm_subscriber *subscriber)
 {
@@ -261,6 +275,11 @@ 
 	switch (subscriber->group->net->auth_policy) {
 	case GSM_AUTH_POLICY_CLOSED:
 		return subscriber->authorized;
+	case GSM_AUTH_POLICY_REGEXP:
+		if (subscriber->authorized)
+			return 1;
+		return subscr_regexp_check(subscriber->group->net,
+					   subscriber->imsi);
 	case GSM_AUTH_POLICY_TOKEN:
 		if (subscriber->authorized)
 			return subscriber->authorized;
@@ -509,10 +528,14 @@ 
 static struct gsm_subscriber *subscr_create(const struct gsm_network *net,
 					    const char *imsi)
 {
-	if (net->subscr_creation_mode != GSM_SUBSCR_DONT_CREATE)
-		return subscr_create_subscriber(net->subscr_group, imsi);
+	if (net->subscr_creation_mode == GSM_SUBSCR_DONT_CREATE)
+		return NULL;
 
-	return NULL;
+	if (net->subscr_creation_mode & GSM_SUBSCR_CREAT_W_REGEXP)
+		if (!subscr_regexp_check(net, imsi))
+			return NULL;
+
+	return subscr_create_subscriber(net->subscr_group, imsi);
 }
 
 /* Parse Chapter 9.2.11 Identity Response */
diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c
index 5d74e04..3f67b9a 100644
--- a/openbsc/src/libmsc/vty_interface_layer3.c
+++ b/openbsc/src/libmsc/vty_interface_layer3.c
@@ -1032,11 +1032,15 @@ 
 }
 
 DEFUN(cfg_nitb_subscr_create, cfg_nitb_subscr_create_cmd,
-      "subscriber-create-on-demand",
-      "Make a new record when a subscriber is first seen.\n")
+      "subscriber-create-on-demand [regexp]",
+      "Make a new record when a subscriber is first seen.\n"
+      "Create subscribers only if IMSI matches the regexp specified in "
+      "authorized-regexp command\n")
 {
 	struct gsm_network *gsmnet = gsmnet_from_vty(vty);
 	gsmnet->subscr_creation_mode = GSM_SUBSCR_CREAT_W_RAND_EXT;
+	if (argc)
+		gsmnet->subscr_creation_mode |= GSM_SUBSCR_CREAT_W_REGEXP;
 	return CMD_SUCCESS;
 }
 
@@ -1070,9 +1074,12 @@ 
 static int config_write_nitb(struct vty *vty)
 {
 	struct gsm_network *gsmnet = gsmnet_from_vty(vty);
+	enum gsm_subscr_creation_mode scm = gsmnet->subscr_creation_mode;
+	const char *reg = (scm & GSM_SUBSCR_CREAT_W_REGEXP) ? " regexp" : "",
+		*pref = scm ? "" : "no ";
 	vty_out(vty, "nitb%s", VTY_NEWLINE);
-	vty_out(vty, " %ssubscriber-create-on-demand%s",
-		gsmnet->subscr_creation_mode ? "" : "no ", VTY_NEWLINE);
+	vty_out(vty, " %ssubscriber-create-on-demand%s%s",
+		pref, reg, VTY_NEWLINE);
 	vty_out(vty, " %sassign-tmsi%s",
 		gsmnet->avoid_tmsi ? "no " : "", VTY_NEWLINE);
 	return CMD_SUCCESS;
diff --git a/openbsc/tests/vty_test_runner.py b/openbsc/tests/vty_test_runner.py
index c088855..c264328 100644
--- a/openbsc/tests/vty_test_runner.py
+++ b/openbsc/tests/vty_test_runner.py
@@ -231,6 +231,22 @@ 
 
         self.assertEquals(self.vty.node(), 'config-mncc-int')
 
+    def testVtyAuthorization(self):
+        self.vty.enable()
+        self.vty.command("configure terminal")
+        self.vty.command("network")
+        self.assertTrue(self.vty.verify("auth policy closed", ['']))
+        self.assertTrue(self.vty.verify("auth policy regexp", ['']))
+        self.assertTrue(self.vty.verify("authorized-regexp ^001", ['']))
+        self.assertTrue(self.vty.verify("authorized-regexp 02$", ['']))
+        self.assertTrue(self.vty.verify("authorized-regexp *123.*", ['']))
+        self.vty.command("end")
+        self.vty.command("configure terminal")
+        self.vty.command("nitb")
+        self.assertTrue(self.vty.verify("subscriber-create-on-demand", ['']))
+        self.assertTrue(self.vty.verify("subscriber-create-on-demand regexp", ['']))
+        self.vty.command("end")
+
     def testSi2Q(self):
         self.vty.enable()
         self.vty.command("configure terminal")