Coverage Report

Created: 2026-10-28 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}