aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSylvain Munaut <tnt@246tNt.com>2009-12-24 00:27:26 +0100
committerSylvain Munaut <tnt@246tNt.com>2010-01-07 10:49:51 +0100
commit9fd4a3ed1205457123645cec08143a49a0677604 (patch)
treef04b0616c42a175bf0a27b7360c414362af94b05
parent17d4bc8aa9f6d56f06ec280b6f97db2b61d90566 (diff)
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>
-rw-r--r--openbsc/include/openbsc/gsm_04_08.h5
-rw-r--r--openbsc/include/openbsc/gsm_data.h18
-rw-r--r--openbsc/include/openbsc/gsm_utils.h12
-rw-r--r--openbsc/src/gsm_04_08.c163
-rw-r--r--openbsc/src/talloc_ctx.c2
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 <time.h>
#include <netinet/in.h>
+#include <openbsc/comp128.h>
#include <openbsc/db.h>
#include <openbsc/msgb.h>
#include <openbsc/bitvec.h>
@@ -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; i<sizeof(op->atuple.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");