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