Protect the communication with the SIM card during a transaction from access by a...
[strongswan.git] / src / libcharon / plugins / eap_sim_pcsc / eap_sim_pcsc_card.c
1 /*
2 * Copyright (C) 2011 Duncan Salerno
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15 #include "eap_sim_pcsc_card.h"
16
17 #include <PCSC/wintypes.h>
18 #include <PCSC/winscard.h>
19 #include <daemon.h>
20
21 typedef struct private_eap_sim_pcsc_card_t private_eap_sim_pcsc_card_t;
22
23 /**
24 * Private data of an eap_sim_pcsc_card_t object.
25 */
26 struct private_eap_sim_pcsc_card_t {
27
28 /**
29 * Public eap_sim_pcsc_card_t interface.
30 */
31 eap_sim_pcsc_card_t public;
32 };
33
34 /**
35 * Maximum length for an IMSI.
36 */
37 #define SIM_IMSI_MAX_LEN 15
38
39 /**
40 * Length of the status at the end of response APDUs.
41 */
42 #define APDU_STATUS_LEN 2
43
44 /**
45 * First byte of status word indicating success.
46 */
47 #define APDU_SW1_SUCCESS 0x90
48
49 /**
50 * First byte of status word indicating there is response data to be read.
51 */
52 #define APDU_SW1_RESPONSE_DATA 0x9f
53
54 /**
55 * Decode IMSI EF (Elementary File) into an ASCII string
56 */
57 static bool decode_imsi_ef(unsigned char *input, int input_len, char *output)
58 {
59 /* Only digits 0-9 valid in IMSIs */
60 static const char bcd_num_digits[] = {
61 '0', '1', '2', '3', '4', '5', '6', '7',
62 '8', '9', '\0', '\0', '\0', '\0', '\0', '\0'
63 };
64 int i;
65
66 /* Check length byte matches how many bytes we have, and that input
67 * is correct length for an IMSI */
68 if (input[0] != input_len-1 || input_len < 2 || input_len > 9)
69 {
70 return FALSE;
71 }
72
73 /* Check type byte is IMSI (bottom 3 bits == 001) */
74 if ((input[1] & 0x07) != 0x01)
75 {
76 return FALSE;
77 }
78 *output++ = bcd_num_digits[input[1] >> 4];
79
80 for (i = 2; i < input_len; i++)
81 {
82 *output++ = bcd_num_digits[input[i] & 0xf];
83 *output++ = bcd_num_digits[input[i] >> 4];
84 }
85
86 *output++ = '\0';
87 return TRUE;
88 }
89
90 METHOD(sim_card_t, get_triplet, bool,
91 private_eap_sim_pcsc_card_t *this, identification_t *id,
92 char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN])
93 {
94 status_t found = FALSE;
95 LONG rv;
96 SCARDCONTEXT hContext;
97 DWORD dwReaders;
98 LPSTR mszReaders;
99 char *cur_reader;
100 char full_nai[128];
101 SCARDHANDLE hCard;
102 enum { DISCONNECTED, CONNECTED, TRANSACTION } hCard_status = DISCONNECTED;
103
104 snprintf(full_nai, sizeof(full_nai), "%Y", id);
105
106 DBG2(DBG_IKE, "looking for triplet: %Y rand %b", id, rand, SIM_RAND_LEN);
107
108 rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
109 if (rv != SCARD_S_SUCCESS)
110 {
111 DBG1(DBG_IKE, "SCardEstablishContext: %s", pcsc_stringify_error(rv));
112 return FALSE;
113 }
114
115 rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
116 if (rv != SCARD_S_SUCCESS)
117 {
118 DBG1(DBG_IKE, "SCardListReaders: %s", pcsc_stringify_error(rv));
119 return FALSE;
120 }
121 mszReaders = malloc(sizeof(char)*dwReaders);
122
123 rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
124 if (rv != SCARD_S_SUCCESS)
125 {
126 DBG1(DBG_IKE, "SCardListReaders: %s", pcsc_stringify_error(rv));
127 return FALSE;
128 }
129
130 /* mszReaders is a multi-string of readers, separated by '\0' and
131 * terminated by an additional '\0' */
132 for (cur_reader = mszReaders; *cur_reader != '\0' && found == FALSE;
133 cur_reader += strlen(cur_reader) + 1)
134 {
135 DWORD dwActiveProtocol = -1;
136 SCARD_IO_REQUEST *pioSendPci;
137 SCARD_IO_REQUEST pioRecvPci;
138 BYTE pbRecvBuffer[64];
139 DWORD dwRecvLength;
140 char imsi[SIM_IMSI_MAX_LEN + 1];
141
142 /* See GSM 11.11 for SIM APDUs */
143 static const BYTE pbSelectMF[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x3f, 0x00 };
144 static const BYTE pbSelectDFGSM[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x7f, 0x20 };
145 static const BYTE pbSelectIMSI[] = { 0xa0, 0xa4, 0x00, 0x00, 0x02, 0x6f, 0x07 };
146 static const BYTE pbReadBinary[] = { 0xa0, 0xb0, 0x00, 0x00, 0x09 };
147 BYTE pbRunGSMAlgorithm[5 + SIM_RAND_LEN] = { 0xa0, 0x88, 0x00, 0x00, 0x10 };
148 static const BYTE pbGetResponse[] = { 0xa0, 0xc0, 0x00, 0x00, 0x0c };
149
150 /* If on 2nd or later reader, make sure we end the transaction
151 * and disconnect card in the previous reader */
152 switch (hCard_status)
153 {
154 case TRANSACTION:
155 SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
156 /* FALLTHRU */
157 case CONNECTED:
158 SCardDisconnect(hCard, SCARD_LEAVE_CARD);
159 /* FALLTHRU */
160 case DISCONNECTED:
161 hCard_status = DISCONNECTED;
162 }
163
164 /* Copy RAND into APDU */
165 memcpy(pbRunGSMAlgorithm + 5, rand, SIM_RAND_LEN);
166
167 rv = SCardConnect(hContext, cur_reader, SCARD_SHARE_SHARED,
168 SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
169 if (rv != SCARD_S_SUCCESS)
170 {
171 DBG1(DBG_IKE, "SCardConnect: %s", pcsc_stringify_error(rv));
172 continue;
173 }
174 hCard_status = CONNECTED;
175
176 switch(dwActiveProtocol)
177 {
178 case SCARD_PROTOCOL_T0:
179 pioSendPci = SCARD_PCI_T0;
180 break;
181 case SCARD_PROTOCOL_T1:
182 pioSendPci = SCARD_PCI_T1;
183 break;
184 default:
185 DBG1(DBG_IKE, "Unknown SCARD_PROTOCOL");
186 continue;
187 }
188
189 /* Start transaction */
190 rv = SCardBeginTransaction(hCard);
191 if (rv != SCARD_S_SUCCESS)
192 {
193 DBG1(DBG_IKE, "SCardBeginTransaction: %s", pcsc_stringify_error(rv));
194 continue;
195 }
196 hCard_status = TRANSACTION;
197
198 /* APDU: Select MF */
199 dwRecvLength = sizeof(pbRecvBuffer);
200 rv = SCardTransmit(hCard, pioSendPci, pbSelectMF, sizeof(pbSelectMF),
201 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
202 if (rv != SCARD_S_SUCCESS)
203 {
204 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
205 continue;
206 }
207 if (dwRecvLength < APDU_STATUS_LEN ||
208 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
209 {
210 DBG1(DBG_IKE, "Select MF failed: %b", pbRecvBuffer, dwRecvLength);
211 continue;
212 }
213
214 /* APDU: Select DF GSM */
215 dwRecvLength = sizeof(pbRecvBuffer);
216 rv = SCardTransmit(hCard, pioSendPci, pbSelectDFGSM, sizeof(pbSelectDFGSM),
217 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
218 if (rv != SCARD_S_SUCCESS)
219 {
220 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
221 continue;
222 }
223 if (dwRecvLength < APDU_STATUS_LEN ||
224 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
225 {
226 DBG1(DBG_IKE, "Select DF GSM failed: %b", pbRecvBuffer, dwRecvLength);
227 continue;
228 }
229
230 /* APDU: Select IMSI */
231 dwRecvLength = sizeof(pbRecvBuffer);
232 rv = SCardTransmit(hCard, pioSendPci, pbSelectIMSI, sizeof(pbSelectIMSI),
233 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
234 if (rv != SCARD_S_SUCCESS)
235 {
236 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
237 continue;
238 }
239 if (dwRecvLength < APDU_STATUS_LEN ||
240 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
241 {
242 DBG1(DBG_IKE, "Select IMSI failed: %b", pbRecvBuffer, dwRecvLength);
243 continue;
244 }
245
246 /* APDU: Read Binary (of IMSI) */
247 dwRecvLength = sizeof(pbRecvBuffer);
248 rv = SCardTransmit(hCard, pioSendPci, pbReadBinary, sizeof(pbReadBinary),
249 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
250 if (rv != SCARD_S_SUCCESS)
251 {
252 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
253 continue;
254 }
255 if (dwRecvLength < APDU_STATUS_LEN ||
256 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_SUCCESS)
257 {
258 DBG1(DBG_IKE, "Select IMSI failed: %b", pbRecvBuffer, dwRecvLength);
259 continue;
260 }
261
262 if (!decode_imsi_ef(pbRecvBuffer, dwRecvLength-APDU_STATUS_LEN, imsi))
263 {
264 DBG1(DBG_IKE, "Couldn't decode IMSI EF: %b",
265 pbRecvBuffer, dwRecvLength);
266 continue;
267 }
268
269 /* The IMSI could be post/prefixed in the full NAI, so just make sure
270 * it's in there */
271 if (!(strlen(full_nai) && strstr(full_nai, imsi)))
272 {
273 DBG1(DBG_IKE, "Not the SIM we're looking for, IMSI: %s", imsi);
274 continue;
275 }
276
277 /* APDU: Run GSM Algorithm */
278 dwRecvLength = sizeof(pbRecvBuffer);
279 rv = SCardTransmit(hCard, pioSendPci,
280 pbRunGSMAlgorithm, sizeof(pbRunGSMAlgorithm),
281 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
282 if (rv != SCARD_S_SUCCESS)
283 {
284 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
285 continue;
286 }
287 if (dwRecvLength < APDU_STATUS_LEN ||
288 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
289 {
290 DBG1(DBG_IKE, "Run GSM Algorithm failed: %b",
291 pbRecvBuffer, dwRecvLength);
292 continue;
293 }
294
295 /* APDU: Get Response (of Run GSM Algorithm) */
296 dwRecvLength = sizeof(pbRecvBuffer);
297 rv = SCardTransmit(hCard, pioSendPci, pbGetResponse, sizeof(pbGetResponse),
298 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
299 if (rv != SCARD_S_SUCCESS)
300 {
301 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
302 continue;
303 }
304
305 if (dwRecvLength < APDU_STATUS_LEN ||
306 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_SUCCESS)
307 {
308 DBG1(DBG_IKE, "Get Response failed: %b", pbRecvBuffer, dwRecvLength);
309 continue;
310 }
311
312 /* Extract out Kc and SRES from response */
313 if (dwRecvLength == SIM_SRES_LEN + SIM_KC_LEN + APDU_STATUS_LEN)
314 {
315 memcpy(sres, pbRecvBuffer, SIM_SRES_LEN);
316 memcpy(kc, pbRecvBuffer+4, SIM_KC_LEN);
317 /* This will also cause the loop to exit */
318 found = TRUE;
319 }
320 else
321 {
322 DBG1(DBG_IKE, "Get Response incorrect length: %b",
323 pbRecvBuffer, dwRecvLength);
324 continue;
325 }
326
327 /* Transaction will be ended and card disconnected at the
328 * beginning of this loop or after this loop */
329 }
330
331 /* Make sure we end any previous transaction and disconnect card */
332 switch (hCard_status)
333 {
334 case TRANSACTION:
335 SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
336 /* FALLTHRU */
337 case CONNECTED:
338 SCardDisconnect(hCard, SCARD_LEAVE_CARD);
339 /* FALLTHRU */
340 case DISCONNECTED:
341 hCard_status = DISCONNECTED;
342 }
343
344 rv = SCardReleaseContext(hContext);
345 if (rv != SCARD_S_SUCCESS)
346 {
347 DBG1(DBG_IKE, "SCardReleaseContext: %s", pcsc_stringify_error(rv));
348 }
349
350 free(mszReaders);
351 return found;
352 }
353
354 METHOD(sim_card_t, get_quintuplet, status_t,
355 private_eap_sim_pcsc_card_t *this, identification_t *id,
356 char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN],
357 char ik[AKA_IK_LEN], char res[AKA_RES_MAX], int *res_len)
358 {
359 return NOT_SUPPORTED;
360 }
361
362 METHOD(eap_sim_pcsc_card_t, destroy, void,
363 private_eap_sim_pcsc_card_t *this)
364 {
365 free(this);
366 }
367
368 /**
369 * See header
370 */
371 eap_sim_pcsc_card_t *eap_sim_pcsc_card_create()
372 {
373 private_eap_sim_pcsc_card_t *this;
374
375 INIT(this,
376 .public = {
377 .card = {
378 .get_triplet = _get_triplet,
379 .get_quintuplet = _get_quintuplet,
380 .resync = (void*)return_false,
381 .get_pseudonym = (void*)return_null,
382 .set_pseudonym = (void*)nop,
383 .get_reauth = (void*)return_null,
384 .set_reauth = (void*)nop,
385 },
386 .destroy = _destroy,
387 },
388 );
389
390 return &this->public;
391 }
392