From 9fd4a3ed1205457123645cec08143a49a0677604 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Thu, 24 Dec 2009 00:27:26 +0100 Subject: 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 --- openbsc/include/openbsc/gsm_04_08.h | 5 ++ openbsc/include/openbsc/gsm_data.h | 18 ++++ openbsc/include/openbsc/gsm_utils.h | 12 +++ openbsc/src/gsm_04_08.c | 163 +++++++++++++++++++++++++++++++++++- openbsc/src/talloc_ctx.c | 2 + 5 files changed, 197 insertions(+), 3 deletions(-) diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h index 9cf8afcf3..9c9894215 100644 --- a/openbsc/include/openbsc/gsm_04_08.h +++ b/openbsc/include/openbsc/gsm_04_08.h @@ -133,6 +133,11 @@ struct gsm48_auth_req { u_int8_t rand[16]; } __attribute__ ((packed)); +/* Section 9.2.3 */ +struct gsm48_auth_resp { + u_int8_t sres[4]; +} __attribute__ ((packed)); + /* Section 9.2.15 */ struct gsm48_loc_upd_req { u_int8_t type:4, diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index 4a6acb52a..3c320dbea 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -81,6 +81,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 { @@ -89,6 +90,12 @@ enum gsm_paging_event { GSM_PAGING_OOM, }; +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, @@ -167,6 +174,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 */ @@ -182,6 +198,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? */ @@ -241,6 +258,7 @@ struct gsm_lchan { * 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; diff --git a/openbsc/include/openbsc/gsm_utils.h b/openbsc/include/openbsc/gsm_utils.h index 56a4120a5..0e2cf4a06 100644 --- a/openbsc/include/openbsc/gsm_utils.h +++ b/openbsc/include/openbsc/gsm_utils.h @@ -37,6 +37,18 @@ int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl); int rxlev2dbm(u_int8_t rxlev); u_int8_t dbm2rxlev(int dbm); +/* According to GSM 04.08 Chapter 10.5.1.6 */ +static inline int ms_cm2_a5n_support(u_int8_t *cm2, int n) { + switch (n) { + case 0: return 1; + case 1: return (cm2[0] & (1<<3)) ? 0 : 1; + case 2: return (cm2[2] & (1<<0)) ? 1 : 0; + case 3: return (cm2[2] & (1<<1)) ? 1 : 0; + default: + return 0; + } +} + /* According to GSM 04.08 Chapter 10.5.2.29 */ static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; } static inline int rach_max_trans_raw2val(int raw) { diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index 61eba2c5a..2f2138b17 100644 --- a/openbsc/src/gsm_04_08.c +++ b/openbsc/src/gsm_04_08.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #define GSM_MAX_USERUSER 128 void *tall_locop_ctx; +void *tall_authciphop_ctx; static const struct tlv_definition rsl_att_tlvdef = { .def = { @@ -179,6 +181,95 @@ struct gsm_lai { static u_int32_t new_callref = 0x80000001; + +static void release_security_operation(struct gsm_lchan *lchan) +{ + if (!lchan->sec_operation) + return; + + talloc_free(lchan->sec_operation); + lchan->sec_operation = NULL; + put_lchan(lchan); +} + +static void allocate_security_operation(struct gsm_lchan *lchan) +{ + use_lchan(lchan) + + lchan->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->subscr; + struct gsm_security_operation *op; + struct gsm_auth_info ainfo; + int done = 0, i; + + /* Check if we _can_ enable encryption. Cases where we can't: + * - Channel already secured (nothing to do) + * - Encryption disabled in config + * - Subscriber equipment doesn't support configured encryption + * - Subscriber doesn't have a Ki or it's invalid + */ + if ((lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) || (!net->a5_encryption)) + done = 1; + else if (!ms_cm2_a5n_support(subscr->equipment.classmark2, + net->a5_encryption)) { + DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption"); + done = 1; + } else { + int rc; + rc = get_authinfo_by_subscr(&ainfo, lchan->subscr); + if (rc < 0) { + DEBUGP(DMM, "No retrievable Ki for subscriber, skipping auth"); + done = 1; + } else if ((ainfo.auth_algo != AUTH_ALGO_COMP128v1) || + (ainfo.a3a8_ki_len != A38_COMP128_KEY_LEN)) { + DEBUGP(DMM, "Unsupported auth settings " + "algo_id=%d ki_len=%d\n", + ainfo.auth_algo, ainfo.a3a8_ki_len); + done = 1; + } + } + + if (done) + return cb ? + cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_NOAVAIL, + NULL, lchan, cb_data) : + 0; + + /* Start an operation (can't have more than one pending !!!) */ + if (lchan->sec_operation) + return -EBUSY; + + allocate_security_operation(lchan); + op = lchan->sec_operation; + op->cb = cb; + op->cb_data = cb_data; + + /* FIXME: If key_seq is known and not too old, use the last Kc */ + + /* Generate challenge & session key */ + for (i=0; iatuple.rand); i++) + op->atuple.rand[i] = random() & 0xff; + + op->atuple.key_seq = 0; /* FIXME */ + comp128(ainfo.a3a8_ki, op->atuple.rand, + op->atuple.sres, op->atuple.kc); + + /* Store the new AuthTuple for the subscriber */ + set_authtuple_for_subscr(&op->atuple, lchan->subscr); + + /* FIXME: Should start a timer for completion ... */ + + /* Send authentication request */ + return gsm48_tx_mm_auth_req(lchan, op->atuple.rand, op->atuple.key_seq); +} + static int authorize_subscriber(struct gsm_loc_updating_operation *loc, struct gsm_subscriber *subscriber) { @@ -270,6 +361,7 @@ static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal, return 0; release_loc_updating_req(lchan); + release_security_operation(lchan); /* Free all transactions that are associated with the released lchan */ /* FIXME: this is not neccessarily the right thing to do, we should @@ -1371,6 +1463,42 @@ 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_network *net = lchan->ts->trx->bts->network; + + DEBUGP(DMM, "MM AUTHENTICATION RESPONSE (sres = %s)\n", + hexdump(ar->sres, 4)); + + /* Safety check */ + if (!lchan->sec_operation) { + DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n"); + return -EIO; + } + + /* Validate SRES */ + if (memcmp(lchan->sec_operation->atuple.sres, ar->sres,4)) { + gsm_cbfn *cb = lchan->sec_operation->cb; + if (cb) + cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, + NULL, lchan, lchan->sec_operation->cb_data); + + release_security_operation(lchan); + 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, lchan->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) { @@ -1404,7 +1532,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", @@ -1543,6 +1671,36 @@ static int gsm48_rx_rr_app_info(struct msgb *msg) return db_apdu_blob_store(msg->lchan->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; + gsm_cbfn *cb; + int rc = 0; + + DEBUGP(DRR, "CIPHERING MODE COMPLETE\n"); + + /* Safety check */ + if (!lchan->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 = lchan->sec_operation->cb; + if (cb) { + rc = cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_SUCCEEDED, + NULL, lchan, lchan->sec_operation->cb_data); + } + + /* Complete the operation */ + release_security_operation(lchan); + + return rc; +} + /* Chapter 9.1.16 Handover complete */ static int gsm48_rx_rr_ho_compl(struct msgb *msg) { @@ -1600,8 +1758,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); diff --git a/openbsc/src/talloc_ctx.c b/openbsc/src/talloc_ctx.c index 5f0ee4de8..516eaf8a4 100644 --- a/openbsc/src/talloc_ctx.c +++ b/openbsc/src/talloc_ctx.c @@ -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"); -- cgit v1.2.3