opensc/src/tools/eidenv.c

438 lines
13 KiB
C

/*
* eidenv.c: EstEID utility
*
* Copyright (C) 2004 Martin Paljak <martin@martinpaljak.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdio.h>
#ifndef _WIN32
#include <unistd.h>
#else
#include <process.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "libopensc/opensc.h"
#include "libopensc/asn1.h"
#include "libopensc/cards.h"
#include "util.h"
static char *opt_reader = NULL;
static int stats = 0;
static int opt_wait = 0;
static char *exec_program = NULL;
static int exit_status = EXIT_FAILURE;
static const struct option options[] = {
{"reader", required_argument, NULL, 'r'},
{"print", no_argument, NULL, 'p'},
{"exec", required_argument, NULL, 'x'},
{"stats", no_argument, NULL, 't'},
{"help", no_argument, NULL, 'h'},
{"wait", no_argument, NULL, 'w'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
/* Probably not used, but needed to build on Windows */
static const char *app_name = "eidenv";
static struct {
const char *name;
const char *env_name;
int recno;
} esteid_data[] = {
{"Surname", "ESTEID_SURNAME", 1},
{"Given names 1", "ESTEID_GIVEN_NAMES1", 2},
{"Given names 2", "ESTEID_GIVEN_NAMES2", 3},
{"Sex", "ESTEID_SEX", 4},
{"Citizenship", "ESTEID_CITIZENSHIP", 5},
{"Date of birth", "ESTEID_DATE_OF_BIRTH", 6},
{"Personal ID code", "ESTEID_PERSONAL_ID", 7},
{"Document number", "ESTEID_DOCUMENT_NR", 8},
{"Expiry date", "ESTEID_EXPIRY_DATE", 9},
{"Place of birth", "ESTEID_PLACE_OF_BIRTH", 10},
{"Issuing date", "ESTEID_ISSUING_DATE", 11},
{"Permit type", "ESTEID_PERMIT_TYPE", 12},
{"Remark 1", "ESTEID_REMARK1", 13},
{"Remark 2", "ESTEID_REMARK2", 14},
{"Remark 3", "ESTEID_REMARK3", 15},
{"Remark 4", "ESTEID_REMARK4", 16},
{NULL, NULL, 0}
};
static void show_version(void)
{
fprintf(stderr,
"eidenv - EstEID utility version " PACKAGE_VERSION "\n"
"\n"
"Copyright (c) 2004 Martin Paljak <martin@martinpaljak.net>\n"
"Licensed under LGPL v2\n");
}
static void show_help(void)
{
show_version();
fprintf(stderr,
"-h --help - show this text and exit\n"
"-v --version - show version and exit\n"
"-r --reader - the reader to use\n"
"-w --wait - wait for a card to be inserted\n"
"-p --print - print the datafile\n"
"-t --stats - show usage counts of keys\n"
"-x --exec - execute a program with data in env vars.\n");
}
static void decode_options(int argc, char **argv)
{
int c;
while ((c = getopt_long(argc, argv,"pwtr:x:hV", options, (int *) 0)) != -1) {
switch (c) {
case 'r':
opt_reader = optarg;
break;
case 't':
stats = !stats;
break;
case 'x':
if (exec_program)
free(exec_program);
exec_program = strdup(optarg);
break;
case 'h':
show_help();
exit(EXIT_SUCCESS);
break;
case 'p':
break;
case 'w':
opt_wait = 1;
break;
case 'V':
show_version();
exit(EXIT_SUCCESS);
break;
default:
show_help();
exit(EXIT_FAILURE);
}
}
}
static void do_esteid(sc_card_t *card)
{
sc_path_t path;
int r, i;
unsigned char buff[512];
if (stats) {
int key_used[4];
sc_format_path("3f00eeee0013", &path);
r = sc_select_file(card, &path, NULL);
if (r) {
fprintf(stderr, "Failed to select key counters: %s\n", sc_strerror(r));
goto out;
}
/* print the counters */
for (i = 1; i <= 4; i++) {
r = sc_read_record(card, i, buff, 128, SC_RECORD_BY_REC_NR);
if (r < 0)
goto out;
key_used[i - 1] = 0xffffff - ((unsigned char) buff[0xc] * 65536
+ (unsigned char) buff[0xd] * 256
+ (unsigned char) buff[0xe]);
}
for (i = 0; i < 2; i++) {
printf("Key generation #%d usage:\n\tsign: %d\n\tauth: %d\n",
i, key_used[i], key_used[i + 2]);
}
exit_status = EXIT_SUCCESS;
goto out;
}
/* Or just read the datafile */
sc_format_path("3f00eeee5044", &path);
r = sc_select_file(card, &path, NULL);
if (r) {
fprintf(stderr, "Failed to select DF: %s\n", sc_strerror(r));
goto out;
}
for (i = 0; esteid_data[i].recno != 0; i++) {
r = sc_read_record(card, esteid_data[i].recno, buff, 50, SC_RECORD_BY_REC_NR);
if (r < 0) {
fprintf (stderr, "Failed to read record %d from card: %s\n",
esteid_data[i].recno, sc_strerror (r));
goto out;
}
buff[r] = '\0';
if (exec_program) {
unsigned char * cp;
cp = malloc(strlen(esteid_data[i].env_name) +
strlen((char *) buff) + 2);
if (cp) {
strcpy((char *) cp,esteid_data[i].env_name);
strcat((char *) cp,"=");
strcat((char *) cp,(char *) buff);
putenv((char *) cp);
}
} else {
printf("%s: %s\n", esteid_data[i].name, buff);
}
}
exit_status = EXIT_SUCCESS;
out:
return;
}
/* Select and read a transparent EF */
static int read_transp(sc_card_t *card, const char *pathstring, unsigned char *buf, int buflen)
{
sc_path_t path;
int r;
sc_format_path(pathstring, &path);
r = sc_select_file(card, &path, NULL);
if (r < 0)
fprintf(stderr, "\nFailed to select file %s: %s\n", pathstring, sc_strerror(r));
else {
r = sc_read_binary(card, 0, buf, buflen, 0);
if (r < 0)
fprintf(stderr, "\nFailed to read %s: %s\n", pathstring, sc_strerror(r));
}
return r;
}
/* Hex-encode the buf, 2*len+1 bytes must be reserved. E.g. {'1','2'} -> {'3','1','3','2','\0'} */
static const char hextable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'E'};
static void bintohex(char *buf, int len)
{
int i;
for (i = len - 1; i >= 0; i--) {
buf[2 * i + 1] = hextable[((unsigned char) buf[i]) % 16];
buf[2 * i] = hextable[((unsigned char) buf[i]) / 16];
}
}
static void exportprint(const char *key, const char *val)
{
if (exec_program) {
char * cp;
cp = malloc(strlen(key) + strlen(val) + 2);
if (cp) {
strcpy(cp, key);
strcat(cp, "=");
strcat(cp, val);
putenv(cp);
free(cp);
}
} else
printf("%s: %s\n", key, val);
}
static void do_belpic(sc_card_t *card)
{
/* Contents of the ID file (3F00\DF01\4031) */
struct {
char cardnumber[12 + 1];
char chipnumber[2 * 16 + 1];
char validfrom[10 + 1];
char validtill[10 + 1];
char deliveringmunicipality[50 + 1]; /* UTF8 */
char nationalnumber[12 + 1];
char name[90 + 1]; /* UTF8 */
char firstnames[75 + 1]; /* UTF8 */
char initial[3 + 1]; /* UTF8 */
char nationality[65 + 1]; /* UTF8 */
char birthlocation[60 + 1]; /* UTF8 */
char birthdate[12 + 1];
char sex[1 + 1];
char noblecondition[30 + 1]; /* UTF8 */
char documenttype[5 + 1];
char specialstatus[5 + 1];
} id_data;
int cardnumberlen = sizeof(id_data.cardnumber);
int chipnumberlen = sizeof(id_data.chipnumber);
int validfromlen = sizeof(id_data.validfrom);
int validtilllen = sizeof(id_data.validtill);
int deliveringmunicipalitylen = sizeof(id_data.deliveringmunicipality);
int nationalnumberlen = sizeof(id_data.nationalnumber);
int namelen = sizeof(id_data.name);
int firstnameslen = sizeof(id_data.firstnames);
int initiallen = sizeof(id_data.initial);
int nationalitylen = sizeof(id_data.nationality);
int birthlocationlen = sizeof(id_data.birthlocation);
int birthdatelen = sizeof(id_data.birthdate);
int sexlen = sizeof(id_data.sex);
int nobleconditionlen = sizeof(id_data.noblecondition);
int documenttypelen = sizeof(id_data.documenttype);
int specialstatuslen = sizeof(id_data.specialstatus);
struct sc_asn1_entry id[] = {
{"cardnumber", SC_ASN1_UTF8STRING, 1, 0, id_data.cardnumber, &cardnumberlen},
{"chipnumber", SC_ASN1_OCTET_STRING, 2, 0, id_data.chipnumber, &chipnumberlen},
{"validfrom", SC_ASN1_UTF8STRING, 3, 0, id_data.validfrom, &validfromlen},
{"validtill", SC_ASN1_UTF8STRING, 4, 0, id_data.validtill, &validtilllen},
{"deliveringmunicipality", SC_ASN1_UTF8STRING, 5, 0, id_data.deliveringmunicipality, &deliveringmunicipalitylen},
{"nationalnumber", SC_ASN1_UTF8STRING, 6, 0, id_data.nationalnumber, &nationalnumberlen},
{"name", SC_ASN1_UTF8STRING, 7, 0, id_data.name, &namelen},
{"firstname(s)", SC_ASN1_UTF8STRING, 8, 0, id_data.firstnames, &firstnameslen},
{"initial", SC_ASN1_UTF8STRING, 9, 0, id_data.initial, &initiallen},
{"nationality", SC_ASN1_UTF8STRING, 10, 0, id_data.nationality, &nationalitylen},
{"birthlocation", SC_ASN1_UTF8STRING, 11, 0, id_data.birthlocation, &birthlocationlen},
{"birthdate", SC_ASN1_UTF8STRING, 12, 0, id_data.birthdate, &birthdatelen},
{"sex", SC_ASN1_UTF8STRING, 13, 0, id_data.sex, &sexlen},
{"noblecondition", SC_ASN1_UTF8STRING, 14, 0, id_data.noblecondition, &nobleconditionlen},
{"documenttype", SC_ASN1_UTF8STRING, 15, 0, id_data.documenttype, &documenttypelen},
{"specialstatus", SC_ASN1_UTF8STRING, 16, 0, id_data.specialstatus, &specialstatuslen},
{NULL, 0, 0, 0, NULL, NULL}
};
/* Contents of the Address file (3F00\DF01\4033) */
struct {
char streetandnumber[63 + 1]; /* UTF8 */
char zipcode[4 + 1];
char municipality[40 + 1]; /* UTF8 */
} address_data;
int streetandnumberlen = sizeof(address_data.streetandnumber);
int zipcodelen = sizeof(address_data.zipcode);
int municipalitylen = sizeof(address_data.municipality);
struct sc_asn1_entry address[] = {
{"streetandnumber", SC_ASN1_UTF8STRING, 1, 0, address_data.streetandnumber, &streetandnumberlen},
{"zipcode", SC_ASN1_UTF8STRING, 2, 0, address_data.zipcode, &zipcodelen},
{"municipal", SC_ASN1_UTF8STRING, 3, 0, address_data.municipality, &municipalitylen},
{NULL, 0, 0, 0, NULL, NULL}};
unsigned char buff[512];
int r;
r = read_transp(card, "3f00df014031", buff, sizeof(buff));
if (r < 0)
goto out;
memset(&id_data, 0, sizeof(id_data));
r = sc_asn1_decode(card->ctx, id, buff, r, NULL, NULL);
if (r < 0) {
fprintf(stderr, "\nFailed to decode the ID file: %s\n", sc_strerror(r));
goto out;
}
exportprint("BELPIC_CARDNUMBER", id_data.cardnumber);
bintohex(id_data.chipnumber, chipnumberlen);
exportprint("BELPIC_CHIPNUMBER", id_data.chipnumber);
exportprint("BELPIC_VALIDFROM", id_data.validfrom);
exportprint("BELPIC_VALIDTILL", id_data.validtill);
exportprint("BELPIC_DELIVERINGMUNICIPALITY", id_data.deliveringmunicipality);
exportprint("BELPIC_NATIONALNUMBER", id_data.nationalnumber);
exportprint("BELPIC_NAME", id_data.name);
exportprint("BELPIC_FIRSTNAMES", id_data.firstnames);
exportprint("BELPIC_INITIAL", id_data.initial);
exportprint("BELPIC_NATIONALITY", id_data.nationality);
exportprint("BELPIC_BIRTHLOCATION", id_data.birthlocation);
exportprint("BELPIC_BIRTHDATE", id_data.birthdate);
exportprint("BELPIC_SEX", id_data.sex);
exportprint("BELPIC_NOBLECONDITION", id_data.noblecondition);
exportprint("BELPIC_DOCUMENTTYPE", id_data.documenttype);
exportprint("BELPIC_SPECIALSTATUS", id_data.specialstatus);
r = read_transp(card, "3f00df014033", buff, sizeof(buff));
if (r < 0)
goto out;
memset(&address_data, 0, sizeof(address_data));
r = sc_asn1_decode(card->ctx, address, buff, r, NULL, NULL);
if (r < 0) {
fprintf(stderr, "\nFailed to decode the Address file: %s\n", sc_strerror(r));
goto out;
}
exportprint("BELPIC_STREETANDNUMBER", address_data.streetandnumber);
exportprint("BELPIC_ZIPCODE", address_data.zipcode);
exportprint("BELPIC_MUNICIPALITY", address_data.municipality);
out:
return;
}
int main(int argc, char **argv)
{
sc_context_t *ctx = NULL;
sc_context_param_t ctx_param;
sc_card_t *card = NULL;
int r;
/* get options */
decode_options(argc, argv);
/* connect to the card */
memset(&ctx_param, 0, sizeof(ctx_param));
ctx_param.ver = 0;
ctx_param.app_name = app_name;
r = sc_context_create(&ctx, &ctx_param);
if (r) {
fprintf(stderr, "Failed to establish context: %s\n",
sc_strerror(r));
return 1;
}
r = util_connect_card(ctx, &card, opt_reader, opt_wait, 0);
if (r) {
fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r));
sc_release_context(ctx);
return 1;
}
/* Check card type */
if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V30)
do_esteid(card);
else if (card->type == SC_CARD_TYPE_BELPIC_EID)
do_belpic(card);
else {
fprintf(stderr, "Not an EstEID or Belpic card!\n");
goto out;
}
if (exec_program) {
char *const largv[] = {exec_program, NULL};
sc_unlock(card);
sc_disconnect_card(card);
sc_release_context(ctx);
execv(exec_program, largv);
/* we should not get here */
perror("execv()");
exit(1);
}
out:
sc_unlock(card);
sc_disconnect_card(card);
sc_release_context(ctx);
exit(exit_status);
}