diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index a99b0c5741..5a17df2b1d 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -40,7 +40,8 @@ libopensc_la_SOURCES_BASE = \ card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \ card-oberthur.c card-belpic.c card-atrust-acos.c \ card-entersafe.c card-epass2003.c card-coolkey.c card-incrypto34.c \ - card-piv.c card-cac.c card-muscle.c card-acos5.c \ + card-piv.c card-cac-common.c card-cac.c card-cac1.c \ + card-muscle.c card-acos5.c \ card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \ card-rtecp.c card-westcos.c card-myeid.c \ card-itacns.c card-authentic.c \ diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 10a3b0a9dd..c498230489 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -19,7 +19,8 @@ OBJECTS = \ card-mcrd.obj card-starcos.obj card-openpgp.obj card-jcop.obj \ card-oberthur.obj card-belpic.obj card-atrust-acos.obj \ card-entersafe.obj card-epass2003.obj card-coolkey.obj \ - card-incrypto34.obj card-cac.obj card-piv.obj card-muscle.obj \ + card-incrypto34.obj card-cac.obj card-cac1.obj card-cac-common.obj \ + card-piv.obj card-muscle.obj \ card-acos5.obj \ card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \ card-rtecp.obj card-westcos.obj card-myeid.obj \ diff --git a/src/libopensc/card-cac-common.c b/src/libopensc/card-cac-common.c new file mode 100644 index 0000000000..6c338bf99f --- /dev/null +++ b/src/libopensc/card-cac-common.c @@ -0,0 +1,116 @@ +/* + * card-cac-common.c: Code shared among CAC1 and CAC2 drivers + * + * Copyright (C) 2018, Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "internal.h" +#include "iso7816.h" +#include "card-cac-common.h" + +/* default certificate labels for the CAC card */ +const char *cac_labels[MAX_CAC_SLOTS] = { + "CAC ID Certificate", + "CAC Email Signature Certificate", + "CAC Email Encryption Certificate", + "CAC Cert 4", + "CAC Cert 5", + "CAC Cert 6", + "CAC Cert 7", + "CAC Cert 8", + "CAC Cert 9", + "CAC Cert 10", + "CAC Cert 11", + "CAC Cert 12", + "CAC Cert 13", + "CAC Cert 14", + "CAC Cert 15", + "CAC Cert 16" +}; + +const char *get_cac_label(int index) +{ + if (index < 0 || index >= MAX_CAC_SLOTS) + return NULL; + + return cac_labels[index]; +} + +static int cac_list_compare_path(const void *a, const void *b) +{ + if (a == NULL || b == NULL) + return 1; + return memcmp( &((cac_object_t *) a)->path, + &((cac_object_t *) b)->path, sizeof(sc_path_t)); +} + +/* For SimCList autocopy, we need to know the size of the data elements */ +static size_t cac_list_meter(const void *el) { + return sizeof(cac_object_t); +} + +cac_private_data_t *cac_new_private_data(void) +{ + cac_private_data_t *priv; + priv = calloc(1, sizeof(cac_private_data_t)); + if (!priv) + return NULL; + list_init(&priv->pki_list); + list_attributes_comparator(&priv->pki_list, cac_list_compare_path); + list_attributes_copy(&priv->pki_list, cac_list_meter, 1); + list_init(&priv->general_list); + list_attributes_comparator(&priv->general_list, cac_list_compare_path); + list_attributes_copy(&priv->general_list, cac_list_meter, 1); + /* set other fields as appropriate */ + + return priv; +} + +void cac_free_private_data(cac_private_data_t *priv) +{ + free(priv->cac_id); + free(priv->cache_buf); + free(priv->aca_path); + list_destroy(&priv->pki_list); + list_destroy(&priv->general_list); + free(priv); + return; +} + +int cac_add_object_to_list(list_t *list, const cac_object_t *object) +{ + if (list_append(list, object) < 0) + return SC_ERROR_UNKNOWN; + return SC_SUCCESS; +} + diff --git a/src/libopensc/card-cac-common.h b/src/libopensc/card-cac-common.h new file mode 100644 index 0000000000..77f1476104 --- /dev/null +++ b/src/libopensc/card-cac-common.h @@ -0,0 +1,89 @@ +/* + * card-cac-common.h: Code shared among CAC1 and CAC2 drivers + * + * Copyright (C) 2018, Red Hat, Inc. + * + * Author: Jakub Jelen + * + * 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 + */ + +#ifndef HAVE_CARD_CAC_COMMON_H +#define HAVE_CARD_CAC_COMMON_H + +#define CAC_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */ + +typedef struct cac_cuid { + u8 gsc_rid[5]; + u8 manufacturer_id; + u8 card_type; + u8 card_id; +} cac_cuid_t; + +/* data structures to store meta data about CAC objects */ +typedef struct cac_object { + const char *name; + int fd; + sc_path_t path; +} cac_object_t; + +/* + * CAC private data per card state + */ +typedef struct cac_private_data { + int object_type; /* select set this so we know how to read the file */ + int cert_next; /* index number for the next certificate found in the list */ + u8 *cache_buf; /* cached version of the currently selected file */ + size_t cache_buf_len; /* length of the cached selected file */ + int cached; /* is the cached selected file valid */ + cac_cuid_t cuid; /* card unique ID from the CCC */ + u8 *cac_id; /* card serial number */ + size_t cac_id_len; /* card serial number len */ + list_t pki_list; /* list of pki containers */ + cac_object_t *pki_current; /* current pki object _ctl function */ + list_t general_list; /* list of general containers */ + cac_object_t *general_current; /* current object for _ctl function */ + sc_path_t *aca_path; /* ACA path to be selected before pin verification */ +} cac_private_data_t; + +#define CAC_DATA(card) ((cac_private_data_t*)card->drv_data) + +/* + * Set up the normal CAC paths + */ +#define CAC_1_RID "\xA0\x00\x00\x00\x79" +#define CAC_TO_AID(x) x, sizeof(x)-1 + + +#define MAX_CAC_SLOTS 16 /* Maximum number of slots is 16 now */ + +/* template for a CAC pki object */ +static const cac_object_t cac_cac_pki_obj = { + "CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, + { CAC_TO_AID(CAC_1_RID "\x01\x00") } } +}; + +/* template for emulated cuid */ +static const cac_cuid_t cac_cac_cuid = { + { 0xa0, 0x00, 0x00, 0x00, 0x79 }, + 2, 2, 0 +}; + +cac_private_data_t *cac_new_private_data(void); +void cac_free_private_data(cac_private_data_t *priv); +int cac_add_object_to_list(list_t *list, const cac_object_t *object); +const char *get_cac_label(int index); + +#endif /* HAVE_CARD_CAC_COMMON_H */ diff --git a/src/libopensc/card-cac.c b/src/libopensc/card-cac.c index bd4e03362f..12cda23665 100644 --- a/src/libopensc/card-cac.c +++ b/src/libopensc/card-cac.c @@ -43,12 +43,7 @@ #endif #ifdef ENABLE_OPENSSL - /* openssl only needed for card administration */ -#include -#include -#include -#include -#include +#include #endif /* ENABLE_OPENSSL */ #include "internal.h" @@ -58,8 +53,8 @@ #include "compression.h" #endif #include "iso7816.h" +#include "card-cac-common.h" -#define CAC_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */ /* * CAC hardware and APDU constants */ @@ -147,20 +142,6 @@ typedef struct cac_card_url { u8 keyCryptoAlgorithm; /* not used for VM cards */ } cac_card_url_t; -typedef struct cac_cuid { - u8 gsc_rid[5]; - u8 manufacturer_id; - u8 card_type; - u8 card_id; -} cac_cuid_t; - -/* data structures to store meta data about CAC objects */ -typedef struct cac_object { - const char *name; - int fd; - sc_path_t path; -} cac_object_t; - #define CAC_MAX_OBJECTS 16 typedef struct { @@ -190,82 +171,10 @@ typedef struct { #define CAC_OBJECT_TYPE_TLV_FILE 4 #define CAC_OBJECT_TYPE_GENERIC 5 -/* - * CAC private data per card state - */ -typedef struct cac_private_data { - int object_type; /* select set this so we know how to read the file */ - int cert_next; /* index number for the next certificate found in the list */ - u8 *cache_buf; /* cached version of the currently selected file */ - size_t cache_buf_len; /* length of the cached selected file */ - int cached; /* is the cached selected file valid */ - cac_cuid_t cuid; /* card unique ID from the CCC */ - u8 *cac_id; /* card serial number */ - size_t cac_id_len; /* card serial number len */ - list_t pki_list; /* list of pki containers */ - cac_object_t *pki_current; /* current pki object _ctl function */ - list_t general_list; /* list of general containers */ - cac_object_t *general_current; /* current object for _ctl function */ - sc_path_t *aca_path; /* ACA path to be selected before pin verification */ -} cac_private_data_t; - -#define CAC_DATA(card) ((cac_private_data_t*)card->drv_data) - -static int cac_list_compare_path(const void *a, const void *b) -{ - if (a == NULL || b == NULL) - return 1; - return memcmp( &((cac_object_t *) a)->path, - &((cac_object_t *) b)->path, sizeof(sc_path_t)); -} - -/* For SimCList autocopy, we need to know the size of the data elements */ -static size_t cac_list_meter(const void *el) { - return sizeof(cac_object_t); -} - -static cac_private_data_t *cac_new_private_data(void) -{ - cac_private_data_t *priv; - priv = calloc(1, sizeof(cac_private_data_t)); - if (!priv) - return NULL; - list_init(&priv->pki_list); - list_attributes_comparator(&priv->pki_list, cac_list_compare_path); - list_attributes_copy(&priv->pki_list, cac_list_meter, 1); - list_init(&priv->general_list); - list_attributes_comparator(&priv->general_list, cac_list_compare_path); - list_attributes_copy(&priv->general_list, cac_list_meter, 1); - /* set other fields as appropriate */ - - return priv; -} - -static void cac_free_private_data(cac_private_data_t *priv) -{ - free(priv->cac_id); - free(priv->cache_buf); - free(priv->aca_path); - list_destroy(&priv->pki_list); - list_destroy(&priv->general_list); - free(priv); - return; -} - -static int cac_add_object_to_list(list_t *list, const cac_object_t *object) -{ - if (list_append(list, object) < 0) - return SC_ERROR_UNKNOWN; - return SC_SUCCESS; -} - /* * Set up the normal CAC paths */ -#define CAC_TO_AID(x) x, sizeof(x)-1 - #define CAC_2_RID "\xA0\x00\x00\x01\x16" -#define CAC_1_RID "\xA0\x00\x00\x00\x79" static const sc_path_t cac_ACA_Path = { "", 0, @@ -279,39 +188,6 @@ static const sc_path_t cac_CCC_Path = { { CAC_TO_AID(CAC_2_RID "\xDB\x00") } }; -#define MAX_CAC_SLOTS 16 /* Maximum number of slots is 16 now */ -/* default certificate labels for the CAC card */ -static const char *cac_labels[MAX_CAC_SLOTS] = { - "CAC ID Certificate", - "CAC Email Signature Certificate", - "CAC Email Encryption Certificate", - "CAC Cert 4", - "CAC Cert 5", - "CAC Cert 6", - "CAC Cert 7", - "CAC Cert 8", - "CAC Cert 9", - "CAC Cert 10", - "CAC Cert 11", - "CAC Cert 12", - "CAC Cert 13", - "CAC Cert 14", - "CAC Cert 15", - "CAC Cert 16" -}; - -/* template for a CAC pki object */ -static const cac_object_t cac_cac_pki_obj = { - "CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, - { CAC_TO_AID(CAC_1_RID "\x01\x00") } } -}; - -/* template for emulated cuid */ -static const cac_cuid_t cac_cac_cuid = { - { 0xa0, 0x00, 0x00, 0x00, 0x79 }, - 2, 2, 0 -}; - /* * CAC general objects defined in 4.3.1.2 of CAC Applet Developer Guide Version 1.0. * doubles as a source for CAC-2 labels. @@ -1178,7 +1054,7 @@ static int cac_get_properties(sc_card_t *card, cac_properties_t *prop) * * The rest is just copied from iso7816_select_file */ -static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out, int type) +static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { struct sc_context *ctx; struct sc_apdu apdu; @@ -1356,7 +1232,7 @@ static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc static int cac_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { - return cac_select_file_by_type(card, in_path, file_out, card->type); + return cac_select_file_by_type(card, in_path, file_out); } static int cac_finish(sc_card_t *card) @@ -1374,13 +1250,13 @@ static int cac_finish(sc_card_t *card) /* select the Card Capabilities Container on CAC-2 */ static int cac_select_CCC(sc_card_t *card) { - return cac_select_file_by_type(card, &cac_CCC_Path, NULL, SC_CARD_TYPE_CAC_II); + return cac_select_file_by_type(card, &cac_CCC_Path, NULL); } /* Select ACA in non-standard location */ static int cac_select_ACA(sc_card_t *card) { - return cac_select_file_by_type(card, &cac_ACA_Path, NULL, SC_CARD_TYPE_CAC_II); + return cac_select_file_by_type(card, &cac_ACA_Path, NULL); } static int cac_path_from_cardurl(sc_card_t *card, sc_path_t *path, cac_card_url_t *val, int len) @@ -1432,7 +1308,7 @@ static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, u8 *aid, int /* Call without OID set will just select the AID without subseqent * OID selection, which we need to figure out just now */ - cac_select_file_by_type(card, &new_object.path, NULL, SC_CARD_TYPE_CAC_II); + cac_select_file_by_type(card, &new_object.path, NULL); r = cac_get_properties(card, &prop); if (r < 0) return SC_ERROR_INTERNAL; @@ -1444,7 +1320,7 @@ static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, u8 *aid, int sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "ACA: pki_object found, cert_next=%d (%s), privkey=%d", - priv->cert_next, cac_labels[priv->cert_next], + priv->cert_next, get_cac_label(priv->cert_next), prop.objects[i].privatekey); /* If the private key is not initialized, we can safely @@ -1460,7 +1336,7 @@ static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, u8 *aid, int memcpy(new_object.path.value, &prop.objects[i].oid, 2); new_object.path.len = 2; new_object.path.type = SC_PATH_TYPE_FILE_ID; - new_object.name = cac_labels[priv->cert_next]; + new_object.name = get_cac_label(priv->cert_next); new_object.fd = priv->cert_next+1; cac_add_object_to_list(&priv->pki_list, &new_object); priv->cert_next++; @@ -1488,7 +1364,7 @@ static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card */ if (priv->cert_next >= MAX_CAC_SLOTS) break; /* don't fail just because we have more certs than we can support */ - new_object.name = cac_labels[priv->cert_next]; + new_object.name = get_cac_label(priv->cert_next); new_object.fd = priv->cert_next+1; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: pki_object found, cert_next=%d (%s),", priv->cert_next, new_object.name); cac_add_object_to_list(&priv->pki_list, &new_object); @@ -1629,7 +1505,7 @@ static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl, if (r < 0) return r; - r = cac_select_file_by_type(card, &new_path, NULL, SC_CARD_TYPE_CAC_II); + r = cac_select_file_by_type(card, &new_path, NULL); if (r < 0) return r; @@ -1740,7 +1616,7 @@ static int cac_select_pki_applet(sc_card_t *card, int index) { sc_path_t applet_path = cac_cac_pki_obj.path; applet_path.aid.value[applet_path.aid.len-1] = index; - return cac_select_file_by_type(card, &applet_path, NULL, SC_CARD_TYPE_CAC_II); + return cac_select_file_by_type(card, &applet_path, NULL); } /* @@ -1785,7 +1661,7 @@ static int cac_populate_cac_alt(sc_card_t *card, int index, cac_private_data_t * for (i = index; i < MAX_CAC_SLOTS; i++) { r = cac_select_pki_applet(card, i); if (r == SC_SUCCESS) { - pki_obj.name = cac_labels[i]; + pki_obj.name = get_cac_label(i); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "CAC: pki_object found, cert_next=%d (%s),", i, pki_obj.name); pki_obj.path.aid.value[pki_obj.path.aid.len-1] = i; @@ -1796,8 +1672,7 @@ static int cac_populate_cac_alt(sc_card_t *card, int index, cac_private_data_t * /* populate non-PKI objects */ for (i=0; i < cac_object_count; i++) { - r = cac_select_file_by_type(card, &cac_objects[i].path, NULL, - SC_CARD_TYPE_CAC_II); + r = cac_select_file_by_type(card, &cac_objects[i].path, NULL); if (r == SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "CAC: obj_object found, cert_next=%d (%s),", diff --git a/src/libopensc/card-cac1.c b/src/libopensc/card-cac1.c new file mode 100644 index 0000000000..858d1bb28e --- /dev/null +++ b/src/libopensc/card-cac1.c @@ -0,0 +1,558 @@ +/* + * card-cac1.c: Support for legacy CAC-1 + * card-default.c: Support for cards with no driver + * + * Copyright (C) 2001, 2002 Juha Yrjölä + * Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert + * Copyright (C) 2006, Identity Alliance, Thomas Harning + * Copyright (C) 2007, EMC, Russell Larner + * Copyright (C) 2016 - 2018, Red Hat, Inc. + * + * CAC driver author: Robert Relyea + * Further work: Jakub Jelen + * + * 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 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef ENABLE_OPENSSL +#include +#endif /* ENABLE_OPENSSL */ + +#include "internal.h" +#include "simpletlv.h" +#include "cardctl.h" +#ifdef ENABLE_ZLIB +#include "compression.h" +#endif +#include "iso7816.h" +#include "card-cac-common.h" + +/* + * CAC hardware and APDU constants + */ +#define CAC_INS_GET_CERTIFICATE 0x36 /* CAC1 command to read a certificate */ + +/* + * OLD cac read certificate, only use with CAC-1 card. + */ +static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_len) +{ + u8 buf[CAC_MAX_SIZE]; + u8 *out_ptr; + size_t size = 0; + size_t left = 0; + size_t len, next_len; + sc_apdu_t apdu; + int r = SC_SUCCESS; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + /* get the size */ + size = left = *out_buf ? *out_len : sizeof(buf); + out_ptr = *out_buf ? *out_buf : buf; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, CAC_INS_GET_CERTIFICATE, 0, 0 ); + next_len = MIN(left, 100); + for (; left > 0; left -= len, out_ptr += len) { + len = next_len; + apdu.resp = out_ptr; + apdu.le = len; + apdu.resplen = left; + r = sc_transmit_apdu(card, &apdu); + if (r < 0) { + break; + } + if (apdu.resplen == 0) { + r = SC_ERROR_INTERNAL; + break; + } + /* in the old CAC-1, 0x63 means 'more data' in addition to 'pin failed' */ + if (apdu.sw1 != 0x63) { + /* we've either finished reading, or hit an error, break */ + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + left -= len; + break; + } + next_len = MIN(left, apdu.sw2); + } + if (r < 0) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); + } + r = size - left; + if (*out_buf == NULL) { + *out_buf = malloc(r); + if (*out_buf == NULL) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, + SC_ERROR_OUT_OF_MEMORY); + } + memcpy(*out_buf, buf, r); + } + *out_len = r; + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + +/* + * Callers of this may be expecting a certificate, + * select file will have saved the object type for us + * as well as set that we want the cert from the object. + */ +static int cac_read_binary(sc_card_t *card, unsigned int idx, + unsigned char *buf, size_t count, unsigned long flags) +{ + cac_private_data_t * priv = CAC_DATA(card); + int r = 0; + u8 *val = NULL; + u8 *cert_ptr; + size_t val_len; + size_t len, cert_len; + u8 cert_type; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* if we didn't return it all last time, return the remainder */ + if (priv->cached) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "returning cached value idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u", + idx, count); + if (idx > priv->cache_buf_len) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_END_REACHED); + } + len = MIN(count, priv->cache_buf_len-idx); + memcpy(buf, &priv->cache_buf[idx], len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, len); + } + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "clearing cache idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u", + idx, count); + if (priv->cache_buf) { + free(priv->cache_buf); + priv->cache_buf = NULL; + priv->cache_buf_len = 0; + } + + r = cac_cac1_get_certificate(card, &val, &val_len); + if (r < 0) + goto done; + + cert_type = val[0]; + cert_ptr = val + 1; + cert_len = val_len - 1; + + /* if the info byte is 1, then the cert is compressed, decompress it */ + if ((cert_type & 0x3) == 1) { +#ifdef ENABLE_ZLIB + r = sc_decompress_alloc(&priv->cache_buf, &priv->cache_buf_len, + cert_ptr, cert_len, COMPRESSION_AUTO); +#else + sc_log(card->ctx, "CAC compression not supported, no zlib"); + r = SC_ERROR_NOT_SUPPORTED; +#endif + if (r) + goto done; + cert_ptr = val; + } else if (cert_len > 0) { + priv->cache_buf = malloc(cert_len); + if (priv->cache_buf == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto done; + } + priv->cache_buf_len = cert_len; + memcpy(priv->cache_buf, cert_ptr, cert_len); + } + + /* OK we've read the data, now copy the required portion out to the callers buffer */ + priv->cached = 1; + len = MIN(count, priv->cache_buf_len-idx); + memcpy(buf, &priv->cache_buf[idx], len); + r = len; +done: + if (val) + free(val); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} + +/* + * CAC cards use SC_PATH_SELECT_OBJECT_ID rather than SC_PATH_SELECT_FILE_ID. In order to use more + * of the PKCS #15 structure, we call the selection SC_PATH_SELECT_FILE_ID, but we set p1 to 2 instead + * of 0. Also cac1 does not do any FCI, but it doesn't understand not selecting it. It returns invalid INS + * if it doesn't like anything about the select, so we always 'request' FCI for CAC1 + * + * The rest is just copied from iso7816_select_file + */ +static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) +{ + struct sc_context *ctx; + struct sc_apdu apdu; + unsigned char buf[SC_MAX_APDU_BUFFER_SIZE]; + unsigned char pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; + int r, pathlen, pathtype; + struct sc_file *file = NULL; + cac_private_data_t * priv = CAC_DATA(card); + + assert(card != NULL && in_path != NULL); + ctx = card->ctx; + + SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); + + memcpy(path, in_path->value, in_path->len); + pathlen = in_path->len; + pathtype = in_path->type; + + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "path->aid=%x %x %x %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u, path->value = %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u path->type=%d (%x)", + in_path->aid.value[0], in_path->aid.value[1], + in_path->aid.value[2], in_path->aid.value[3], + in_path->aid.value[4], in_path->aid.value[5], + in_path->aid.value[6], in_path->aid.len, in_path->value[0], + in_path->value[1], in_path->value[2], in_path->value[3], + in_path->len, in_path->type, in_path->type); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "file_out=%p index=%d count=%d\n", + file_out, in_path->index, in_path->count); + + /* Sigh, sc_key_select expects paths to keys to have specific formats. There is no override. + * we have to add some bytes to the path to make it happy. A better fix would be to give sc_key_file + * a flag that says 'no, really this path is fine'. We only need to do this for private keys */ + if ((pathlen > 2) && (pathlen <= 4) && memcmp(path, "\x3F\x00", 2) == 0) { + if (pathlen > 2) { + path += 2; + pathlen -= 2; + } + } + + + /* CAC has multiple different type of objects that aren't PKCS #15. When we read + * them we need convert them to something PKCS #15 would understand. Find the object + * and object type here: + */ + if (priv) { /* don't record anything if we haven't been initialized yet */ + /* forget any old cached values */ + if (priv->cache_buf) { + free(priv->cache_buf); + priv->cache_buf = NULL; + } + priv->cache_buf_len = 0; + priv->cached = 0; + } + + if (in_path->aid.len) { + if (!pathlen) { + memcpy(path, in_path->aid.value, in_path->aid.len); + pathlen = in_path->aid.len; + pathtype = SC_PATH_TYPE_DF_NAME; + } else { + /* First, select the application */ + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"select application" ); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 4, 0); + apdu.data = in_path->aid.value; + apdu.datalen = in_path->aid.len; + apdu.lc = in_path->aid.len; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) + LOG_FUNC_RETURN(ctx, r); + + } + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0); + + switch (pathtype) { + /* ideally we would had SC_PATH_TYPE_OBJECT_ID and add code to the iso7816 select. + * Unfortunately we'd also need to update the caching code as well. For now just + * use FILE_ID and change p1 here */ + case SC_PATH_TYPE_FILE_ID: + apdu.p1 = 2; + if (pathlen != 2) + return SC_ERROR_INVALID_ARGUMENTS; + break; + case SC_PATH_TYPE_DF_NAME: + apdu.p1 = 4; + break; + default: + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } + apdu.lc = pathlen; + apdu.data = path; + apdu.datalen = pathlen; + apdu.resp = buf; + apdu.resplen = sizeof(buf); + apdu.le = sc_get_max_recv_size(card) < 256 ? sc_get_max_recv_size(card) : 256; + apdu.p2 = 0x00; + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); + + if (file_out == NULL) { + /* For some cards 'SELECT' can be only with request to return FCI/FCP. */ + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (apdu.sw1 == 0x6A && apdu.sw2 == 0x86) { + apdu.p2 = 0x00; + apdu.resplen = sizeof(buf); + if (sc_transmit_apdu(card, &apdu) == SC_SUCCESS) + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + } + if (apdu.sw1 == 0x61) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + LOG_FUNC_RETURN(ctx, r); + } + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) + LOG_FUNC_RETURN(ctx, r); + + /* CAC cards never return FCI, fake one */ + file = sc_file_new(); + if (file == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + file->path = *in_path; + file->size = CAC_MAX_SIZE; /* we don't know how big, just give a large size until we can read the file */ + + *file_out = file; + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); + +} + +static int cac_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) +{ + return cac_select_file_by_type(card, in_path, file_out); +} + +static int cac_finish(sc_card_t *card) +{ + cac_private_data_t * priv = CAC_DATA(card); + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + if (priv) { + cac_free_private_data(priv); + } + return SC_SUCCESS; +} + + +/* select a CAC pki applet by index */ +static int cac_select_pki_applet(sc_card_t *card, int index) +{ + sc_path_t applet_path = cac_cac_pki_obj.path; + applet_path.aid.value[applet_path.aid.len-1] = index; + return cac_select_file_by_type(card, &applet_path, NULL); +} + +/* + * Find the first existing CAC applet. If none found, then this isn't a CAC + */ +static int cac_find_first_pki_applet(sc_card_t *card, int *index_out) +{ + int r, i; + + for (i = 0; i < MAX_CAC_SLOTS; i++) { + r = cac_select_pki_applet(card, i); + if (r == SC_SUCCESS) { + u8 data[2]; + sc_apdu_t apdu; + + /* Try to read first two bytes of the buffer to + * make sure it is not just malfunctioning card + */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2, + CAC_INS_GET_CERTIFICATE, 0x00, 0x00); + apdu.le = 0x02; + apdu.resplen = 2; + apdu.resp = data; + r = sc_transmit_apdu(card, &apdu); + /* SW1 = 0x63 means more data in CAC1 */ + if (r == SC_SUCCESS && apdu.sw1 != 0x63) + continue; + + *index_out = i; + return SC_SUCCESS; + } + } + return SC_ERROR_OBJECT_NOT_FOUND; +} + +static int cac_populate_cac1(sc_card_t *card, int index, cac_private_data_t *priv) +{ + int r, i; + cac_object_t pki_obj = cac_cac_pki_obj; + u8 buf[100]; + u8 *val; + size_t val_len; + + /* populate PKI objects */ + for (i = index; i < MAX_CAC_SLOTS; i++) { + r = cac_select_pki_applet(card, i); + if (r == SC_SUCCESS) { + pki_obj.name = get_cac_label(i); + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, + "CAC: pki_object found, cert_next=%d (%s),", i, pki_obj.name); + pki_obj.path.aid.value[pki_obj.path.aid.len-1] = i; + pki_obj.fd = i+1; /* don't use id of zero */ + cac_add_object_to_list(&priv->pki_list, &pki_obj); + } + } + + /* + * create a cuid to simulate the cac 2 cuid. + */ + priv->cuid = cac_cac_cuid; + /* create a serial number by hashing the first 100 bytes of the + * first certificate on the card */ + r = cac_select_pki_applet(card, index); + if (r < 0) { + return r; /* shouldn't happen unless the card has been removed or is malfunctioning */ + } + val = buf; + val_len = sizeof(buf); + r = cac_cac1_get_certificate(card, &val, &val_len); + if (r >= 0) { + priv->cac_id = malloc(20); + if (priv->cac_id == NULL) { + return SC_ERROR_OUT_OF_MEMORY; + } +#ifdef ENABLE_OPENSSL + SHA1(val, val_len, priv->cac_id); + priv->cac_id_len = 20; + sc_debug_hex(card->ctx, SC_LOG_DEBUG_VERBOSE, + "cuid", priv->cac_id, priv->cac_id_len); +#else + sc_log(card->ctx, "OpenSSL Required"); + return SC_ERROR_NOT_SUPPORTED; +#endif /* ENABLE_OPENSSL */ + } + return SC_SUCCESS; +} + +/* + * Look for a CAC card. If it exists, initialize our data structures + */ +static int cac_find_and_initialize(sc_card_t *card, int initialize) +{ + int r, index; + cac_private_data_t *priv = NULL; + + /* already initialized? */ + if (card->drv_data) { + return SC_SUCCESS; + } + + /* is this a CAC Alt token without any accompanying structures */ + r = cac_find_first_pki_applet(card, &index); + if (r == SC_SUCCESS) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "PKI applet found, is bare CAC-1"); + if (!initialize) /* match card only */ + return r; + + if (!priv) { + priv = cac_new_private_data(); + if (!priv) + return SC_ERROR_OUT_OF_MEMORY; + } + card->drv_data = priv; /* needed for the read_binary() */ + r = cac_populate_cac1(card, index, priv); + if (r == SC_SUCCESS) { + card->type = SC_CARD_TYPE_CAC_I; + return r; + } + card->drv_data = NULL; /* reset on failure */ + } + if (priv) { + cac_free_private_data(priv); + } + return r; +} + + +/* NOTE: returns a bool, 1 card matches, 0 it does not */ +static int cac_match_card(sc_card_t *card) +{ + int r; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + /* Since we send an APDU, the card's logout function may be called... + * however it may be in dirty memory */ + card->ops->logout = NULL; + + r = cac_find_and_initialize(card, 0); + return (r == SC_SUCCESS); /* never match */ +} + + +static int cac_init(sc_card_t *card) +{ + int r; + unsigned long flags; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + r = cac_find_and_initialize(card, 1); + if (r < 0) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_CARD); + } + flags = SC_ALGORITHM_RSA_RAW; + + _sc_card_add_rsa_alg(card, 1024, flags, 0); /* mandatory */ + _sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */ + _sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */ + + card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO; + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); +} + +static struct sc_card_operations cac_ops; + +static struct sc_card_driver cac1_drv = { + "Common Access Card (CAC 1)", + "cac1", + &cac_ops, + NULL, 0, NULL +}; + +static struct sc_card_driver * sc_get_driver(void) +{ + /* Inherit most of the things from the CAC driver */ + struct sc_card_driver *cac_drv = sc_get_cac_driver(); + + cac_ops = *cac_drv->ops; + cac_ops.match_card = cac_match_card; + cac_ops.init = cac_init; + cac_ops.finish = cac_finish; + + cac_ops.select_file = cac_select_file; /* need to record object type */ + cac_ops.read_binary = cac_read_binary; + + return &cac1_drv; +} + + +struct sc_card_driver * sc_get_cac1_driver(void) +{ + return sc_get_driver(); +} diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 5d545b3583..f4df17fb04 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -286,6 +286,7 @@ extern sc_card_driver_t *sc_get_gids_driver(void); extern sc_card_driver_t *sc_get_jpki_driver(void); extern sc_card_driver_t *sc_get_coolkey_driver(void); extern sc_card_driver_t *sc_get_cac_driver(void); +extern sc_card_driver_t *sc_get_cac1_driver(void); extern sc_card_driver_t *sc_get_npa_driver(void); #ifdef __cplusplus diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index 162a41f8bf..dff75ed8be 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -147,6 +147,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "openpgp", (void *(*)(void)) sc_get_openpgp_driver }, { "jpki", (void *(*)(void)) sc_get_jpki_driver }, { "npa", (void *(*)(void)) sc_get_npa_driver }, + { "cac1", (void *(*)(void)) sc_get_cac1_driver }, /* The default driver should be last, as it handles all the * unrecognized cards. */ { "default", (void *(*)(void)) sc_get_default_driver },