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