diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h index e0120795f..3960bbe25 100644 --- a/openbsc/include/openbsc/gsm_data.h +++ b/openbsc/include/openbsc/gsm_data.h @@ -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; diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index d5d006822..03f62ab18 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -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 diff --git a/openbsc/src/auth.c b/openbsc/src/auth.c new file mode 100644 index 000000000..5a54da020 --- /dev/null +++ b/openbsc/src/auth.c @@ -0,0 +1,103 @@ +/* Authentication related functions */ + +/* + * (C) 2010 by Sylvain Munaut + * + * 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 +#include +#include + +#include + +#include + + +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; irand); 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; +} + diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c index ed2b4e5b0..256315852 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 @@ -54,6 +55,7 @@ #include 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); diff --git a/openbsc/src/talloc_ctx.c b/openbsc/src/talloc_ctx.c index 4b373b4ae..8e7ec230f 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");