aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2014-11-28 14:55:25 +0100
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2014-12-24 16:50:30 +0100
commit98a95ac17f701d94d2fa62d91cfe42a5beef4f23 (patch)
treea41eba64f19692721521dc927b258387d9151828 /openbsc/src
parent7921ab1593f45f12588e074ad1280472416ae930 (diff)
sgsn: Add a subscriber based authentication phase
This implements the MAP way of subscriber validation when the MS tries to perform an Attach Request: 1. perform authentication (optionally invoke the sendAuthInfo procedure), starts the Auth & Ciph procedure 2. perform update location 3. insert subscriber data 4. finish the update location 5. Attach Accept / Attach Reject The authentication triplets are used and eventually updated if all of them have been used. This is currently accessible via the VTY interface by the following commands: - update-subscriber imsi IMSI update-auth-info - update-subscriber imsi IMSI update-location-result (ok|ERR-CAUSE) Sponsored-by: On-Waves ehf
Diffstat (limited to 'openbsc/src')
-rw-r--r--openbsc/src/gprs/gprs_subscriber.c91
-rw-r--r--openbsc/src/gprs/sgsn_auth.c79
-rw-r--r--openbsc/src/gprs/sgsn_vty.c72
3 files changed, 200 insertions, 42 deletions
diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c
index 16753315c..1f210298f 100644
--- a/openbsc/src/gprs/gprs_subscriber.c
+++ b/openbsc/src/gprs/gprs_subscriber.c
@@ -92,9 +92,18 @@ void gprs_subscr_put_and_cancel(struct gsm_subscriber *subscr)
gprs_subscr_delete(subscr);
}
-int gprs_subscr_query(struct gsm_subscriber *subscr)
+int gprs_subscr_query_auth_info(struct gsm_subscriber *subscr)
{
- /* TODO: Implement remote query to MSC, ... */
+ /* TODO: Implement remote query to HLR, ... */
+
+ LOGMMCTXP(LOGL_INFO, subscr->sgsn_data->mm,
+ "subscriber auth info is not available (remote query NYI)\n");
+ return -ENOTSUP;
+}
+
+int gprs_subscr_location_update(struct gsm_subscriber *subscr)
+{
+ /* TODO: Implement remote query to HLR, ... */
LOGMMCTXP(LOGL_INFO, subscr->sgsn_data->mm,
"subscriber data is not available (remote query NYI)\n");
@@ -105,56 +114,80 @@ void gprs_subscr_update(struct gsm_subscriber *subscr)
{
LOGMMCTXP(LOGL_DEBUG, subscr->sgsn_data->mm, "Updating subscriber data\n");
- subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_PENDING;
+ subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING;
+ subscr->flags &= ~GSM_SUBSCRIBER_FIRST_CONTACT;
+
+ sgsn_update_subscriber_data(subscr->sgsn_data->mm, subscr);
+}
+
+void gprs_subscr_update_auth_info(struct gsm_subscriber *subscr)
+{
+ LOGMMCTXP(LOGL_DEBUG, subscr->sgsn_data->mm,
+ "Updating subscriber authentication info\n");
+
+ subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING;
subscr->flags &= ~GSM_SUBSCRIBER_FIRST_CONTACT;
sgsn_update_subscriber_data(subscr->sgsn_data->mm, subscr);
}
-int gprs_subscr_request_update(struct sgsn_mm_ctx *mmctx)
+struct gsm_subscriber *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx)
{
struct gsm_subscriber *subscr = NULL;
- int need_update = 0;
- int rc;
- LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber data update\n");
+ if (mmctx->subscr)
+ return subscr_get(mmctx->subscr);
- if (mmctx->subscr) {
- subscr = subscr_get(mmctx->subscr);
- } else if (mmctx->imsi[0]) {
+ if (mmctx->imsi[0])
subscr = gprs_subscr_get_by_imsi(mmctx->imsi);
- need_update = 1;
- }
if (!subscr) {
subscr = gprs_subscr_get_or_create(mmctx->imsi);
subscr->flags |= GSM_SUBSCRIBER_FIRST_CONTACT;
- need_update = 1;
}
if (strcpy(subscr->equipment.imei, mmctx->imei) != 0) {
strncpy(subscr->equipment.imei, mmctx->imei, GSM_IMEI_LENGTH-1);
subscr->equipment.imei[GSM_IMEI_LENGTH-1] = 0;
- need_update = 1;
}
- if (subscr->lac != mmctx->ra.lac) {
+ if (subscr->lac != mmctx->ra.lac)
subscr->lac = mmctx->ra.lac;
- need_update = 1;
- }
- if (need_update) {
- subscr->flags |= GPRS_SUBSCRIBER_UPDATE_PENDING;
- if (!mmctx->subscr) {
- subscr->sgsn_data->mm = mmctx;
- mmctx->subscr = subscr_get(subscr);
- }
+ subscr->sgsn_data->mm = mmctx;
+ mmctx->subscr = subscr_get(subscr);
- rc = gprs_subscr_query(subscr);
- subscr_put(subscr);
- return rc;
- }
- gprs_subscr_update(subscr);
+ return subscr;
+}
+
+int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx)
+{
+ struct gsm_subscriber *subscr = NULL;
+ int rc;
+
+ LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber data update\n");
+
+ subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
+
+ subscr->flags |= GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING;
+
+ rc = gprs_subscr_location_update(subscr);
+ subscr_put(subscr);
+ return rc;
+}
+
+int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx)
+{
+ struct gsm_subscriber *subscr = NULL;
+ int rc;
+
+ LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber authentication info\n");
+
+ subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
+
+ subscr->flags |= GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING;
+
+ rc = gprs_subscr_query_auth_info(subscr);
subscr_put(subscr);
- return 0;
+ return rc;
}
diff --git a/openbsc/src/gprs/sgsn_auth.c b/openbsc/src/gprs/sgsn_auth.c
index b065c061a..3755a51d4 100644
--- a/openbsc/src/gprs/sgsn_auth.c
+++ b/openbsc/src/gprs/sgsn_auth.c
@@ -23,7 +23,7 @@
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gsm_subscriber.h>
-
+#include <openbsc/gsm_04_08_gprs.h>
#include <openbsc/debug.h>
const struct value_string auth_state_names[] = {
@@ -107,11 +107,12 @@ enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx)
if (!mmctx->subscr)
return mmctx->auth_state;
- if (mmctx->subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING)
+ if (mmctx->subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK)
return mmctx->auth_state;
if (mmctx->subscr->sgsn_data->authenticate &&
- !mmctx->is_authenticated)
+ (!mmctx->is_authenticated ||
+ mmctx->subscr->sgsn_data->auth_triplets_updated))
return SGSN_AUTH_AUTHENTICATE;
if (mmctx->subscr->authorized)
@@ -141,20 +142,60 @@ enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx)
return SGSN_AUTH_REJECTED;
}
+/*
+ * This function is directly called by e.g. the GMM layer. It returns either
+ * after calling sgsn_auth_update directly or after triggering an asynchronous
+ * procedure which will call sgsn_auth_update later on.
+ */
int sgsn_auth_request(struct sgsn_mm_ctx *mmctx)
{
+ struct gsm_subscriber *subscr;
+ struct gsm_auth_tuple *at;
+ int need_update_location;
+ int rc;
+
LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting authorization\n");
- if (sgsn->cfg.auth_policy == SGSN_AUTH_POLICY_REMOTE && !mmctx->subscr) {
- if (gprs_subscr_request_update(mmctx) >= 0) {
+ if (sgsn->cfg.auth_policy != SGSN_AUTH_POLICY_REMOTE) {
+ sgsn_auth_update(mmctx);
+ return 0;
+ }
+
+ need_update_location =
+ mmctx->subscr == NULL ||
+ mmctx->pending_req == GSM48_MT_GMM_ATTACH_REQ;
+
+ /* This has the side effect of registering the subscr with the mmctx */
+ subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
+ subscr_put(subscr);
+
+ OSMO_ASSERT(mmctx->subscr != NULL);
+
+ if (mmctx->subscr->sgsn_data->authenticate && !mmctx->is_authenticated) {
+ /* Find next tuple */
+ at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq);
+
+ if (!at) {
+ /* No valid tuple found, request fresh ones */
+ mmctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
LOGMMCTXP(LOGL_INFO, mmctx,
- "Missing information, requesting subscriber data\n");
- return 0;
+ "Requesting authentication tuples\n");
+ rc = gprs_subscr_request_auth_info(mmctx);
+ if (rc >= 0)
+ return 0;
+
+ return rc;
}
+
+ mmctx->auth_triplet = *at;
+ } else if (need_update_location) {
+ LOGMMCTXP(LOGL_INFO, mmctx,
+ "Missing information, requesting subscriber data\n");
+ if (gprs_subscr_request_update_location(mmctx) >= 0)
+ return 0;
}
sgsn_auth_update(mmctx);
-
return 0;
}
@@ -162,6 +203,7 @@ void sgsn_auth_update(struct sgsn_mm_ctx *mmctx)
{
enum sgsn_auth_state auth_state;
struct gsm_subscriber *subscr = mmctx->subscr;
+ struct gsm_auth_tuple *at;
auth_state = sgsn_auth_state(mmctx);
@@ -170,13 +212,27 @@ void sgsn_auth_update(struct sgsn_mm_ctx *mmctx)
get_value_string(sgsn_auth_state_names, auth_state));
if (auth_state == SGSN_AUTH_UNKNOWN && subscr &&
- !(subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING)) {
- /* Reject requests if gprs_subscr_request_update fails */
+ !(subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK)) {
+ /* Reject requests if gprs_subscr_request_update_location fails */
LOGMMCTXP(LOGL_ERROR, mmctx,
"Missing information, authorization not possible\n");
auth_state = SGSN_AUTH_REJECTED;
}
+ if (auth_state == SGSN_AUTH_AUTHENTICATE &&
+ mmctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) {
+ /* The current tuple is not valid, but we are possibly called
+ * because new auth tuples have been received */
+ at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq);
+ if (!at) {
+ LOGMMCTXP(LOGL_ERROR, mmctx,
+ "Missing auth tuples, authorization not possible\n");
+ auth_state = SGSN_AUTH_REJECTED;
+ } else {
+ mmctx->auth_triplet = *at;
+ }
+ }
+
if (mmctx->auth_state == auth_state)
return;
@@ -188,6 +244,9 @@ void sgsn_auth_update(struct sgsn_mm_ctx *mmctx)
switch (auth_state) {
case SGSN_AUTH_AUTHENTICATE:
+ if (subscr)
+ subscr->sgsn_data->auth_triplets_updated = 0;
+
gsm0408_gprs_authenticate(mmctx);
break;
case SGSN_AUTH_ACCEPTED:
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
index 99c59853b..9b925a8cb 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -428,13 +428,15 @@ static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr,
}
if (subscr->flags)
- vty_out(vty, " Flags: %s%s%s%s",
+ vty_out(vty, " Flags: %s%s%s%s%s",
subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ?
"FIRST_CONTACT " : "",
subscr->flags & GPRS_SUBSCRIBER_CANCELLED ?
"CANCELLED " : "",
- subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING ?
- "UPDATE_PENDING " : "",
+ subscr->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ?
+ "UPDATE_LOCATION_PENDING " : "",
+ subscr->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ?
+ "AUTH_INFO_PENDING " : "",
VTY_NEWLINE);
vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
@@ -587,6 +589,68 @@ DEFUN(update_subscr_commit, update_subscr_commit_cmd,
return CMD_SUCCESS;
}
+#define UL_ERR_STR "system-failure|data-missing|unexpected-data-value|" \
+ "unknown-subscriber|roaming-not-allowed"
+
+#define UL_ERR_HELP \
+ "Force error code SystemFailure\n" \
+ "Force error code DataMissing\n" \
+ "Force error code UnexpectedDataValue\n" \
+ "Force error code UnknownSubscriber\n" \
+ "Force error code RoamingNotAllowed\n"
+
+DEFUN(update_subscr_update_location_result, update_subscr_update_location_result_cmd,
+ UPDATE_SUBSCR_STR "update-location-result (ok|" UL_ERR_STR ")",
+ UPDATE_SUBSCR_HELP
+ "Complete the update location procedure\n"
+ "The update location request succeeded\n"
+ UL_ERR_HELP)
+{
+ const char *imsi = argv[0];
+ const char *ret_code_str = argv[1];
+
+ struct gsm_subscriber *subscr;
+
+ subscr = gprs_subscr_get_by_imsi(imsi);
+ if (!subscr) {
+ vty_out(vty, "%% unable to get subscriber record for %s\n", imsi);
+ return CMD_WARNING;
+ }
+ if (strcmp(ret_code_str, "ok") == 0)
+ subscr->authorized = 1;
+ else
+ subscr->authorized = 0;
+
+ gprs_subscr_update(subscr);
+
+ subscr_put(subscr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd,
+ UPDATE_SUBSCR_STR "update-auth-info",
+ UPDATE_SUBSCR_HELP
+ "Complete the send authentication info procedure\n")
+{
+ const char *imsi = argv[0];
+
+ struct gsm_subscriber *subscr;
+
+ subscr = gprs_subscr_get_by_imsi(imsi);
+ if (!subscr) {
+ vty_out(vty, "%% unable to get subscriber record for %s\n", imsi);
+ return CMD_WARNING;
+ }
+
+ gprs_subscr_update_auth_info(subscr);
+
+ subscr_put(subscr);
+
+ return CMD_SUCCESS;
+}
+
+
int sgsn_vty_init(void)
{
install_element_ve(&show_sgsn_cmd);
@@ -600,6 +664,8 @@ int sgsn_vty_init(void)
install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd);
install_element(ENABLE_NODE, &update_subscr_cancel_cmd);
install_element(ENABLE_NODE, &update_subscr_commit_cmd);
+ install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd);
+ install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd);
install_element(CONFIG_NODE, &cfg_sgsn_cmd);
install_node(&sgsn_node, config_write_sgsn);