Compare commits

...

5 Commits

Author SHA1 Message Date
Domonkos P. Tomcsanyi cea991aea8 Updated entity name in copyright statements 2022-05-18 17:32:30 +02:00
Tomcsányi, Domonkos 0ed91dc681 simaka_manager: Some more debug logging on success/failure cases 2021-08-26 19:48:11 +02:00
Tomcsányi, Domonkos 65f576bd9c ignore mismatch in received identity.
This is needed, because the standard mandates that the remote entity
must be configured as ims (mimicking the APN setting I think), but on
the other hand the ePDG will identify itself with its FQDN in the end. I
tested this and this is currently the only way to do it with strongswan
I think, because you cannot configure different identities.
2021-08-26 19:47:15 +02:00
Tomcsányi, Domonkos 18b4a240dd permit SHA-1, as some ePDGs require that 2021-08-26 19:46:49 +02:00
Tomcsányi, Domonkos f68dcde6c5 Add support for EAP-AKA against USIM in PC/SC reader 2021-08-26 19:45:59 +02:00
11 changed files with 1061 additions and 4 deletions

View File

@ -1,4 +1,4 @@
# strongSwan Configuration #
## Patched version, containing patch to disable parsing of AUTH payload in IKEv2 Phase2, because we only want EAP-AKA ##
## Overview ##

View File

