diff options
author | Jacob Erlbeck <jerlbeck@sysmocom.de> | 2014-11-28 14:55:25 +0100 |
---|---|---|
committer | Holger Hans Peter Freyther <holger@moiji-mobile.com> | 2014-12-24 16:50:30 +0100 |
commit | 98a95ac17f701d94d2fa62d91cfe42a5beef4f23 (patch) | |
tree | a41eba64f19692721521dc927b258387d9151828 /openbsc/src | |
parent | 7921ab1593f45f12588e074ad1280472416ae930 (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.c | 91 | ||||
-rw-r--r-- | openbsc/src/gprs/sgsn_auth.c | 79 | ||||
-rw-r--r-- | openbsc/src/gprs/sgsn_vty.c | 72 |
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); |