gsm_04_08: Add a operation to enable ciphering on a lchan

This will take care of the auth/check/enable cipher sequence
and call a callback function when done.

Currently the negotiated Kc is saved but not re-used, so
there is an authentication each time ...

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
This commit is contained in:
Sylvain Munaut 2009-12-24 00:27:26 +01:00 committed by Harald Welte
parent 31ac307130
commit 30a15384f4
5 changed files with 282 additions and 4 deletions

View File

@ -65,6 +65,7 @@ enum gsm_chreq_reason_t {
enum gsm_hooks {
GSM_HOOK_NM_SWLOAD,
GSM_HOOK_RR_PAGING,
GSM_HOOK_RR_SECURITY,
};
enum gsm_paging_event {
@ -104,6 +105,12 @@ struct openbsc_msgb_cb {
#define msgb_bcid(__x) OBSC_MSGB_CB(__x)->bssgp_cell_id
#define msgb_llch(__x) OBSC_MSGB_CB(__x)->llch
enum gsm_security_event {
GSM_SECURITY_NOAVAIL,
GSM_SECURITY_AUTH_FAILED,
GSM_SECURITY_SUCCEEDED,
};
struct msgb;
typedef int gsm_cbfn(unsigned int hooknum,
unsigned int event,
@ -179,6 +186,15 @@ struct gsm_loc_updating_operation {
unsigned int waiting_for_imei : 1;
};
/*
* AUTHENTICATION/CIPHERING state
*/
struct gsm_security_operation {
struct gsm_auth_tuple atuple;
gsm_cbfn *cb;
void *cb_data;
};
/* Maximum number of neighbor cells whose average we track */
#define MAX_NEIGH_MEAS 10
/* Maximum size of the averaging window for neighbor cells */
@ -194,6 +210,7 @@ struct neigh_meas_proc {
};
#define MAX_A5_KEY_LEN (128/8)
#define A38_COMP128_KEY_LEN 16
#define RSL_ENC_ALG_A5(x) (x+1)
/* is the data link established? who established it? */
@ -223,6 +240,7 @@ struct gsm_subscriber_connection {
* Operations that have a state and might be pending
*/
struct gsm_loc_updating_operation *loc_operation;
struct gsm_security_operation *sec_operation;
/* use count. how many users use this channel */
unsigned int use_count;

View File

@ -23,7 +23,7 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
libmsc_a_SOURCES = gsm_subscriber.c db.c \
mncc.c gsm_04_08.c gsm_04_11.c transaction.c \
token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c \
handover_logic.c handover_decision.c meas_rep.c
handover_logic.c handover_decision.c meas_rep.c auth.c
libvty_a_SOURCES = common_vty.c

103
openbsc/src/auth.c Normal file
View File

@ -0,0 +1,103 @@
/* Authentication related functions */
/*
* (C) 2010 by Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <openbsc/db.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <osmocore/comp128.h>
#include <stdlib.h>
static int
_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple)
{
if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) {
DEBUGP(DMM, "Invalid COMP128v1 key (len=%d) %s",
ainfo->a3a8_ki_len,
hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len));
return -1;
}
comp128(ainfo->a3a8_ki, atuple->rand, atuple->sres, atuple->kc);
return 0;
}
/* Return values
* -1 -> Internal error
* 0 -> Not available
* 1 -> Tuple returned, need to do auth, then enable cipher
* 2 -> Tuple returned, need to enable cipher
*/
int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple,
struct gsm_subscriber *subscr, int key_seq)
{
struct gsm_auth_info ainfo;
int i, rc;
/* Get subscriber info (if any) */
rc = db_get_authinfo_for_subscr(&ainfo, subscr);
if (rc < 0) {
DEBUGP(DMM, "No retrievable Ki for subscriber, skipping auth");
return rc == -ENOENT ? 0 : -1;
}
/* If possible, re-use the last tuple and skip auth */
rc = db_get_lastauthtuple_for_subscr(atuple, subscr);
if ((rc == 0) &&
(atuple->key_seq != GSM_KEY_SEQ_INVAL) &&
(atuple->use_count < 3))
{
atuple->use_count++;
db_sync_lastauthtuple_for_subscr(atuple, subscr);
return 2;
}
/* Generate a new one */
atuple->use_count = 1;
atuple->key_seq = (atuple->key_seq + 1) % 7;
for (i=0; i<sizeof(atuple->rand); i++)
atuple->rand[i] = random() & 0xff;
switch (ainfo.auth_algo) {
case AUTH_ALGO_NONE:
return 0;
case AUTH_ALGO_COMP128v1:
if (_use_comp128_v1(&ainfo, atuple))
return 0;
break;
default:
DEBUGP(DMM, "Unsupported auth type algo_id=%d\n",
ainfo.auth_algo);
return 0;
}
db_sync_lastauthtuple_for_subscr(atuple, subscr);
return 1;
}

View File

@ -30,6 +30,7 @@
#include <time.h>
#include <netinet/in.h>
#include <openbsc/auth.h>
#include <openbsc/db.h>
#include <osmocore/msgb.h>
#include <osmocore/bitvec.h>
@ -54,6 +55,7 @@
#include <openbsc/silent_call.h>
void *tall_locop_ctx;
void *tall_authciphop_ctx;
int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi);
static int gsm48_tx_simple(struct gsm_lchan *lchan,
@ -68,6 +70,91 @@ struct gsm_lai {
static u_int32_t new_callref = 0x80000001;
static void release_security_operation(struct gsm_subscriber_connection *conn)
{
if (!conn->sec_operation)
return;
talloc_free(conn->sec_operation);
conn->sec_operation = NULL;
put_subscr_con(conn);
}
static void allocate_security_operation(struct gsm_subscriber_connection *conn)
{
use_subscr_con(conn)
conn->sec_operation = talloc_zero(tall_authciphop_ctx,
struct gsm_security_operation);
}
int gsm48_secure_channel(struct gsm_lchan *lchan, int key_seq,
gsm_cbfn *cb, void *cb_data)
{
struct gsm_network *net = lchan->ts->trx->bts->network;
struct gsm_subscriber *subscr = lchan->conn.subscr;
struct gsm_security_operation *op;
struct gsm_auth_tuple atuple;
int status = -1, rc;
/* Check if we _can_ enable encryption. Cases where we can't:
* - Encryption disabled in config
* - Channel already secured (nothing to do)
* - Subscriber equipment doesn't support configured encryption
*/
if (!net->a5_encryption) {
status = GSM_SECURITY_NOAVAIL;
} else if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) {
DEBUGP(DMM, "Requesting to secure an already secure channel");
status = GSM_SECURITY_SUCCEEDED;
} else if (!ms_cm2_a5n_support(subscr->equipment.classmark2,
net->a5_encryption)) {
DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption");
status = GSM_SECURITY_NOAVAIL;
}
/* If not done yet, try to get info for this user */
if (status < 0) {
rc = auth_get_tuple_for_subscr(&atuple, subscr, key_seq);
if (rc <= 0)
status = GSM_SECURITY_NOAVAIL;
}
/* Are we done yet ? */
if (status >= 0)
return cb ?
cb(GSM_HOOK_RR_SECURITY, status, NULL, lchan, cb_data) :
0;
/* Start an operation (can't have more than one pending !!!) */
if (lchan->conn.sec_operation)
return -EBUSY;
allocate_security_operation(&lchan->conn);
op = lchan->conn.sec_operation;
op->cb = cb;
op->cb_data = cb_data;
memcpy(&op->atuple, &atuple, sizeof(struct gsm_auth_tuple));
/* FIXME: Should start a timer for completion ... */
/* Then do whatever is needed ... */
if (rc == 1) {
/* Start authentication */
return gsm48_tx_mm_auth_req(lchan, op->atuple.rand, op->atuple.key_seq);
} else if (rc == 2) {
/* Start ciphering directly */
lchan->encr.alg_id = RSL_ENC_ALG_A5(net->a5_encryption);
lchan->encr.key_len = 8;
memcpy(lchan->encr.key, op->atuple.kc, 8);
return gsm48_send_rr_ciph_mode(lchan, 0);
}
return -EINVAL; /* not reached */
}
static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
struct gsm_subscriber *subscriber)
{
@ -160,6 +247,7 @@ static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal,
return 0;
release_loc_updating_req(&lchan->conn);
release_security_operation(&lchan->conn);
/* Free all transactions that are associated with the released lchan */
/* FIXME: this is not neccessarily the right thing to do, we should
@ -735,6 +823,43 @@ static int gsm48_rx_mm_status(struct msgb *msg)
return 0;
}
/* Chapter 9.2.3: Authentication Response */
static int gsm48_rx_mm_auth_resp(struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
struct gsm48_auth_resp *ar = (struct gsm48_auth_resp*) gh->data;
struct gsm_lchan *lchan = msg->lchan;
struct gsm_subscriber_connection *conn = &msg->lchan->conn;
struct gsm_network *net = lchan->ts->trx->bts->network;
DEBUGP(DMM, "MM AUTHENTICATION RESPONSE (sres = %s)\n",
hexdump(ar->sres, 4));
/* Safety check */
if (!conn->sec_operation) {
DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n");
return -EIO;
}
/* Validate SRES */
if (memcmp(conn->sec_operation->atuple.sres, ar->sres,4)) {
gsm_cbfn *cb = conn->sec_operation->cb;
if (cb)
cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED,
NULL, lchan, conn->sec_operation->cb_data);
release_security_operation(conn);
return gsm48_tx_mm_auth_rej(lchan);
}
/* Start ciphering */
lchan->encr.alg_id = RSL_ENC_ALG_A5(net->a5_encryption);
lchan->encr.key_len = 8;
memcpy(msg->lchan->encr.key, conn->sec_operation->atuple.kc, 8);
return gsm48_send_rr_ciph_mode(msg->lchan, 0);
}
/* Receive a GSM 04.08 Mobility Management (MM) message */
static int gsm0408_rcv_mm(struct msgb *msg)
{
@ -768,7 +893,7 @@ static int gsm0408_rcv_mm(struct msgb *msg)
DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n");
break;
case GSM48_MT_MM_AUTH_RESP:
DEBUGP(DMM, "AUTHENTICATION RESPONSE: Not implemented\n");
rc = gsm48_rx_mm_auth_resp(msg);
break;
default:
LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n",
@ -910,6 +1035,37 @@ static int gsm48_rx_rr_app_info(struct msgb *msg)
return db_apdu_blob_store(msg->lchan->conn.subscr, apdu_id_flags, apdu_len, apdu_data);
}
/* Chapter 9.1.10 Ciphering Mode Complete */
static int gsm48_rx_rr_ciph_m_compl(struct msgb *msg)
{
struct gsm_lchan *lchan = msg->lchan;
struct gsm_subscriber_connection *conn = &lchan->conn;
gsm_cbfn *cb;
int rc = 0;
DEBUGP(DRR, "CIPHERING MODE COMPLETE\n");
/* Safety check */
if (!conn->sec_operation) {
DEBUGP(DRR, "No authentication/cipher operation in progress !!!\n");
return -EIO;
}
/* FIXME: check for MI (if any) */
/* Call back whatever was in progress (if anything) ... */
cb = conn->sec_operation->cb;
if (cb) {
rc = cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_SUCCEEDED,
NULL, lchan, conn->sec_operation->cb_data);
}
/* Complete the operation */
release_security_operation(conn);
return rc;
}
/* Chapter 9.1.16 Handover complete */
static int gsm48_rx_rr_ho_compl(struct msgb *msg)
{
@ -967,8 +1123,7 @@ static int gsm0408_rcv_rr(struct msgb *msg)
rc = gsm48_rx_rr_app_info(msg);
break;
case GSM48_MT_RR_CIPH_M_COMPL:
DEBUGP(DRR, "CIPHERING MODE COMPLETE\n");
/* FIXME: check for MI (if any) */
rc = gsm48_rx_rr_ciph_m_compl(msg);
break;
case GSM48_MT_RR_HANDO_COMPL:
rc = gsm48_rx_rr_ho_compl(msg);

View File

@ -4,6 +4,7 @@
extern void *tall_msgb_ctx;
extern void *tall_fle_ctx;
extern void *tall_locop_ctx;
extern void *tall_authciphop_ctx;
extern void *tall_gsms_ctx;
extern void *tall_subscr_ctx;
extern void *tall_sub_req_ctx;
@ -22,6 +23,7 @@ void talloc_ctx_init(void)
tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0,
"bs11_file_list_entry");
tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper");
tall_authciphop_ctx = talloc_named_const(tall_bsc_ctx, 0, "auth_ciph_oper");
tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 0, "sms");
tall_subscr_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscriber");
tall_sub_req_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscr_request");