1 /*
2 * dir.c: Stuff for handling EF(DIR)
3 *
4 * Copyright (C) 2001, 2002 Juha Yrjölä <juha.yrjola@iki.fi>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #if HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "internal.h"
30 #include "asn1.h"
31
32 struct app_entry {
33 const u8 *aid;
34 size_t aid_len;
35 const char *desc;
36 };
37
38 static const struct app_entry apps[] = {
39 { (const u8 *) "\xA0\x00\x00\x00\x63PKCS-15", 12, "PKCS #15" },
40 { (const u8 *) "\xA0\x00\x00\x01\x77PKCS-15", 12, "Belgian eID" },
41 { (const u8 *) "\x44\x46\x20\x69\x73\x73\x75\x65\x72", 9, "Portugal eID" },
42 { (const u8 *) "\xE8\x28\xBD\x08\x0F\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E", 15, "ESIGN"},
43 { (const u8 *) "\xE8\x28\xBD\x08\x0F\x80\x25\x00\x00\x01\xFF\x00\x10", 13, "CPx IAS"},
44 { (const u8 *) "\xE8\x28\xBD\x08\x0F\x80\x25\x00\x00\x01\xFF\x00\x20", 13, "CPx IAS CL"},
45 { (const u8 *) "\xE8\x28\xBD\x08\x0F\xD2\x50\x45\x43\x43\x2D\x65\x49\x44", 14, "ECC eID"},
46 { (const u8 *) "\xE8\x28\xBD\x08\x0F\xD2\x50\x47\x65\x6E\x65\x72\x69\x63", 14, "ECC Generic PKI"},
47 };
48
49 static const struct sc_asn1_entry c_asn1_dirrecord[] = {
50 { "aid", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 15, 0, NULL, NULL },
51 { "label", SC_ASN1_UTF8STRING, SC_ASN1_APP | 16, SC_ASN1_OPTIONAL, NULL, NULL },
52 { "path", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 17, SC_ASN1_OPTIONAL, NULL, NULL },
53 { "ddo", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 19 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL },
54 { NULL, 0, 0, 0, NULL, NULL }
55 };
56
57 static const struct sc_asn1_entry c_asn1_dir[] = {
58 { "dirRecord", SC_ASN1_STRUCT, SC_ASN1_APP | 1 | SC_ASN1_CONS, 0, NULL, NULL },
59 { NULL, 0, 0, 0, NULL, NULL }
60 };
61
62
63 static int
parse_dir_record(sc_card_t * card,u8 ** buf,size_t * buflen,int rec_nr)64 parse_dir_record(sc_card_t *card, u8 ** buf, size_t *buflen, int rec_nr)
65 {
66 struct sc_context *ctx = card->ctx;
67 struct sc_asn1_entry asn1_dirrecord[5], asn1_dir[2];
68 scconf_block *conf_block = NULL;
69 sc_app_info_t *app = NULL;
70 struct sc_aid aid;
71 u8 label[128], path[128], ddo[128];
72 size_t label_len = sizeof(label) - 1, path_len = sizeof(path), ddo_len = sizeof(ddo);
73 int r;
74
75 LOG_FUNC_CALLED(ctx);
76 aid.len = sizeof(aid.value);
77
78 memset(label, 0, sizeof(label));
79 sc_copy_asn1_entry(c_asn1_dirrecord, asn1_dirrecord);
80 sc_copy_asn1_entry(c_asn1_dir, asn1_dir);
81 sc_format_asn1_entry(asn1_dir + 0, asn1_dirrecord, NULL, 0);
82 sc_format_asn1_entry(asn1_dirrecord + 0, aid.value, &aid.len, 0);
83 sc_format_asn1_entry(asn1_dirrecord + 1, label, &label_len, 0);
84 sc_format_asn1_entry(asn1_dirrecord + 2, path, &path_len, 0);
85 sc_format_asn1_entry(asn1_dirrecord + 3, ddo, &ddo_len, 0);
86
87 r = sc_asn1_decode(ctx, asn1_dir, *buf, *buflen, (const u8 **) buf, buflen);
88 if (r == SC_ERROR_ASN1_END_OF_CONTENTS)
89 LOG_FUNC_RETURN(ctx, r);
90 LOG_TEST_RET(ctx, r, "EF(DIR) parsing failed");
91
92 conf_block = sc_get_conf_block(ctx, "framework", "pkcs15", 1);
93 if (conf_block) {
94 scconf_block **blocks = NULL;
95 char aid_str[SC_MAX_AID_STRING_SIZE];
96 int ignore_app = 0;
97
98 sc_bin_to_hex(aid.value, aid.len, aid_str, sizeof(aid_str), 0);
99 blocks = scconf_find_blocks(card->ctx->conf, conf_block, "application", aid_str);
100 if (blocks) {
101 ignore_app = (blocks[0] && scconf_get_str(blocks[0], "disable", 0));
102 free(blocks);
103 }
104
105 if (ignore_app) {
106 sc_log(ctx, "Application '%s' ignored", aid_str);
107 LOG_FUNC_RETURN(ctx, SC_SUCCESS);
108 }
109 }
110
111 app = calloc(1, sizeof(struct sc_app_info));
112 if (app == NULL)
113 LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
114
115 memcpy(&app->aid, &aid, sizeof(struct sc_aid));
116
117 if (asn1_dirrecord[1].flags & SC_ASN1_PRESENT)
118 app->label = strdup((char *) label);
119 else
120 app->label = NULL;
121
122 if (asn1_dirrecord[2].flags & SC_ASN1_PRESENT && path_len > 0) {
123 /* application path present: ignore AID */
124 if (path_len > SC_MAX_PATH_SIZE) {
125 free(app->label);
126 free(app);
127 LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "Application path is too long.");
128 }
129 memcpy(app->path.value, path, path_len);
130 app->path.len = path_len;
131 app->path.type = SC_PATH_TYPE_PATH;
132 }
133 else {
134 /* application path not present: use AID as application path */
135 memcpy(app->path.value, aid.value, aid.len);
136 app->path.len = aid.len;
137 app->path.type = SC_PATH_TYPE_DF_NAME;
138 }
139
140 if (asn1_dirrecord[3].flags & SC_ASN1_PRESENT) {
141 app->ddo.value = malloc(ddo_len);
142 if (app->ddo.value == NULL) {
143 free(app->label);
144 free(app);
145 LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate DDO value");
146 }
147 memcpy(app->ddo.value, ddo, ddo_len);
148 app->ddo.len = ddo_len;
149 } else {
150 app->ddo.value = NULL;
151 app->ddo.len = 0;
152 }
153
154 app->rec_nr = rec_nr;
155 card->app[card->app_count] = app;
156 card->app_count++;
157
158 LOG_FUNC_RETURN(ctx, SC_SUCCESS);
159 }
160
161
sc_enum_apps(sc_card_t * card)162 int sc_enum_apps(sc_card_t *card)
163 {
164 struct sc_context *ctx = card->ctx;
165 sc_path_t path;
166 int ef_structure;
167 size_t file_size, jj;
168 int r, ii, idx;
169 struct sc_file *ef_dir = NULL;
170
171 LOG_FUNC_CALLED(ctx);
172
173 sc_free_apps(card);
174 card->app_count = 0;
175
176 sc_format_path("3F002F00", &path);
177 r = sc_select_file(card, &path, &ef_dir);
178 if (r < 0)
179 sc_file_free(ef_dir);
180 LOG_TEST_RET(ctx, r, "Cannot select EF.DIR file");
181
182 if (ef_dir->type != SC_FILE_TYPE_WORKING_EF) {
183 sc_file_free(ef_dir);
184 LOG_TEST_RET(ctx, SC_ERROR_INVALID_CARD, "EF(DIR) is not a working EF.");
185 }
186
187 ef_structure = ef_dir->ef_structure;
188 file_size = ef_dir->size;
189 sc_file_free(ef_dir);
190 if (ef_structure == SC_FILE_EF_TRANSPARENT) {
191 u8 *buf = NULL, *p;
192 size_t bufsize;
193
194 if (file_size == 0)
195 LOG_FUNC_RETURN(ctx, 0);
196 if (file_size > MAX_FILE_SIZE)
197 LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA);
198
199 buf = malloc(file_size);
200 if (buf == NULL)
201 LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
202 p = buf;
203 r = sc_read_binary(card, 0, buf, file_size, 0);
204 if (r < 0) {
205 free(buf);
206 LOG_TEST_RET(ctx, r, "sc_read_binary() failed");
207 }
208 bufsize = r;
209 while (bufsize > 0) {
210 if (card->app_count == SC_MAX_CARD_APPS) {
211 sc_log(ctx, "Too many applications on card");
212 break;
213 }
214 r = parse_dir_record(card, &p, &bufsize, -1);
215 if (r)
216 break;
217 }
218 if (buf)
219 free(buf);
220
221 }
222 else { /* record structure */
223 unsigned char buf[256], *p;
224 unsigned int rec_nr;
225 size_t rec_size;
226
227 /* Arbitrary set '16' as maximal number of records to check out:
228 * to avoid endless loop because of some incomplete cards/drivers */
229 for (rec_nr = 1; rec_nr < 16; rec_nr++) {
230 r = sc_read_record(card, rec_nr, buf, sizeof(buf), SC_RECORD_BY_REC_NR);
231 if (r == SC_ERROR_RECORD_NOT_FOUND)
232 break;
233 LOG_TEST_RET(ctx, r, "read_record() failed");
234
235 if (card->app_count == SC_MAX_CARD_APPS) {
236 sc_log(ctx, "Too many applications on card");
237 break;
238 }
239
240 rec_size = r;
241 p = buf;
242 parse_dir_record(card, &p, &rec_size, (int)rec_nr);
243 }
244 }
245
246 /* Move known PKCS#15 applications to the head of the list */
247 for (ii=0, idx=0; ii<card->app_count; ii++) {
248 for (jj=0; jj < sizeof(apps)/sizeof(apps[0]); jj++) {
249 if (apps[jj].aid_len != card->app[ii]->aid.len)
250 continue;
251 if (memcmp(apps[jj].aid, card->app[ii]->aid.value, apps[jj].aid_len))
252 continue;
253 break;
254 }
255
256 if (ii != idx && jj < sizeof(apps)/sizeof(apps[0])) {
257 struct sc_app_info *tmp = card->app[idx];
258
259 card->app[idx] = card->app[ii];
260 card->app[ii] = tmp;
261 idx++;
262 }
263 }
264
265 LOG_FUNC_RETURN(ctx, SC_SUCCESS);
266 }
267
sc_free_apps(sc_card_t * card)268 void sc_free_apps(sc_card_t *card)
269 {
270 int i;
271
272 for (i = 0; i < card->app_count; i++) {
273 free(card->app[i]->label);
274 free(card->app[i]->ddo.value);
275 free(card->app[i]);
276 }
277 card->app_count = -1;
278 }
279
encode_dir_record(sc_context_t * ctx,const sc_app_info_t * app,u8 ** buf,size_t * buflen)280 static int encode_dir_record(sc_context_t *ctx, const sc_app_info_t *app,
281 u8 **buf, size_t *buflen)
282 {
283 struct sc_asn1_entry asn1_dirrecord[5], asn1_dir[2];
284 sc_app_info_t tapp = *app;
285 int r;
286 size_t label_len;
287
288 sc_copy_asn1_entry(c_asn1_dirrecord, asn1_dirrecord);
289 sc_copy_asn1_entry(c_asn1_dir, asn1_dir);
290 sc_format_asn1_entry(asn1_dir + 0, asn1_dirrecord, NULL, 1);
291 sc_format_asn1_entry(asn1_dirrecord + 0, (void *) tapp.aid.value, (void *) &tapp.aid.len, 1);
292 if (tapp.label != NULL) {
293 label_len = strlen(tapp.label);
294 sc_format_asn1_entry(asn1_dirrecord + 1, tapp.label, &label_len, 1);
295 }
296 if (tapp.path.len)
297 sc_format_asn1_entry(asn1_dirrecord + 2, (void *) tapp.path.value,
298 (void *) &tapp.path.len, 1);
299 if (tapp.ddo.value != NULL && tapp.ddo.len)
300 sc_format_asn1_entry(asn1_dirrecord + 3, (void *) tapp.ddo.value,
301 (void *) &tapp.ddo.len, 1);
302 r = sc_asn1_encode(ctx, asn1_dir, buf, buflen);
303 LOG_TEST_RET(ctx, r, "Encode DIR record error");
304
305 return SC_SUCCESS;
306 }
307
update_transparent(sc_card_t * card,sc_file_t * file)308 static int update_transparent(sc_card_t *card, sc_file_t *file)
309 {
310 u8 *rec, *buf = NULL, *tmp;
311 size_t rec_size, buf_size = 0;
312 int i, r;
313
314 for (i = 0; i < card->app_count; i++) {
315 r = encode_dir_record(card->ctx, card->app[i], &rec, &rec_size);
316 if (r) {
317 if (buf)
318 free(buf);
319 return r;
320 }
321 if (!rec_size)
322 continue;
323 tmp = (u8 *) realloc(buf, buf_size + rec_size);
324 if (!tmp) {
325 if (rec)
326 free(rec);
327 if (buf)
328 free(buf);
329 return SC_ERROR_OUT_OF_MEMORY;
330 }
331 buf = tmp;
332 memcpy(buf + buf_size, rec, rec_size);
333 buf_size += rec_size;
334 free(rec);
335 rec=NULL;
336 }
337 if (file->size > buf_size) {
338 tmp = (u8 *) realloc(buf, file->size);
339 if (!tmp) {
340 free(buf);
341 return SC_ERROR_OUT_OF_MEMORY;
342 }
343 buf = tmp;
344 memset(buf + buf_size, 0, file->size - buf_size);
345 buf_size = file->size;
346 }
347 r = sc_update_binary(card, 0, buf, buf_size, 0);
348 free(buf);
349 LOG_TEST_RET(card->ctx, r, "Unable to update EF(DIR)");
350
351 return SC_SUCCESS;
352 }
353
update_single_record(sc_card_t * card,sc_app_info_t * app)354 static int update_single_record(sc_card_t *card, sc_app_info_t *app)
355 {
356 u8 *rec;
357 size_t rec_size;
358 int r;
359
360 r = encode_dir_record(card->ctx, app, &rec, &rec_size);
361 if (r)
362 return r;
363 if (app->rec_nr > 0)
364 r = sc_update_record(card, (unsigned int)app->rec_nr, rec, rec_size, SC_RECORD_BY_REC_NR);
365 else if (app->rec_nr == 0) {
366 /* create new record entry */
367 r = sc_append_record(card, rec, rec_size, 0);
368 if (r == SC_ERROR_NOT_SUPPORTED) {
369 /* if the card doesn't support APPEND RECORD we try a
370 * UPDATE RECORD on the next unused record (and hope
371 * that there is a record with this index).
372 */
373 int rec_nr = 0, i;
374 for(i = 0; i < card->app_count; i++)
375 if (card->app[i]->rec_nr > rec_nr)
376 rec_nr = card->app[i]->rec_nr;
377 rec_nr++;
378 r = sc_update_record(card, (unsigned int)rec_nr, rec, rec_size, SC_RECORD_BY_REC_NR);
379 }
380 } else {
381 sc_log(card->ctx, "invalid record number\n");
382 r = SC_ERROR_INTERNAL;
383 }
384 free(rec);
385 LOG_TEST_RET(card->ctx, r, "Unable to update EF(DIR) record");
386 return 0;
387 }
388
update_records(sc_card_t * card)389 static int update_records(sc_card_t *card)
390 {
391 int i, r;
392
393 for (i = 0; i < card->app_count; i++) {
394 r = update_single_record(card, card->app[i]);
395 if (r)
396 return r;
397 }
398 return 0;
399 }
400
sc_update_dir(sc_card_t * card,sc_app_info_t * app)401 int sc_update_dir(sc_card_t *card, sc_app_info_t *app)
402 {
403 sc_path_t path;
404 sc_file_t *file;
405 int r;
406
407 sc_format_path("3F002F00", &path);
408
409 r = sc_select_file(card, &path, &file);
410 LOG_TEST_RET(card->ctx, r, "unable to select EF(DIR)");
411
412 if (file->ef_structure == SC_FILE_EF_TRANSPARENT)
413 r = update_transparent(card, file);
414 else if (app == NULL)
415 r = update_records(card);
416 else
417 r = update_single_record(card, app);
418 sc_file_free(file);
419 return r;
420 }
421