From 762c39ccc65e0307a32b89e8bf7daaad2b6b66b4 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Fri, 30 Aug 2019 20:47:02 +0200 Subject: Move out sgsn to its own subdir Change-Id: I16fccc0eadf588599b9e5578d0f4dbaf9df81737 --- src/sgsn/sgsn_auth.c | 312 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 src/sgsn/sgsn_auth.c (limited to 'src/sgsn/sgsn_auth.c') diff --git a/src/sgsn/sgsn_auth.c b/src/sgsn/sgsn_auth.c new file mode 100644 index 000000000..b8d803590 --- /dev/null +++ b/src/sgsn/sgsn_auth.c @@ -0,0 +1,312 @@ +/* MS authorization and subscriber data handling */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +const struct value_string auth_state_names[] = { + { SGSN_AUTH_ACCEPTED, "accepted"}, + { SGSN_AUTH_REJECTED, "rejected"}, + { SGSN_AUTH_UNKNOWN, "unknown"}, + { SGSN_AUTH_AUTHENTICATE, "authenticate" }, + { SGSN_AUTH_UMTS_RESYNC, "UMTS-resync" }, + { 0, NULL } +}; + +const struct value_string *sgsn_auth_state_names = auth_state_names; + +void sgsn_auth_init(struct sgsn_instance *sgsn) +{ + INIT_LLIST_HEAD(&sgsn->cfg.imsi_acl); +} + +struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, const struct sgsn_config *cfg) +{ + struct imsi_acl_entry *acl; + llist_for_each_entry(acl, &cfg->imsi_acl, list) { + if (!strcmp(imsi, acl->imsi)) + return acl; + } + return NULL; +} + +int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg) +{ + struct imsi_acl_entry *acl; + + if (sgsn_acl_lookup(imsi, cfg)) + return -EEXIST; + + acl = talloc_zero(NULL, struct imsi_acl_entry); + if (!acl) + return -ENOMEM; + osmo_strlcpy(acl->imsi, imsi, sizeof(acl->imsi)); + + llist_add(&acl->list, &cfg->imsi_acl); + + return 0; +} + +int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg) +{ + struct imsi_acl_entry *acl; + + acl = sgsn_acl_lookup(imsi, cfg); + if (!acl) + return -ENODEV; + + llist_del(&acl->list); + talloc_free(acl); + + return 0; +} + +enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx) +{ + char mccmnc[16]; + int check_net = 0; + int check_acl = 0; + + OSMO_ASSERT(mmctx); + + switch (sgsn->cfg.auth_policy) { + case SGSN_AUTH_POLICY_OPEN: + return SGSN_AUTH_ACCEPTED; + + case SGSN_AUTH_POLICY_CLOSED: + check_net = 1; + check_acl = 1; + break; + + case SGSN_AUTH_POLICY_ACL_ONLY: + check_acl = 1; + break; + + case SGSN_AUTH_POLICY_REMOTE: + if (!mmctx->subscr) + return mmctx->auth_state; + + if (mmctx->subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK) + return mmctx->auth_state; + + if (sgsn->cfg.require_authentication && + (!sgsn_mm_ctx_is_authenticated(mmctx) || + mmctx->subscr->sgsn_data->auth_triplets_updated)) + return SGSN_AUTH_AUTHENTICATE; + + if (mmctx->subscr->authorized) + return SGSN_AUTH_ACCEPTED; + + return SGSN_AUTH_REJECTED; + } + + if (!strlen(mmctx->imsi)) { + LOGMMCTXP(LOGL_NOTICE, mmctx, + "Missing IMSI, authorization state not known\n"); + return SGSN_AUTH_UNKNOWN; + } + + if (check_net) { + /* We simply assume that the IMSI exists, as long as it is part + * of 'our' network */ + snprintf(mccmnc, sizeof(mccmnc), "%s%s", + osmo_mcc_name(mmctx->ra.mcc), + osmo_mnc_name(mmctx->ra.mnc, mmctx->ra.mnc_3_digits)); + if (strncmp(mccmnc, mmctx->imsi, mmctx->ra.mnc_3_digits ? 6 : 5) == 0) + return SGSN_AUTH_ACCEPTED; + } + + if (check_acl && sgsn_acl_lookup(mmctx->imsi, &sgsn->cfg)) + return SGSN_AUTH_ACCEPTED; + + 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 gprs_subscr *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) { + sgsn_auth_update(mmctx); + return 0; + } + + need_update_location = sgsn->cfg.require_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); + gprs_subscr_put(subscr); + + OSMO_ASSERT(mmctx->subscr != NULL); + + if (sgsn->cfg.require_authentication && !sgsn_mm_ctx_is_authenticated(mmctx)) { + /* 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, + "Requesting authentication tuples\n"); + rc = gprs_subscr_request_auth_info(mmctx, NULL, NULL); + 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"); + rc = gprs_subscr_request_update_location(mmctx); + if (rc >= 0) + return 0; + + return rc; + } + + sgsn_auth_update(mmctx); + return 0; +} + +void sgsn_auth_update(struct sgsn_mm_ctx *mmctx) +{ + enum sgsn_auth_state auth_state; + struct gprs_subscr *subscr = mmctx->subscr; + struct gsm_auth_tuple *at; + int gmm_cause; + + auth_state = sgsn_auth_state(mmctx); + + LOGMMCTXP(LOGL_DEBUG, mmctx, "Updating authorization (%s -> %s)\n", + get_value_string(sgsn_auth_state_names, mmctx->auth_state), + get_value_string(sgsn_auth_state_names, auth_state)); + + if (auth_state == SGSN_AUTH_UNKNOWN && subscr && + !(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; + + LOGMMCTXP(LOGL_INFO, mmctx, "Got authorization update: state %s -> %s\n", + get_value_string(sgsn_auth_state_names, mmctx->auth_state), + get_value_string(sgsn_auth_state_names, auth_state)); + + mmctx->auth_state = auth_state; + + 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: + gsm0408_gprs_access_granted(mmctx); + break; + case SGSN_AUTH_REJECTED: + gmm_cause = + subscr ? subscr->sgsn_data->error_cause : + SGSN_ERROR_CAUSE_NONE; + + if (subscr && (subscr->flags & GPRS_SUBSCRIBER_CANCELLED) != 0) + gsm0408_gprs_access_cancelled(mmctx, gmm_cause); + else + gsm0408_gprs_access_denied(mmctx, gmm_cause); + break; + default: + break; + } +} + +struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx, + unsigned key_seq) +{ + unsigned count; + unsigned idx; + struct gsm_auth_tuple *at = NULL; + + struct sgsn_subscriber_data *sdata; + + if (!mmctx->subscr) + return NULL; + + if (key_seq == GSM_KEY_SEQ_INVAL) + /* Start with 0 after increment module array size */ + idx = ARRAY_SIZE(sdata->auth_triplets) - 1; + else + idx = key_seq; + + sdata = mmctx->subscr->sgsn_data; + + /* Find next tuple */ + for (count = ARRAY_SIZE(sdata->auth_triplets); count > 0; count--) { + idx = (idx + 1) % ARRAY_SIZE(sdata->auth_triplets); + + if (sdata->auth_triplets[idx].key_seq == GSM_KEY_SEQ_INVAL) + continue; + + if (sdata->auth_triplets[idx].use_count == 0) { + at = &sdata->auth_triplets[idx]; + at->use_count = 1; + return at; + } + } + + return NULL; +} -- cgit v1.2.3