1 /*
2 * eidenv.c: EstEID utility
3 *
4 * Copyright (C) 2004 Martin Paljak <martin@martinpaljak.net>
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 "config.h"
22
23 #include <stdio.h>
24 #ifndef _WIN32
25 #include <unistd.h>
26 #else
27 #include <process.h>
28 #endif
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include <getopt.h>
33 #include "libopensc/opensc.h"
34 #include "libopensc/asn1.h"
35 #include "libopensc/cards.h"
36 #include "util.h"
37
38 static char *opt_reader = NULL;
39 static int stats = 0;
40 static int opt_wait = 0;
41 static char *exec_program = NULL;
42 static int exit_status = EXIT_FAILURE;
43
44 static const struct option options[] = {
45 {"reader", required_argument, NULL, 'r'},
46 {"print", no_argument, NULL, 'p'},
47 {"exec", required_argument, NULL, 'x'},
48 {"stats", no_argument, NULL, 't'},
49 {"help", no_argument, NULL, 'h'},
50 {"wait", no_argument, NULL, 'w'},
51 {"version", no_argument, NULL, 'V'},
52 {NULL, 0, NULL, 0}
53 };
54
55 /* Probably not used, but needed to build on Windows */
56 static const char *app_name = "eidenv";
57
58 static struct {
59 const char *name;
60 const char *env_name;
61 int recno;
62 } esteid_data[] = {
63 {"Surname", "ESTEID_SURNAME", 1},
64 {"Given names 1", "ESTEID_GIVEN_NAMES1", 2},
65 {"Given names 2", "ESTEID_GIVEN_NAMES2", 3},
66 {"Sex", "ESTEID_SEX", 4},
67 {"Citizenship", "ESTEID_CITIZENSHIP", 5},
68 {"Date of birth", "ESTEID_DATE_OF_BIRTH", 6},
69 {"Personal ID code", "ESTEID_PERSONAL_ID", 7},
70 {"Document number", "ESTEID_DOCUMENT_NR", 8},
71 {"Expiry date", "ESTEID_EXPIRY_DATE", 9},
72 {"Place of birth", "ESTEID_PLACE_OF_BIRTH", 10},
73 {"Issuing date", "ESTEID_ISSUING_DATE", 11},
74 {"Permit type", "ESTEID_PERMIT_TYPE", 12},
75 {"Remark 1", "ESTEID_REMARK1", 13},
76 {"Remark 2", "ESTEID_REMARK2", 14},
77 {"Remark 3", "ESTEID_REMARK3", 15},
78 {"Remark 4", "ESTEID_REMARK4", 16},
79 {NULL, NULL, 0}
80 };
81
show_version(void)82 static void show_version(void)
83 {
84 fprintf(stderr,
85 "eidenv - EstEID utility version " PACKAGE_VERSION "\n"
86 "\n"
87 "Copyright (c) 2004 Martin Paljak <martin@martinpaljak.net>\n"
88 "Licensed under LGPL v2\n");
89 }
90
show_help(void)91 static void show_help(void)
92 {
93 show_version();
94 fprintf(stderr,
95 "-h --help - show this text and exit\n"
96 "-v --version - show version and exit\n"
97 "-r --reader - the reader to use\n"
98 "-w --wait - wait for a card to be inserted\n"
99 "-p --print - print the datafile\n"
100 "-t --stats - show usage counts of keys\n"
101 "-x --exec - execute a program with data in env vars.\n");
102 }
103
decode_options(int argc,char ** argv)104 static void decode_options(int argc, char **argv)
105 {
106 int c;
107
108 while ((c = getopt_long(argc, argv,"pwtr:x:hV", options, (int *) 0)) != -1) {
109
110 switch (c) {
111 case 'r':
112 opt_reader = optarg;
113 break;
114 case 't':
115 stats = !stats;
116 break;
117 case 'x':
118 if (exec_program)
119 free(exec_program);
120 exec_program = strdup(optarg);
121 break;
122 case 'h':
123 show_help();
124 exit(EXIT_SUCCESS);
125 break;
126 case 'p':
127 break;
128 case 'w':
129 opt_wait = 1;
130 break;
131 case 'V':
132 show_version();
133 exit(EXIT_SUCCESS);
134 break;
135 default:
136 show_help();
137 exit(EXIT_FAILURE);
138 }
139 }
140 }
141
do_esteid(sc_card_t * card)142 static void do_esteid(sc_card_t *card)
143 {
144 sc_path_t path;
145 int r, i;
146 unsigned char buff[512];
147
148 if (stats) {
149 int key_used[4];
150 sc_format_path("3f00eeee0013", &path);
151 r = sc_select_file(card, &path, NULL);
152 if (r) {
153 fprintf(stderr, "Failed to select key counters: %s\n", sc_strerror(r));
154 goto out;
155 }
156
157 /* print the counters */
158 for (i = 1; i <= 4; i++) {
159 r = sc_read_record(card, i, buff, 128, SC_RECORD_BY_REC_NR);
160 if (r < 0)
161 goto out;
162 key_used[i - 1] = 0xffffff - ((unsigned char) buff[0xc] * 65536
163 + (unsigned char) buff[0xd] * 256
164 + (unsigned char) buff[0xe]);
165 }
166 for (i = 0; i < 2; i++) {
167 printf("Key generation #%d usage:\n\tsign: %d\n\tauth: %d\n",
168 i, key_used[i], key_used[i + 2]);
169 }
170 exit_status = EXIT_SUCCESS;
171 goto out;
172 }
173
174 /* Or just read the datafile */
175 sc_format_path("3f00eeee5044", &path);
176 r = sc_select_file(card, &path, NULL);
177 if (r) {
178 fprintf(stderr, "Failed to select DF: %s\n", sc_strerror(r));
179 goto out;
180 }
181
182 for (i = 0; esteid_data[i].recno != 0; i++) {
183 r = sc_read_record(card, esteid_data[i].recno, buff, 50, SC_RECORD_BY_REC_NR);
184 if (r < 0) {
185 fprintf (stderr, "Failed to read record %d from card: %s\n",
186 esteid_data[i].recno, sc_strerror (r));
187 goto out;
188 }
189 buff[r] = '\0';
190 if (exec_program) {
191 unsigned char * cp;
192 cp = malloc(strlen(esteid_data[i].env_name) +
193 strlen((char *) buff) + 2);
194 if (cp) {
195 strcpy((char *) cp,esteid_data[i].env_name);
196 strcat((char *) cp,"=");
197 strcat((char *) cp,(char *) buff);
198 putenv((char *) cp);
199 }
200 } else {
201 printf("%s: %s\n", esteid_data[i].name, buff);
202 }
203 }
204
205 exit_status = EXIT_SUCCESS;
206
207 out:
208 return;
209 }
210
211 /* Select and read a transparent EF */
read_transp(sc_card_t * card,const char * pathstring,unsigned char * buf,int buflen)212 static int read_transp(sc_card_t *card, const char *pathstring, unsigned char *buf, int buflen)
213 {
214 sc_path_t path;
215 int r;
216
217 sc_format_path(pathstring, &path);
218 r = sc_select_file(card, &path, NULL);
219 if (r < 0)
220 fprintf(stderr, "\nFailed to select file %s: %s\n", pathstring, sc_strerror(r));
221 else {
222 r = sc_read_binary(card, 0, buf, buflen, 0);
223 if (r < 0)
224 fprintf(stderr, "\nFailed to read %s: %s\n", pathstring, sc_strerror(r));
225 }
226
227 return r;
228 }
229
230 /* Hex-encode the buf, 2*len+1 bytes must be reserved. E.g. {'1','2'} -> {'3','1','3','2','\0'} */
231 static const char hextable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'E'};
bintohex(char * buf,int len)232 static void bintohex(char *buf, int len)
233 {
234 int i;
235 for (i = len - 1; i >= 0; i--) {
236 buf[2 * i + 1] = hextable[((unsigned char) buf[i]) % 16];
237 buf[2 * i] = hextable[((unsigned char) buf[i]) / 16];
238 }
239 }
240
exportprint(const char * key,const char * val)241 static void exportprint(const char *key, const char *val)
242 {
243 if (exec_program) {
244 char * cp;
245 cp = malloc(strlen(key) + strlen(val) + 2);
246 if (cp) {
247 strcpy(cp, key);
248 strcat(cp, "=");
249 strcat(cp, val);
250 putenv(cp);
251 free(cp);
252 }
253 } else
254 printf("%s: %s\n", key, val);
255 }
256
do_belpic(sc_card_t * card)257 static void do_belpic(sc_card_t *card)
258 {
259 /* Contents of the ID file (3F00\DF01\4031) */
260 struct {
261 char cardnumber[12 + 1];
262 char chipnumber[2 * 16 + 1];
263 char validfrom[10 + 1];
264 char validtill[10 + 1];
265 char deliveringmunicipality[50 + 1]; /* UTF8 */
266 char nationalnumber[12 + 1];
267 char name[90 + 1]; /* UTF8 */
268 char firstnames[75 + 1]; /* UTF8 */
269 char initial[3 + 1]; /* UTF8 */
270 char nationality[65 + 1]; /* UTF8 */
271 char birthlocation[60 + 1]; /* UTF8 */
272 char birthdate[12 + 1];
273 char sex[1 + 1];
274 char noblecondition[30 + 1]; /* UTF8 */
275 char documenttype[5 + 1];
276 char specialstatus[5 + 1];
277 } id_data;
278 int cardnumberlen = sizeof(id_data.cardnumber);
279 int chipnumberlen = sizeof(id_data.chipnumber);
280 int validfromlen = sizeof(id_data.validfrom);
281 int validtilllen = sizeof(id_data.validtill);
282 int deliveringmunicipalitylen = sizeof(id_data.deliveringmunicipality);
283 int nationalnumberlen = sizeof(id_data.nationalnumber);
284 int namelen = sizeof(id_data.name);
285 int firstnameslen = sizeof(id_data.firstnames);
286 int initiallen = sizeof(id_data.initial);
287 int nationalitylen = sizeof(id_data.nationality);
288 int birthlocationlen = sizeof(id_data.birthlocation);
289 int birthdatelen = sizeof(id_data.birthdate);
290 int sexlen = sizeof(id_data.sex);
291 int nobleconditionlen = sizeof(id_data.noblecondition);
292 int documenttypelen = sizeof(id_data.documenttype);
293 int specialstatuslen = sizeof(id_data.specialstatus);
294
295 struct sc_asn1_entry id[] = {
296 {"cardnumber", SC_ASN1_UTF8STRING, 1, 0, id_data.cardnumber, &cardnumberlen},
297 {"chipnumber", SC_ASN1_OCTET_STRING, 2, 0, id_data.chipnumber, &chipnumberlen},
298 {"validfrom", SC_ASN1_UTF8STRING, 3, 0, id_data.validfrom, &validfromlen},
299 {"validtill", SC_ASN1_UTF8STRING, 4, 0, id_data.validtill, &validtilllen},
300 {"deliveringmunicipality", SC_ASN1_UTF8STRING, 5, 0, id_data.deliveringmunicipality, &deliveringmunicipalitylen},
301 {"nationalnumber", SC_ASN1_UTF8STRING, 6, 0, id_data.nationalnumber, &nationalnumberlen},
302 {"name", SC_ASN1_UTF8STRING, 7, 0, id_data.name, &namelen},
303 {"firstname(s)", SC_ASN1_UTF8STRING, 8, 0, id_data.firstnames, &firstnameslen},
304 {"initial", SC_ASN1_UTF8STRING, 9, 0, id_data.initial, &initiallen},
305 {"nationality", SC_ASN1_UTF8STRING, 10, 0, id_data.nationality, &nationalitylen},
306 {"birthlocation", SC_ASN1_UTF8STRING, 11, 0, id_data.birthlocation, &birthlocationlen},
307 {"birthdate", SC_ASN1_UTF8STRING, 12, 0, id_data.birthdate, &birthdatelen},
308 {"sex", SC_ASN1_UTF8STRING, 13, 0, id_data.sex, &sexlen},
309 {"noblecondition", SC_ASN1_UTF8STRING, 14, 0, id_data.noblecondition, &nobleconditionlen},
310 {"documenttype", SC_ASN1_UTF8STRING, 15, 0, id_data.documenttype, &documenttypelen},
311 {"specialstatus", SC_ASN1_UTF8STRING, 16, 0, id_data.specialstatus, &specialstatuslen},
312 {NULL, 0, 0, 0, NULL, NULL}
313 };
314
315 /* Contents of the Address file (3F00\DF01\4033) */
316 struct {
317 char streetandnumber[63 + 1]; /* UTF8 */
318 char zipcode[4 + 1];
319 char municipality[40 + 1]; /* UTF8 */
320 } address_data;
321 int streetandnumberlen = sizeof(address_data.streetandnumber);
322 int zipcodelen = sizeof(address_data.zipcode);
323 int municipalitylen = sizeof(address_data.municipality);
324 struct sc_asn1_entry address[] = {
325 {"streetandnumber", SC_ASN1_UTF8STRING, 1, 0, address_data.streetandnumber, &streetandnumberlen},
326 {"zipcode", SC_ASN1_UTF8STRING, 2, 0, address_data.zipcode, &zipcodelen},
327 {"municipal", SC_ASN1_UTF8STRING, 3, 0, address_data.municipality, &municipalitylen},
328 {NULL, 0, 0, 0, NULL, NULL}};
329
330 unsigned char buff[512];
331 int r;
332
333 r = read_transp(card, "3f00df014031", buff, sizeof(buff));
334 if (r < 0)
335 goto out;
336
337 memset(&id_data, 0, sizeof(id_data));
338
339 r = sc_asn1_decode(card->ctx, id, buff, r, NULL, NULL);
340 if (r < 0) {
341 fprintf(stderr, "\nFailed to decode the ID file: %s\n", sc_strerror(r));
342 goto out;
343 }
344
345 exportprint("BELPIC_CARDNUMBER", id_data.cardnumber);
346 bintohex(id_data.chipnumber, chipnumberlen);
347 exportprint("BELPIC_CHIPNUMBER", id_data.chipnumber);
348 exportprint("BELPIC_VALIDFROM", id_data.validfrom);
349 exportprint("BELPIC_VALIDTILL", id_data.validtill);
350 exportprint("BELPIC_DELIVERINGMUNICIPALITY", id_data.deliveringmunicipality);
351 exportprint("BELPIC_NATIONALNUMBER", id_data.nationalnumber);
352 exportprint("BELPIC_NAME", id_data.name);
353 exportprint("BELPIC_FIRSTNAMES", id_data.firstnames);
354 exportprint("BELPIC_INITIAL", id_data.initial);
355 exportprint("BELPIC_NATIONALITY", id_data.nationality);
356 exportprint("BELPIC_BIRTHLOCATION", id_data.birthlocation);
357 exportprint("BELPIC_BIRTHDATE", id_data.birthdate);
358 exportprint("BELPIC_SEX", id_data.sex);
359 exportprint("BELPIC_NOBLECONDITION", id_data.noblecondition);
360 exportprint("BELPIC_DOCUMENTTYPE", id_data.documenttype);
361 exportprint("BELPIC_SPECIALSTATUS", id_data.specialstatus);
362
363 r = read_transp(card, "3f00df014033", buff, sizeof(buff));
364 if (r < 0)
365 goto out;
366
367 memset(&address_data, 0, sizeof(address_data));
368
369 r = sc_asn1_decode(card->ctx, address, buff, r, NULL, NULL);
370 if (r < 0) {
371 fprintf(stderr, "\nFailed to decode the Address file: %s\n", sc_strerror(r));
372 goto out;
373 }
374
375 exportprint("BELPIC_STREETANDNUMBER", address_data.streetandnumber);
376 exportprint("BELPIC_ZIPCODE", address_data.zipcode);
377 exportprint("BELPIC_MUNICIPALITY", address_data.municipality);
378
379 out:
380 return;
381 }
382
main(int argc,char ** argv)383 int main(int argc, char **argv)
384 {
385 sc_context_t *ctx = NULL;
386 sc_context_param_t ctx_param;
387 sc_card_t *card = NULL;
388 int r;
389
390 /* get options */
391 decode_options(argc, argv);
392
393 /* connect to the card */
394 memset(&ctx_param, 0, sizeof(ctx_param));
395 ctx_param.ver = 0;
396 ctx_param.app_name = app_name;
397
398 r = sc_context_create(&ctx, &ctx_param);
399 if (r) {
400 fprintf(stderr, "Failed to establish context: %s\n",
401 sc_strerror(r));
402 return 1;
403 }
404 r = util_connect_card(ctx, &card, opt_reader, opt_wait, 0);
405 if (r) {
406 fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r));
407 sc_release_context(ctx);
408 return 1;
409 }
410
411 /* Check card type */
412 if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V30)
413 do_esteid(card);
414 else if (card->type == SC_CARD_TYPE_BELPIC_EID)
415 do_belpic(card);
416 else {
417 fprintf(stderr, "Not an EstEID or Belpic card!\n");
418 goto out;
419 }
420
421 if (exec_program) {
422 char *const largv[] = {exec_program, NULL};
423 sc_unlock(card);
424 sc_disconnect_card(card);
425 sc_release_context(ctx);
426 execv(exec_program, largv);
427 /* we should not get here */
428 perror("execv()");
429 exit(1);
430 }
431
432 out:
433 sc_unlock(card);
434 sc_disconnect_card(card);
435 sc_release_context(ctx);
436 exit(exit_status);
437 }
438