make EIA and EEA algorithms configurable (#323)

this patch allows to configure the EIA and EEA algorithms
the UE uses and announces during attach. They can
be specified as a comma-separated list.

The patch also removes a dedicated NAS config class and uses
the default nas_args_t type for this. There was a full
duplication of members before.

The patch also converts nas_args_t and usim_args_t into a class
with a default constructor that initialize all booleans.
master
Andre Puschmann 6 years ago committed by GitHub
parent 43a8a8e195
commit c8f8c16d50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -31,30 +31,8 @@
#include "srslte/common/security.h" #include "srslte/common/security.h"
#include <string> #include <string>
namespace srslte { namespace srslte {
class srslte_nas_config_t
{
public:
srslte_nas_config_t(uint32_t lcid_ = 0, std::string apn_ = "", std::string apn_protocol_ = "", std::string user_ = "", std::string pass_ = "", bool force_imsi_attach_ = false)
:lcid(lcid_),
apn(apn_),
apn_protocol(apn_protocol_),
user(user_),
pass(pass_),
force_imsi_attach(force_imsi_attach_)
{}
uint32_t lcid;
std::string apn;
std::string apn_protocol;
std::string user;
std::string pass;
bool force_imsi_attach;
};
class srslte_gw_config_t class srslte_gw_config_t
{ {
public: public:

@ -39,13 +39,19 @@ using srslte::byte_buffer_t;
namespace srsue { namespace srsue {
typedef struct { class nas_args_t
{
public:
nas_args_t() : force_imsi_attach(false) {}
std::string apn_name; std::string apn_name;
std::string apn_protocol; std::string apn_protocol;
std::string apn_user; std::string apn_user;
std::string apn_pass; std::string apn_pass;
bool force_imsi_attach; bool force_imsi_attach;
} nas_args_t; std::string eia;
std::string eea;
};
// EMM states (3GPP 24.302 v10.0.0) // EMM states (3GPP 24.302 v10.0.0)
typedef enum { typedef enum {
@ -62,9 +68,6 @@ static const char emm_state_text[EMM_STATE_N_ITEMS][100] = {"NULL",
"DEREGISTERED INITIATED", "DEREGISTERED INITIATED",
"TRACKING AREA UPDATE INITIATED"}; "TRACKING AREA UPDATE INITIATED"};
static const bool eia_caps[8] = {false, true, true, false, false, false, false, false};
static const bool eea_caps[8] = {true, true, true, false, false, false, false, false};
class nas class nas
: public nas_interface_rrc, : public nas_interface_rrc,
public nas_interface_ue, public nas_interface_ue,
@ -72,11 +75,11 @@ class nas
{ {
public: public:
nas(); nas();
void init(usim_interface_nas *usim_, void init(usim_interface_nas* usim_,
rrc_interface_nas *rrc_, rrc_interface_nas* rrc_,
gw_interface_nas *gw_, gw_interface_nas* gw_,
srslte::log *nas_log_, srslte::log* nas_log_,
srslte::srslte_nas_config_t cfg_); nas_args_t args_);
void stop(); void stop();
emm_state_t get_state(); emm_state_t get_state();
@ -105,7 +108,7 @@ private:
usim_interface_nas *usim; usim_interface_nas *usim;
gw_interface_nas *gw; gw_interface_nas *gw;
srslte::srslte_nas_config_t cfg; nas_args_t cfg;
emm_state_t state; emm_state_t state;
@ -145,6 +148,8 @@ private:
uint8_t transaction_id; uint8_t transaction_id;
// Security // Security
bool eia_caps[8];
bool eea_caps[8];
uint8_t k_nas_enc[32]; uint8_t k_nas_enc[32];
uint8_t k_nas_int[32]; uint8_t k_nas_int[32];
@ -234,6 +239,20 @@ private:
} }
return true; return true;
} }
std::vector<uint8_t> split_string(const std::string input)
{
std::vector<uint8_t> list;
std::stringstream ss(input);
while (ss.good()) {
std::string substr;
getline(ss, substr, ',');
if (not substr.empty()) {
list.push_back(atoi(substr.c_str()));
}
}
return list;
}
}; };
} // namespace srsue } // namespace srsue

@ -40,7 +40,10 @@ typedef enum{
auth_algo_xor, auth_algo_xor,
}auth_algo_t; }auth_algo_t;
typedef struct{ class usim_args_t
{
public:
usim_args_t() : using_op(false) {}
std::string mode; std::string mode;
std::string algo; std::string algo;
bool using_op; bool using_op;
@ -51,7 +54,7 @@ typedef struct{
std::string k; std::string k;
std::string pin; std::string pin;
std::string reader; std::string reader;
}usim_args_t; };
class usim_base class usim_base
:public usim_interface_nas :public usim_interface_nas

@ -92,6 +92,8 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("nas.user", bpo::value<string>(&args->nas.apn_user)->default_value(""), "Username for CHAP authentication") ("nas.user", bpo::value<string>(&args->nas.apn_user)->default_value(""), "Username for CHAP authentication")
("nas.pass", bpo::value<string>(&args->nas.apn_pass)->default_value(""), "Password for CHAP authentication") ("nas.pass", bpo::value<string>(&args->nas.apn_pass)->default_value(""), "Password for CHAP authentication")
("nas.force_imsi_attach", bpo::value<bool>(&args->nas.force_imsi_attach)->default_value(false), "Whether to always perform an IMSI attach") ("nas.force_imsi_attach", bpo::value<bool>(&args->nas.force_imsi_attach)->default_value(false), "Whether to always perform an IMSI attach")
("nas.eia", bpo::value<string>(&args->nas.eia)->default_value("1,2"), "List of integrity algorithms included in UE capabilities")
("nas.eea", bpo::value<string>(&args->nas.eea)->default_value("0,1,2"), "List of ciphering algorithms included in UE capabilities")
("pcap.enable", bpo::value<bool>(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark") ("pcap.enable", bpo::value<bool>(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark")
("pcap.filename", bpo::value<string>(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename") ("pcap.filename", bpo::value<string>(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename")

@ -116,7 +116,6 @@ bool ue::init(all_args_t *args_) {
phy_log.push_back(NULL); phy_log.push_back(NULL);
} }
mac_log.set_level(level(args->log.mac_level)); mac_log.set_level(level(args->log.mac_level));
rlc_log.set_level(level(args->log.rlc_level)); rlc_log.set_level(level(args->log.rlc_level));
pdcp_log.set_level(level(args->log.pdcp_level)); pdcp_log.set_level(level(args->log.pdcp_level));
@ -242,9 +241,7 @@ bool ue::init(all_args_t *args_) {
mac.init(&phy, &rlc, &rrc, &mac_log); mac.init(&phy, &rlc, &rrc, &mac_log);
rlc.init(&pdcp, &rrc, this, &rlc_log, &mac, 0 /* RB_ID_SRB0 */); 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); pdcp.init(&rlc, &rrc, &gw, &pdcp_log, 0 /* RB_ID_SRB0 */, SECURITY_DIRECTION_UPLINK);
nas.init(usim, &rrc, &gw, &nas_log, args->nas);
srslte_nas_config_t nas_cfg(1, args->nas.apn_name, args->nas.apn_protocol, args->nas.apn_user, args->nas.apn_pass, args->nas.force_imsi_attach); /* RB_ID_SRB1 */
nas.init(usim, &rrc, &gw, &nas_log, nas_cfg);
gw.init(&pdcp, &nas, &gw_log, 3 /* RB_ID_DRB1 */); gw.init(&pdcp, &nas, &gw_log, 3 /* RB_ID_DRB1 */);
gw.set_netmask(args->expert.ip_netmask); gw.set_netmask(args->expert.ip_netmask);
gw.set_tundevname(args->expert.ip_devname); gw.set_tundevname(args->expert.ip_devname);

@ -55,13 +55,12 @@ nas::nas()
plmn_is_selected = false; plmn_is_selected = false;
chap_id = 0; chap_id = 0;
memset(ipv6_if_id, 0, sizeof(ipv6_if_id)); memset(ipv6_if_id, 0, sizeof(ipv6_if_id));
bzero(eia_caps, sizeof(eia_caps));
bzero(eea_caps, sizeof(eea_caps));
} }
void nas::init(usim_interface_nas *usim_, void nas::init(
rrc_interface_nas *rrc_, usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, srslte::log* nas_log_, nas_args_t cfg_)
gw_interface_nas *gw_,
srslte::log *nas_log_,
srslte::srslte_nas_config_t cfg_)
{ {
pool = byte_buffer_pool::get_instance(); pool = byte_buffer_pool::get_instance();
usim = usim_; usim = usim_;
@ -78,6 +77,33 @@ void nas::init(usim_interface_nas *usim_,
srslte::mcc_to_bytes(mcc, &home_plmn.mcc[0]); srslte::mcc_to_bytes(mcc, &home_plmn.mcc[0]);
srslte::mnc_to_bytes(mnc, home_plmn.mnc); srslte::mnc_to_bytes(mnc, home_plmn.mnc);
} }
// parse and sanity check EIA list
std::vector<uint8_t> cap_list = split_string(cfg_.eia);
if (cap_list.empty()) {
nas_log->error("Empty EIA list. Select at least one EIA algorithm.\n");
}
for (std::vector<uint8_t>::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) {
if (*it != 0 && *it < 3) {
eia_caps[*it] = true;
} else {
nas_log->error("EIA%d is not a valid EIA algorithm.\n", *it);
}
}
// parse and sanity check EEA list
cap_list = split_string(cfg_.eea);
if (cap_list.empty()) {
nas_log->error("Empty EEA list. Select at least one EEA algorithm.\n");
}
for (std::vector<uint8_t>::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) {
if (*it < 3) {
eea_caps[*it] = true;
} else {
nas_log->error("EEA%d is not a valid EEA algorithm.\n", *it);
}
}
cfg = cfg_; cfg = cfg_;
if((read_ctxt_file(&ctxt))) { if((read_ctxt_file(&ctxt))) {
@ -1297,7 +1323,7 @@ void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg) {
} }
// Set the optional flags // Set the optional flags
if (cfg.apn == "") { if (cfg.apn_name == "") {
pdn_con_req.esm_info_transfer_flag_present = false; pdn_con_req.esm_info_transfer_flag_present = false;
} else { } else {
// request ESM info transfer is APN is specified // request ESM info transfer is APN is specified
@ -1573,29 +1599,24 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) {
esm_info_resp.proc_transaction_id = proc_transaction_id; esm_info_resp.proc_transaction_id = proc_transaction_id;
esm_info_resp.eps_bearer_id = 0; // respone shall always have no bearer assigned esm_info_resp.eps_bearer_id = 0; // respone shall always have no bearer assigned
if (cfg.apn == "") { if (cfg.apn_name == "") {
esm_info_resp.apn_present = false; esm_info_resp.apn_present = false;
} else { } else {
nas_log->debug("Including APN %s in ESM info response\n", cfg.apn.c_str()); nas_log->debug("Including APN %s in ESM info response\n", cfg.apn_name.c_str());
esm_info_resp.apn_present = true; esm_info_resp.apn_present = true;
int len = std::min((int)cfg.apn.length(), LIBLTE_STRING_LEN-1); int len = std::min((int)cfg.apn_name.length(), LIBLTE_STRING_LEN - 1);
strncpy(esm_info_resp.apn.apn, cfg.apn.c_str(), len); strncpy(esm_info_resp.apn.apn, cfg.apn_name.c_str(), len);
esm_info_resp.apn.apn[len] = '\0'; esm_info_resp.apn.apn[len] = '\0';
} }
if (cfg.apn_user != "" && cfg.apn_user.length() < LIBLTE_STRING_LEN && cfg.apn_pass != "" &&
cfg.apn_pass.length() < LIBLTE_STRING_LEN) {
if (cfg.user != "" && cfg.user.length() < LIBLTE_STRING_LEN && nas_log->debug("Including CHAP authentication for user %s in ESM info response\n", cfg.apn_user.c_str());
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 // Generate CHAP challenge
uint16_t len = 1 /* CHAP code */ + uint16_t len = 1 /* CHAP code */ + 1 /* ID */ + 2 /* complete length */ + 1 /* data value size */ +
1 /* ID */ + 16 /* data value */ + cfg.apn_user.length();
2 /* complete length */ +
1 /* data value size */ +
16 /* data value */ +
cfg.user.length();
uint8_t challenge[len]; uint8_t challenge[len];
bzero(challenge, len*sizeof(uint8_t)); bzero(challenge, len*sizeof(uint8_t));
@ -1611,8 +1632,8 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) {
} }
// add user as name field // add user as name field
for (size_t i = 0; i < cfg.user.length(); i++) { for (size_t i = 0; i < cfg.apn_user.length(); i++) {
const char *name = cfg.user.c_str(); const char* name = cfg.apn_user.c_str();
challenge[21 + i] = name[i]; challenge[21 + i] = name[i];
} }
@ -1626,32 +1647,30 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) {
response[4] = 16; response[4] = 16;
// Generate response value // Generate response value
uint16_t resp_val_len = 16 /* MD5 len */ + uint16_t resp_val_len = 16 /* MD5 len */ + 1 /* ID */ + cfg.apn_pass.length();
1 /* ID */ +
cfg.pass.length();
uint8_t resp_val[resp_val_len]; uint8_t resp_val[resp_val_len];
resp_val[0] = chap_id; resp_val[0] = chap_id;
// add secret // add secret
for (size_t i = 0; i < cfg.pass.length(); i++) { for (size_t i = 0; i < cfg.apn_pass.length(); i++) {
const char* pass = cfg.pass.c_str(); const char* pass = cfg.apn_pass.c_str();
resp_val[1 + i] = pass[i]; resp_val[1 + i] = pass[i];
} }
// copy original challenge behind secret // copy original challenge behind secret
uint8_t *chal_val = &challenge[5]; uint8_t *chal_val = &challenge[5];
memcpy(&resp_val[1+cfg.pass.length()], chal_val, 16); memcpy(&resp_val[1 + cfg.apn_pass.length()], chal_val, 16);
// Compute MD5 of resp_val and add to response // Compute MD5 of resp_val and add to response
security_md5(resp_val, resp_val_len, &response[5]); security_md5(resp_val, resp_val_len, &response[5]);
// add user as name field again // add user as name field again
for (size_t i = 0; i < cfg.user.length(); i++) { for (size_t i = 0; i < cfg.apn_user.length(); i++) {
const char *name = cfg.user.c_str(); const char* name = cfg.apn_user.c_str();
response[21 + i] = name[i]; response[21 + i] = name[i];
} }
// Add challenge and resposne to ESM info response // Add challenge and response to ESM info response
esm_info_resp.protocol_cnfg_opts_present = true; esm_info_resp.protocol_cnfg_opts_present = true;
esm_info_resp.protocol_cnfg_opts.opt[0].id = LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_CHAP; 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)); memcpy(esm_info_resp.protocol_cnfg_opts.opt[0].contents, challenge, sizeof(challenge));

@ -170,8 +170,9 @@ int security_command_test()
usim.init(&args, &usim_log); usim.init(&args, &usim_log);
srsue::nas nas; srsue::nas nas;
srslte_nas_config_t cfg; nas_args_t cfg;
ZERO_OBJECT(cfg); cfg.eia = "1,2";
cfg.eea = "0,1,2";
nas.init(&usim, &rrc_dummy, &gw, &nas_log, cfg); nas.init(&usim, &rrc_dummy, &gw, &nas_log, cfg);
// push auth request PDU to NAS to generate security context // push auth request PDU to NAS to generate security context
@ -230,10 +231,9 @@ int mme_attach_request_test()
args.op = "63BFA50EE6523365FF14C1F45F88737D"; args.op = "63BFA50EE6523365FF14C1F45F88737D";
usim.init(&args, &usim_log); usim.init(&args, &usim_log);
srslte_nas_config_t nas_cfg; nas_args_t nas_cfg;
ZERO_OBJECT(nas_cfg);
nas_cfg.force_imsi_attach = true; nas_cfg.force_imsi_attach = true;
nas_cfg.apn = "test123"; nas_cfg.apn_name = "test123";
srsue::nas nas; srsue::nas nas;
srsue::gw gw; srsue::gw gw;
@ -302,11 +302,10 @@ int esm_info_request_test()
pool = byte_buffer_pool::get_instance(); pool = byte_buffer_pool::get_instance();
srsue::nas nas; srsue::nas nas;
srslte_nas_config_t cfg; nas_args_t cfg;
ZERO_OBJECT(cfg); cfg.apn_name = "srslte";
cfg.apn = "srslte"; cfg.apn_user = "srsuser";
cfg.user = "srsuser"; cfg.apn_pass = "srspass";
cfg.pass = "srspass";
cfg.force_imsi_attach = true; cfg.force_imsi_attach = true;
nas.init(&usim, &rrc_dummy, &gw, &nas_log, cfg); nas.init(&usim, &rrc_dummy, &gw, &nas_log, cfg);

@ -124,6 +124,10 @@ imei = 353490069873319
# user: Username for CHAP authentication # user: Username for CHAP authentication
# pass: Password for CHAP authentication # pass: Password for CHAP authentication
# force_imsi_attach: Whether to always perform an IMSI attach # force_imsi_attach: Whether to always perform an IMSI attach
# eia: List of integrity algorithms included in UE capabilities
# Supported: 1 - Snow3G, 2 - AES
# eea: List of ciphering algorithms included in UE capabilities
# Supported: 0 - NULL, 1 - Snow3G, 2 - AES
##################################################################### #####################################################################
[nas] [nas]
#apn = internetinternet #apn = internetinternet
@ -131,6 +135,8 @@ imei = 353490069873319
#user = srsuser #user = srsuser
#pass = srspass #pass = srspass
#force_imsi_attach = false #force_imsi_attach = false
#eia = 1,2
#eea = 0,1,2
[gui] [gui]
enable = false enable = false

Loading…
Cancel
Save