@ -3,6 +3,8 @@
# Copyright (C) 2006-2019 Andreas Steffen
# Copyright (C) 2006-2014 Martin Willi
# HSR Hochschule fuer Technik Rapperswil
# Copyright (C) 2017 Domonkos P. Tomcsanyi
# umlaut Communications Gmbh.
#
# 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
@ -199,7 +201,8 @@ ARG_ENABL_SET([eap-sim-pcsc], [enable EAP-SIM backend based on a smartcard rea
ARG_ENABL_SET([eap-aka], [enable EAP AKA authentication module.])
ARG_ENABL_SET([eap-aka-3gpp], [enable EAP AKA backend implementing 3GPP MILENAGE algorithms in software.])
ARG_ENABL_SET([eap-aka-3gpp2], [enable EAP AKA backend implementing 3GPP2 algorithms in software. Requires libgmp.])
ARG_ENABL_SET([eap-simaka-sql], [enable EAP-SIM/AKA backend based on a triplet/quintuplet SQL database.])
ARG_ENABL_SET([eap-simaka-pcsc], [enable EAP-SIM/AKA backend based on a smartcard reader. Requires libpcsclite.])
ARG_ENABL_SET([eap-simaka-sql], [enable EAP-SIM/AKA backend based on a triplet/quintuplet SQL database.])
ARG_ENABL_SET([eap-simaka-pseudonym], [enable EAP-SIM/AKA pseudonym storage plugin.])
ARG_ENABL_SET([eap-simaka-reauth], [enable EAP-SIM/AKA reauthentication data storage plugin.])
ARG_ENABL_SET([eap-identity], [enable EAP module providing EAP-Identity helper.])
@ -1203,6 +1206,12 @@ if test x$eap_sim_pcsc = xtrue; then
AC_SUBST(pcsclite_LIBS)
fi
if test x$eap_simaka_pcsc = xtrue; then
PKG_CHECK_MODULES(pcsclite, [libpcsclite])
AC_SUBST(pcsclite_CFLAGS)
AC_SUBST(pcsclite_LIBS)
fi
if test x$nm = xtrue; then
PKG_CHECK_MODULES(nm, [gthread-2.0 libnm])
AC_SUBST(nm_CFLAGS)
@ -1533,6 +1542,7 @@ ADD_PLUGIN([eap-sim-pcsc], [c charon])
ADD_PLUGIN([eap-aka], [c charon])
ADD_PLUGIN([eap-aka-3gpp], [c charon])
ADD_PLUGIN([eap-aka-3gpp2], [c charon])
ADD_PLUGIN([eap-simaka-pcsc], [c charon])
ADD_PLUGIN([eap-simaka-sql], [c charon])
ADD_PLUGIN([eap-simaka-pseudonym], [c charon])
ADD_PLUGIN([eap-simaka-reauth], [c charon])
@ -1704,6 +1714,7 @@ AM_CONDITIONAL(USE_RADATTR, test x$radattr = xtrue)
AM_CONDITIONAL(USE_EAP_SIM, test x$eap_sim = xtrue)
AM_CONDITIONAL(USE_EAP_SIM_FILE, test x$eap_sim_file = xtrue)
AM_CONDITIONAL(USE_EAP_SIM_PCSC, test x$eap_sim_pcsc = xtrue)
AM_CONDITIONAL(USE_EAP_SIMAKA_PCSC, test x$eap_simaka_pcsc = xtrue)
AM_CONDITIONAL(USE_EAP_SIMAKA_SQL, test x$eap_simaka_sql = xtrue)
AM_CONDITIONAL(USE_EAP_SIMAKA_PSEUDONYM, test x$eap_simaka_pseudonym = xtrue)
AM_CONDITIONAL(USE_EAP_SIMAKA_REAUTH, test x$eap_simaka_reauth = xtrue)
@ -1996,6 +2007,7 @@ AC_CONFIG_FILES([
src/libcharon/plugins/eap_sim/Makefile
src/libcharon/plugins/eap_sim_file/Makefile
src/libcharon/plugins/eap_sim_pcsc/Makefile
src/libcharon/plugins/eap_simaka_pcsc/Makefile
src/libcharon/plugins/eap_simaka_sql/Makefile
src/libcharon/plugins/eap_simaka_pseudonym/Makefile
src/libcharon/plugins/eap_simaka_reauth/Makefile

View File

@ -369,6 +369,13 @@ if MONOLITHIC
endif
endif
if USE_EAP_SIMAKA_PCSC
SUBDIRS += plugins/eap_simaka_pcsc
if MONOLITHIC
libcharon_la_LIBADD += plugins/eap_simaka_pcsc/libstrongswan-eap-simaka-pcsc.la
endif
endif
if USE_EAP_SIMAKA_REAUTH
SUBDIRS += plugins/eap_simaka_reauth
if MONOLITHIC

View File

@ -0,0 +1,23 @@
AM_CPPFLAGS = \
-I$(top_srcdir)/src/libstrongswan \
-I$(top_srcdir)/src/libcharon \
-I$(top_srcdir)/src/libsimaka
AM_CFLAGS = \
${pcsclite_CFLAGS} \
$(PLUGIN_CFLAGS)
libstrongswan_eap_simaka_pcsc_la_LIBADD = ${pcsclite_LIBS}
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-simaka-pcsc.la
else
plugin_LTLIBRARIES = libstrongswan-eap-simaka-pcsc.la
libstrongswan_eap_simaka_pcsc_la_LIBADD += $(top_builddir)/src/libsimaka/libsimaka.la
endif
libstrongswan_eap_simaka_pcsc_la_SOURCES = \
eap_simaka_pcsc_plugin.h eap_simaka_pcsc_plugin.c \
eap_simaka_pcsc_card.h eap_simaka_pcsc_card.c
libstrongswan_eap_simaka_pcsc_la_LDFLAGS = -module -avoid-version

View File

@ -0,0 +1,828 @@
/*
* Copyright (C) 2017 Domonkos P. Tomcsanyi
* umlaut Communications Gmbh.
*
* 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. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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.
*/
#define _GNU_SOURCE
#include "eap_simaka_pcsc_card.h"
#include <PCSC/wintypes.h>
#include <PCSC/winscard.h>
#include <daemon.h>
#include <string.h>
typedef struct private_eap_simaka_pcsc_card_t private_eap_simaka_pcsc_card_t;
/**
* Private data of an eap_simaka_pcsc_card_t object.
*/
struct private_eap_simaka_pcsc_card_t {
/**
* Public eap_simaka_pcsc_card_t interface.
*/
eap_simaka_pcsc_card_t public;
};
/**
* Maximum length for an IMSI.
*/
#define SIM_IMSI_MAX_LEN 15
/**
* Length of the status at the end of response APDUs.
*/
#define APDU_STATUS_LEN 2
/**
* First byte of status word indicating success.
*/
#define APDU_SW1_SUCCESS 0x90
/**
* First byte of status word indicating there is response data to be read.
*/
#define APDU_SW1_RESPONSE_DATA 0x9f
/*
* Communication status
*/
#define ERROR_NONE 0
#define ERROR_SCARD 1
#define ERROR_CARD_ERROR 2
/**
* Decode IMSI EF (Elementary File) into an ASCII string
*/
static bool decode_imsi_ef(unsigned char *input, int input_len, char *output)
{
/* Only digits 0-9 valid in IMSIs */
static const char bcd_num_digits[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '\0', '\0', '\0', '\0', '\0', '\0'
};
int i;
/* Check length byte matches how many bytes we have, and that input
* is correct length for an IMSI */
if (input[0] != input_len-1 || input_len < 2 || input_len > 9)
{
return FALSE;
}
/* Check type byte is IMSI (bottom 3 bits == 001) */
if ((input[1] & 0x07) != 0x01)
{
return FALSE;
}
*output++ = bcd_num_digits[input[1] >> 4];
for (i = 2; i < input_len; i++)
{
*output++ = bcd_num_digits[input[i] & 0xf];
*output++ = bcd_num_digits[input[i] >> 4];
}
*output++ = '\0';
return TRUE;
}
METHOD(simaka_card_t, get_triplet, bool,
private_eap_simaka_pcsc_card_t *this, identification_t *id,
char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN])
{
status_t found = FALSE;
LONG rv;
SCARDCONTEXT hContext;
DWORD dwReaders;
LPSTR mszReaders;
char *cur_reader;
char full_nai[128];
SCARDHANDLE hCard;
enum { DISCONNECTED, CONNECTED, TRANSACTION } hCard_status = DISCONNECTED;
snprintf(full_nai, sizeof(full_nai), "%Y", id);
DBG2(DBG_IKE, "looking for triplet: %Y rand %b", id, rand, SIM_RAND_LEN);
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardEstablishContext: %s", pcsc_stringify_error(rv));
return FALSE;
}
rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardListReaders: %s", pcsc_stringify_error(rv));
return FALSE;
}
mszReaders = malloc(sizeof(char)*dwReaders);
rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardListReaders: %s", pcsc_stringify_error(rv));
return FALSE;
}
/* mszReaders is a multi-string of readers, separated by '\0' and
* terminated by an additional '\0' */
for (cur_reader = mszReaders; *cur_reader != '\0' && found == FALSE;
cur_reader += strlen(cur_reader) + 1)
{
DWORD dwActiveProtocol = -1;
const SCARD_IO_REQUEST *pioSendPci;
SCARD_IO_REQUEST pioRecvPci;
BYTE pbRecvBuffer[64];
DWORD dwRecvLength;
char imsi[SIM_IMSI_MAX_LEN + 1];
/* See GSM 11.11 for SIM APDUs */
static const BYTE pbSelectMF[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x3f, 0x00 };
static const BYTE pbSelectDFGSM[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x7f, 0x20 };
static const BYTE pbSelectIMSI[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x6f, 0x07 };
static const BYTE pbReadBinary[] = { 0xa0, 0xb0, 0x00, 0x00, 0x09 };
BYTE pbRunGSMAlgorithm[5 + SIM_RAND_LEN] = { 0xa0, 0x88, 0x00, 0x00, 0x10 };
static const BYTE pbGetResponse[] = { 0xa0, 0xc0, 0x00, 0x00, 0x0c };
/* If on 2nd or later reader, make sure we end the transaction
* and disconnect card in the previous reader */
switch (hCard_status)
{
case TRANSACTION:
SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
/* FALLTHRU */
case CONNECTED:
SCardDisconnect(hCard, SCARD_LEAVE_CARD);
/* FALLTHRU */
case DISCONNECTED:
hCard_status = DISCONNECTED;
}
/* Copy RAND into APDU */
memcpy(pbRunGSMAlgorithm + 5, rand, SIM_RAND_LEN);
rv = SCardConnect(hContext, cur_reader, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardConnect: %s", pcsc_stringify_error(rv));
continue;
}
hCard_status = CONNECTED;
switch(dwActiveProtocol)
{
case SCARD_PROTOCOL_T0:
pioSendPci = SCARD_PCI_T0;
break;
case SCARD_PROTOCOL_T1:
pioSendPci = SCARD_PCI_T1;
break;
default:
DBG1(DBG_IKE, "Unknown SCARD_PROTOCOL");
continue;
}
/* Start transaction */
rv = SCardBeginTransaction(hCard);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardBeginTransaction: %s", pcsc_stringify_error(rv));
continue;
}
hCard_status = TRANSACTION;
/* APDU: Select MF */
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, pbSelectMF, sizeof(pbSelectMF),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
{
DBG1(DBG_IKE, "Select MF failed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}
/* APDU: Select DF GSM */
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, pbSelectDFGSM, sizeof(pbSelectDFGSM),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
{
DBG1(DBG_IKE, "Select DF GSM failed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}
/* APDU: Select IMSI */
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, pbSelectIMSI, sizeof(pbSelectIMSI),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
{
DBG1(DBG_IKE, "Select IMSI failed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}
/* APDU: Read Binary (of IMSI) */
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, pbReadBinary, sizeof(pbReadBinary),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_SUCCESS)
{
DBG1(DBG_IKE, "Select IMSI failed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}
if (!decode_imsi_ef(pbRecvBuffer, dwRecvLength-APDU_STATUS_LEN, imsi))
{
DBG1(DBG_IKE, "Couldn't decode IMSI EF: %b",
pbRecvBuffer, (u_int)dwRecvLength);
continue;
}
/* The IMSI could be post/prefixed in the full NAI, so just make sure
* it's in there */
if (!(strlen(full_nai) && strstr(full_nai, imsi)))
{
DBG1(DBG_IKE, "Not the SIM we're looking for, IMSI: %s", imsi);
continue;
}
/* APDU: Run GSM Algorithm */
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci,
pbRunGSMAlgorithm, sizeof(pbRunGSMAlgorithm),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
{
DBG1(DBG_IKE, "Run GSM Algorithm failed: %b",
pbRecvBuffer, (u_int)dwRecvLength);
continue;
}
/* APDU: Get Response (of Run GSM Algorithm) */
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, pbGetResponse, sizeof(pbGetResponse),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_SUCCESS)
{
DBG1(DBG_IKE, "Get Response failed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}
/* Extract out Kc and SRES from response */
if (dwRecvLength == SIM_SRES_LEN + SIM_KC_LEN + APDU_STATUS_LEN)
{
memcpy(sres, pbRecvBuffer, SIM_SRES_LEN);
memcpy(kc, pbRecvBuffer+4, SIM_KC_LEN);
/* This will also cause the loop to exit */
found = TRUE;
}
else
{
DBG1(DBG_IKE, "Get Response incorrect length: %b",
pbRecvBuffer, (u_int)dwRecvLength);
continue;
}
/* Transaction will be ended and card disconnected at the
* beginning of this loop or after this loop */
}
/* Make sure we end any previous transaction and disconnect card */
switch (hCard_status)
{
case TRANSACTION:
SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
/* FALLTHRU */
case CONNECTED:
SCardDisconnect(hCard, SCARD_LEAVE_CARD);
/* FALLTHRU */
case DISCONNECTED:
hCard_status = DISCONNECTED;
}
rv = SCardReleaseContext(hContext);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardReleaseContext: %s", pcsc_stringify_error(rv));
}
free(mszReaders);
return found;
}
METHOD(simaka_card_t, get_quintuplet, status_t,
private_eap_simaka_pcsc_card_t *this, identification_t *id,
char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN],
char ik[AKA_IK_LEN], char res[AKA_RES_MAX], int *res_len)
{
status_t found = SUCCESS;
LONG rv;
SCARDCONTEXT hContext;
DWORD dwReaders;
LPSTR mszReaders;
char *cur_reader;
char full_nai[128];
SCARDHANDLE hCard;
enum { DISCONNECTED, CONNECTED, TRANSACTION } hCard_status = DISCONNECTED;
snprintf(full_nai, sizeof(full_nai), "%Y", id);
DBG2(DBG_IKE, "looking for quintuplet: %Y rand %b", id, rand, SIM_RAND_LEN);
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardEstablishContext: %s", pcsc_stringify_error(rv));
return FALSE;
}
rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardListReaders: %s", pcsc_stringify_error(rv));
return FALSE;
}
mszReaders = malloc(sizeof(char)*dwReaders);
rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardListReaders: %s", pcsc_stringify_error(rv));
return FALSE;
}
/* mszReaders is a multi-string of readers, separated by '\0' and
* terminated by an additional '\0' */
for (cur_reader = mszReaders; *cur_reader != '\0' && found == FALSE;
cur_reader += strlen(cur_reader) + 1)
{
DWORD dwActiveProtocol = -1;
const SCARD_IO_REQUEST *pioSendPci;
SCARD_IO_REQUEST pioRecvPci;
BYTE pbRecvBuffer[512];
DWORD dwRecvLength;
char imsi[SIM_IMSI_MAX_LEN + 1];
char aid_pattern[] = {0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02}; //based on mitshell/card USIM.py:SELECT_ADF_USIM()
char *correct_aid = NULL;
int resLen = 0;
/* USIM APDUs */
BYTE abSelectEFDIR[] = {0x00, 0xA4, 0x08, 0x04, 0x02, 0x2F, 0x00};
// CLA SELECT P1 P2 len
// 0x00 0xC0 0x00 0x00 0x00
BYTE abGetResponse[] = {0x00, 0xC0, 0x00, 0x00, 0x1C};
// CLA SELECT P1 (by fileID) P2(UICC) len DATA (EF_IMSI address)
// 0x00 0xA4 0x00 0x04 0x02 0x6F 0x07
BYTE abSelectIMSI[] = {0x00, 0xA4, 0x00, 0x04, 0x02, 0x6F, 0x07};
BYTE abReadRecord[] = {0x00, 0xB2, 0x01, 0x04, 0x00}; //Le byte (last one) set to 0x00 so complete record is read up to 256 bytes
// CLA SELECT P1 (AID) P2(UICC) len DATA (AID)
// 0x00 0xA4 0x04 0x04 '0xc', '0xa0', '0x0', '0x0', '0x0', '0x87', '0x10', '0x2', '0xff', '0x49', '0xff', '0x5', '0x89'
BYTE abSelectUICC[] = {0x00, 0xA4, 0x04, 0x04};
BYTE abReadBinary[] = {0x00, 0xB0, 0x00, 0x00, 0x09};
BYTE abAuthenticate[5 + 1 + SIM_RAND_LEN + 1 + SIM_RAND_LEN] = { 0x00, 0x88, 0x00, 0x81, 0x22, 0x10 }; //TOTAL_LEN + LEN(RAND) + RAND + LEN(AUTN) + AUTN
int i,j;
/* If on 2nd or later reader, make sure we end the transaction
* and disconnect card in the previous reader */
switch (hCard_status)
{
case TRANSACTION:
SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
/* FALLTHRU */
case CONNECTED:
SCardDisconnect(hCard, SCARD_LEAVE_CARD);
/* FALLTHRU */
case DISCONNECTED:
hCard_status = DISCONNECTED;
}
/* Copy RAND into APDU */
memcpy(abAuthenticate + 6, rand, SIM_RAND_LEN);
abAuthenticate[6 + SIM_RAND_LEN] = 0x10; //LEN of AUTN
memcpy(abAuthenticate + 6 + SIM_RAND_LEN + 1, autn, SIM_RAND_LEN); //Copy AUTN into APDU
rv = SCardConnect(hContext, cur_reader, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardConnect: %s", pcsc_stringify_error(rv));
continue;
}
hCard_status = CONNECTED;
switch(dwActiveProtocol)
{
case SCARD_PROTOCOL_T0:
pioSendPci = SCARD_PCI_T0;
break;
case SCARD_PROTOCOL_T1:
pioSendPci = SCARD_PCI_T1;
break;
default:
DBG1(DBG_IKE, "Unknown SCARD_PROTOCOL");
continue;
}
/* Start transaction */
rv = SCardBeginTransaction(hCard);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardBeginTransaction: %s", pcsc_stringify_error(rv));
continue;
}
hCard_status = TRANSACTION;
/* APDU: Select EFDIR */
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, abSelectEFDIR, sizeof(abSelectEFDIR),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (pbRecvBuffer[0] == 0x61 && dwRecvLength < 3)
{ // Response bytes available, GET RESPONSE needs to be run
abGetResponse[4] = pbRecvBuffer[1]; //setting the expected length to the one sent by the card
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, abGetResponse, sizeof(abGetResponse),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
}
else if ((pbRecvBuffer[0] == 0x6C && dwRecvLength < 3) || (pbRecvBuffer[0] == 0x67 && dwRecvLength < 3)) //WRONG length used, correcting it
{
abSelectEFDIR[4] = pbRecvBuffer[1]; //setting the expected length to the one sent by the card
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, abSelectEFDIR, sizeof(abSelectEFDIR),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
}
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
/*
if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
{
DBG1(DBG_IKE, "Select EFDIR failed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}*/
for(j=0; j<5; j++) //Fingers crossed there is no SIM card with more than 5 applications in its EF_DIR
{
/* APDU: Read Record */
dwRecvLength = sizeof(pbRecvBuffer);
abReadRecord[2] = j;
rv = SCardTransmit(hCard, pioSendPci, abReadRecord, sizeof(abReadRecord),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
/*
if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
{
DBG1(DBG_IKE, "Read Record failed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}
*/
DBG1(DBG_IKE, "READ RECORD #%d: %b", j, pbRecvBuffer,
(u_int)dwRecvLength);
if ((pbRecvBuffer[0] == 0x6C && dwRecvLength < 3) || (pbRecvBuffer[0] == 0x67 && dwRecvLength < 3)) //WRONG length used, correcting it
{
abReadRecord[4] = pbRecvBuffer[1]; //setting the expected length to the one sent by the card
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, abReadRecord, sizeof(abReadRecord),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
}
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
if ((pbRecvBuffer[0]== 0x61) && (pbRecvBuffer[2] == 0x4F) && dwRecvLength > 6)
{
correct_aid = memmem(pbRecvBuffer, dwRecvLength, aid_pattern, sizeof(aid_pattern));
DBG1(DBG_IKE, "Detecting AIDs...");
if(correct_aid)
break;
} else {
DBG1(DBG_IKE, "Failed to get AID, will not be able to proceed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}
}
if(!correct_aid)
{
DBG1(DBG_IKE, "NOT finding USIM AID (see ETSI TS 101 220 Annex E) pattern, will not be able to proceed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
} else {
unsigned char aid[pbRecvBuffer[3]]; //the transaction buffer contains the right AID with its length
for(i=0; i < sizeof(aid); i++) {
aid[i] = (*correct_aid);
correct_aid++;
}
unsigned char final_apdu[sizeof(aid)+4];
for (i=0; i < sizeof(abSelectUICC); i++) {
final_apdu[i] = abSelectUICC[i];
}
final_apdu[sizeof(abSelectUICC)] = sizeof(aid); //len byte
for (i=0; i < sizeof(aid); i++) { //adding AID to the APDU
final_apdu[i+5] = aid[i];
}
DBG1(DBG_IKE, "Got AID: %b", aid,
sizeof(aid));
DBG1(DBG_IKE, "Selecting UICC...");
/* APDU: Select UICC */
dwRecvLength = sizeof(pbRecvBuffer);
DBG1(DBG_IKE, "Sending APDU: %b", final_apdu,
(u_int)pbRecvBuffer[3]+5);
rv = SCardTransmit(hCard, pioSendPci, final_apdu, pbRecvBuffer[3]+5,
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
if (pbRecvBuffer[0] == 0x61 && dwRecvLength < 3)
{ // Response bytes available, GET RESPONSE needs to be run
abGetResponse[4] = pbRecvBuffer[1]; //setting the expected length to the one sent by the card
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, abGetResponse, sizeof(abGetResponse),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
}
}
/* if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
{
DBG1(DBG_IKE, "Select UICC failed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}
*/
DBG1(DBG_IKE, "Selecting IMSI...");
/* APDU: Select IMSI */
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, abSelectIMSI, sizeof(abSelectIMSI),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
if (pbRecvBuffer[0] == 0x61 && dwRecvLength < 3)
{ // Response bytes available, GET RESPONSE needs to be run
abGetResponse[4] = pbRecvBuffer[1]; //setting the expected length to the one sent by the card
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, abGetResponse, sizeof(abGetResponse),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
}
/*
if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
{
DBG1(DBG_IKE, "Select IMSI failed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}
*/
/* APDU: Read Binary (of IMSI) */
DBG1(DBG_IKE, "Reading binary of IMSI...");
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, abReadBinary, sizeof(abReadBinary),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
DBG1(DBG_IKE, "Response: %b", pbRecvBuffer,
(u_int)dwRecvLength);
if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_SUCCESS)
{
DBG1(DBG_IKE, "Read binary of IMSI failed: %b", pbRecvBuffer,
(u_int)dwRecvLength);
continue;
}
if (!decode_imsi_ef(pbRecvBuffer, dwRecvLength-APDU_STATUS_LEN, imsi))
{
DBG1(DBG_IKE, "Couldn't decode IMSI EF: %b",
pbRecvBuffer, (u_int)dwRecvLength);
continue;
}
DBG1(DBG_IKE, "Got IMSI: %s", imsi);
/* The IMSI could be post/prefixed in the full NAI, so just make sure
* it's in there */
if (!(strlen(full_nai) && strstr(full_nai, imsi)))
{
DBG1(DBG_IKE, "Not the SIM we're looking for, IMSI: %s", imsi);
continue;
}
/* APDU: Authenticate */
DBG1(DBG_IKE, "Running AUTHENTICATE...");
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci,
abAuthenticate, sizeof(abAuthenticate),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
continue;
}
if (pbRecvBuffer[0] == 0x61 && dwRecvLength < 3)
{ // Response bytes available, GET RESPONSE needs to be run
abGetResponse[4] = pbRecvBuffer[1]; //setting the expected length to the one sent by the card
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, pioSendPci, abGetResponse, sizeof(abGetResponse),
&pioRecvPci, pbRecvBuffer, &dwRecvLength);
}
DBG1(DBG_IKE, "Response: %b",
pbRecvBuffer, (u_int)dwRecvLength);
/* if (dwRecvLength < APDU_STATUS_LEN ||
pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
{
DBG1(DBG_IKE, "Authenticate failed: %b",
pbRecvBuffer, (u_int)dwRecvLength);
continue;
}
*/
/* Parsing data from the response into RES, CK, IK */
if(pbRecvBuffer[0] == 0xDB)
{
DBG1(DBG_IKE, "Successful 3G authentication");
if(pbRecvBuffer[1] == 0x08 || pbRecvBuffer[1] == 0x04) //RES
{
resLen = pbRecvBuffer[1];
for(i=0; i< pbRecvBuffer[1]; i++)
{
res[i] = pbRecvBuffer[i+2];
}
(*res_len) = resLen;
} else {
DBG1(DBG_IKE, "RES not 8 or 4 byte long, can't copy it.\n");
continue;
}
if(pbRecvBuffer[resLen+2] == 0x10) //CK SUCCESS_BYTE 0xDB(len=1) + RES_LEN(len1) + resLen
{
for(i=0; i<16; i++)
{
ck[i] = pbRecvBuffer[i+resLen+3];
}
} else {
DBG1(DBG_IKE, "CK not 16 byte long, can't copy it\n");
continue;
}
if(pbRecvBuffer[resLen+3+16] == 0x10) //IK SUCCESS_BYTE(len1) + RES_LEN(len1) + res + CK_LEN(len1) + CK(len16)
{
for(i=0; i<16; i++)
{
ik[i] = pbRecvBuffer[i+ resLen+3+16+1];
}
} else {
DBG1(DBG_IKE, "IK not 16 byte long, can't copy it\n");
continue;
}
DBG1(DBG_IKE, "KEYs established. RES: %b", res, resLen);
DBG1(DBG_IKE, "KEYs established. CK: %b", ck, 16);
DBG1(DBG_IKE, "KEYs established. IK: %b", ik, 16);
found = SUCCESS;
}
if(pbRecvBuffer[0] == 0xDC)
{
DBG1(DBG_IKE, "Sync error between SIM card and network, currently NOT supported.\n");
return NOT_SUPPORTED;
}
/* Transaction will be ended and card disconnected at the
* beginning of this loop or after this loop */
}
/* Make sure we end any previous transaction and disconnect card */
switch (hCard_status)
{
case TRANSACTION:
SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
/* FALLTHRU */
case CONNECTED:
SCardDisconnect(hCard, SCARD_LEAVE_CARD);
/* FALLTHRU */
case DISCONNECTED:
hCard_status = DISCONNECTED;
}
rv = SCardReleaseContext(hContext);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardReleaseContext: %s", pcsc_stringify_error(rv));
}
free(mszReaders);
return found;
}
METHOD(eap_simaka_pcsc_card_t, destroy, void,
private_eap_simaka_pcsc_card_t *this)
{
free(this);
}
/**
* See header
*/
eap_simaka_pcsc_card_t *eap_simaka_pcsc_card_create()
{
private_eap_simaka_pcsc_card_t *this;
INIT(this,
.public = {
.card = {
.get_triplet = _get_triplet,
.get_quintuplet = _get_quintuplet,
.resync = (void*)return_false,
.get_pseudonym = (void*)return_null,
.set_pseudonym = (void*)nop,
.get_reauth = (void*)return_null,
.set_reauth = (void*)nop,
},
.destroy = _destroy,
},
);
return &this->public;
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2017 Domonkos P. Tomcsanyi
* umlaut Communications Gmbh.
*
* 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. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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.
*/
#ifndef EAP_SIMAKA_PCSC_CARD_H_
#define EAP_SIMAKA_PCSC_CARD_H_
#include <simaka_card.h>
typedef struct eap_simaka_pcsc_card_t eap_simaka_pcsc_card_t;
/**
* SIM card implementing PC/SC backend.
*/
struct eap_simaka_pcsc_card_t {
/**
* Implements simaka_card_t interface
*/
simaka_card_t card;
/**
* Destroy a eap_simaka_pcsc_card_t.
*/
void (*destroy)(eap_simaka_pcsc_card_t *this);
};
/**
* Create a eap_simaka_pcsc_card instance.
*/
eap_simaka_pcsc_card_t *eap_simaka_pcsc_card_create();
#endif /** EAP_SIMAKA_PCSC_CARD_H_ @}*/

View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2017 Domonkos P. Tomcsanyi
* umlaut Communications Gmbh.
*
* 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. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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.
*/
#include "eap_simaka_pcsc_plugin.h"
#include "eap_simaka_pcsc_card.h"
#include <daemon.h>
typedef struct private_eap_simaka_pcsc_t private_eap_simaka_pcsc_t;
/**
* Private data of an eap_simaka_pcsc_t object.
*/
struct private_eap_simaka_pcsc_t {
/**
* Public eap_simaka_pcsc_plugin_t interface.
*/
eap_simaka_pcsc_plugin_t public;
/**
* SIM card
*/
eap_simaka_pcsc_card_t *card;
};
METHOD(plugin_t, get_name, char*,
private_eap_simaka_pcsc_t *this)
{
return "eap-simaka-pcsc";
}
/**
* Callback providing our card to register
*/
static simaka_card_t* get_card(private_eap_simaka_pcsc_t *this)
{
if (!this->card)
{
this->card = eap_simaka_pcsc_card_create();
}
return &this->card->card;
}
METHOD(plugin_t, get_features, int,
private_eap_simaka_pcsc_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_CALLBACK(simaka_manager_register, get_card),
PLUGIN_PROVIDE(CUSTOM, "aka-card"),
PLUGIN_DEPENDS(CUSTOM, "aka-manager"),
PLUGIN_PROVIDE(CUSTOM, "sim-card"),
PLUGIN_DEPENDS(CUSTOM, "sim-manager"),
};
*features = f;
return countof(f);
}
METHOD(plugin_t, destroy, void,
private_eap_simaka_pcsc_t *this)
{
DESTROY_IF(this->card);
free(this);
}
/**
* See header
*/
plugin_t *eap_simaka_pcsc_plugin_create()
{
private_eap_simaka_pcsc_t *this;
INIT(this,
.public = {
.plugin = {
.get_name = _get_name,
.get_features = _get_features,
.destroy = _destroy,
},
},
);
return &this->public.plugin;
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2017 Domonkos P. Tomcsanyi
* umlaut Communications Gmbh.
*
* 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. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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.
*/
#ifndef EAP_SIMAKA_PCSC_PLUGIN_H_
#define EAP_SIMAKA_PCSC_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_simaka_pcsc_plugin_t eap_simaka_pcsc_plugin_t;
/**
* Plugin to provide EAP-AKA PC/SC based USIM card backend
*/
struct eap_simaka_pcsc_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_SIMAKA_PCSC_PLUGIN_H_ @}*/

View File

@ -121,13 +121,18 @@ METHOD(simaka_manager_t, card_get_quintuplet, status_t,
switch (status)
{ /* try next on error, but not on INVALID_STATE */
case SUCCESS:
DBG1(DBG_LIB, "SUCCESS");
case INVALID_STATE:
DBG1(DBG_LIB, "INVALID STATE");
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
return status;
case NOT_SUPPORTED:
DBG1(DBG_LIB, "NOT_SUPPORTED");
case FAILED:
DBG1(DBG_LIB, "FAILED");
default:
DBG1(DBG_LIB, "DEFAULT");
tried++;
continue;
}

View File

@ -944,6 +944,7 @@ METHOD(auth_cfg_t, complies, bool,
id1 = (identification_t*)value;
id2 = get(this, t1);
/*
if (!id2 || !id2->matches(id2, id1))
{
if (t1 == AUTH_RULE_IDENTITY &&
@ -965,6 +966,10 @@ METHOD(auth_cfg_t, complies, bool,
"EAP ", id1);
}
}
*/
DBG1(DBG_CFG, "constraint check failed, but we are ignoring it for now: %sidentity '%Y'"
" required ", t1 == AUTH_RULE_IDENTITY ? "" :
"EAP ", id1);
break;
}
case AUTH_RULE_AUTH_CLASS:

View File

@ -343,16 +343,16 @@ bool hasher_algorithm_for_ikev2(hash_algorithm_t alg)
{
switch (alg)
{
case HASH_IDENTITY:
case HASH_SHA1:
case HASH_SHA256:
case HASH_SHA384:
case HASH_SHA512:
return TRUE;
case HASH_IDENTITY:
case HASH_UNKNOWN:
case HASH_MD2:
case HASH_MD4:
case HASH_MD5:
case HASH_SHA1:
case HASH_SHA224:
case HASH_SHA3_224:
case HASH_SHA3_256: