diff --git a/lib/include/srslte/common/interfaces_common.h b/lib/include/srslte/common/interfaces_common.h index 50eb024b5..5051cacbe 100644 --- a/lib/include/srslte/common/interfaces_common.h +++ b/lib/include/srslte/common/interfaces_common.h @@ -38,13 +38,17 @@ namespace srslte { class srslte_nas_config_t { public: - srslte_nas_config_t(uint32_t lcid_ = 0, std::string apn_ = "") + srslte_nas_config_t(uint32_t lcid_ = 0, std::string apn_ = "", std::string user_ = "", std::string pass_ = "") :lcid(lcid_), - apn(apn_) + apn(apn_), + user(user_), + pass(pass_) {} uint32_t lcid; std::string apn; + std::string user; + std::string pass; }; diff --git a/lib/include/srslte/common/security.h b/lib/include/srslte/common/security.h index f88d48a17..61b46d640 100644 --- a/lib/include/srslte/common/security.h +++ b/lib/include/srslte/common/security.h @@ -123,6 +123,11 @@ uint8_t security_128_eia2( uint8_t *key, uint32_t msg_len, uint8_t *mac); +uint8_t security_md5(const uint8_t *input, + size_t len, + uint8_t *output); + + /****************************************************************************** * Encryption / Decryption *****************************************************************************/ diff --git a/lib/src/common/security.cc b/lib/src/common/security.cc index b10b5beab..63cd478c0 100644 --- a/lib/src/common/security.cc +++ b/lib/src/common/security.cc @@ -29,6 +29,13 @@ #include "srslte/common/liblte_security.h" #include "srslte/common/snow_3g.h" +#ifdef HAVE_MBEDTLS +#include "mbedtls/md5.h" +#endif +#ifdef HAVE_POLARSSL +#include "polarssl/md5.h" +#endif + namespace srslte { /****************************************************************************** @@ -166,6 +173,19 @@ uint8_t security_128_eia2( uint8_t *key, mac); } +uint8_t security_md5(const uint8_t *input, size_t len, uint8_t *output) +{ + memset(output, 0x00, 16); +#ifdef HAVE_MBEDTLS + mbedtls_md5(input, len, output); +#endif // HAVE_MBEDTLS +#ifdef HAVE_POLARSSL + md5(input, len, output); +#endif + return SRSLTE_SUCCESS; +} + + /****************************************************************************** * Encryption / Decryption *****************************************************************************/ diff --git a/srsue/hdr/ue_base.h b/srsue/hdr/ue_base.h index 63bb19533..41abd15e0 100644 --- a/srsue/hdr/ue_base.h +++ b/srsue/hdr/ue_base.h @@ -128,7 +128,9 @@ typedef struct { usim_args_t usim; rrc_args_t rrc; std::string ue_category_str; - std::string apn; + std::string apn_name; + std::string apn_user; + std::string apn_pass; expert_args_t expert; }all_args_t; diff --git a/srsue/hdr/upper/nas.h b/srsue/hdr/upper/nas.h index 7879d50f3..e84b4002a 100644 --- a/srsue/hdr/upper/nas.h +++ b/srsue/hdr/upper/nas.h @@ -127,6 +127,8 @@ private: uint32_t ip_addr; uint8_t eps_bearer_id; + uint8_t chap_id; + uint8_t transaction_id; // Security diff --git a/srsue/src/main.cc b/srsue/src/main.cc index fd7d0e161..10759321e 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -83,7 +83,9 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { "UECapabilityInformation message. Default 0xe6041c00") ("rrc.ue_category", bpo::value(&args->ue_category_str)->default_value("4"), "UE Category (1 to 5)") - ("nas.apn", bpo::value(&args->apn)->default_value(""), "Set Access Point Name (APN) for data services") + ("nas.apn", bpo::value(&args->apn_name)->default_value(""), "Set Access Point Name (APN) for data services") + ("nas.user", bpo::value(&args->apn_user)->default_value(""), "Username for CHAP authentication") + ("nas.pass", bpo::value(&args->apn_pass)->default_value(""), "Password for CHAP authentication") ("pcap.enable", bpo::value(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark") ("pcap.filename", bpo::value(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename") diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index ea73c3f13..53dcf0528 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -205,7 +205,7 @@ bool ue::init(all_args_t *args_) { rlc.init(&pdcp, &rrc, this, &rlc_log, &mac, 0 /* RB_ID_SRB0 */); pdcp.init(&rlc, &rrc, &gw, &pdcp_log, 0 /* RB_ID_SRB0 */, SECURITY_DIRECTION_UPLINK); - srslte_nas_config_t nas_cfg(1, args->apn); /* RB_ID_SRB1 */ + srslte_nas_config_t nas_cfg(1, args->apn_name, args->apn_user, args->apn_pass); /* RB_ID_SRB1 */ nas.init(usim, &rrc, &gw, &nas_log, nas_cfg); gw.init(&pdcp, &nas, &gw_log, 3 /* RB_ID_DRB1 */); diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc index 7a73ab5a6..af24d70e6 100644 --- a/srsue/src/upper/nas.cc +++ b/srsue/src/upper/nas.cc @@ -53,6 +53,7 @@ nas::nas() ctxt.cipher_algo = CIPHERING_ALGORITHM_ID_EEA0; ctxt.integ_algo = INTEGRITY_ALGORITHM_ID_EIA0; plmn_is_selected = false; + chap_id = 0; } void nas::init(usim_interface_nas *usim_, @@ -1218,12 +1219,93 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) { if (cfg.apn == "") { esm_info_resp.apn_present = false; } else { + nas_log->debug("Including APN %s in ESM info response\n", cfg.apn.c_str()); esm_info_resp.apn_present = true; int len = std::min((int)cfg.apn.length(), LIBLTE_STRING_LEN); strncpy(esm_info_resp.apn.apn, cfg.apn.c_str(), len); esm_info_resp.apn.apn[len - 1] = '\0'; } - esm_info_resp.protocol_cnfg_opts_present = false; + + + if (cfg.user != "" && cfg.user.length() < LIBLTE_STRING_LEN && + cfg.pass != "" && cfg.pass.length() < LIBLTE_STRING_LEN) { + + nas_log->debug("Including CHAP authentication for user %s in ESM info response\n", cfg.user.c_str()); + + // Generate CHAP challenge + uint16_t len = 1 /* CHAP code */ + + 1 /* ID */ + + 2 /* complete length */ + + 1 /* data value size */ + + 16 /* data value */ + + cfg.user.length(); + + uint8_t challenge[len]; + challenge[0] = 0x01; // challenge code + challenge[1] = chap_id; // ID + + challenge[2] = (len >> 8) & 0xff; + challenge[3] = len & 0xff; + challenge[4] = 16; + + uint8_t chal_val[16] = { 0xed, 0x0b, 0x26, 0x26, 0xed, 0x0b, 0x26, 0x26, + 0xed, 0x0b, 0x26, 0x26, 0xed, 0x0b, 0x26, 0x26 }; + for (int i = 0; i < 16; i++) { + challenge[5 + i] = chal_val[i]; + } + + // add user as name field + for (size_t i = 0; i < cfg.user.length(); i++) { + const char *name = cfg.user.c_str(); + challenge[21 + i] = name[i]; + } + + // Generate response + uint8_t response[len]; + response[0] = 0x02; // response code + response[1] = chap_id; + response[2] = (len >> 8) & 0xff; + response[3] = len & 0xff; + response[4] = 16; + + // Generate response value + uint16_t resp_val_len = 16 /* MD5 len */ + + 1 /* ID */ + + cfg.pass.length(); + uint8_t resp_val[resp_val_len]; + resp_val[0] = chap_id; + + // add secret + for (size_t i = 0; i < cfg.pass.length(); i++) { + const char* pass = cfg.pass.c_str(); + resp_val[1 + i] = pass[i]; + } + + // copy original challenge behind secret + memcpy(&resp_val[1+cfg.pass.length()], chal_val, 16); + + // Compute MD5 of resp_val and add to response + security_md5(resp_val, resp_val_len, &response[5]); + + // add user as name field again + for (size_t i = 0; i < cfg.user.length(); i++) { + const char *name = cfg.user.c_str(); + response[21 + i] = name[i]; + } + + // Add challenge and resposne to ESM info response + esm_info_resp.protocol_cnfg_opts_present = true; + esm_info_resp.protocol_cnfg_opts.opt[0].id = LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_CHAP; + memcpy(esm_info_resp.protocol_cnfg_opts.opt[0].contents, challenge, sizeof(challenge)); + esm_info_resp.protocol_cnfg_opts.opt[0].len = sizeof(challenge); + + esm_info_resp.protocol_cnfg_opts.opt[1].id = LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_CHAP; + memcpy(esm_info_resp.protocol_cnfg_opts.opt[1].contents, response, sizeof(response)); + esm_info_resp.protocol_cnfg_opts.opt[1].len = sizeof(response); + esm_info_resp.protocol_cnfg_opts.N_opts = 2; + } else { + esm_info_resp.protocol_cnfg_opts_present = false; + } byte_buffer_t *pdu = pool_allocate; if (!pdu) { @@ -1260,6 +1342,7 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) { rrc->write_sdu(cfg.lcid, pdu); ctxt.tx_count++; + chap_id++; } diff --git a/srsue/test/upper/nas_test.cc b/srsue/test/upper/nas_test.cc index 9a3054c6e..589e24da9 100644 --- a/srsue/test/upper/nas_test.cc +++ b/srsue/test/upper/nas_test.cc @@ -277,6 +277,8 @@ int esm_info_request_test() srsue::nas nas; srslte_nas_config_t cfg; cfg.apn = "srslte"; + cfg.user = "srsuser"; + cfg.pass = "srspass"; nas.init(&usim, &rrc_dummy, &gw, &nas_log, cfg); // push ESM info request PDU to NAS to generate response