add support for multiple encryption algorithms and a5/4
Change-Id: Ie6700c4e9d2df1eb5fde1b971e287b62668cc2de Related: SYS#5324
This commit is contained in:
parent
a33f00637e
commit
2f898265d0
|
@ -175,6 +175,7 @@ struct sgsn_mm_ctx {
|
|||
/* Iu: CK, IK, KSI */
|
||||
/* CKSN */
|
||||
enum gprs_ciph_algo ciph_algo;
|
||||
uint8_t ue_cipher_mask;
|
||||
/* Auth & Ciphering Request reference from 3GPP TS 24.008 § 10.5.5.19: */
|
||||
uint8_t ac_ref_nr_used;
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ struct sgsn_config {
|
|||
struct gprs_ns2_inst *nsi;
|
||||
|
||||
enum sgsn_auth_policy auth_policy;
|
||||
enum gprs_ciph_algo cipher;
|
||||
uint8_t cipher_support_mask;
|
||||
struct llist_head imsi_acl;
|
||||
|
||||
struct sockaddr_in gsup_server_addr;
|
||||
|
|
|
@ -445,6 +445,17 @@ static bool mmctx_is_r99(const struct sgsn_mm_ctx *mm)
|
|||
return false;
|
||||
}
|
||||
|
||||
static enum gprs_ciph_algo gprs_ms_net_select_best_gea(uint8_t net_mask, uint8_t ms_mask) {
|
||||
uint8_t common_mask = net_mask & ms_mask;
|
||||
uint8_t r = 0;
|
||||
|
||||
while (common_mask >>= 1) {
|
||||
r++;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* 3GPP TS 24.008 § 9.4.9: Authentication and Ciphering Request */
|
||||
int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm,
|
||||
const struct osmo_auth_vector *vec,
|
||||
|
@ -1147,6 +1158,21 @@ static void mmctx_handle_rat_change(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
|||
|
||||
}
|
||||
|
||||
static uint8_t gprs_ms_net_cap_gea_mask(const uint8_t *ms_net_cap, uint8_t cap_len)
|
||||
{
|
||||
uint8_t mask = (1 << GPRS_ALGO_GEA0);
|
||||
mask |= (0x80 & ms_net_cap[0]) ? (1 << GPRS_ALGO_GEA1) : 0;
|
||||
|
||||
if (cap_len < 2)
|
||||
return mask;
|
||||
|
||||
/* extended GEA bits start from 2nd bit of the next byte */
|
||||
mask |= (0x40 & ms_net_cap[1]) ? (1 << GPRS_ALGO_GEA2) : 0;
|
||||
mask |= (0x20 & ms_net_cap[1]) ? (1 << GPRS_ALGO_GEA3) : 0;
|
||||
mask |= (0x10 & ms_net_cap[1]) ? (1 << GPRS_ALGO_GEA4) : 0;
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* 3GPP TS 24.008 § 9.4.1 Attach request */
|
||||
static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
|
||||
struct gprs_llc_llme *llme)
|
||||
|
@ -1290,15 +1316,27 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
|
|||
ctx->ms_radio_access_capa.len);
|
||||
ctx->ms_network_capa.len = msnc_len;
|
||||
memcpy(ctx->ms_network_capa.buf, msnc, msnc_len);
|
||||
if (!gprs_ms_net_cap_gea_supported(ctx->ms_network_capa.buf, msnc_len,
|
||||
ctx->ciph_algo)) {
|
||||
|
||||
ctx->ue_cipher_mask = gprs_ms_net_cap_gea_mask(ctx->ms_network_capa.buf, msnc_len);
|
||||
|
||||
if (!(ctx->ue_cipher_mask & sgsn->cfg.cipher_support_mask)) {
|
||||
reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with MI "
|
||||
"%s because MS do not support required %s "
|
||||
"encryption\n", mi_log_string,
|
||||
get_value_string(gprs_cipher_names,ctx->ciph_algo));
|
||||
"%s because MS do not support required encryption, mask UE:0x%02x NW:0x%02x \n",
|
||||
mi_log_string, ctx->ue_cipher_mask, sgsn->cfg.cipher_support_mask);
|
||||
goto rejected;
|
||||
}
|
||||
|
||||
/* just assume that everythig is fine if the phone offers a5/4:
|
||||
* it requires a valid umts security context which we can only have after
|
||||
* 1) IDENTITY REQUEST to know what to ask the HLR for
|
||||
* 2) and AUTHENTICATION AND CIPHERING REQUEST
|
||||
* ... but 2) already requires selecting a cipher mode.
|
||||
* So let's just assume we will have the auth data required to make it work.
|
||||
*/
|
||||
|
||||
ctx->ciph_algo = gprs_ms_net_select_best_gea(ctx->ue_cipher_mask, sgsn->cfg.cipher_support_mask);
|
||||
|
||||
#ifdef PTMSI_ALLOC
|
||||
/* Allocate a new P-TMSI (+ P-TMSI signature) and update TLLI */
|
||||
ptmsi_update(ctx);
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
#include <osmocom/sgsn/gprs_sndcp_comp.h>
|
||||
#include <osmocom/sgsn/gprs_sndcp.h>
|
||||
|
||||
#include <osmocom/crypt/kdf.h>
|
||||
|
||||
const struct value_string gprs_llc_llme_state_names[] = {
|
||||
{ GPRS_LLMS_UNASSIGNED, "UNASSIGNED" },
|
||||
{ GPRS_LLMS_ASSIGNED, "ASSIGNED" },
|
||||
|
@ -1042,8 +1044,13 @@ void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme)
|
|||
llme->algo = mm->ciph_algo;
|
||||
if (llme->cksn != mm->auth_triplet.key_seq &&
|
||||
mm->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) {
|
||||
memcpy(llme->kc, mm->auth_triplet.vec.kc,
|
||||
gprs_cipher_key_length(mm->ciph_algo));
|
||||
|
||||
/* gea4 needs kc128 */
|
||||
if (mm->ciph_algo == GPRS_ALGO_GEA4)
|
||||
osmo_kdf_kc128(mm->auth_triplet.vec.ck, mm->auth_triplet.vec.ik, llme->kc);
|
||||
else
|
||||
memcpy(llme->kc, mm->auth_triplet.vec.kc, gprs_cipher_key_length(mm->ciph_algo));
|
||||
|
||||
llme->cksn = mm->auth_triplet.key_seq;
|
||||
}
|
||||
} else
|
||||
|
|
|
@ -293,11 +293,8 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli,
|
|||
memcpy(&ctx->ra, raid, sizeof(ctx->ra));
|
||||
ctx->ran_type = MM_CTX_T_GERAN_Gb;
|
||||
ctx->gb.tlli = tlli;
|
||||
ctx->ciph_algo = sgsn->cfg.cipher;
|
||||
osmo_fsm_inst_update_id_f(ctx->gb.mm_state_fsm, "%" PRIu32, tlli);
|
||||
|
||||
LOGMMCTXP(LOGL_DEBUG, ctx, "Allocated with %s cipher.\n",
|
||||
get_value_string(gprs_cipher_names, ctx->ciph_algo));
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
|
|
@ -206,6 +206,7 @@ static int config_write_sgsn(struct vty *vty)
|
|||
struct apn_ctx *actx;
|
||||
struct ares_addr_node *server;
|
||||
struct sgsn_mme_ctx *mme;
|
||||
int i;
|
||||
|
||||
vty_out(vty, "sgsn%s", VTY_NEWLINE);
|
||||
|
||||
|
@ -236,10 +237,15 @@ static int config_write_sgsn(struct vty *vty)
|
|||
for (server = sgsn->ares_servers; server; server = server->next)
|
||||
vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE);
|
||||
|
||||
if (g_cfg->cipher != GPRS_ALGO_GEA0)
|
||||
vty_out(vty, " encryption %s%s",
|
||||
get_value_string(gprs_cipher_names, g_cfg->cipher),
|
||||
VTY_NEWLINE);
|
||||
if (g_cfg->cipher_support_mask != 0) {
|
||||
vty_out(vty, " encryption gea");
|
||||
|
||||
for (i = 0; i < _GPRS_ALGO_NUM; i++)
|
||||
if (g_cfg->cipher_support_mask >> i & 1)
|
||||
vty_out(vty, " %u", i);
|
||||
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
}
|
||||
if (g_cfg->sgsn_ipa_name)
|
||||
vty_out(vty, " gsup ipa-name %s%s", g_cfg->sgsn_ipa_name, VTY_NEWLINE);
|
||||
if (g_cfg->gsup_server_addr.sin_addr.s_addr)
|
||||
|
@ -721,15 +727,19 @@ DEFUN(imsi_acl, cfg_imsi_acl_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_encrypt, cfg_encrypt_cmd,
|
||||
DEFUN_DEPRECATED(cfg_encrypt, cfg_encrypt_cmd,
|
||||
"encryption (GEA0|GEA1|GEA2|GEA3|GEA4)",
|
||||
"Set encryption algorithm for SGSN\n"
|
||||
"Use GEA0 (no encryption)\n"
|
||||
"Use GEA1\nUse GEA2\nUse GEA3\nUse GEA4\n")
|
||||
{
|
||||
enum gprs_ciph_algo c = get_string_value(gprs_cipher_names, argv[0]);
|
||||
|
||||
if (strcmp(argv[0], "gea") == 0)
|
||||
return CMD_SUCCESS;
|
||||
|
||||
if (c != GPRS_ALGO_GEA0) {
|
||||
if (!gprs_cipher_supported(c)) {
|
||||
if (gprs_cipher_supported(c) <= 0) {
|
||||
vty_out(vty, "%% cipher %s is unsupported in current version%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
@ -741,7 +751,46 @@ DEFUN(cfg_encrypt, cfg_encrypt_cmd,
|
|||
}
|
||||
}
|
||||
|
||||
g_cfg->cipher = c;
|
||||
g_cfg->cipher_support_mask |= (1 << c);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_encrypt2, cfg_encrypt2_cmd,
|
||||
"encryption gea <0-4> [<0-4>] [<0-4>] [<0-4>] [<0-4>]",
|
||||
"Set encryption algorithms for SGSN\n"
|
||||
"GPRS Encryption Algorithm\n"
|
||||
"GEAn Algorithm Number\n"
|
||||
"GEAn Algorithm Number\n"
|
||||
"GEAn Algorithm Number\n"
|
||||
"GEAn Algorithm Number\n"
|
||||
"GEAn Algorithm Number\n")
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
g_cfg->cipher_support_mask = 0;
|
||||
for (i = 0; i < argc; i++)
|
||||
g_cfg->cipher_support_mask |= (1 << atoi(argv[i]));
|
||||
|
||||
for (i = 0; i < _GPRS_ALGO_NUM; i++) {
|
||||
if (g_cfg->cipher_support_mask >> i & 1) {
|
||||
|
||||
if (i == GPRS_ALGO_GEA0)
|
||||
continue;
|
||||
|
||||
if (gprs_cipher_supported(i) <= 0) {
|
||||
vty_out(vty, "%% cipher %d is unsupported in current version%s", i, VTY_NEWLINE);
|
||||
return CMD_ERR_INCOMPLETE;
|
||||
}
|
||||
|
||||
if (!g_cfg->require_authentication) {
|
||||
vty_out(vty, "%% unable to use encryption %s without authentication: please adjust auth-policy%s",
|
||||
argv[i], VTY_NEWLINE);
|
||||
return CMD_ERR_INCOMPLETE;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -1640,7 +1689,11 @@ int sgsn_vty_init(struct sgsn_config *cfg)
|
|||
install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
|
||||
install_element(SGSN_NODE, &cfg_auth_policy_cmd);
|
||||
install_element(SGSN_NODE, &cfg_authentication_cmd);
|
||||
|
||||
/* order matters here: ensure we attempt to parse our new command first! */
|
||||
install_element(SGSN_NODE, &cfg_encrypt2_cmd);
|
||||
install_element(SGSN_NODE, &cfg_encrypt_cmd);
|
||||
|
||||
install_element(SGSN_NODE, &cfg_gsup_ipa_name_cmd);
|
||||
install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
|
||||
install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
|
||||
|
@ -1691,6 +1744,8 @@ int sgsn_parse_config(const char *config_file)
|
|||
/* make sure sgsn_vty_init() was called before this */
|
||||
OSMO_ASSERT(g_cfg);
|
||||
|
||||
g_cfg->cipher_support_mask = 0x1; /* support GEA0 by default unless specific encryption config exists */
|
||||
|
||||
rc = vty_read_config_file(config_file, NULL);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
|
||||
|
|
|
@ -35,7 +35,7 @@ OsmoSGSN(config-sgsn)# list
|
|||
imsi-acl (add|del) IMSI
|
||||
auth-policy (accept-all|closed|acl-only|remote)
|
||||
authentication (optional|required)
|
||||
encryption (GEA0|GEA1|GEA2|GEA3|GEA4)
|
||||
encryption gea <0-4> [<0-4>] [<0-4>] [<0-4>] [<0-4>]
|
||||
gsup ipa-name NAME
|
||||
gsup remote-ip A.B.C.D
|
||||
gsup remote-port <0-65535>
|
||||
|
|
|
@ -48,6 +48,7 @@ static struct sgsn_instance sgsn_inst = {
|
|||
.cfg = {
|
||||
.gtp_statedir = "./",
|
||||
.auth_policy = SGSN_AUTH_POLICY_CLOSED,
|
||||
.cipher_support_mask = 0x1,
|
||||
},
|
||||
};
|
||||
struct sgsn_instance *sgsn = &sgsn_inst;
|
||||
|
|
Loading…
Reference in New Issue