root/trunk/src/libopensc/dir.c

Revision 3084, 10.8 KB (checked in by aj, 2 years ago)

convert to utf-8.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
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#include "internal.h"
22#include "asn1.h"
23#include <assert.h>
24#include <stdlib.h>
25#include <string.h>
26
27struct app_entry {
28        const u8 *aid;
29        size_t aid_len;
30        const char *desc;
31};
32
33static const struct app_entry apps[] = {
34        { (const u8 *) "\xA0\x00\x00\x00\x63PKCS-15", 12, "PKCS #15" },
35        { (const u8 *) "\xA0\x00\x00\x01\x77PKCS-15", 12, "Belgian eID" },
36};
37
38static const struct app_entry * find_app_entry(const u8 * aid, size_t aid_len)
39{
40        size_t i;
41
42        for (i = 0; i < sizeof(apps)/sizeof(apps[0]); i++) {
43                if (apps[i].aid_len == aid_len &&
44                    memcmp(apps[i].aid, aid, aid_len) == 0)
45                        return &apps[i];
46        }
47        return NULL;
48}
49
50const sc_app_info_t * sc_find_pkcs15_app(sc_card_t *card)
51{
52        const sc_app_info_t *app = NULL;
53        unsigned int i;
54
55        i = sizeof(apps)/sizeof(apps[0]);
56        while (!app && i--)
57                app = sc_find_app_by_aid(card, apps[i].aid, apps[i].aid_len);
58
59        return app;
60}
61
62static const struct sc_asn1_entry c_asn1_dirrecord[] = {
63        { "aid",   SC_ASN1_OCTET_STRING, SC_ASN1_APP | 15, 0, NULL, NULL },
64        { "label", SC_ASN1_UTF8STRING,   SC_ASN1_APP | 16, SC_ASN1_OPTIONAL, NULL, NULL },
65        { "path",  SC_ASN1_OCTET_STRING, SC_ASN1_APP | 17, SC_ASN1_OPTIONAL, NULL, NULL },
66        { "ddo",   SC_ASN1_OCTET_STRING, SC_ASN1_APP | 19 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL },
67        { NULL, 0, 0, 0, NULL, NULL }
68};
69
70static const struct sc_asn1_entry c_asn1_dir[] = {
71        { "dirRecord", SC_ASN1_STRUCT, SC_ASN1_APP | 1 | SC_ASN1_CONS, 0, NULL, NULL },
72        { NULL, 0, 0, 0, NULL, NULL }
73};
74
75static int parse_dir_record(sc_card_t *card, u8 ** buf, size_t *buflen,
76                            int rec_nr)
77{
78        struct sc_asn1_entry asn1_dirrecord[5], asn1_dir[2];
79        sc_app_info_t *app = NULL;
80        const struct app_entry *ae;
81        int r;
82        u8 aid[128], label[128], path[128];
83        u8 ddo[128];
84        size_t aid_len = sizeof(aid), label_len = sizeof(label),
85               path_len = sizeof(path), ddo_len = sizeof(ddo);
86
87        sc_copy_asn1_entry(c_asn1_dirrecord, asn1_dirrecord);
88        sc_copy_asn1_entry(c_asn1_dir, asn1_dir);
89        sc_format_asn1_entry(asn1_dir + 0, asn1_dirrecord, NULL, 0);
90        sc_format_asn1_entry(asn1_dirrecord + 0, aid, &aid_len, 0);
91        sc_format_asn1_entry(asn1_dirrecord + 1, label, &label_len, 0);
92        sc_format_asn1_entry(asn1_dirrecord + 2, path, &path_len, 0);
93        sc_format_asn1_entry(asn1_dirrecord + 3, ddo, &ddo_len, 0);
94       
95        r = sc_asn1_decode(card->ctx, asn1_dir, *buf, *buflen, (const u8 **) buf, buflen);
96        if (r == SC_ERROR_ASN1_END_OF_CONTENTS)
97                return r;
98        if (r) {
99                sc_error(card->ctx, "EF(DIR) parsing failed: %s\n",
100                      sc_strerror(r));
101                return r;
102        }
103        if (aid_len > SC_MAX_AID_SIZE) {
104                sc_error(card->ctx, "AID is too long.\n");
105                return SC_ERROR_INVALID_ASN1_OBJECT;
106        }
107        app = (sc_app_info_t *) malloc(sizeof(sc_app_info_t));
108        if (app == NULL)
109                return SC_ERROR_OUT_OF_MEMORY;
110       
111        memcpy(app->aid, aid, aid_len);
112        app->aid_len = aid_len;
113        if (asn1_dirrecord[1].flags & SC_ASN1_PRESENT)
114                app->label = strdup((char *) label);
115        else
116                app->label = NULL;
117        if (asn1_dirrecord[2].flags & SC_ASN1_PRESENT) {
118                if (path_len > SC_MAX_PATH_SIZE) {
119                        sc_error(card->ctx, "Application path is too long.\n");
120                        free(app);
121                        return SC_ERROR_INVALID_ASN1_OBJECT;
122                }
123                memcpy(app->path.value, path, path_len);
124                app->path.len = path_len;       
125                app->path.type = SC_PATH_TYPE_PATH;
126        } else if (aid_len < sizeof(app->path.value)) {
127                memcpy(app->path.value, aid, aid_len);
128                app->path.len = aid_len;
129                app->path.type = SC_PATH_TYPE_DF_NAME;
130        } else
131                app->path.len = 0;
132        if (asn1_dirrecord[3].flags & SC_ASN1_PRESENT) {
133                app->ddo = (u8 *) malloc(ddo_len);
134                if (app->ddo == NULL) {
135                        free(app);
136                        return SC_ERROR_OUT_OF_MEMORY;
137                }
138                memcpy(app->ddo, ddo, ddo_len);
139                app->ddo_len = ddo_len;
140        } else {
141                app->ddo = NULL;
142                app->ddo_len = 0;
143        }
144        ae = find_app_entry(aid, aid_len);
145        if (ae != NULL)
146                app->desc = ae->desc;
147        else
148                app->desc = NULL;
149        app->rec_nr = rec_nr;
150        card->app[card->app_count] = app;
151        card->app_count++;
152       
153        return 0;
154}
155
156int sc_enum_apps(sc_card_t *card)
157{
158        sc_path_t path;
159        int ef_structure;
160        size_t file_size;
161        int r;
162
163        if (card->app_count < 0)
164                card->app_count = 0;
165        sc_format_path("3F002F00", &path);
166        if (card->ef_dir != NULL) {
167                sc_file_free(card->ef_dir);
168                card->ef_dir = NULL;
169        }
170        sc_ctx_suppress_errors_on(card->ctx);
171        r = sc_select_file(card, &path, &card->ef_dir);
172        sc_ctx_suppress_errors_off(card->ctx);
173        if (r)
174                return r;
175        if (card->ef_dir->type != SC_FILE_TYPE_WORKING_EF) {
176                sc_debug(card->ctx, "EF(DIR) is not a working EF.\n");
177                sc_file_free(card->ef_dir);
178                card->ef_dir = NULL;
179                return SC_ERROR_INVALID_CARD;
180        }
181        ef_structure = card->ef_dir->ef_structure;
182        file_size = card->ef_dir->size;
183        if (file_size == 0)
184                return 0;
185        if (ef_structure == SC_FILE_EF_TRANSPARENT) {
186                u8 *buf = NULL, *p;
187                size_t bufsize;
188               
189                buf = (u8 *) malloc(file_size);
190                if (buf == NULL)
191                        return SC_ERROR_OUT_OF_MEMORY;
192                p = buf;
193                r = sc_read_binary(card, 0, buf, file_size, 0);
194                if (r < 0) {
195                        free(buf);
196                        SC_TEST_RET(card->ctx, r, "sc_read_binary() failed");
197                }
198                bufsize = r;
199                while (bufsize > 0) {
200                        if (card->app_count == SC_MAX_CARD_APPS) {
201                                sc_error(card->ctx, "Too many applications on card");
202                                break;
203                        }
204                        r = parse_dir_record(card, &p, &bufsize, -1);
205                        if (r)
206                                break;
207                }
208                if (buf)
209                        free(buf);
210
211        } else {        /* record structure */
212                u8 buf[256], *p;
213                unsigned int rec_nr;
214                size_t       rec_size;
215               
216                for (rec_nr = 1; ; rec_nr++) {
217                        sc_ctx_suppress_errors_on(card->ctx);
218                        r = sc_read_record(card, rec_nr, buf, sizeof(buf),
219                                                SC_RECORD_BY_REC_NR);
220                        sc_ctx_suppress_errors_off(card->ctx);
221                        if (r == SC_ERROR_RECORD_NOT_FOUND)
222                                break;
223                        SC_TEST_RET(card->ctx, r, "read_record() failed");
224                        if (card->app_count == SC_MAX_CARD_APPS) {
225                                sc_error(card->ctx, "Too many applications on card");
226                                break;
227                        }
228                        rec_size = r;
229                        p = buf;
230                        parse_dir_record(card, &p, &rec_size, (int)rec_nr);
231                }
232        }
233        return card->app_count;
234}
235
236void sc_free_apps(sc_card_t *card)
237{
238        int     i;
239
240        for (i = 0; i < card->app_count; i++) {
241                if (card->app[i]->label)
242                        free(card->app[i]->label);
243                if (card->app[i]->ddo)
244                        free(card->app[i]->ddo);
245                free(card->app[i]);
246        }
247        card->app_count = -1;
248}
249
250const sc_app_info_t * sc_find_app_by_aid(sc_card_t *card,
251                                              const u8 *aid, size_t aid_len)
252{
253        int i;
254
255        assert(card->app_count > 0);
256        for (i = 0; i < card->app_count; i++) {
257                if (card->app[i]->aid_len == aid_len &&
258                    memcmp(card->app[i]->aid, aid, aid_len) == 0)
259                        return card->app[i];
260        }
261        return NULL;
262}
263
264static int encode_dir_record(sc_context_t *ctx, const sc_app_info_t *app,
265                             u8 **buf, size_t *buflen)
266{
267        struct sc_asn1_entry asn1_dirrecord[5], asn1_dir[2];
268        sc_app_info_t   tapp = *app;
269        int r;
270        size_t label_len;
271
272        sc_copy_asn1_entry(c_asn1_dirrecord, asn1_dirrecord);
273        sc_copy_asn1_entry(c_asn1_dir, asn1_dir);
274        sc_format_asn1_entry(asn1_dir + 0, asn1_dirrecord, NULL, 1);
275        sc_format_asn1_entry(asn1_dirrecord + 0, (void *) tapp.aid, (void *) &tapp.aid_len, 1);
276        if (tapp.label != NULL) {
277                label_len = strlen(tapp.label);
278                sc_format_asn1_entry(asn1_dirrecord + 1, tapp.label, &label_len, 1);
279        }
280        if (tapp.path.len)
281                sc_format_asn1_entry(asn1_dirrecord + 2, (void *) tapp.path.value,
282                                     (void *) &tapp.path.len, 1);
283        if (tapp.ddo != NULL)
284                sc_format_asn1_entry(asn1_dirrecord + 3, (void *) tapp.ddo,
285                                     (void *) &tapp.ddo_len, 1);
286        r = sc_asn1_encode(ctx, asn1_dir, buf, buflen);
287        if (r) {
288                sc_error(ctx, "sc_asn1_encode() failed: %s\n",
289                      sc_strerror(r));
290                return r;
291        }
292        return 0;
293}
294
295static int update_transparent(sc_card_t *card, sc_file_t *file)
296{
297        u8 *rec, *buf = NULL, *tmp;
298        size_t rec_size, buf_size = 0;
299        int i, r;
300
301        for (i = 0; i < card->app_count; i++) {
302                r = encode_dir_record(card->ctx, card->app[i], &rec, &rec_size);
303                if (r) {
304                        if (rec)
305                                free(rec);
306                        if (buf)
307                                free(buf);
308                        return r;
309                }
310                tmp = (u8 *) realloc(buf, buf_size + rec_size);
311                if (!tmp) {
312                        if (rec)
313                                free(rec);
314                        if (buf)
315                                free(buf);
316                        return SC_ERROR_OUT_OF_MEMORY;
317                }
318                buf = tmp;
319                memcpy(buf + buf_size, rec, rec_size);
320                buf_size += rec_size;
321                free(rec);
322                rec=NULL;
323        }
324        if (file->size > buf_size) {
325                tmp = (u8 *) realloc(buf, file->size);
326                if (!tmp) {
327                        free(buf);
328                        return SC_ERROR_OUT_OF_MEMORY;
329                }
330                buf = tmp;
331                memset(buf + buf_size, 0, file->size - buf_size);
332                buf_size = file->size;
333        }
334        r = sc_update_binary(card, 0, buf, buf_size, 0);
335        free(buf);
336        SC_TEST_RET(card->ctx, r, "Unable to update EF(DIR)");
337       
338        return 0;
339}
340
341static int update_single_record(sc_card_t *card, sc_file_t *file,
342                                sc_app_info_t *app)
343{
344        u8 *rec;
345        size_t rec_size;
346        int r;
347       
348        r = encode_dir_record(card->ctx, app, &rec, &rec_size);
349        if (r)
350                return r;
351        if (app->rec_nr > 0)
352                r = sc_update_record(card, (unsigned int)app->rec_nr, rec, rec_size, SC_RECORD_BY_REC_NR);
353        else if (app->rec_nr == 0) {
354                /* create new record entry */
355                sc_ctx_suppress_errors_on(card->ctx);
356                r = sc_append_record(card, rec, rec_size, 0);
357                sc_ctx_suppress_errors_off(card->ctx);
358                if (r == SC_ERROR_NOT_SUPPORTED) {
359                        /* if the card doesn't support APPEND RECORD we try a
360                         * UPDATE RECORD on the next unused record (and hope
361                         * that there is a record with this index).
362                         */
363                        int rec_nr = 0, i;
364                        for(i = 0; i < card->app_count; i++)
365                                if (card->app[i]->rec_nr > rec_nr)
366                                        rec_nr = card->app[i]->rec_nr;
367                        rec_nr++;
368                        r = sc_update_record(card, (unsigned int)rec_nr, rec, rec_size, SC_RECORD_BY_REC_NR);
369                }
370        } else {
371                sc_error(card->ctx, "invalid record number\n");
372                r = SC_ERROR_INTERNAL;
373        }
374        free(rec);
375        SC_TEST_RET(card->ctx, r, "Unable to update EF(DIR) record");
376        return 0;
377}
378
379static int update_records(sc_card_t *card, sc_file_t *file)
380{
381        int i, r;
382
383        for (i = 0; i < card->app_count; i++) {
384                r = update_single_record(card, file, card->app[i]);
385                if (r)
386                        return r;
387        }
388        return 0;
389}
390
391int sc_update_dir(sc_card_t *card, sc_app_info_t *app)
392{
393        sc_path_t path;
394        sc_file_t *file;
395        int r;
396       
397        sc_format_path("3F002F00", &path);
398
399        r = sc_select_file(card, &path, &file);
400        SC_TEST_RET(card->ctx, r, "unable to select EF(DIR)");
401        if (file->ef_structure == SC_FILE_EF_TRANSPARENT)
402                r = update_transparent(card, file);
403        else if (app == NULL)
404                r = update_records(card, file);
405        else
406                r = update_single_record(card, file, app);
407        sc_file_free(file);
408        return r;
409}
Note: See TracBrowser for help on using the browser.