c3f0f24b36f1f11bd0e5272a57e1164e53e1862c
[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(simaka_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,
211 (u_int)dwRecvLength);
212 continue;
213 }
214
215 /* APDU: Select DF GSM */
216 dwRecvLength = sizeof(pbRecvBuffer);
217 rv = SCardTransmit(hCard, pioSendPci, pbSelectDFGSM, sizeof(pbSelectDFGSM),
218 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
219 if (rv != SCARD_S_SUCCESS)
220 {
221 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
222 continue;
223 }
224 if (dwRecvLength < APDU_STATUS_LEN ||
225 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
226 {
227 DBG1(DBG_IKE, "Select DF GSM failed: %b", pbRecvBuffer,
228 (u_int)dwRecvLength);
229 continue;
230 }
231
232 /* APDU: Select IMSI */
233 dwRecvLength = sizeof(pbRecvBuffer);
234 rv = SCardTransmit(hCard, pioSendPci, pbSelectIMSI, sizeof(pbSelectIMSI),
235 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
236 if (rv != SCARD_S_SUCCESS)
237 {
238 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
239 continue;
240 }
241 if (dwRecvLength < APDU_STATUS_LEN ||
242 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
243 {
244 DBG1(DBG_IKE, "Select IMSI failed: %b", pbRecvBuffer,
245 (u_int)dwRecvLength);
246 continue;
247 }
248
249 /* APDU: Read Binary (of IMSI) */
250 dwRecvLength = sizeof(pbRecvBuffer);
251 rv = SCardTransmit(hCard, pioSendPci, pbReadBinary, sizeof(pbReadBinary),
252 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
253 if (rv != SCARD_S_SUCCESS)
254 {
255 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
256 continue;
257 }
258 if (dwRecvLength < APDU_STATUS_LEN ||
259 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_SUCCESS)
260 {
261 DBG1(DBG_IKE, "Select IMSI failed: %b", pbRecvBuffer,
262 (u_int)dwRecvLength);
263 continue;
264 }
265
266 if (!decode_imsi_ef(pbRecvBuffer, dwRecvLength-APDU_STATUS_LEN, imsi))
267 {
268 DBG1(DBG_IKE, "Couldn't decode IMSI EF: %b",
269 pbRecvBuffer, (u_int)dwRecvLength);
270 continue;
271 }
272
273 /* The IMSI could be post/prefixed in the full NAI, so just make sure
274 * it's in there */
275 if (!(strlen(full_nai) && strstr(full_nai, imsi)))
276 {
277 DBG1(DBG_IKE, "Not the SIM we're looking for, IMSI: %s", imsi);
278 continue;
279 }
280
281 /* APDU: Run GSM Algorithm */
282 dwRecvLength = sizeof(pbRecvBuffer);
283 rv = SCardTransmit(hCard, pioSendPci,
284 pbRunGSMAlgorithm, sizeof(pbRunGSMAlgorithm),
285 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
286 if (rv != SCARD_S_SUCCESS)
287 {
288 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
289 continue;
290 }
291 if (dwRecvLength < APDU_STATUS_LEN ||
292 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_RESPONSE_DATA)
293 {
294 DBG1(DBG_IKE, "Run GSM Algorithm failed: %b",
295 pbRecvBuffer, (u_int)dwRecvLength);
296 continue;
297 }
298
299 /* APDU: Get Response (of Run GSM Algorithm) */
300 dwRecvLength = sizeof(pbRecvBuffer);
301 rv = SCardTransmit(hCard, pioSendPci, pbGetResponse, sizeof(pbGetResponse),
302 &pioRecvPci, pbRecvBuffer, &dwRecvLength);
303 if (rv != SCARD_S_SUCCESS)
304 {
305 DBG1(DBG_IKE, "SCardTransmit: %s", pcsc_stringify_error(rv));
306 continue;
307 }
308
309 if (dwRecvLength < APDU_STATUS_LEN ||
310 pbRecvBuffer[dwRecvLength-APDU_STATUS_LEN] != APDU_SW1_SUCCESS)
311 {
312 DBG1(DBG_IKE, "Get Response failed: %b", pbRecvBuffer,
313 (u_int)dwRecvLength);
314 continue;
315 }
316
317 /* Extract out Kc and SRES from response */
318 if (dwRecvLength == SIM_SRES_LEN + SIM_KC_LEN + APDU_STATUS_LEN)
319 {
320 memcpy(sres, pbRecvBuffer, SIM_SRES_LEN);
321 memcpy(kc, pbRecvBuffer+4, SIM_KC_LEN);
322 /* This will also cause the loop to exit */
323 found = TRUE;
324 }
325 else
326 {
327 DBG1(DBG_IKE, "Get Response incorrect length: %b",
328 pbRecvBuffer, (u_int)dwRecvLength);
329 continue;
330 }
331
332 /* Transaction will be ended and card disconnected at the
333 * beginning of this loop or after this loop */
334 }
335
336 /* Make sure we end any previous transaction and disconnect card */
337 switch (hCard_status)
338 {
339 case TRANSACTION:
340 SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
341 /* FALLTHRU */
342 case CONNECTED:
343 SCardDisconnect(hCard, SCARD_LEAVE_CARD);
344 /* FALLTHRU */
345 case DISCONNECTED:
346 hCard_status = DISCONNECTED;
347 }
348
349 rv = SCardReleaseContext(hContext);
350 if (rv != SCARD_S_SUCCESS)
351 {
352 DBG1(DBG_IKE, "SCardReleaseContext: %s", pcsc_stringify_error(rv));
353 }
354
355 free(mszReaders);
356 return found;
357 }
358
359 METHOD(simaka_card_t, get_quintuplet, status_t,
360 private_eap_sim_pcsc_card_t *this, identification_t *id,
361 char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN],
362 char ik[AKA_IK_LEN], char res[AKA_RES_MAX], int *res_len)
363 {
364 return NOT_SUPPORTED;
365 }
366
367 METHOD(eap_sim_pcsc_card_t, destroy, void,
368 private_eap_sim_pcsc_card_t *this)
369 {
370 free(this);
371 }
372
373 /**
374 * See header
375 */
376 eap_sim_pcsc_card_t *eap_sim_pcsc_card_create()
377 {
378 private_eap_sim_pcsc_card_t *this;
379
380 INIT(this,
381 .public = {
382 .card = {
383 .get_triplet = _get_triplet,
384 .get_quintuplet = _get_quintuplet,
385 .resync = (void*)return_false,
386 .get_pseudonym = (void*)return_null,
387 .set_pseudonym = (void*)nop,
388 .get_reauth = (void*)return_null,
389 .set_reauth = (void*)nop,
390 },
391 .destroy = _destroy,
392 },
393 );
394
395 return &this->public;
396 }
397