start of osmo-epdg plugin
- simple gsup/ipa working - strongswan is requesting tuples via GSUP. - strongswan client can authenticate - SWu-IKEv2 can't authenticate ToDos: - gsup: disconnect/reconnect - gsup: failures cases - blocking queue needs to be cleaned up - fix coding style
This commit is contained in:
parent
e7e6a51fb1
commit
1c92c4d83e
|
@ -212,6 +212,7 @@ ARG_ENABL_SET([eap-peap], [enable EAP PEAP authentication module.])
|
|||
ARG_ENABL_SET([eap-tnc], [enable EAP TNC trusted network connect module.])
|
||||
ARG_ENABL_SET([eap-dynamic], [enable dynamic EAP proxy module.])
|
||||
ARG_ENABL_SET([eap-radius], [enable RADIUS proxy authentication module.])
|
||||
ARG_ENABL_SET([osmo-epdg], [enable osmo-epdg plugin.])
|
||||
ARG_ENABL_SET([ext-auth], [enable plugin calling an external authorization script.])
|
||||
ARG_ENABL_SET([ipseckey], [enable IPSECKEY authentication plugin.])
|
||||
ARG_ENABL_SET([keychain], [enables OS X Keychain Services credential set.])
|
||||
|
@ -431,6 +432,11 @@ if test x$eap_aka_3gpp2 = xtrue; then
|
|||
gmp=true;
|
||||
fi
|
||||
|
||||
if test x$osmo_epdg = xtrue; then
|
||||
simaka=true;
|
||||
eap_aka=true;
|
||||
fi
|
||||
|
||||
if test x$eap_aka = xtrue; then
|
||||
fips_prf=true;
|
||||
simaka=true;
|
||||
|
@ -1513,6 +1519,7 @@ ADD_PLUGIN([kernel-pfkey], [c charon starter nm cmd])
|
|||
ADD_PLUGIN([kernel-pfroute], [c charon starter nm cmd])
|
||||
ADD_PLUGIN([kernel-netlink], [c charon starter nm cmd])
|
||||
ADD_PLUGIN([resolve], [c charon cmd])
|
||||
ADD_PLUGIN([osmo-epdg], [c charon])
|
||||
ADD_PLUGIN([save-keys], [c])
|
||||
ADD_PLUGIN([socket-default], [c charon nm cmd])
|
||||
ADD_PLUGIN([socket-dynamic], [c charon cmd])
|
||||
|
@ -1720,6 +1727,7 @@ AM_CONDITIONAL(USE_EAP_PEAP, test x$eap_peap = xtrue)
|
|||
AM_CONDITIONAL(USE_EAP_TNC, test x$eap_tnc = xtrue)
|
||||
AM_CONDITIONAL(USE_EAP_DYNAMIC, test x$eap_dynamic = xtrue)
|
||||
AM_CONDITIONAL(USE_EAP_RADIUS, test x$eap_radius = xtrue)
|
||||
AM_CONDITIONAL(USE_OSMO_EPDG, test x$osmo_epdg = xtrue)
|
||||
AM_CONDITIONAL(USE_XAUTH_GENERIC, test x$xauth_generic = xtrue)
|
||||
AM_CONDITIONAL(USE_XAUTH_EAP, test x$xauth_eap = xtrue)
|
||||
AM_CONDITIONAL(USE_XAUTH_PAM, test x$xauth_pam = xtrue)
|
||||
|
@ -2005,6 +2013,7 @@ AC_CONFIG_FILES([
|
|||
src/libcharon/plugins/eap_peap/Makefile
|
||||
src/libcharon/plugins/eap_tnc/Makefile
|
||||
src/libcharon/plugins/eap_radius/Makefile
|
||||
src/libcharon/plugins/osmo_epdg/Makefile
|
||||
src/libcharon/plugins/xauth_generic/Makefile
|
||||
src/libcharon/plugins/xauth_eap/Makefile
|
||||
src/libcharon/plugins/xauth_pam/Makefile
|
||||
|
|
|
@ -467,6 +467,13 @@ if MONOLITHIC
|
|||
endif
|
||||
endif
|
||||
|
||||
if USE_OSMO_EPDG
|
||||
SUBDIRS += plugins/osmo_epdg
|
||||
if MONOLITHIC
|
||||
libcharon_la_LIBADD += plugins/osmo_epdg/libstrongswan-osmo-epdg.la
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_TLS
|
||||
if MONOLITHIC
|
||||
# otherwise this library is linked to eap_tls
|
||||
|
|
|
@ -949,6 +949,46 @@ METHOD(bus_t, ike_reestablish_post, void,
|
|||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
|
||||
METHOD(bus_t, eap_authorize, bool,
|
||||
private_bus_t *this, identification_t *id, bool final)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
ike_sa_t *ike_sa;
|
||||
entry_t *entry;
|
||||
bool keep, success = TRUE;
|
||||
|
||||
ike_sa = this->thread_sa->get(this->thread_sa);
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
enumerator = this->listeners->create_enumerator(this->listeners);
|
||||
while (enumerator->enumerate(enumerator, &entry))
|
||||
{
|
||||
if (entry->calling || !entry->listener->eap_authorize)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
entry->calling++;
|
||||
keep = entry->listener->eap_authorize(entry->listener, ike_sa, id,
|
||||
final, &success);
|
||||
entry->calling--;
|
||||
if (!keep)
|
||||
{
|
||||
unregister_listener(this, entry, enumerator);
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->mutex->unlock(this->mutex);
|
||||
if (!success)
|
||||
{
|
||||
alert(this, ALERT_EAP_AUTHORIZATION_FAILED);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
METHOD(bus_t, authorize, bool,
|
||||
private_bus_t *this, bool final)
|
||||
{
|
||||
|
@ -1154,6 +1194,7 @@ bus_t *bus_create()
|
|||
.child_rekey = _child_rekey,
|
||||
.children_migrate = _children_migrate,
|
||||
.authorize = _authorize,
|
||||
.eap_authorize = _eap_authorize,
|
||||
.narrow = _narrow,
|
||||
.assign_vips = _assign_vips,
|
||||
.handle_vips = _handle_vips,
|
||||
|
|
|
@ -157,6 +157,8 @@ enum alert_t {
|
|||
ALERT_CERT_EXCEEDED_PATH_LEN,
|
||||
/** Certificate rejected; other policy violation, certificate_t */
|
||||
ALERT_CERT_POLICY_VIOLATION,
|
||||
/** an eap_authorize() hook failed, no argument */
|
||||
ALERT_EAP_AUTHORIZATION_FAILED,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -332,6 +334,14 @@ struct bus_t {
|
|||
*/
|
||||
bool (*authorize)(bus_t *this, bool final);
|
||||
|
||||
/**
|
||||
* EAP authorization hook.
|
||||
*
|
||||
* @param final TRUE if this is the final invocation
|
||||
* @return TRUE to establish IKE_SA, FALSE to send AUTH_FAILED
|
||||
*/
|
||||
bool (*eap_authorize)(bus_t *this, identification_t *id, bool final);
|
||||
|
||||
/**
|
||||
* CHILD_SA traffic selector narrowing hook.
|
||||
*
|
||||
|
|
|
@ -238,6 +238,21 @@ struct listener_t {
|
|||
bool (*children_migrate)(listener_t *this, ike_sa_t *ike_sa,
|
||||
ike_sa_id_t *new, uint32_t unique);
|
||||
|
||||
/**
|
||||
* Hook called to invoke additional eap authorization rules.
|
||||
*
|
||||
* An eap authorization hook gets invoked only before sending the EAP_SUCCESS.
|
||||
*
|
||||
* @param ike_sa IKE_SA to authorize
|
||||
* @param id EAP identification
|
||||
* @param final TRUE if this is the final hook invocation
|
||||
* @param success set to TRUE to complete IKE_SA, FALSE abort
|
||||
* @return TRUE to stay registered, FALSE to unregister
|
||||
*/
|
||||
bool (*eap_authorize)(listener_t *this, ike_sa_t *ike_sa,
|
||||
identification_t *id,
|
||||
bool final, bool *success);
|
||||
|
||||
/**
|
||||
* Hook called to invoke additional authorization rules.
|
||||
*
|
||||
|
|
|
@ -116,6 +116,11 @@ struct private_eap_aka_server_t {
|
|||
* Did the client send a synchronize request?
|
||||
*/
|
||||
bool synchronized;
|
||||
|
||||
/**
|
||||
* Auth data contains porperty on EAP_SUCCESS.
|
||||
*/
|
||||
auth_cfg_t *auth;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -430,6 +435,8 @@ static status_t process_challenge(private_eap_aka_server_t *this,
|
|||
DBG1(DBG_IKE, "received RES does not match XRES");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
this->auth->add(this->auth, AUTH_RULE_EAP_IDENTITY, this->permanent->clone(this->permanent));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -643,6 +650,12 @@ METHOD(eap_method_t, get_type, eap_type_t,
|
|||
return EAP_AKA;
|
||||
}
|
||||
|
||||
METHOD(eap_method_t, get_auth, auth_cfg_t*,
|
||||
private_eap_aka_server_t *this)
|
||||
{
|
||||
return this->auth;
|
||||
}
|
||||
|
||||
METHOD(eap_method_t, get_msk, status_t,
|
||||
private_eap_aka_server_t *this, chunk_t *msk)
|
||||
{
|
||||
|
@ -677,6 +690,7 @@ METHOD(eap_method_t, destroy, void,
|
|||
{
|
||||
this->crypto->destroy(this->crypto);
|
||||
this->permanent->destroy(this->permanent);
|
||||
this->auth->destroy(this->auth);
|
||||
DESTROY_IF(this->pseudonym);
|
||||
DESTROY_IF(this->reauth);
|
||||
free(this->xres.ptr);
|
||||
|
@ -703,12 +717,14 @@ eap_aka_server_t *eap_aka_server_create(identification_t *server,
|
|||
.get_type = _get_type,
|
||||
.is_mutual = _is_mutual,
|
||||
.get_msk = _get_msk,
|
||||
.get_auth = _get_auth,
|
||||
.get_identifier = _get_identifier,
|
||||
.set_identifier = _set_identifier,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
},
|
||||
.crypto = simaka_crypto_create(EAP_AKA),
|
||||
.auth = auth_cfg_create(),
|
||||
.mgr = lib->get(lib, "aka-manager"),
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/libstrongswan \
|
||||
-I$(top_srcdir)/src/libcharon \
|
||||
-I$(top_srcdir)/src/libsimaka
|
||||
|
||||
AM_CFLAGS = \
|
||||
$(PLUGIN_CFLAGS)
|
||||
|
||||
libstrongswan_osmo_epdg_la_LDFLAGS = -module -avoid-version
|
||||
libstrongswan_osmo_epdg_la_LIBADD = -lgmp -losmogsm
|
||||
|
||||
if MONOLITHIC
|
||||
noinst_LTLIBRARIES = libstrongswan-osmo-epdg.la
|
||||
else
|
||||
plugin_LTLIBRARIES = libstrongswan-osmo-epdg.la
|
||||
libstrongswan_osmo_epdg_la_LIBADD += $(top_builddir)/src/libsimaka/libsimaka.la
|
||||
endif
|
||||
|
||||
libstrongswan_osmo_epdg_la_SOURCES = \
|
||||
osmo_epdg_plugin.h osmo_epdg_plugin.c \
|
||||
osmo_epdg_provider.h osmo_epdg_provider.c \
|
||||
osmo_epdg_listener.h osmo_epdg_listener.c \
|
||||
gsup_client.h gsup_client.c \
|
||||
ipa_client.h ipa_client.c \
|
||||
osmo_epdg_utils.h osmo_epdg_utils.c
|
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* TODO: check license */
|
||||
|
||||
#include <collections/enumerator.h>
|
||||
#include <collections/linked_list.h>
|
||||
#include <collections/blocking_queue.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
#include <threading/mutex.h>
|
||||
#include <threading/thread.h>
|
||||
#include <threading/condvar.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
#include <library.h>
|
||||
|
||||
#include "ipa_client.h"
|
||||
#include "gsup_client.h"
|
||||
#include "osmo_epdg_utils.h"
|
||||
|
||||
typedef struct gsup_request_t gsup_request_t;
|
||||
struct gsup_request_t {
|
||||
/**
|
||||
* Mutex used to synchronize access to the condvar
|
||||
*/
|
||||
mutex_t *mutex;
|
||||
|
||||
/**
|
||||
* Condvar used to wait for a response
|
||||
*/
|
||||
condvar_t *condvar;
|
||||
|
||||
struct msgb *msg;
|
||||
enum osmo_gsup_message_type msg_type;
|
||||
osmo_epdg_gsup_response_t *resp;
|
||||
};
|
||||
|
||||
typedef struct private_osmo_epdg_gsup_client_t private_osmo_epdg_gsup_client_t;
|
||||
struct private_osmo_epdg_gsup_client_t {
|
||||
/**
|
||||
* Public osmo_epdg_gsup_client_t
|
||||
*/
|
||||
osmo_epdg_gsup_client_t public;
|
||||
|
||||
osmo_epdg_ipa_client_t *ipa;
|
||||
|
||||
/**
|
||||
* List of all pending requests
|
||||
*/
|
||||
blocking_queue_t *pending;
|
||||
|
||||
/**
|
||||
* Current request which isn't part of linked list
|
||||
*/
|
||||
gsup_request_t *current_request;
|
||||
|
||||
/**
|
||||
* Mutex to protect current_request
|
||||
*/
|
||||
mutex_t *mutex;
|
||||
|
||||
char *uri;
|
||||
|
||||
stream_t *stream;
|
||||
};
|
||||
|
||||
static gsup_request_t *gsup_request_create(enum osmo_gsup_message_type msg_type, struct msgb *msg)
|
||||
{
|
||||
gsup_request_t *req = calloc(1, sizeof(gsup_request_t));
|
||||
if (!req)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
req->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
|
||||
req->condvar = condvar_create(CONDVAR_TYPE_DEFAULT);
|
||||
req->msg_type = msg_type;
|
||||
req->msg = msg;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static void gsup_request_destroy(private_osmo_epdg_gsup_client_t *this, gsup_request_t *req)
|
||||
{
|
||||
if (!req)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->mutex)
|
||||
{
|
||||
req->mutex->destroy(req->mutex);
|
||||
}
|
||||
|
||||
if (req->condvar)
|
||||
{
|
||||
req->condvar->destroy(req->condvar);
|
||||
}
|
||||
|
||||
if (req->msg)
|
||||
{
|
||||
free(req->msg);
|
||||
}
|
||||
|
||||
if (req->resp)
|
||||
{
|
||||
free(req->resp);
|
||||
}
|
||||
free(req);
|
||||
}
|
||||
|
||||
static struct msgb *encode_to_msgb(struct osmo_gsup_message *gsup_msg)
|
||||
{
|
||||
chunk_t msg_chunk;
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
msg_chunk = chunk_alloc(4000);
|
||||
if (msg_chunk.ptr == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg = chunk_to_msgb(&msg_chunk);
|
||||
if (!msg)
|
||||
{
|
||||
goto free_msg;
|
||||
}
|
||||
|
||||
/* reserve headroom */
|
||||
msgb_reserve(msg, 64);
|
||||
ret = osmo_gsup_encode(msg, gsup_msg);
|
||||
if (ret)
|
||||
{
|
||||
DBG1(DBG_NET, "GSUP: couldn't encode gsup message %d.", ret);
|
||||
goto free_msg;
|
||||
}
|
||||
|
||||
return msg;
|
||||
|
||||
free_msg:
|
||||
chunk_free(&msg_chunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* enqueue a message/request to be send out and wait for the response.
|
||||
*
|
||||
* when exiting enqueue, it must be guaranteed the req isn't referenced by anything
|
||||
* @param timeout_ms A timeout in ms
|
||||
* @return TRUE is the request timed out.
|
||||
*/
|
||||
static bool enqueue(private_osmo_epdg_gsup_client_t *this, gsup_request_t *req, u_int timeout_ms)
|
||||
{
|
||||
bool ret = FALSE;
|
||||
|
||||
DBG1(DBG_NET, "Enqueuing message. Waiting %d ms for an answer", timeout_ms);
|
||||
req->mutex->lock(req->mutex);
|
||||
this->pending->enqueue(this->pending, req);
|
||||
ret = req->condvar->timed_wait(req->condvar, req->mutex, timeout_ms);
|
||||
if (ret)
|
||||
{
|
||||
void *found = this->pending->remove(this->pending, req);
|
||||
if (!found)
|
||||
{
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->current_request == req)
|
||||
{
|
||||
this->current_request = NULL;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
DBG1(DBG_NET, "Message timedout!");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_gsup_client_t, send_auth_request, osmo_epdg_gsup_response_t*,
|
||||
private_osmo_epdg_gsup_client_t *this, char *imsi, uint8_t cn_domain, chunk_t *auts, chunk_t *auts_rand)
|
||||
{
|
||||
struct osmo_gsup_message gsup_msg = {0};
|
||||
struct msgb *msg;
|
||||
|
||||
DBG1(DBG_NET, "Send Auth Request for %s", imsi);
|
||||
gsup_msg.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
|
||||
gsup_msg.num_auth_vectors = 1;
|
||||
gsup_msg.current_rat_type = OSMO_RAT_EUTRAN_SGS;
|
||||
|
||||
if (!imsi || strlen(imsi) == 0)
|
||||
{
|
||||
/* TODO: inval imsi! */
|
||||
return NULL;
|
||||
}
|
||||
strncpy(gsup_msg.imsi, imsi, sizeof(gsup_msg.imsi));
|
||||
|
||||
switch (cn_domain)
|
||||
{
|
||||
case 0:
|
||||
/* empty cn_domain */
|
||||
break;
|
||||
case OSMO_GSUP_CN_DOMAIN_PS:
|
||||
case OSMO_GSUP_CN_DOMAIN_CS:
|
||||
gsup_msg.cn_domain = cn_domain;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "GSUP: SAIR: Ignoring invalid cn_domain message.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (auts && auts->ptr && auts->len != 0)
|
||||
{
|
||||
if (auts->len != 14)
|
||||
{
|
||||
/* TODO: inval auts */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsup_msg.auts = auts->ptr;
|
||||
}
|
||||
|
||||
/* TODO check for other sizes */
|
||||
if (auts_rand && auts_rand->ptr && auts_rand->len != 0)
|
||||
{
|
||||
if (auts_rand->len != 16)
|
||||
{
|
||||
/* TODO: inval auts */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsup_msg.rand = auts_rand->ptr;
|
||||
}
|
||||
|
||||
msg = encode_to_msgb(&gsup_msg);
|
||||
if (!msg)
|
||||
{
|
||||
DBG1(DBG_NET, "Couldn't alloc/encode gsup message.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool timedout = FALSE;
|
||||
gsup_request_t *req = gsup_request_create(OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST, msg);
|
||||
osmo_epdg_gsup_response_t *resp = NULL;
|
||||
timedout = enqueue(this, req, 5000);
|
||||
if (timedout)
|
||||
{
|
||||
gsup_request_destroy(this, req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resp = req->resp;
|
||||
req->resp = NULL;
|
||||
gsup_request_destroy(this, req);
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_gsup_client_t, update_location, osmo_epdg_gsup_response_t *,
|
||||
private_osmo_epdg_gsup_client_t *this, char *imsi, uint8_t cn_domain)
|
||||
{
|
||||
struct osmo_gsup_message gsup_msg = {0};
|
||||
struct msgb *msg;
|
||||
|
||||
gsup_msg.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
|
||||
gsup_msg.current_rat_type = OSMO_RAT_EUTRAN_SGS;
|
||||
|
||||
if (!imsi || strlen(imsi) == 0)
|
||||
{
|
||||
DBG1(DBG_NET, "GSUP: ULR: Invalid IMSI!");
|
||||
return NULL;
|
||||
}
|
||||
strncpy(gsup_msg.imsi, imsi, sizeof(gsup_msg.imsi));
|
||||
|
||||
switch (cn_domain)
|
||||
{
|
||||
case 0:
|
||||
/* empty cn_domain */
|
||||
break;
|
||||
case OSMO_GSUP_CN_DOMAIN_PS:
|
||||
case OSMO_GSUP_CN_DOMAIN_CS:
|
||||
gsup_msg.cn_domain = cn_domain;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "GSUP: ULR: Ignoring invalid cn_domain message.");
|
||||
break;
|
||||
}
|
||||
|
||||
msg = encode_to_msgb(&gsup_msg);
|
||||
if (!msg)
|
||||
{
|
||||
DBG1(DBG_NET, "GSUP: ULR: Couldn't alloc/encode gsup message.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool timedout = FALSE;
|
||||
gsup_request_t *req = gsup_request_create(OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST, msg);
|
||||
osmo_epdg_gsup_response_t *resp = NULL;
|
||||
timedout = enqueue(this, req, 5000);
|
||||
if (timedout)
|
||||
{
|
||||
gsup_request_destroy(this, req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resp = req->resp;
|
||||
req->resp = NULL;
|
||||
gsup_request_destroy(this, req);
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_gsup_client_t, destroy, void,
|
||||
private_osmo_epdg_gsup_client_t *this)
|
||||
{
|
||||
free(this->uri);
|
||||
free(this);
|
||||
}
|
||||
|
||||
void tx_insert_data_result(private_osmo_epdg_gsup_client_t *this, char *imsi, uint8_t cn_domain)
|
||||
{
|
||||
struct osmo_gsup_message gsup_msg = {0};
|
||||
struct msgb *msg;
|
||||
|
||||
gsup_msg.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT;
|
||||
if (!imsi || strlen(imsi) == 0)
|
||||
{
|
||||
DBG1(DBG_NET, "GSUP: ULR: Invalid IMSI!");
|
||||
}
|
||||
strncpy(gsup_msg.imsi, imsi, sizeof(gsup_msg.imsi));
|
||||
|
||||
switch (cn_domain)
|
||||
{
|
||||
case 0:
|
||||
/* empty cn_domain */
|
||||
break;
|
||||
case OSMO_GSUP_CN_DOMAIN_PS:
|
||||
case OSMO_GSUP_CN_DOMAIN_CS:
|
||||
gsup_msg.cn_domain = cn_domain;
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "GSUP: ULR: Ignoring invalid cn_domain message.");
|
||||
break;
|
||||
}
|
||||
|
||||
msg = encode_to_msgb(&gsup_msg);
|
||||
if (!msg)
|
||||
{
|
||||
DBG1(DBG_NET, "GSUP: ULR: Couldn't alloc/encode gsup message.");
|
||||
}
|
||||
this->ipa->send(this->ipa, IPAC_PROTO_EXT_GSUP, msg);
|
||||
}
|
||||
|
||||
static void signal_request(gsup_request_t *req, osmo_epdg_gsup_response_t *resp)
|
||||
{
|
||||
req->mutex->lock(req->mutex);
|
||||
req->resp = resp;
|
||||
req->condvar->signal(req->condvar);
|
||||
req->mutex->unlock(req->mutex);
|
||||
}
|
||||
|
||||
static bool on_recv_pdu(void *data, osmo_epdg_ipa_client_t *client, struct msgb *pdu)
|
||||
{
|
||||
private_osmo_epdg_gsup_client_t *this = data;
|
||||
osmo_epdg_gsup_response_t *resp;
|
||||
int ret;
|
||||
|
||||
resp = calloc(1, sizeof(*resp));
|
||||
if (!resp)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ret = osmo_gsup_decode(msgb_l2(pdu), msgb_l2len(pdu), &resp->gsup);
|
||||
if (ret) {
|
||||
// TODO: failed to decode response. Close connection
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (resp->gsup.message_type)
|
||||
{
|
||||
case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
|
||||
tx_insert_data_result(this, resp->gsup.imsi, resp->gsup.cn_domain);
|
||||
goto out;
|
||||
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
|
||||
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
|
||||
case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR:
|
||||
case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
|
||||
this->mutex->lock(this->mutex);
|
||||
if ((this->current_request->msg_type & 0xfffffffc) != (resp->gsup.message_type & 0xfffffffc))
|
||||
{
|
||||
/* Request, Result, Error, Other are encoded in the last 2 bits */
|
||||
DBG1(DBG_NET, "GSUP: received non matching Result. Requested %s but received %s",
|
||||
osmo_gsup_message_type_name(this->current_request->msg_type),
|
||||
osmo_gsup_message_type_name(resp->gsup.message_type));
|
||||
goto out;
|
||||
}
|
||||
if (!this->current_request)
|
||||
{
|
||||
DBG2(DBG_NET, "GSUP: received response when no request waiting %02x. This might came too late.", resp->gsup.message_type);
|
||||
this->mutex->unlock(this->mutex);
|
||||
goto out;
|
||||
}
|
||||
signal_request(this->current_request, resp);
|
||||
this->current_request = NULL;
|
||||
this->mutex->unlock(this->mutex);
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "GSUP received unknown message type %02x", resp->gsup.message_type);
|
||||
goto out;
|
||||
}
|
||||
free(pdu);
|
||||
return TRUE;
|
||||
|
||||
out:
|
||||
free(resp);
|
||||
free(pdu);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int disconnect_gsup(private_osmo_epdg_gsup_client_t *this)
|
||||
{
|
||||
this->ipa->disconnect(this->ipa);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: worker thread which sends out enqueue'd message ! */
|
||||
static job_requeue_t queue_worker(private_osmo_epdg_gsup_client_t *this)
|
||||
{
|
||||
int ret;
|
||||
gsup_request_t *req;
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->current_request)
|
||||
{
|
||||
/* TODO: should we join the signal? */
|
||||
this->mutex->unlock(this->mutex);
|
||||
return JOB_REQUEUE_FAIR;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
/* TODO: replace pending with a thread safe queue, but non-blocking */
|
||||
req = this->pending->dequeue(this->pending);
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->current_request)
|
||||
{
|
||||
/* TODO: how could this happen? */
|
||||
this->mutex->unlock(this->mutex);
|
||||
signal_request(req, NULL);
|
||||
return JOB_REQUEUE_FAIR;
|
||||
}
|
||||
this->current_request = req;
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
ret = this->ipa->send(this->ipa, IPAC_PROTO_EXT_GSUP, req->msg);
|
||||
req->msg = NULL;
|
||||
if (ret < 0)
|
||||
{
|
||||
/* TODO: disconnect & reconnect, but request is lost for now */
|
||||
/* TODO: wake up request */
|
||||
signal_request(req, NULL);
|
||||
}
|
||||
return JOB_REQUEUE_FAIR;
|
||||
}
|
||||
|
||||
osmo_epdg_gsup_client_t *osmo_epdg_gsup_client_create(char *uri)
|
||||
{
|
||||
private_osmo_epdg_gsup_client_t *this;
|
||||
DBG1(DBG_NET, "Starting osmo-epdg");
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.send_auth_request = _send_auth_request,
|
||||
.update_location = _update_location,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.uri = strdup(uri),
|
||||
.pending = blocking_queue_create(),
|
||||
.current_request = NULL,
|
||||
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
.ipa = osmo_epdg_ipa_client_create(uri),
|
||||
);
|
||||
this->ipa->on_recv(this->ipa, IPAC_PROTO_EXT_GSUP, on_recv_pdu, this);
|
||||
/* I would more like to have either an internal event which unblocks the queue. */
|
||||
/* src/libipsec/ipsec_event_relay.c */
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)callback_job_create_with_prio((callback_job_cb_t)queue_worker,
|
||||
this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup osmo_epdg_gsup_client osmo_epdg_gsup_client
|
||||
* @{ @ingroup eap_simaka_sql
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_GSUP_CLIENT_H_
|
||||
#define OSMO_EPDG_GSUP_CLIENT_H_
|
||||
|
||||
#include <utils/chunk.h>
|
||||
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
|
||||
struct osmo_epdg_gsup_response_t {
|
||||
struct osmo_gsup_message gsup;
|
||||
};
|
||||
typedef struct osmo_epdg_gsup_response_t osmo_epdg_gsup_response_t;
|
||||
|
||||
typedef struct osmo_epdg_gsup_client_t osmo_epdg_gsup_client_t;
|
||||
|
||||
/**
|
||||
* foo
|
||||
*/
|
||||
struct osmo_epdg_gsup_client_t {
|
||||
/**
|
||||
* Send Authentication Info Request
|
||||
*
|
||||
* @param imsi IMSI encoded as human-readable string (IMSI MAX = 15)
|
||||
* @param auts (optional)
|
||||
* @param auts_rand (optional)
|
||||
* @return NULL or the osmo_epdg_gsup_response_t
|
||||
*/
|
||||
osmo_epdg_gsup_response_t *(*send_auth_request)(osmo_epdg_gsup_client_t *this,
|
||||
char *imsi, uint8_t cn_domain, chunk_t *auts, chunk_t *auts_rand);
|
||||
|
||||
/**
|
||||
* Update Location Request
|
||||
*
|
||||
* @return NULL or the osmo_gsup_message
|
||||
*/
|
||||
osmo_epdg_gsup_response_t *(*update_location)(osmo_epdg_gsup_client_t *this,
|
||||
char *imsi,
|
||||
uint8_t cn_domain);
|
||||
|
||||
/**
|
||||
* Destroy a osmo_epdg_gsup_client_t.
|
||||
*/
|
||||
void (*destroy)(osmo_epdg_gsup_client_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a osmo_epdg_gsup_client instance.
|
||||
*
|
||||
* @param address the address of the gsup server */
|
||||
osmo_epdg_gsup_client_t *osmo_epdg_gsup_client_create(char *addr);
|
||||
|
||||
#endif /** OSMO_EPDG_GSUP_CLIENT_H_ @}*/
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
#include <collections/enumerator.h>
|
||||
#include <collections/linked_list.h>
|
||||
#include <collections/blocking_queue.h>
|
||||
#include <threading/mutex.h>
|
||||
#include <threading/thread.h>
|
||||
#include <threading/condvar.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
|
||||
#include <library.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "ipa_client.h"
|
||||
#include "osmo_epdg_utils.h"
|
||||
|
||||
typedef struct private_osmo_epdg_ipa_client_t private_osmo_epdg_ipa_client_t;
|
||||
struct private_osmo_epdg_ipa_client_t {
|
||||
/**
|
||||
* Public osmo_epdg_ipa_client_t
|
||||
*/
|
||||
osmo_epdg_ipa_client_t public;
|
||||
|
||||
char *uri;
|
||||
|
||||
stream_t *stream;
|
||||
|
||||
ipa_cb_t osmo_cb;
|
||||
void *osmo_cb_data;
|
||||
};
|
||||
|
||||
METHOD(osmo_epdg_ipa_client_t, on_error, int,
|
||||
private_osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, ipa_cb_t cb, void *data)
|
||||
{
|
||||
/* TODO: on error ? */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
METHOD(osmo_epdg_ipa_client_t, on_recv, int,
|
||||
private_osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, ipa_cb_t cb, void *data)
|
||||
{
|
||||
/* TODO: protect it by mutex?! */
|
||||
this->osmo_cb = NULL;
|
||||
this->osmo_cb_data = data;
|
||||
this->osmo_cb = cb;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_ipa_client_t, destroy, void,
|
||||
private_osmo_epdg_ipa_client_t *this)
|
||||
{
|
||||
free(this->uri);
|
||||
free(this);
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_ipa_client_t, disconnect, int,
|
||||
private_osmo_epdg_ipa_client_t *this)
|
||||
{
|
||||
this->stream->destroy(this->stream);
|
||||
this->stream = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_ipa_client_t, send_pdu, ssize_t,
|
||||
private_osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, struct msgb *msg)
|
||||
{
|
||||
struct ipaccess_head *head;
|
||||
int len;
|
||||
head = (struct ipaccess_head *) msgb_push(msg, sizeof(struct ipaccess_head) + 1);
|
||||
head->proto = IPAC_PROTO_OSMO;
|
||||
head->len = htons(msgb_length(msg) - (sizeof(struct ipaccess_head)));
|
||||
head->data[0] = osmo_proto;
|
||||
len = msgb_length(msg);
|
||||
if (!this->stream->write_all(this->stream, msgb_data(msg), msgb_length(msg)))
|
||||
{
|
||||
// TODO: write error
|
||||
free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
free(msg);
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool read_error(private_osmo_epdg_ipa_client_t *this, int read_err)
|
||||
{
|
||||
DBG1(DBG_NET, "IPA client failed to read with %d. Disconnecting", read_err);
|
||||
this->public.disconnect(&this->public);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* send a simple IPA PDU with the base protocol */
|
||||
static void ipa_pdu_base_send_simple(private_osmo_epdg_ipa_client_t *this, uint8_t msg_type)
|
||||
{
|
||||
struct ipaccess_head *head = calloc(1, sizeof(*head) + 1);
|
||||
head->proto = IPAC_PROTO_IPACCESS;
|
||||
head->len = htons(1);
|
||||
head->data[0] = msg_type;
|
||||
|
||||
if (!this->stream->write_all(this->stream, head, sizeof(*head) + 1))
|
||||
{
|
||||
/* TODO: write error */
|
||||
}
|
||||
|
||||
free(head);
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void ipa_tag_put_str(struct msgb *resp, uint8_t tag, const char *value)
|
||||
{
|
||||
char *buf;
|
||||
ssize_t len = strlen(value);
|
||||
|
||||
msgb_put_u16(resp, len + 1);
|
||||
msgb_put_u8(resp, tag);
|
||||
buf = msgb_put(resp, len);
|
||||
memcpy(buf, value, len);
|
||||
}
|
||||
|
||||
static void ipa_resp_tag_encode(private_osmo_epdg_ipa_client_t *this, struct msgb *resp, uint8_t tag)
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case IPAC_IDTAG_SERNR:
|
||||
case IPAC_IDTAG_UNITNAME:
|
||||
ipa_tag_put_str(resp, tag, "SWAN-00-00-00-00-00-00");
|
||||
break;
|
||||
case IPAC_IDTAG_LOCATION1:
|
||||
case IPAC_IDTAG_LOCATION2:
|
||||
case IPAC_IDTAG_EQUIPVERS:
|
||||
case IPAC_IDTAG_SWVERSION:
|
||||
ipa_tag_put_str(resp, tag, "00:00:00:00:00:00");
|
||||
break;
|
||||
case IPAC_IDTAG_UNIT:
|
||||
ipa_tag_put_str(resp, tag, "0/0/0");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void protocol_error(private_osmo_epdg_ipa_client_t *this, char *error_msg)
|
||||
{
|
||||
/* TODO: protocol error */
|
||||
DBG1(DBG_NET, error_msg);
|
||||
return;
|
||||
}
|
||||
/* send a IPA PDU (base protocol) ID Response message */
|
||||
static void ipa_pdu_base_send_id_resp(private_osmo_epdg_ipa_client_t *this, struct msgb *req)
|
||||
{
|
||||
struct msgb *resp;
|
||||
struct ipaccess_head *resp_head;
|
||||
|
||||
chunk_t resp_pdu = chunk_alloc(IPA_ALLOC_SIZE);
|
||||
if (!resp_pdu.ptr)
|
||||
{
|
||||
/* TODO: alloc err */
|
||||
return;
|
||||
}
|
||||
|
||||
resp = chunk_to_msgb(&resp_pdu);
|
||||
|
||||
/* remove the ipaccess header so we can use msg_pull on the message */
|
||||
msgb_pull(req, sizeof(struct ipaccess_head));
|
||||
if (msgb_length(req) < 1)
|
||||
{
|
||||
protocol_error(this, "Invalid IPA ID Request message.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* prepare our response message */
|
||||
msgb_reserve(resp, 128);
|
||||
resp_head = (struct ipaccess_head *) msgb_put(resp, sizeof(struct ipaccess_head));
|
||||
resp->l1h = (void *)resp_head;
|
||||
resp->l2h = resp->tail;
|
||||
resp_head->proto = IPAC_PROTO_IPACCESS;
|
||||
msgb_put_u8(resp, IPAC_MSGT_ID_RESP);
|
||||
/* remove IPA message type */
|
||||
msgb_pull_u8(req);
|
||||
|
||||
/* ID Request contains: a list of [0x1, tag] */
|
||||
while (msgb_length(req) >= 2)
|
||||
{
|
||||
uint8_t len = msgb_pull_u8(req);
|
||||
if (len != 1)
|
||||
{
|
||||
if (msgb_length(req) < len)
|
||||
{
|
||||
protocol_error(this, "Invalid IPA ID Request message");
|
||||
goto out;
|
||||
}
|
||||
/* ignoring this requested LValue */
|
||||
DBG1(DBG_NET, "IPA ignoring IPA ID Request tag with size != 1");
|
||||
msgb_pull(req, len);
|
||||
continue;
|
||||
}
|
||||
uint8_t tag = msgb_pull_u8(req);
|
||||
ipa_resp_tag_encode(this, resp, tag);
|
||||
}
|
||||
resp_head->len = htons(msgb_l2len(resp));
|
||||
if (!this->stream->write_all(this->stream, msgb_l1(resp), msgb_l1len(resp)))
|
||||
{
|
||||
// TODO: write error
|
||||
return;
|
||||
}
|
||||
|
||||
ipa_pdu_base_send_simple(this, IPAC_MSGT_ID_ACK);
|
||||
out:
|
||||
chunk_free(&resp_pdu);
|
||||
return;
|
||||
}
|
||||
|
||||
static void on_recv_ipa_pdu(private_osmo_epdg_ipa_client_t *this, struct msgb *pdu)
|
||||
{
|
||||
if (msgb_length(pdu) < sizeof(struct ipaccess_head) + 1)
|
||||
{
|
||||
/* TODO: invalid package */
|
||||
return;
|
||||
}
|
||||
|
||||
struct ipaccess_head *head = (struct ipaccess_head *) msgb_data(pdu);
|
||||
uint8_t msg_type = head->data[0];
|
||||
switch (msg_type)
|
||||
{
|
||||
case IPAC_MSGT_PING:
|
||||
ipa_pdu_base_send_simple(this, IPAC_MSGT_PONG);
|
||||
break;
|
||||
case IPAC_MSGT_PONG:
|
||||
/* ignore. We don't implement an own PING/PONG timer */
|
||||
break;
|
||||
case IPAC_MSGT_ID_GET:
|
||||
ipa_pdu_base_send_id_resp(this, pdu);
|
||||
break;
|
||||
case IPAC_MSGT_ID_ACK:
|
||||
/* ignore. An ACK means everything the ID got accepted */
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "IPA client Received an unknown IPA PDU %02x", msg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CALLBACK(on_stream_read, bool,
|
||||
private_osmo_epdg_ipa_client_t *this, stream_t *stream)
|
||||
{
|
||||
uint16_t len;
|
||||
ssize_t hlen;
|
||||
chunk_t req_chunk;
|
||||
struct ipaccess_head head;
|
||||
struct msgb *req;
|
||||
|
||||
DBG2(DBG_NET, "on stream read!");
|
||||
hlen = stream->read(stream, &head, sizeof(head), FALSE);
|
||||
if (hlen <= 0)
|
||||
{
|
||||
if (errno == EWOULDBLOCK)
|
||||
{
|
||||
DBG2(DBG_NET, "on stream read EWOULDBLOCK!");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DBG2(DBG_NET, "on stream errno not EWOULDBLOCK %d!", hlen);
|
||||
return read_error(this, errno);
|
||||
}
|
||||
DBG2(DBG_NET, "on stream hlen %d!", hlen);
|
||||
if (hlen < sizeof(head))
|
||||
{
|
||||
if (!stream->read_all(stream, ((void*)&head) + hlen, sizeof(head) - hlen))
|
||||
{
|
||||
return read_error(this, errno);
|
||||
}
|
||||
}
|
||||
len = ntohs(head.len);
|
||||
if ((len + sizeof(head)) > IPA_ALLOC_SIZE)
|
||||
{
|
||||
/* TODO: pkg too big */
|
||||
return read_error(this, EINVAL);
|
||||
}
|
||||
|
||||
req_chunk = chunk_alloc(IPA_ALLOC_SIZE + sizeof(struct msgb));
|
||||
if (!req_chunk.ptr)
|
||||
{
|
||||
/* TODO: -ENOMEM; */
|
||||
return FALSE;
|
||||
}
|
||||
req = chunk_to_msgb(&req_chunk);
|
||||
memcpy(msgb_put(req, sizeof(head)), &head, sizeof(head));
|
||||
/* TODO: either wait here with a timeout or don't care on this stream read */
|
||||
if (!stream->read_all(stream, msgb_put(req, len), len))
|
||||
{
|
||||
chunk_free(&req_chunk);
|
||||
return read_error(this, errno);
|
||||
}
|
||||
|
||||
switch (head.proto)
|
||||
{
|
||||
case IPAC_PROTO_IPACCESS:
|
||||
on_recv_ipa_pdu(this, req);
|
||||
break;
|
||||
case IPAC_PROTO_OSMO:
|
||||
/* will take care of the response */
|
||||
if (msgb_length(req) < sizeof(struct ipaccess_head) + 1)
|
||||
{
|
||||
/* TODO: inval pdu */
|
||||
chunk_free(&req_chunk);
|
||||
break;
|
||||
}
|
||||
req->l1h = req->head;
|
||||
req->l2h = req->l1h + sizeof(struct ipaccess_head) + 1;
|
||||
DBG2(DBG_NET, "IPA client: pushing osmo pdu");
|
||||
if (this->osmo_cb)
|
||||
{
|
||||
this->osmo_cb(this->osmo_cb_data, &this->public, req);
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_free(&req_chunk);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_NET, "IPA client: ignoring unknown proto %02x", head.proto);
|
||||
chunk_free(&req_chunk);
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int connect_ipa(private_osmo_epdg_ipa_client_t *this)
|
||||
{
|
||||
|
||||
DBG1(DBG_NET, "IPA client connecting to %s", this->uri);
|
||||
if (this->stream != NULL)
|
||||
{
|
||||
DBG1(DBG_NET, "closing old ipa conncetion %s", this->uri);
|
||||
this->public.disconnect(&this->public);
|
||||
}
|
||||
|
||||
this->stream = lib->streams->connect(lib->streams, this->uri);
|
||||
if (!this->stream)
|
||||
{
|
||||
/* TODO: failed to connect */
|
||||
/* TODO: re-schedule the connect */
|
||||
DBG1(DBG_NET, "failed to connect the ipa %s", this->uri);
|
||||
return -EINVAL;
|
||||
}
|
||||
DBG1(DBG_NET, "IPA client connected");
|
||||
|
||||
/* TODO: check if we need this
|
||||
* ensure we get the first bytes after the connect
|
||||
* on_stream_read(this, this->stream); */
|
||||
|
||||
/* TODO: can a race happen here when data arrives between those 2 calls? */
|
||||
this->stream->on_read(this->stream, on_stream_read, this);
|
||||
on_stream_read(this, this->stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
osmo_epdg_ipa_client_t *osmo_epdg_ipa_client_create(char *uri)
|
||||
{
|
||||
private_osmo_epdg_ipa_client_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.on_recv = _on_recv,
|
||||
.on_error = _on_error,
|
||||
.send = _send_pdu,
|
||||
.disconnect = _disconnect,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.uri = strdup(uri),
|
||||
.stream = NULL,
|
||||
);
|
||||
connect_ipa(this);
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup osmo_epdg_ipa_client osmo_epdg_ipa_client
|
||||
* @{ @ingroup eap_simaka_sql
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_IPA_CLIENT_H_
|
||||
#define OSMO_EPDG_IPA_CLIENT_H_
|
||||
|
||||
#include <utils/chunk.h>
|
||||
|
||||
struct msgb;
|
||||
typedef struct osmo_epdg_ipa_client_t osmo_epdg_ipa_client_t;
|
||||
typedef bool (*ipa_cb_t)(void *data, osmo_epdg_ipa_client_t *client, struct msgb *pdu);
|
||||
|
||||
/**
|
||||
* IP Access Protocol
|
||||
*/
|
||||
struct osmo_epdg_ipa_client_t {
|
||||
/**
|
||||
* Register a callback to invoke when a IPA PDU of proto arrived.
|
||||
*
|
||||
* Only called when a full PDU has arrived.
|
||||
*
|
||||
* @param cb callback function, NULL to unregister
|
||||
* @param data data to pass to callback
|
||||
*/
|
||||
int (*on_recv)(osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, ipa_cb_t cb, void *data);
|
||||
|
||||
/**
|
||||
* Send a PDU over the IPA connection
|
||||
*
|
||||
* @param proto define the protocol
|
||||
* @param buf data buffer to write
|
||||
* @param len number of bytes to write
|
||||
* @return number of bytes written, -1 on error
|
||||
*/
|
||||
ssize_t (*send)(osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, struct msgb *msg);
|
||||
|
||||
int (*on_error)(osmo_epdg_ipa_client_t *this, uint8_t osmo_proto, ipa_cb_t cb, void *data);
|
||||
|
||||
// TODO: unsure if we need this int (*connect)(osmo_epdg_ipa_client_t *this);
|
||||
int (*disconnect)(osmo_epdg_ipa_client_t *this);
|
||||
/**
|
||||
* Destroy a osmo_epdg_ipa_client_t.
|
||||
*/
|
||||
void (*destroy)(osmo_epdg_ipa_client_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a osmo_epdg_ipa_client instance.
|
||||
*
|
||||
* @param address the address of the gsup server */
|
||||
osmo_epdg_ipa_client_t *osmo_epdg_ipa_client_create(char *addr);
|
||||
|
||||
#endif /** OSMO_EPDG_IPA_CLIENT_H_ @}*/
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
#include <daemon.h>
|
||||
#include <plugins/plugin.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "osmo_epdg_plugin.h"
|
||||
#include "osmo_epdg_listener.h"
|
||||
#include "osmo_epdg_utils.h"
|
||||
|
||||
typedef struct private_osmo_epdg_listener_t private_osmo_epdg_listener_t;
|
||||
|
||||
/**
|
||||
* Private data of an osmo_epdg_listener_t object.
|
||||
*/
|
||||
struct private_osmo_epdg_listener_t {
|
||||
/**
|
||||
* Public osmo_epdg_listener_t interface.
|
||||
*/
|
||||
osmo_epdg_listener_t public;
|
||||
|
||||
osmo_epdg_gsup_client_t *gsup;
|
||||
};
|
||||
|
||||
METHOD(listener_t, eap_authorize, bool,
|
||||
private_osmo_epdg_listener_t *this, ike_sa_t *ike_sa,
|
||||
identification_t *id, bool final, bool *success)
|
||||
{
|
||||
char imsi[16] = {0};
|
||||
|
||||
if (!id)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: authorize: no id given. Failing.");
|
||||
goto err;
|
||||
}
|
||||
if (get_imsi(id, imsi, sizeof(imsi) - 1))
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: authorize: Can't find IMSI in EAP identity.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
osmo_epdg_gsup_response_t *resp = this->gsup->update_location(this->gsup, imsi, OSMO_GSUP_CN_DOMAIN_PS);
|
||||
if (!resp)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: GSUP: couldn't send Update Location.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (resp->gsup.message_type != OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg_listener: Update Location Error! Cause: %02x", resp->gsup.cause);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
err:
|
||||
*success = FALSE;
|
||||
/* keep still subscribed */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(listener_t, authorize, bool,
|
||||
private_osmo_epdg_listener_t *this, ike_sa_t *ike_sa,
|
||||
bool final, bool *success)
|
||||
{
|
||||
DBG1(DBG_NET, "Authorized: uniq 0x%08x, name %s final: %d, eap: %d!",
|
||||
ike_sa->get_unique_id(ike_sa),
|
||||
ike_sa->get_name(ike_sa),
|
||||
final,
|
||||
ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED));
|
||||
|
||||
sleep(1);
|
||||
if (final)
|
||||
{
|
||||
/* TODO: create new Tunnel and save Tunnel information locally */
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_listener_t, destroy, void,
|
||||
private_osmo_epdg_listener_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
osmo_epdg_listener_t *osmo_epdg_listener_create(osmo_epdg_gsup_client_t *gsup)
|
||||
{
|
||||
private_osmo_epdg_listener_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.listener = {
|
||||
.authorize = _authorize,
|
||||
.eap_authorize = _eap_authorize,
|
||||
},
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.gsup = gsup,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup osmo_epdg_listener osmo_epdg_listener
|
||||
* @{ @ingroup osmo_epdg
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_LISTENER_H_
|
||||
#define OSMO_EPDG_LISTENER_H_
|
||||
|
||||
#include <bus/listeners/listener.h>
|
||||
#include "gsup_client.h"
|
||||
|
||||
typedef struct osmo_epdg_listener_t osmo_epdg_listener_t;
|
||||
|
||||
/**
|
||||
* SIM listener implementation using a set of AKA functions.
|
||||
*/
|
||||
struct osmo_epdg_listener_t {
|
||||
|
||||
/**
|
||||
* Implements listener_t interface.
|
||||
*/
|
||||
listener_t listener;
|
||||
|
||||
/**
|
||||
* Destroy a osmo_epdg_listener_t.
|
||||
*/
|
||||
void (*destroy)(osmo_epdg_listener_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a osmo_epdg_listener instance.
|
||||
*/
|
||||
osmo_epdg_listener_t *osmo_epdg_listener_create(osmo_epdg_gsup_client_t *gsup);
|
||||
|
||||
#endif /** OSMO_EPDG_LISTENER_H_ @}*/
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
#include <daemon.h>
|
||||
#include <plugins/plugin.h>
|
||||
|
||||
#include "osmo_epdg_plugin.h"
|
||||
#include "osmo_epdg_provider.h"
|
||||
#include "osmo_epdg_listener.h"
|
||||
|
||||
typedef struct private_osmo_epdg_t private_osmo_epdg_t;
|
||||
|
||||
/**
|
||||
* Private data of an eap_osmo_epdg_t object.
|
||||
*/
|
||||
struct private_osmo_epdg_t {
|
||||
/**
|
||||
* Public osmo_epdg_plugin_t interface.
|
||||
*/
|
||||
osmo_epdg_plugin_t public;
|
||||
|
||||
/**
|
||||
* SIM AKA provider
|
||||
*/
|
||||
osmo_epdg_provider_t *provider;
|
||||
|
||||
osmo_epdg_listener_t *listener;
|
||||
};
|
||||
|
||||
METHOD(plugin_t, get_name, char*,
|
||||
private_osmo_epdg_t *this)
|
||||
{
|
||||
return "osmo-epdg";
|
||||
}
|
||||
|
||||
static bool register_functions(private_osmo_epdg_t *this,
|
||||
plugin_feature_t *feature, bool reg, void *data)
|
||||
{
|
||||
if (reg)
|
||||
{
|
||||
osmo_epdg_gsup_client_t *gsup = osmo_epdg_gsup_client_create("tcp://127.0.0.1:4222");
|
||||
this->provider = osmo_epdg_provider_create(gsup);
|
||||
this->listener = osmo_epdg_listener_create(gsup);
|
||||
charon->bus->add_listener(charon->bus, &this->listener->listener);
|
||||
return TRUE;
|
||||
}
|
||||
if (this->listener)
|
||||
{
|
||||
charon->bus->remove_listener(charon->bus, &this->listener->listener);
|
||||
}
|
||||
this->provider->destroy(this->provider);
|
||||
this->provider = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback providing our provider to register
|
||||
*/
|
||||
static simaka_provider_t* get_provider(private_osmo_epdg_t *this)
|
||||
{
|
||||
return &this->provider->provider;
|
||||
}
|
||||
|
||||
METHOD(plugin_t, get_features, int,
|
||||
private_osmo_epdg_t *this, plugin_feature_t *features[])
|
||||
{
|
||||
static plugin_feature_t f[] = {
|
||||
PLUGIN_CALLBACK((void*)register_functions, NULL),
|
||||
PLUGIN_PROVIDE(CUSTOM, "osmo-epdg"),
|
||||
PLUGIN_CALLBACK(simaka_manager_register, get_provider),
|
||||
PLUGIN_PROVIDE(CUSTOM, "aka-provider"),
|
||||
PLUGIN_DEPENDS(CUSTOM, "aka-manager"),
|
||||
};
|
||||
*features = f;
|
||||
return countof(f);
|
||||
}
|
||||
|
||||
METHOD(plugin_t, destroy, void,
|
||||
private_osmo_epdg_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
plugin_t *osmo_epdg_plugin_create()
|
||||
{
|
||||
private_osmo_epdg_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.plugin = {
|
||||
.get_name = _get_name,
|
||||
.get_features = _get_features,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return &this->public.plugin;
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup eap_osmo_epdg eap_aka_3gpp2
|
||||
* @ingroup cplugins
|
||||
*
|
||||
* @defgroup eap_osmo_epdg_plugin eap_aka_3gpp2_plugin
|
||||
* @{ @ingroup eap_osmo_epdg
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_PLUGIN_H_
|
||||
#define OSMO_EPDG_PLUGIN_H_
|
||||
|
||||
#include <plugins/plugin.h>
|
||||
|
||||
typedef struct osmo_epdg_plugin_t osmo_epdg_plugin_t;
|
||||
|
||||
struct osmo_epdg_plugin_t {
|
||||
|
||||
/**
|
||||
* implements plugin interface
|
||||
*/
|
||||
plugin_t plugin;
|
||||
};
|
||||
|
||||
#endif /** OSMO_EPDG_PLUGIN_H_ @}*/
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "osmo_epdg_provider.h"
|
||||
#include "osmo_epdg_utils.h"
|
||||
#include "gsup_client.h"
|
||||
|
||||
#include <daemon.h>
|
||||
#include <credentials/keys/shared_key.h>
|
||||
|
||||
#define AKA_SQN_LEN 6
|
||||
#define AKA_K_LEN 16
|
||||
#define AKA_OPC_LEN 16
|
||||
#define AKA_MAC_LEN 8
|
||||
#define AKA_AK_LEN 6
|
||||
#define AKA_AMF_LEN 2
|
||||
#define AKA_RES_LEN 8
|
||||
|
||||
typedef struct private_osmo_epdg_provider_t private_osmo_epdg_provider_t;
|
||||
|
||||
/**
|
||||
* Private data of an osmo_epdg_provider_t object.
|
||||
*/
|
||||
struct private_osmo_epdg_provider_t {
|
||||
|
||||
/**
|
||||
* Public osmo_epdg_provider_t interface.
|
||||
*/
|
||||
osmo_epdg_provider_t public;
|
||||
|
||||
osmo_epdg_gsup_client_t *gsup;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a shared key K from the credential database
|
||||
*/
|
||||
bool osmo_epdg_get_k(identification_t *id, char k[AKA_K_LEN])
|
||||
{
|
||||
shared_key_t *shared;
|
||||
chunk_t key;
|
||||
|
||||
shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP, id, NULL);
|
||||
if (shared == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
key = shared->get_key(shared);
|
||||
memset(k, '\0', AKA_K_LEN);
|
||||
memcpy(k, key.ptr, min(key.len, AKA_K_LEN));
|
||||
shared->destroy(shared);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(simaka_provider_t, get_quintuplet, bool,
|
||||
private_osmo_epdg_provider_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char xres[AKA_RES_MAX], int *xres_len,
|
||||
char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN])
|
||||
{
|
||||
char imsi[17] = {0};
|
||||
if (get_imsi(id, imsi, sizeof(imsi) - 1))
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: get_quintuplet: Can't find IMSI in EAP identity.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* TODO: check before if this is a null terminated string */
|
||||
osmo_epdg_gsup_response_t *resp = this->gsup->send_auth_request(
|
||||
this->gsup, imsi, OSMO_GSUP_CN_DOMAIN_PS, NULL, NULL);
|
||||
if (!resp)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (resp->gsup.message_type != OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg_provider: SendAuthInfo Error! Cause: %02x", resp->gsup.cause);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct osmo_auth_vector *auth = &resp->gsup.auth_vectors[0];
|
||||
if (resp->gsup.num_auth_vectors == 0)
|
||||
{
|
||||
/* TODO: invalid auth data received */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(rand, auth->rand, AKA_RAND_LEN);
|
||||
memcpy(ck, auth->ck, AKA_CK_LEN);
|
||||
memcpy(ik, auth->ik, AKA_IK_LEN);
|
||||
memcpy(autn, auth->autn, AKA_AUTN_LEN);
|
||||
memcpy(xres, auth->res, auth->res_len);
|
||||
*xres_len = auth->res_len;
|
||||
|
||||
free(resp);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(simaka_provider_t, resync, bool,
|
||||
private_osmo_epdg_provider_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN])
|
||||
{
|
||||
/* TODO: invalid auth data received */
|
||||
/* prepare and fill up the struct */
|
||||
/* send pdu blocking */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(osmo_epdg_provider_t, destroy, void,
|
||||
private_osmo_epdg_provider_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
osmo_epdg_provider_t *osmo_epdg_provider_create(osmo_epdg_gsup_client_t *gsup)
|
||||
{
|
||||
private_osmo_epdg_provider_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.provider = {
|
||||
.get_triplet = (void*)return_false,
|
||||
.get_quintuplet = _get_quintuplet,
|
||||
.resync = _resync,
|
||||
.is_pseudonym = (void*)return_null,
|
||||
.gen_pseudonym = (void*)return_null,
|
||||
.is_reauth = (void*)return_null,
|
||||
.gen_reauth = (void*)return_null,
|
||||
},
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.gsup = gsup,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO: check license
|
||||
|
||||
/**
|
||||
* @defgroup osmo_epdg_listener osmo_epdg_listener
|
||||
* @{ @ingroup osmo_epdg
|
||||
*/
|
||||
|
||||
#ifndef OSMO_EPDG_PROVIDER_H_
|
||||
#define OSMO_EPDG_PROVIDER_H_
|
||||
|
||||
#include <simaka_provider.h>
|
||||
#include "gsup_client.h"
|
||||
|
||||
typedef struct osmo_epdg_provider_t osmo_epdg_provider_t;
|
||||
|
||||
/**
|
||||
* SIM provider implementation using a set of AKA functions.
|
||||
*/
|
||||
struct osmo_epdg_provider_t {
|
||||
|
||||
/**
|
||||
* Implements simaka_provider_t interface.
|
||||
*/
|
||||
simaka_provider_t provider;
|
||||
|
||||
/**
|
||||
* Destroy a osmo_epdg_provider_t.
|
||||
*/
|
||||
void (*destroy)(osmo_epdg_provider_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a osmo_epdg_provider instance.
|
||||
*/
|
||||
osmo_epdg_provider_t *osmo_epdg_provider_create(osmo_epdg_gsup_client_t *gsup);
|
||||
|
||||
#endif /** OSMO_EPDG_PROVIDER_H_ @}*/
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <utils/utils.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include "osmo_epdg_utils.h"
|
||||
|
||||
struct msgb *chunk_to_msgb(chunk_t *chunk)
|
||||
{
|
||||
struct msgb *msg;
|
||||
if (chunk->len < sizeof(*msg))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg = (struct msgb *) chunk->ptr;
|
||||
memset(msg, 0x00, sizeof(*msg));
|
||||
msg->data_len = chunk->len - sizeof(*msg);
|
||||
msg->len = 0;
|
||||
msg->data = msg->_data;
|
||||
msg->head = msg->_data;
|
||||
msg->tail = msg->_data;
|
||||
return msg;
|
||||
}
|
||||
|
||||
int get_imsi(identification_t *id, char *imsi, size_t imsi_len)
|
||||
{
|
||||
chunk_t nai = id->get_encoding(id);
|
||||
/* TODO: maybe use regex? */
|
||||
/* 099942123456789@mnc042.mcc999.3gpp... */
|
||||
if (nai.len < 17)
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: Invalid NAI %s.", nai);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (nai.ptr[0] != '0')
|
||||
{
|
||||
DBG1(DBG_NET, "epdg: Invalid NAI. Only support IMSI (starting with 0). %s.",
|
||||
nai);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
strncpy(imsi, nai.ptr + 1, min(15, imsi_len));
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <acouzens@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <utils/chunk.h>
|
||||
#include <utils/identification.h>
|
||||
|
||||
|
||||
#define IPA_ALLOC_SIZE 1200
|
||||
|
||||
struct msgb *chunk_to_msgb(chunk_t *chunk);
|
||||
int get_imsi(identification_t *id, char *imsi, size_t imsi_len);
|
|
@ -240,6 +240,32 @@ static void replace_eap_identity(private_eap_authenticator_t *this)
|
|||
cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, eap_identity);
|
||||
}
|
||||
|
||||
static identification_t *get_identification(private_eap_authenticator_t *this)
|
||||
{
|
||||
identification_t *id;
|
||||
auth_cfg_t *auth = NULL;
|
||||
|
||||
if (this->eap_identity)
|
||||
{
|
||||
return this->eap_identity;
|
||||
}
|
||||
|
||||
if (this->method->get_auth)
|
||||
{
|
||||
auth = this->method->get_auth(this->method);
|
||||
if (auth)
|
||||
{
|
||||
id = auth->get(auth, AUTH_RULE_EAP_IDENTITY);
|
||||
if (id)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle EAP exchange as server
|
||||
*/
|
||||
|
@ -320,6 +346,14 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
|
|||
DBG1(DBG_IKE, "EAP method %N succeeded, %sMSK established",
|
||||
eap_type_names, type, this->msk.ptr ? "" : "no ");
|
||||
}
|
||||
|
||||
if (!charon->bus->eap_authorize(charon->bus, get_identification(this), TRUE))
|
||||
{
|
||||
DBG1(DBG_IKE, "eap_authorization hook forbids EAP_SUCCESS, canceling");
|
||||
this->eap_complete = FALSE;
|
||||
return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in));
|
||||
}
|
||||
|
||||
this->ike_sa->set_condition(this->ike_sa, COND_EAP_AUTHENTICATED,
|
||||
TRUE);
|
||||
this->eap_complete = TRUE;
|
||||
|
|
Loading…
Reference in New Issue