summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2014-10-24 18:09:54 +0200
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2014-11-14 09:58:28 +0100
commit423f8bfa027d544867dcf7afc188a8ea5221e2bc (patch)
tree1a757951e6e28872c05e47c0bd25b50845e2de42
parent0c06f98ced2b70801147eb52d6596079e473dfc9 (diff)
sgsn: Make authorization asynchronous
Currently the authorization of an IMSI is done by checking ACLs synchronously which is not feasible when the subscriber data has to be retrieved from an external source. This patch changes this by using a callback when the information is available. This is also done when only ACL are checked, in this case the callback is invoked from within sgsn_auth_request(). The callback function sgsn_update_subscriber_data calls sgsn_auth_update which in turn calls either gsm0408_gprs_access_granted or gsm0408_gprs_access_denied. gsm48_gmm_authorize is extended by a call to sgsn_auth_request when IMSI and IMEI are available but the auth_state is unknown. The change has been successfully tested with single phones (E71 and IPhone 5c). Sponsored-by: On-Waves ehf
-rw-r--r--openbsc/include/openbsc/gprs_gmm.h2
-rw-r--r--openbsc/include/openbsc/gprs_sgsn.h25
-rw-r--r--openbsc/src/gprs/gprs_gmm.c78
-rw-r--r--openbsc/src/gprs/gprs_sgsn.c8
-rw-r--r--openbsc/src/gprs/sgsn_auth.c73
5 files changed, 166 insertions, 20 deletions
diff --git a/openbsc/include/openbsc/gprs_gmm.h b/openbsc/include/openbsc/gprs_gmm.h
index 0a7c5b355..e22706b84 100644
--- a/openbsc/include/openbsc/gprs_gmm.h
+++ b/openbsc/include/openbsc/gprs_gmm.h
@@ -13,6 +13,8 @@ int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp);
int gsm0408_gprs_rcvmsg(struct msgb *msg, struct gprs_llc_llme *llme);
int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx);
int gsm0408_gprs_force_reattach_oldmsg(struct msgb *msg);
+void gsm0408_gprs_access_granted(struct sgsn_mm_ctx *mmctx);
+void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *mmctx);
int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli);
int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli,
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index af21eaddb..0cb722067 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -54,6 +54,13 @@ enum gprs_t3350_mode {
GMM_T3350_MODE_PTMSI_REALL,
};
+/* Authorization/ACL handling */
+enum sgsn_auth_state {
+ SGSN_AUTH_UNKNOWN,
+ SGSN_AUTH_ACCEPTED,
+ SGSN_AUTH_REJECTED
+};
+
#define MS_RADIO_ACCESS_CAPA
/* According to TS 03.60, Table 5: SGSN MM and PDP Contexts */
@@ -118,7 +125,7 @@ struct sgsn_mm_ctx {
* where mm->T == 3350 => mm->t3350_mode == f(mm->pending_req). Check
* whether one of them can be dropped. */
- int is_authorized;
+ enum sgsn_auth_state auth_state;
};
#define LOGMMCTXP(level, mm, fmt, args...) \
@@ -249,12 +256,17 @@ struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *, uint16_t port);
int sgsn_ctrl_cmds_install(void);
/*
- * ACL handling
+ * Authorization/ACL handling
*/
struct imsi_acl_entry {
struct llist_head list;
char imsi[16+1];
};
+
+struct sgsn_subscriber_data {
+ enum sgsn_auth_state auth_state;
+};
+
struct sgsn_config;
struct sgsn_instance;
@@ -262,6 +274,15 @@ void sgsn_auth_init(struct sgsn_instance *sgi);
struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, struct sgsn_config *cfg);
int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg);
int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg);
+/* Request authorization */
+int sgsn_auth_request(struct sgsn_mm_ctx *mm, struct sgsn_config *cfg);
+enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mm,
+ struct sgsn_config *cfg);
+void sgsn_auth_update(struct sgsn_mm_ctx *mm, struct sgsn_subscriber_data *sd);
+
+/* Called on subscriber data updates */
+void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx,
+ struct sgsn_subscriber_data *sd);
int gprs_sndcp_vty_init(void);
struct sgsn_instance;
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 95bf73b5a..a966949a4 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -642,24 +642,22 @@ static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx)
/* All information required for authentication is available */
ctx->t3370_id_type = GSM_MI_TYPE_NONE;
- if (!ctx->is_authorized) {
- /* As a temorary hack, we simply assume that the IMSI exists,
- * as long as it is part of 'our' network */
- char mccmnc[16];
- int rc;
- snprintf(mccmnc, sizeof(mccmnc), "%03d%02d",
- ctx->ra.mcc, ctx->ra.mnc);
- if (strncmp(mccmnc, ctx->imsi, 5) &&
- (sgsn->cfg.acl_enabled &&
- !sgsn_acl_lookup(ctx->imsi, &sgsn->cfg))) {
- LOGP(DMM, LOGL_NOTICE, "Rejecting ATTACH REQUEST IMSI=%s\n",
- ctx->imsi);
- rc = gsm48_tx_gmm_att_rej(ctx,
- GMM_CAUSE_GPRS_NOTALLOWED);
- mm_ctx_cleanup_free(ctx, "ATTACH REJECT");
- return rc;
- }
- ctx->is_authorized = 1;
+ if (ctx->auth_state == SGSN_AUTH_UNKNOWN) {
+ /* Request authorization, this leads to a call to
+ * sgsn_update_subscriber_data which in turn calls
+ * gsm0408_gprs_access_granted or gsm0408_gprs_access_denied */
+
+ sgsn_auth_request(ctx, &sgsn->cfg);
+ /* Note that gsm48_gmm_authorize can be called recursively via
+ * sgsn_auth_request iff ctx->auth_info changes to AUTH_ACCEPTED
+ */
+ return 0;
+ }
+
+ if (ctx->auth_state != SGSN_AUTH_ACCEPTED) {
+ LOGMMCTXP(LOGL_NOTICE, ctx,
+ "authorization is denied, aborting procedure\n");
+ return -EACCES;
}
/* The MS is authorized */
@@ -689,6 +687,50 @@ static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx)
return 0;
}
+void gsm0408_gprs_access_granted(struct sgsn_mm_ctx *ctx)
+{
+ switch (ctx->mm_state) {
+ case GMM_COMMON_PROC_INIT:
+ LOGP(DMM, LOGL_NOTICE,
+ "Authorized, continuing procedure, IMSI=%s\n",
+ ctx->imsi);
+ /* Continue with the authorization */
+ gsm48_gmm_authorize(ctx);
+ break;
+ default:
+ LOGP(DMM, LOGL_INFO,
+ "Authorized, ignored, IMSI=%s\n",
+ ctx->imsi);
+ }
+}
+
+void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *ctx)
+{
+ switch (ctx->mm_state) {
+ case GMM_COMMON_PROC_INIT:
+ LOGP(DMM, LOGL_NOTICE,
+ "Not authorized, rejecting ATTACH REQUEST, IMSI=%s\n",
+ ctx->imsi);
+ gsm48_tx_gmm_att_rej(ctx, GMM_CAUSE_GPRS_NOTALLOWED);
+ mm_ctx_cleanup_free(ctx, "GPRS ATTACH REJECT");
+ break;
+ case GMM_REGISTERED_NORMAL:
+ case GMM_REGISTERED_SUSPENDED:
+ LOGP(DMM, LOGL_NOTICE,
+ "Authorization lost, detaching, IMSI=%s\n",
+ ctx->imsi);
+ gsm48_tx_gmm_detach_req(
+ ctx, GPRS_DET_T_MT_REATT_NOTREQ, GMM_CAUSE_GPRS_NOTALLOWED);
+
+ mm_ctx_cleanup_free(ctx, "auth lost");
+ break;
+ default:
+ LOGP(DMM, LOGL_INFO,
+ "Authorization lost, ignored, IMSI=%s\n",
+ ctx->imsi);
+ }
+}
+
/* Parse Chapter 9.4.13 Identity Response */
static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg)
{
diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c
index c4ff3c27d..daf9483bd 100644
--- a/openbsc/src/gprs/gprs_sgsn.c
+++ b/openbsc/src/gprs/gprs_sgsn.c
@@ -451,3 +451,11 @@ int sgsn_force_reattach_oldmsg(struct msgb *oldmsg)
return gsm0408_gprs_force_reattach_oldmsg(oldmsg);
}
+void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx,
+ struct sgsn_subscriber_data *sd)
+{
+ OSMO_ASSERT(mmctx);
+
+ if (sd->auth_state != mmctx->auth_state)
+ sgsn_auth_update(mmctx, sd);
+}
diff --git a/openbsc/src/gprs/sgsn_auth.c b/openbsc/src/gprs/sgsn_auth.c
index 0a85934b0..e123909f2 100644
--- a/openbsc/src/gprs/sgsn_auth.c
+++ b/openbsc/src/gprs/sgsn_auth.c
@@ -21,6 +21,16 @@
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sgsn.h>
+#include <openbsc/gprs_gmm.h>
+
+#include <openbsc/debug.h>
+
+const struct value_string auth_state_names[] = {
+ { SGSN_AUTH_ACCEPTED, "accepted"},
+ { SGSN_AUTH_REJECTED, "rejected"},
+ { SGSN_AUTH_UNKNOWN, "unknown"},
+ { 0, NULL }
+};
void sgsn_auth_init(struct sgsn_instance *sgi)
{
@@ -69,3 +79,66 @@ int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg)
return 0;
}
+enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx,
+ struct sgsn_config *cfg)
+{
+ char mccmnc[16];
+
+ OSMO_ASSERT(mmctx);
+
+ if (!sgsn->cfg.acl_enabled)
+ return SGSN_AUTH_ACCEPTED;
+
+ if (!strlen(mmctx->imsi)) {
+ LOGMMCTXP(LOGL_NOTICE, mmctx,
+ "Missing IMSI, authorization state not known\n");
+ return SGSN_AUTH_UNKNOWN;
+ }
+
+ /* As a temorary hack, we simply assume that the IMSI exists,
+ * as long as it is part of 'our' network */
+ snprintf(mccmnc, sizeof(mccmnc), "%03d%02d", mmctx->ra.mcc, mmctx->ra.mnc);
+ if (strncmp(mccmnc, mmctx->imsi, 5) == 0)
+ return SGSN_AUTH_ACCEPTED;
+
+ if (sgsn_acl_lookup(mmctx->imsi, &sgsn->cfg))
+ return SGSN_AUTH_ACCEPTED;
+
+ return SGSN_AUTH_REJECTED;
+}
+
+int sgsn_auth_request(struct sgsn_mm_ctx *mmctx, struct sgsn_config *cfg)
+{
+ struct sgsn_subscriber_data sd = {0};
+
+ sd.auth_state = sgsn_auth_state(mmctx, cfg);
+
+ if (sd.auth_state == SGSN_AUTH_UNKNOWN) {
+ LOGMMCTXP(LOGL_ERROR, mmctx,
+ "Missing information, authorization not possible\n");
+ sd.auth_state = SGSN_AUTH_REJECTED;
+ }
+
+ /* This will call sgsn_auth_update if auth_state has changed */
+ sgsn_update_subscriber_data(mmctx, &sd);
+ return 0;
+}
+
+void sgsn_auth_update(struct sgsn_mm_ctx *mmctx, struct sgsn_subscriber_data *sd)
+{
+ LOGMMCTXP(LOGL_INFO, mmctx, "Got authorization update: state %s\n",
+ get_value_string(auth_state_names, sd->auth_state));
+
+ mmctx->auth_state = sd->auth_state;
+
+ switch (sd->auth_state) {
+ case SGSN_AUTH_ACCEPTED:
+ gsm0408_gprs_access_granted(mmctx);
+ break;
+ case SGSN_AUTH_REJECTED:
+ gsm0408_gprs_access_denied(mmctx);
+ break;
+ default:
+ break;
+ }
+}