Added SIM card backend based on pcsc-lite

This commit is contained in:
Duncan Salerno 2011-04-04 08:51:50 +02:00 committed by Martin Willi
parent 4ceb31f941
commit 80dca77a50
7 changed files with 527 additions and 0 deletions

View File

@ -112,6 +112,7 @@ ARG_ENABL_SET([unit-tests], [enable unit tests on IKEv2 daemon startup.])
ARG_ENABL_SET([load-tester], [enable load testing plugin for IKEv2 daemon.])
ARG_ENABL_SET([eap-sim], [enable SIM authentication module for EAP.])
ARG_ENABL_SET([eap-sim-file], [enable EAP-SIM backend based on a triplet file.])
ARG_ENABL_SET([eap-sim-pcsc], [enable EAP-SIM 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.])
@ -632,6 +633,12 @@ if test x$maemo = xtrue; then
AC_SUBST(dbusservicedir)
fi
if test x$eap_sim_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_EXISTS([libnm-glib],
[PKG_CHECK_MODULES(nm, [NetworkManager gthread-2.0 libnm-glib libnm-glib-vpn])],
@ -761,6 +768,7 @@ ADD_PLUGIN([updown], [c libcharon])
ADD_PLUGIN([eap-identity], [c libcharon])
ADD_PLUGIN([eap-sim], [c libcharon])
ADD_PLUGIN([eap-sim-file], [c libcharon])
ADD_PLUGIN([eap-sim-pcsc], [c libcharon])
ADD_PLUGIN([eap-simaka-sql], [c libcharon])
ADD_PLUGIN([eap-simaka-pseudonym], [c libcharon])
ADD_PLUGIN([eap-simaka-reauth], [c libcharon])
@ -872,6 +880,7 @@ AM_CONDITIONAL(USE_DUPLICHECK, test x$duplicheck = xtrue)
AM_CONDITIONAL(USE_COUPLING, test x$coupling = 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_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)
@ -1027,6 +1036,7 @@ AC_OUTPUT(
src/libcharon/plugins/eap_gtc/Makefile
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_sql/Makefile
src/libcharon/plugins/eap_simaka_pseudonym/Makefile
src/libcharon/plugins/eap_simaka_reauth/Makefile

View File

@ -223,6 +223,13 @@ if MONOLITHIC
endif
endif
if USE_EAP_SIM_PCSC
SUBDIRS += plugins/eap_sim_pcsc
if MONOLITHIC
libcharon_la_LIBADD += plugins/eap_sim_pcsc/libstrongswan-eap-sim-pcsc.la
endif
endif
if USE_EAP_SIMAKA_SQL
SUBDIRS += plugins/eap_simaka_sql
if MONOLITHIC

View File

@ -0,0 +1,18 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \
-I$(top_srcdir)/src/libcharon ${pcsclite_CFLAGS}
AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${sysconfdir}\"
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-sim-pcsc.la
else
plugin_LTLIBRARIES = libstrongswan-eap-sim-pcsc.la
endif
libstrongswan_eap_sim_pcsc_la_SOURCES = \
eap_sim_pcsc_plugin.h eap_sim_pcsc_plugin.c \
eap_sim_pcsc_card.h eap_sim_pcsc_card.c
libstrongswan_eap_sim_pcsc_la_LDFLAGS = -module -avoid-version
libstrongswan_eap_sim_pcsc_la_LIBADD = ${pcsclite_LIBS}

View File

@ -0,0 +1,342 @@
/*
* Copyright (C) 2011 Duncan Salerno
*
* 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_sim_pcsc_card.h"
#include <PCSC/wintypes.h>
#include <PCSC/winscard.h>
#include <daemon.h>
typedef struct private_eap_sim_pcsc_card_t private_eap_sim_pcsc_card_t;
/**
* Private data of an eap_sim_pcsc_card_t object.
*/
struct private_eap_sim_pcsc_card_t {
/**
* Public eap_sim_pcsc_card_t interface.
*/
eap_sim_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
/**
* 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 */
if ((input[1] & 0xf) != 0x9)
{
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;
}
/**
* Implementation of sim_card_t.get_triplet
*/
static bool get_triplet(private_eap_sim_pcsc_card_t *this,
identification_t *id, char *rand, char *sres, char *kc)
{
status_t found = FALSE;
LONG rv;
SCARDCONTEXT hContext;
DWORD dwReaders;
LPSTR mszReaders;
char *cur_reader;
char full_nai[128];
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;
SCARDHANDLE hCard;
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 */
BYTE pbSelectMF[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x3f, 0x00 };
BYTE pbSelectDFGSM[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x7f, 0x20 };
BYTE pbSelectIMSI[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x6f, 0x07 };
BYTE pbReadBinary[] = { 0xa0, 0xb0, 0x00, 0x00, 0x09 };
BYTE pbRunGSMAlgorithm[] = { 0xa0, 0x88, 0x00, 0x00, 0x10, /*RAND*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE pbGetResponse[] = { 0xa0, 0xc0, 0x00, 0x00, 0x0c };
/* 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;
}
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;
}
/* 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, 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, 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, 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, dwRecvLength);
continue;
}
if (!decode_imsi_ef(pbRecvBuffer, dwRecvLength-APDU_STATUS_LEN, imsi) )
{
DBG1(DBG_IKE, "Couldn't decode IMSI EF: %b", pbRecvBuffer, 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, 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, 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, dwRecvLength);
continue;
}
rv = SCardDisconnect(hCard, SCARD_LEAVE_CARD);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardDisconnect: %s", pcsc_stringify_error(rv));
continue;
}
}
rv = SCardReleaseContext(hContext);
if (rv != SCARD_S_SUCCESS)
{
DBG1(DBG_IKE, "SCardReleaseContext: %s", pcsc_stringify_error(rv));
}
free(mszReaders);
return found;
}
/**
* Implementation of sim_card_t.get_quintuplet
*/
static status_t get_quintuplet()
{
return NOT_SUPPORTED;
}
/**
* Implementation of eap_sim_pcsc_card_t.destroy.
*/
static void destroy(private_eap_sim_pcsc_card_t *this)
{
free(this);
}
/**
* See header
*/
eap_sim_pcsc_card_t *eap_sim_pcsc_card_create()
{
private_eap_sim_pcsc_card_t *this = malloc_thing(private_eap_sim_pcsc_card_t);
this->public.card.get_triplet = (bool(*)(sim_card_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))get_triplet;
this->public.card.get_quintuplet = (status_t(*)(sim_card_t*, 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))get_quintuplet;
this->public.card.resync = (bool(*)(sim_card_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))return_false;
this->public.card.get_pseudonym = (identification_t*(*)(sim_card_t*, identification_t *perm))return_null;
this->public.card.set_pseudonym = (void(*)(sim_card_t*, identification_t *id, identification_t *pseudonym))nop;
this->public.card.get_reauth = (identification_t*(*)(sim_card_t*, identification_t *id, char mk[HASH_SIZE_SHA1], u_int16_t *counter))return_null;
this->public.card.set_reauth = (void(*)(sim_card_t*, identification_t *id, identification_t* next, char mk[HASH_SIZE_SHA1], u_int16_t counter))nop;
this->public.destroy = (void(*)(eap_sim_pcsc_card_t*))destroy;
return &this->public;
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2011 Duncan Salerno
*
* 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.
*/
/**
* @defgroup eap_sim_pcsc_card eap_sim_pcsc_card
* @{ @ingroup eap_sim_pcsc
*/
#ifndef EAP_SIM_PCSC_CARD_H_
#define EAP_SIM_PCSC_CARD_H_
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_sim_pcsc_card_t eap_sim_pcsc_card_t;
/**
* SIM card implementation using a PCSC reader.
*/
struct eap_sim_pcsc_card_t {
/**
* Implements sim_card_t interface
*/
sim_card_t card;
/**
* Destroy a eap_sim_pcsc_card_t.
*/
void (*destroy)(eap_sim_pcsc_card_t *this);
};
/**
* Create a eap_sim_pcsc_card instance.
*/
eap_sim_pcsc_card_t *eap_sim_pcsc_card_create();
#endif /** EAP_SIM_PCSC_CARD_H_ @}*/

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2011 Duncan Salerno
*
* 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_sim_pcsc_plugin.h"
#include "eap_sim_pcsc_card.h"
#include <daemon.h>
typedef struct private_eap_sim_pcsc_t private_eap_sim_pcsc_t;
/**
* Private data of an eap_sim_pcsc_t object.
*/
struct private_eap_sim_pcsc_t {
/**
* Public eap_sim_pcsc_plugin_t interface.
*/
eap_sim_pcsc_plugin_t public;
/**
* SIM card
*/
eap_sim_pcsc_card_t *card;
};
/**
* Implementation of eap_sim_pcsc_t.destroy.
*/
static void destroy(private_eap_sim_pcsc_t *this)
{
charon->sim->remove_card(charon->sim, &this->card->card);
this->card->destroy(this->card);
free(this);
}
/**
* See header
*/
plugin_t *eap_sim_pcsc_plugin_create()
{
private_eap_sim_pcsc_t *this = malloc_thing(private_eap_sim_pcsc_t);
this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
this->card = eap_sim_pcsc_card_create();
charon->sim->add_card(charon->sim, &this->card->card);
return &this->public.plugin;
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2011 Duncan Salerno
*
* 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.
*/
/**
* @defgroup eap_sim_pcsc eap_sim_pcsc
* @ingroup cplugins
*
* @defgroup eap_sim_pcsc_plugin eap_sim_pcsc_plugin
* @{ @ingroup eap_sim_pcsc
*/
#ifndef EAP_SIM_PCSC_PLUGIN_H_
#define EAP_SIM_PCSC_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_sim_pcsc_plugin_t eap_sim_pcsc_plugin_t;
/**
* Plugin to provide a SIM card from a PCSC reader.
*/
struct eap_sim_pcsc_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_SIM_PCSC_PLUGIN_H_ @}*/