opensc/src/libopensc/card-cac.c

2012 lines
57 KiB
C
Raw Normal View History

/*
* card-cac.c: Support for CAC from NIST SP800-73
* card-default.c: Support for cards with no driver
*
* Copyright (C) 2001, 2002 Juha Yrjölä <juha.yrjola@iki.fi>
* Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert <deengert@anl.gov>
* Copyright (C) 2006, Identity Alliance, Thomas Harning <thomas.harning@identityalliance.com>
* Copyright (C) 2007, EMC, Russell Larner <rlarner@rsa.com>
* Copyright (C) 2016 - 2018, Red Hat, Inc.
*
* CAC driver author: Robert Relyea <rrelyea@redhat.com>
* Further work: Jakub Jelen <jjelen@redhat.com>
*
* 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 <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#ifdef ENABLE_OPENSSL
/* openssl only needed for card administration */
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#endif /* ENABLE_OPENSSL */
#include "internal.h"
#include "simpletlv.h"
#include "cardctl.h"
#ifdef ENABLE_ZLIB
#include "compression.h"
#endif
#include "iso7816.h"
#define CAC_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */
/*
* CAC hardware and APDU constants
*/
#define CAC_MAX_CHUNK_SIZE 240
#define CAC_INS_GET_CERTIFICATE 0x36 /* CAC1 command to read a certificate */
#define CAC_INS_SIGN_DECRYPT 0x42 /* A crypto operation */
#define CAC_INS_READ_FILE 0x52 /* read a TL or V file */
#define CAC_INS_GET_ACR 0x4c
#define CAC_INS_GET_PROPERTIES 0x56
#define CAC_P1_STEP 0x80
#define CAC_P1_FINAL 0x00
#define CAC_FILE_TAG 1
#define CAC_FILE_VALUE 2
/* TAGS in a TL file */
#define CAC_TAG_CERTIFICATE 0x70
#define CAC_TAG_CERTINFO 0x71
2018-04-10 08:39:18 +00:00
#define CAC_TAG_MSCUID 0x72
#define CAC_TAG_CUID 0xF0
#define CAC_TAG_CC_VERSION_NUMBER 0xF1
#define CAC_TAG_GRAMMAR_VERION_NUMBER 0xF2
#define CAC_TAG_CARDURL 0xF3
#define CAC_TAG_PKCS15 0xF4
#define CAC_TAG_ACCESS_CONTROL 0xF6
#define CAC_TAG_DATA_MODEL 0xF5
#define CAC_TAG_CARD_APDU 0xF7
#define CAC_TAG_REDIRECTION 0xFA
#define CAC_TAG_CAPABILITY_TUPLES 0xFB
#define CAC_TAG_STATUS_TUPLES 0xFC
#define CAC_TAG_NEXT_CCC 0xFD
#define CAC_TAG_ERROR_CODES 0xFE
#define CAC_TAG_APPLET_FAMILY 0x01
#define CAC_TAG_NUMBER_APPLETS 0x94
#define CAC_TAG_APPLET_ENTRY 0x93
#define CAC_TAG_APPLET_AID 0x92
#define CAC_TAG_APPLET_INFORMATION 0x01
#define CAC_TAG_NUMBER_OF_OBJECTS 0x40
#define CAC_TAG_TV_BUFFER 0x50
#define CAC_TAG_PKI_OBJECT 0x51
#define CAC_TAG_OBJECT_ID 0x41
#define CAC_TAG_BUFFER_PROPERTIES 0x42
#define CAC_TAG_PKI_PROPERTIES 0x43
#define CAC_APP_TYPE_GENERAL 0x01
#define CAC_APP_TYPE_SKI 0x02
#define CAC_APP_TYPE_PKI 0x04
#define CAC_ACR_ACR 0x00
#define CAC_ACR_APPLET_OBJECT 0x10
#define CAC_ACR_AMP 0x20
#define CAC_ACR_SERVICE 0x21
/* hardware data structures (returned in the CCC) */
/* part of the card_url */
typedef struct cac_access_profile {
u8 GCACR_listID;
u8 GCACR_readTagListACRID;
u8 GCACR_updatevalueACRID;
u8 GCACR_readvalueACRID;
u8 GCACR_createACRID;
u8 GCACR_deleteACRID;
u8 CryptoACR_listID;
u8 CryptoACR_getChallengeACRID;
u8 CryptoACR_internalAuthenicateACRID;
u8 CryptoACR_pkiComputeACRID;
u8 CryptoACR_readTagListACRID;
u8 CryptoACR_updatevalueACRID;
u8 CryptoACR_readvalueACRID;
u8 CryptoACR_createACRID;
u8 CryptoACR_deleteACRID;
} cac_access_profile_t;
/* part of the card url */
typedef struct cac_access_key_info {
u8 keyFileID[2];
u8 keynumber;
} cac_access_key_info_t;
typedef struct cac_card_url {
u8 rid[5];
u8 cardApplicationType;
u8 objectID[2];
u8 applicationID[2];
cac_access_profile_t accessProfile;
u8 pinID; /* not used for VM cards */
cac_access_key_info_t accessKeyInfo; /* not used for VM cards */
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 10
typedef struct {
unsigned int num_objects;
/* OID has two bytes */
unsigned char objects[CAC_MAX_OBJECTS][2];
/* Format is NOT SimpleTLV? */
unsigned char simpletlv[CAC_MAX_OBJECTS];
/* Is certificate object and private key is initialized */
unsigned char privatekey[CAC_MAX_OBJECTS];
} cac_properties_t;
/*
* Flags for Current Selected Object Type
* CAC files are TLV files, with TL and V separated. For generic
* containers we reintegrate the TL anv V portions into a single
* file to read. Certs are also TLV files, but pkcs15 wants the
* actual certificate. At select time we know the patch which tells
* us what time of files we want to read. We remember that type
* so that read_binary can do the appropriate processing.
*/
#define CAC_OBJECT_TYPE_CERT 1
#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)
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 */
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"
2017-11-03 09:55:35 +00:00
static const sc_path_t cac_ACA_Path = {
"", 0,
0,0,SC_PATH_TYPE_DF_NAME,
{ CAC_TO_AID(CAC_1_RID "\x10\x00") }
};
static const sc_path_t cac_CCC_Path = {
"", 0,
0,0,SC_PATH_TYPE_DF_NAME,
{ CAC_TO_AID(CAC_2_RID "\xDB\x00") }
};
#define MAX_CAC_SLOTS 10 /* arbitrary, just needs to be 'large enough' */
/* 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 3",
"CAC Cert 4",
"CAC Cert 5",
"CAC Cert 6",
"CAC Cert 7",
"CAC Cert 8",
"CAC Cert 9"
};
/* template for a cac1 pki object */
static const cac_object_t cac_cac1_pki_obj = {
"CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
{ CAC_TO_AID(CAC_1_RID "\x01\x00") } }
};
/* template for cac1 cuid */
static const cac_cuid_t cac_cac1_cuid = {
{ 0xa0, 0x00, 0x00, 0x00, 0x79 },
2, 2, 0
};
/*
* CAC-1 general objects defined in 4.3.1.2 of CAC Applet Developer Guide Version 1.0.
* doubles as a source for CAC-2 labels.
*/
static const cac_object_t cac_1_objects[] = {
{ "Person Instance", 0x200, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
{ CAC_TO_AID(CAC_1_RID "\x02\x00") }}},
{ "Personnel", 0x201, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
{ CAC_TO_AID(CAC_1_RID "\x02\x01") }}},
{ "Benefits", 0x202, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
{ CAC_TO_AID(CAC_1_RID "\x02\x02") }}},
2018-05-02 15:33:47 +00:00
{ "Other Benefits", 0x203, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
{ CAC_TO_AID(CAC_1_RID "\x02\x03") }}},
{ "PKI Credential", 0x2FD, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
{ CAC_TO_AID(CAC_1_RID "\x02\xFD") }}},
{ "PKI Certificate", 0x2FE, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
{ CAC_TO_AID(CAC_1_RID "\x02\xFE") }}},
};
static const int cac_1_object_count = sizeof(cac_1_objects)/sizeof(cac_1_objects[0]);
/*
* use the object id to find our object info on the object in our CAC-1 list
*/
static const cac_object_t *cac_find_obj_by_id(unsigned short object_id)
{
int i;
for (i=0; i < cac_1_object_count; i++) {
if (cac_1_objects[i].fd == object_id) {
return &cac_1_objects[i];
}
}
return NULL;
}
/*
* Lookup the path in the pki list to see if it is a cert path
*/
static int cac_is_cert(cac_private_data_t * priv, const sc_path_t *in_path)
{
cac_object_t test_obj;
test_obj.path = *in_path;
test_obj.path.index = 0;
test_obj.path.count = 0;
return (list_contains(&priv->pki_list, &test_obj) != 0);
}
/*
* Send a command and receive data.
*
* A caller may provide a buffer, and length to read. If not provided,
* an internal 4096 byte buffer is used, and a copy is returned to the
* caller. that need to be freed by the caller.
*
* modelled after a similar function in card-piv.c
*/
static int cac_apdu_io(sc_card_t *card, int ins, int p1, int p2,
const u8 * sendbuf, size_t sendbuflen, u8 ** recvbuf,
size_t * recvbuflen)
{
int r;
sc_apdu_t apdu;
u8 rbufinitbuf[CAC_MAX_SIZE];
u8 *rbuf;
size_t rbuflen;
unsigned int apdu_case = SC_APDU_CASE_1;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"%02x %02x %02x %"SC_FORMAT_LEN_SIZE_T"u : %"SC_FORMAT_LEN_SIZE_T"u %"SC_FORMAT_LEN_SIZE_T"u\n",
ins, p1, p2, sendbuflen, card->max_send_size,
card->max_recv_size);
rbuf = rbufinitbuf;
rbuflen = sizeof(rbufinitbuf);
/* if caller provided a buffer and length */
if (recvbuf && *recvbuf && recvbuflen && *recvbuflen) {
rbuf = *recvbuf;
rbuflen = *recvbuflen;
}
if (recvbuf) {
if (sendbuf)
apdu_case = SC_APDU_CASE_4_SHORT;
else
apdu_case = SC_APDU_CASE_2_SHORT;
} else if (sendbuf)
apdu_case = SC_APDU_CASE_3_SHORT;
sc_format_apdu(card, &apdu, apdu_case, ins, p1, p2);
apdu.lc = sendbuflen;
apdu.datalen = sendbuflen;
apdu.data = sendbuf;
if (recvbuf) {
apdu.resp = rbuf;
apdu.le = (rbuflen > 255) ? 255 : rbuflen;
apdu.resplen = rbuflen;
} else {
apdu.resp = rbuf;
apdu.le = 0;
apdu.resplen = 0;
}
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"calling sc_transmit_apdu flags=%lx le=%"SC_FORMAT_LEN_SIZE_T"u, resplen=%"SC_FORMAT_LEN_SIZE_T"u, resp=%p",
apdu.flags, apdu.le, apdu.resplen, apdu.resp);
/* with new adpu.c and chaining, this actually reads the whole object */
r = sc_transmit_apdu(card, &apdu);
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"result r=%d apdu.resplen=%"SC_FORMAT_LEN_SIZE_T"u sw1=%02x sw2=%02x",
r, apdu.resplen, apdu.sw1, apdu.sw2);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Transmit failed");
goto err;
}
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card returned error ");
goto err;
}
if (recvbuflen) {
if (recvbuf && *recvbuf == NULL) {
*recvbuf = malloc(apdu.resplen);
if (*recvbuf == NULL) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
memcpy(*recvbuf, rbuf, apdu.resplen);
}
*recvbuflen = apdu.resplen;
r = *recvbuflen;
}
err:
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
/*
* Get ACR of currently ACA applet identified by the acr_type
* 5.3.3.5 Get ACR APDU
*/
static int
cac_get_acr(sc_card_t *card, int acr_type, u8 **out_buf, size_t *out_len)
{
u8 *out = NULL;
u8 *out_ptr;
/* XXX assuming it will not be longer than 255 B */
size_t len = 256;
int r;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
/* for simplicity we support only ACR without arguments now */
if (acr_type != 0x00 && acr_type != 0x10
&& acr_type != 0x20 && acr_type != 0x21) {
return SC_ERROR_INVALID_ARGUMENTS;
}
out = out_ptr = malloc(len);
r = cac_apdu_io(card, CAC_INS_GET_ACR, acr_type, 0, NULL, 0, &out_ptr, &len);
if (len == 0) {
r = SC_ERROR_FILE_NOT_FOUND;
}
if (r < 0)
goto fail;
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"got %"SC_FORMAT_LEN_SIZE_T"u bytes out_ptr=%p", len, out_ptr);
*out_len = len;
*out_buf = out;
return SC_SUCCESS;
fail:
if (out)
free(out);
*out_len = 0;
return r;
}
/*
* Read a CAC TLV file. Parameters specify if the TLV file is TL (Tag/Length) file or a V (value) file
*/
#define HIGH_BYTE_OF_SHORT(x) (((x)>> 8) & 0xff)
#define LOW_BYTE_OF_SHORT(x) ((x) & 0xff)
static int cac_read_file(sc_card_t *card, int file_type, u8 **out_buf, size_t *out_len)
{
u8 params[2];
u8 count[2];
u8 *out = NULL;
u8 *out_ptr;
size_t offset = 0;
size_t size = 0;
size_t left = 0;
size_t len;
int r;
params[0] = file_type;
params[1] = 2;
/* get the size */
len = sizeof(count);
out_ptr = count;
r = cac_apdu_io(card, CAC_INS_READ_FILE, 0, 0, &params[0], sizeof(params), &out_ptr, &len);
if (len == 0) {
r = SC_ERROR_FILE_NOT_FOUND;
}
if (r < 0)
goto fail;
left = size = lebytes2ushort(count);
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"got %"SC_FORMAT_LEN_SIZE_T"u bytes out_ptr=%p count&=%p count[0]=0x%02x count[1]=0x%02x, len=0x%04"SC_FORMAT_LEN_SIZE_T"x (%"SC_FORMAT_LEN_SIZE_T"u)",
len, out_ptr, &count, count[0], count[1], size, size);
out = out_ptr = malloc(size);
if (out == NULL) {
r = SC_ERROR_OUT_OF_MEMORY;
goto fail;
}
for (offset += 2; left > 0; offset += len, left -= len, out_ptr += len) {
len = MIN(left, CAC_MAX_CHUNK_SIZE);
params[1] = len;
r = cac_apdu_io(card, CAC_INS_READ_FILE, HIGH_BYTE_OF_SHORT(offset), LOW_BYTE_OF_SHORT(offset),
&params[0], sizeof(params), &out_ptr, &len);
/* if there is no data, assume there is no file */
if (len == 0) {
r = SC_ERROR_FILE_NOT_FOUND;
}
if (r < 0) {
goto fail;
}
}
*out_len = size;
*out_buf = out;
return SC_SUCCESS;
fail:
if (out)
free(out);
*out_len = 0;
return r;
}
/*
* 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;
Coverity fixes (#1012) card-cac.c * CLANG_WARNING: The left operand of '<' is a garbage value card-coolkey.c * CLANG_WARNING: overwriting variable * CPPCHECK_WARNING: memory leak / overwrite variable * CLANG_WARNING: null pointer dereference * UNUSED_VALUE: unused return value card-gids.c * CLANG_WARNING: Branch condition evaluates to a garbage value * SIZEOF_MISMATCH: suspicious_sizeof card-myeid.c * RESOURCE_LEAK: Variable "buf" going out of scope leaks the storage it points to. * CLANG_WARNING: overwriting variable * (rewrite not to confuse coverity) pkcs15-cac.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-coolkey.c * UNUSED_VALUE: unused return value pkcs15-piv.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-sc-hsm.c * DEADCODE pkcs11/framework-pkcs15.c * RESOURCE_LEAK: Variable "p15_cert" going out of scope leaks the storage it points to. pkcs15init/pkcs15-lib.c * CLANG_WARNING: Assigned value is garbage or undefined pkcs15init/pkcs15-myeid.c * UNREACHABLE: Probably wrong placement of code block tests/p15dump.c * IDENTICAL_BRANCHES pkcs15-init.c * CLANG_WARNING: Potential leak of memory pointed to by 'args.der_encoded.value' pkcs15-tool.c * RESOURCE_LEAK: Variable "cert" going out of scope leaks the storage it points to. * MISSING_BREAK: The above case falls through to this one. sc-hsm-tool.c * CLANG_WARNING: Potential leak of memory pointed to by 'sp' westcos-tool.c * FORWARD_NULL: Passing null pointer "pin" to "unlock_pin", which dereferences it. * (rewrite not to confuse coverity) card-cac.c * Avoid malloc with 0 argument gids-tool.c * FORWARD_NULL -- copy&paste error scconf.c * CLANG_WARNING: Call to 'malloc' has an allocation size of 0 bytes closes #982
2017-04-03 11:43:30 +00:00
int r = SC_SUCCESS;
/* 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;
}
/* 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) {
return r;
}
r = size - left;
if (*out_buf == NULL) {
*out_buf = malloc(r);
if (*out_buf == NULL) {
return SC_ERROR_OUT_OF_MEMORY;
}
memcpy(*out_buf, buf, r);
}
*out_len = r;
return r;
}
/* Create a fake tag/length file in Simple TLV for cac1 cards based on the val_len.
*/
int cac_cac1_get_cert_tag(sc_card_t *card, size_t val_len, u8 **tlp, size_t *tl_len_p)
{
static const u8 cac_cac1_cert_tag[] = { CAC_TAG_CERTINFO, 1, CAC_TAG_CERTIFICATE, 0xff, 0, 0 };
u8 *tl, *tl1, *tl2;
size_t tl_len;
int rv = SC_SUCCESS;
tl_len = sizeof(cac_cac1_cert_tag);
tl = malloc(tl_len);
if (tl == NULL)
return SC_ERROR_OUT_OF_MEMORY;
memcpy(tl, cac_cac1_cert_tag, tl_len);
rv = sc_simpletlv_put_tag(CAC_TAG_CERTINFO, 1, tl, tl_len, &tl1);
if (rv != SC_SUCCESS)
goto failure;
val_len -= 1; /* one byte is CERTINFO Value */
tl_len -= (tl1 - tl);
rv = sc_simpletlv_put_tag(CAC_TAG_CERTIFICATE, val_len, tl1, tl_len, &tl2);
if (rv != SC_SUCCESS)
goto failure;
*tlp = tl;
*tl_len_p = (tl2 - tl);
return SC_SUCCESS;
failure:
free(tl);
return rv;
}
/*
* 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 *tl = NULL, *val = NULL;
u8 *tl_ptr, *val_ptr, *tlv_ptr, *tl_start;
u8 *cert_ptr;
size_t tl_len, val_len, tlv_len;
size_t len, tl_head_len, cert_len;
u8 cert_type, tag;
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;
}
if (priv->object_type <= 0)
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
if ((card->type == SC_CARD_TYPE_CAC_I) && (priv->object_type == CAC_OBJECT_TYPE_CERT)) {
/* SPICE smart card emulator only presents CAC-1 cards with the old CAC-1 interface as
* certs. If we are a cac 1 card, use the old interface */
r = cac_cac1_get_certificate(card, &val, &val_len);
if (r == SC_ERROR_INS_NOT_SUPPORTED) {
/* The CACv1 instruction is not recognized. Try with CACv2 */
card->type = SC_CARD_TYPE_CAC_II;
} else if (r < 0)
goto done;
}
if ((card->type == SC_CARD_TYPE_CAC_I) && (priv->object_type == CAC_OBJECT_TYPE_CERT)) {
r = cac_cac1_get_cert_tag(card, val_len, &tl, &tl_len);
if (r < 0)
goto done;
} else {
r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len);
if (r < 0) {
goto done;
}
r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len);
if (r < 0)
goto done;
}
switch (priv->object_type) {
case CAC_OBJECT_TYPE_TLV_FILE:
tlv_len = tl_len + val_len;
priv->cache_buf = malloc(tlv_len);
if (priv->cache_buf == NULL) {
r = SC_ERROR_OUT_OF_MEMORY;
goto done;
}
priv->cache_buf_len = tlv_len;
for (tl_ptr = tl, val_ptr=val, tlv_ptr = priv->cache_buf;
tl_len >= 2 /*&& val_len >= 0*/ && tlv_len > 0;
val_len -= len, tlv_len -= len, val_ptr += len, tlv_ptr += len) {
/* get the tag and the length */
tl_start = tl_ptr;
if (sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len) != SC_SUCCESS)
break;
tl_head_len = (tl_ptr - tl_start);
sc_simpletlv_put_tag(tag, len, tlv_ptr, tlv_len, &tlv_ptr);
tlv_len -= tl_head_len;
tl_len -= tl_head_len;
/* don't crash on bad data */
if (val_len < len) {
len = val_len;
}
/* if we run out of return space, truncate */
if (tlv_len < len) {
len = tlv_len;
}
memcpy(tlv_ptr, val_ptr, len);
}
break;
case CAC_OBJECT_TYPE_CERT:
/* read file */
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
" obj= cert_file, val_len=%"SC_FORMAT_LEN_SIZE_T"u (0x%04"SC_FORMAT_LEN_SIZE_T"x)",
val_len, val_len);
cert_len = 0;
cert_ptr = NULL;
cert_type = 0;
for (tl_ptr = tl, val_ptr=val; tl_len >= 2;
val_len -= len, val_ptr += len, tl_len -= tl_head_len) {
tl_start = tl_ptr;
if (sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len) != SC_SUCCESS)
break;
tl_head_len = tl_ptr - tl_start;
if (tag == CAC_TAG_CERTIFICATE) {
cert_len = len;
cert_ptr = val_ptr;
}
if (tag == CAC_TAG_CERTINFO) {
if ((len >= 1) && (val_len >=1)) {
cert_type = *val_ptr;
}
2018-04-10 08:39:18 +00:00
}
if (tag == CAC_TAG_MSCUID) {
sc_log_hex(card->ctx, "MSCUID", val_ptr, len);
}
if ((val_len < len) || (tl_len < tl_head_len)) {
break;
}
}
/* 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
Coverity fixes (#1012) card-cac.c * CLANG_WARNING: The left operand of '<' is a garbage value card-coolkey.c * CLANG_WARNING: overwriting variable * CPPCHECK_WARNING: memory leak / overwrite variable * CLANG_WARNING: null pointer dereference * UNUSED_VALUE: unused return value card-gids.c * CLANG_WARNING: Branch condition evaluates to a garbage value * SIZEOF_MISMATCH: suspicious_sizeof card-myeid.c * RESOURCE_LEAK: Variable "buf" going out of scope leaks the storage it points to. * CLANG_WARNING: overwriting variable * (rewrite not to confuse coverity) pkcs15-cac.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-coolkey.c * UNUSED_VALUE: unused return value pkcs15-piv.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-sc-hsm.c * DEADCODE pkcs11/framework-pkcs15.c * RESOURCE_LEAK: Variable "p15_cert" going out of scope leaks the storage it points to. pkcs15init/pkcs15-lib.c * CLANG_WARNING: Assigned value is garbage or undefined pkcs15init/pkcs15-myeid.c * UNREACHABLE: Probably wrong placement of code block tests/p15dump.c * IDENTICAL_BRANCHES pkcs15-init.c * CLANG_WARNING: Potential leak of memory pointed to by 'args.der_encoded.value' pkcs15-tool.c * RESOURCE_LEAK: Variable "cert" going out of scope leaks the storage it points to. * MISSING_BREAK: The above case falls through to this one. sc-hsm-tool.c * CLANG_WARNING: Potential leak of memory pointed to by 'sp' westcos-tool.c * FORWARD_NULL: Passing null pointer "pin" to "unlock_pin", which dereferences it. * (rewrite not to confuse coverity) card-cac.c * Avoid malloc with 0 argument gids-tool.c * FORWARD_NULL -- copy&paste error scconf.c * CLANG_WARNING: Call to 'malloc' has an allocation size of 0 bytes closes #982
2017-04-03 11:43:30 +00:00
sc_log(card->ctx, "CAC compression not supported, no zlib");
r = SC_ERROR_NOT_SUPPORTED;
#endif
if (r)
goto done;
Coverity fixes (#1012) card-cac.c * CLANG_WARNING: The left operand of '<' is a garbage value card-coolkey.c * CLANG_WARNING: overwriting variable * CPPCHECK_WARNING: memory leak / overwrite variable * CLANG_WARNING: null pointer dereference * UNUSED_VALUE: unused return value card-gids.c * CLANG_WARNING: Branch condition evaluates to a garbage value * SIZEOF_MISMATCH: suspicious_sizeof card-myeid.c * RESOURCE_LEAK: Variable "buf" going out of scope leaks the storage it points to. * CLANG_WARNING: overwriting variable * (rewrite not to confuse coverity) pkcs15-cac.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-coolkey.c * UNUSED_VALUE: unused return value pkcs15-piv.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-sc-hsm.c * DEADCODE pkcs11/framework-pkcs15.c * RESOURCE_LEAK: Variable "p15_cert" going out of scope leaks the storage it points to. pkcs15init/pkcs15-lib.c * CLANG_WARNING: Assigned value is garbage or undefined pkcs15init/pkcs15-myeid.c * UNREACHABLE: Probably wrong placement of code block tests/p15dump.c * IDENTICAL_BRANCHES pkcs15-init.c * CLANG_WARNING: Potential leak of memory pointed to by 'args.der_encoded.value' pkcs15-tool.c * RESOURCE_LEAK: Variable "cert" going out of scope leaks the storage it points to. * MISSING_BREAK: The above case falls through to this one. sc-hsm-tool.c * CLANG_WARNING: Potential leak of memory pointed to by 'sp' westcos-tool.c * FORWARD_NULL: Passing null pointer "pin" to "unlock_pin", which dereferences it. * (rewrite not to confuse coverity) card-cac.c * Avoid malloc with 0 argument gids-tool.c * FORWARD_NULL -- copy&paste error scconf.c * CLANG_WARNING: Call to 'malloc' has an allocation size of 0 bytes closes #982
2017-04-03 11:43:30 +00:00
} 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);
Coverity fixes (#1012) card-cac.c * CLANG_WARNING: The left operand of '<' is a garbage value card-coolkey.c * CLANG_WARNING: overwriting variable * CPPCHECK_WARNING: memory leak / overwrite variable * CLANG_WARNING: null pointer dereference * UNUSED_VALUE: unused return value card-gids.c * CLANG_WARNING: Branch condition evaluates to a garbage value * SIZEOF_MISMATCH: suspicious_sizeof card-myeid.c * RESOURCE_LEAK: Variable "buf" going out of scope leaks the storage it points to. * CLANG_WARNING: overwriting variable * (rewrite not to confuse coverity) pkcs15-cac.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-coolkey.c * UNUSED_VALUE: unused return value pkcs15-piv.c * RESOURCE_LEAK: Variable "cert_out" going out of scope leaks the storage it points to. pkcs15-sc-hsm.c * DEADCODE pkcs11/framework-pkcs15.c * RESOURCE_LEAK: Variable "p15_cert" going out of scope leaks the storage it points to. pkcs15init/pkcs15-lib.c * CLANG_WARNING: Assigned value is garbage or undefined pkcs15init/pkcs15-myeid.c * UNREACHABLE: Probably wrong placement of code block tests/p15dump.c * IDENTICAL_BRANCHES pkcs15-init.c * CLANG_WARNING: Potential leak of memory pointed to by 'args.der_encoded.value' pkcs15-tool.c * RESOURCE_LEAK: Variable "cert" going out of scope leaks the storage it points to. * MISSING_BREAK: The above case falls through to this one. sc-hsm-tool.c * CLANG_WARNING: Potential leak of memory pointed to by 'sp' westcos-tool.c * FORWARD_NULL: Passing null pointer "pin" to "unlock_pin", which dereferences it. * (rewrite not to confuse coverity) card-cac.c * Avoid malloc with 0 argument gids-tool.c * FORWARD_NULL -- copy&paste error scconf.c * CLANG_WARNING: Call to 'malloc' has an allocation size of 0 bytes closes #982
2017-04-03 11:43:30 +00:00
} else {
sc_log(card->ctx, "Can't read zero-length certificate");
goto done;
}
break;
case CAC_OBJECT_TYPE_GENERIC:
/* TODO
* We have some two buffers in unknown encoding that we
* need to present in PKCS#15 layer.
*/
default:
/* Unknown object type */
2018-05-17 07:44:07 +00:00
sc_log(card->ctx, "Unknown object type: %x", priv->object_type);
r = SC_ERROR_INTERNAL;
goto done;
}
/* 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 (tl)
free(tl);
if (val)
free(val);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
/* CAC driver is read only */
static int cac_write_binary(sc_card_t *card, unsigned int idx,
const u8 *buf, size_t count, unsigned long flags)
{
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED);
}
/* initialize getting a list and return the number of elements in the list */
static int cac_get_init_and_get_count(list_t *list, cac_object_t **entry, int *countp)
{
*countp = list_size(list);
list_iterator_start(list);
*entry = list_iterator_next(list);
return SC_SUCCESS;
}
/* finalize the list iterator */
static int cac_final_iterator(list_t *list)
{
list_iterator_stop(list);
return SC_SUCCESS;
}
/* fill in the obj_info for the current object on the list and advance to the next object */
static int cac_fill_object_info(list_t *list, cac_object_t **entry, sc_pkcs15_data_info_t *obj_info)
{
memset(obj_info, 0, sizeof(sc_pkcs15_data_info_t));
if (*entry == NULL) {
return SC_ERROR_FILE_END_REACHED;
}
obj_info->path = (*entry)->path;
obj_info->path.count = CAC_MAX_SIZE-1; /* read something from the object */
obj_info->id.value[0] = ((*entry)->fd >> 8) & 0xff;
obj_info->id.value[1] = (*entry)->fd & 0xff;
obj_info->id.len = 2;
strncpy(obj_info->app_label, (*entry)->name, SC_PKCS15_MAX_LABEL_SIZE-1);
*entry = list_iterator_next(list);
return SC_SUCCESS;
}
static int cac_get_serial_nr_from_CUID(sc_card_t* card, sc_serial_number_t* serial)
{
cac_private_data_t * priv = CAC_DATA(card);
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
if (card->serialnr.len) {
*serial = card->serialnr;
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
}
if (priv->cac_id_len) {
serial->len = MIN(priv->cac_id_len, SC_MAX_SERIALNR);
memcpy(serial->value, priv->cac_id, priv->cac_id_len);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
}
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND);
}
static int cac_get_ACA_path(sc_card_t *card, sc_path_t *path)
{
cac_private_data_t * priv = CAC_DATA(card);
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
if (priv->aca_path) {
*path = *priv->aca_path;
}
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
}
static int cac_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
{
cac_private_data_t * priv = CAC_DATA(card);
LOG_FUNC_CALLED(card->ctx);
sc_log(card->ctx, "cmd=%ld ptr=%p", cmd, ptr);
if (priv == NULL) {
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
}
switch(cmd) {
case SC_CARDCTL_CAC_GET_ACA_PATH:
return cac_get_ACA_path(card, (sc_path_t *) ptr);
case SC_CARDCTL_GET_SERIALNR:
return cac_get_serial_nr_from_CUID(card, (sc_serial_number_t *) ptr);
case SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS:
return cac_get_init_and_get_count(&priv->general_list, &priv->general_current, (int *)ptr);
case SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS:
return cac_get_init_and_get_count(&priv->pki_list, &priv->pki_current, (int *)ptr);
case SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT:
return cac_fill_object_info(&priv->general_list, &priv->general_current, (sc_pkcs15_data_info_t *)ptr);
case SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT:
return cac_fill_object_info(&priv->pki_list, &priv->pki_current, (sc_pkcs15_data_info_t *)ptr);
case SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS:
return cac_final_iterator(&priv->general_list);
case SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS:
return cac_final_iterator(&priv->pki_list);
}
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
}
static int cac_get_challenge(sc_card_t *card, u8 *rnd, size_t len)
{
/* CAC requires 8 byte response */
u8 rbuf[8];
size_t out_len = sizeof rbuf;
int r;
LOG_FUNC_CALLED(card->ctx);
r = cac_apdu_io(card, 0x84, 0x00, 0x00, NULL, 0, (u8 **) &rbuf, &out_len);
LOG_TEST_RET(card->ctx, r, "Could not get challenge");
if (len < out_len) {
out_len = len;
}
memcpy(rnd, rbuf, out_len);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, (int) out_len);
}
static int cac_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num)
{
int r = SC_SUCCESS;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"flags=%08lx op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%"SC_FORMAT_LEN_SIZE_T"u\n",
env->flags, env->operation, env->algorithm,
env->algorithm_flags, env->algorithm_ref, env->key_ref[0],
env->key_ref_len);
if (env->algorithm != SC_ALGORITHM_RSA) {
r = SC_ERROR_NO_CARD_SUPPORT;
}
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
}
static int cac_restore_security_env(sc_card_t *card, int se_num)
{
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
}
static int cac_rsa_op(sc_card_t *card,
const u8 * data, size_t datalen,
u8 * out, size_t outlen)
{
int r;
u8 *outp, *rbuf;
size_t rbuflen, outplen;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n",
datalen, outlen);
outp = out;
outplen = outlen;
/* Not strictly necessary. This code requires the caller to have selected the correct PKI container
* and authenticated to that container with the verifyPin command... All of this under the reader lock.
* The PKCS #15 higher level driver code does all this correctly (it's the same for all cards, just
* different sets of APDU's that need to be called), so this call is really a little bit of paranoia */
r = sc_lock(card);
if (r != SC_SUCCESS)
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
rbuf = NULL;
rbuflen = 0;
for (; datalen > CAC_MAX_CHUNK_SIZE; data += CAC_MAX_CHUNK_SIZE, datalen -= CAC_MAX_CHUNK_SIZE) {
r = cac_apdu_io(card, CAC_INS_SIGN_DECRYPT, CAC_P1_STEP, 0,
data, CAC_MAX_CHUNK_SIZE, &rbuf, &rbuflen);
if (r < 0) {
break;
}
if (rbuflen != 0) {
int n = MIN(rbuflen, outplen);
memcpy(outp,rbuf, n);
outp += n;
outplen -= n;
}
free(rbuf);
rbuf = NULL;
rbuflen = 0;
}
if (r < 0) {
goto err;
}
rbuf = NULL;
rbuflen = 0;
r = cac_apdu_io(card, CAC_INS_SIGN_DECRYPT, CAC_P1_FINAL, 0, data, datalen, &rbuf, &rbuflen);
if (r < 0) {
goto err;
}
if (rbuflen != 0) {
int n = MIN(rbuflen, outplen);
memcpy(outp,rbuf, n);
/*outp += n; unused */
outplen -= n;
}
free(rbuf);
rbuf = NULL;
r = outlen-outplen;
err:
sc_unlock(card);
if (r < 0) {
sc_mem_clear(out, outlen);
}
if (rbuf) {
free(rbuf);
}
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
static int cac_compute_signature(sc_card_t *card,
const u8 * data, size_t datalen,
u8 * out, size_t outlen)
{
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, cac_rsa_op(card, data, datalen, out, outlen));
}
static int cac_decipher(sc_card_t *card,
const u8 * data, size_t datalen,
u8 * out, size_t outlen)
{
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, cac_rsa_op(card, data, datalen, out, outlen));
}
static int parse_properties_object(sc_card_t *card, u8 type,
u8 *data, size_t data_len, cac_properties_t *prop, size_t *i)
{
size_t len;
u8 *val, *val_end, tag;
int parsed = 0;
if (data_len < 11)
return -1;
/* Initilize: non-PKI applet */
prop->privatekey[*i] = 0;
val = data;
val_end = data + data_len;
for (; val < val_end; val += len) {
/* get the tag and the length */
if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS)
break;
switch (tag) {
case CAC_TAG_OBJECT_ID:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: Object ID = 0x%02x 0x%02x", val[0], val[1]);
memcpy(&prop->objects[*i], val, 2);
parsed++;
break;
case CAC_TAG_BUFFER_PROPERTIES:
/* First byte is "Type of Tag Supported" */
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: Buffer Properties: Type of Tag Supported = 0x%02x",
val[0]);
prop->simpletlv[*i] = val[0];
parsed++;
break;
case CAC_TAG_PKI_PROPERTIES:
/* 4th byte is "Private Key Initialized" */
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: PKI Properties: Private Key Initialized = 0x%02x",
val[2]);
prop->privatekey[*i] = val[2];
parsed++;
break;
default:
/* ignore tags we don't understand */
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: Unknown (0x%02x)",tag );
break;
}
}
if (parsed < 2)
return SC_ERROR_INVALID_DATA;
(*i)++;
return SC_SUCCESS;
}
static int cac_get_properties(sc_card_t *card, cac_properties_t *prop)
{
u8 *rbuf = NULL;
size_t rbuflen = 0, len;
u8 *val, *val_end, tag;
size_t i = 0;
int r;
prop->num_objects = 0;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
r = cac_apdu_io(card, CAC_INS_GET_PROPERTIES, 0x01, 0x00, NULL, 0,
&rbuf, &rbuflen);
if (r < 0)
return r;
val = rbuf;
val_end = val + rbuflen;
for (; val < val_end; val += len) {
/* get the tag and the length */
if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS)
break;
switch (tag) {
case CAC_TAG_APPLET_INFORMATION:
if (len != 5)
break;
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: Applet Information: Family: 0x%0x", val[0]);
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
" Applet Version: 0x%02x 0x%02x 0x%02x 0x%02x",
val[1], val[2], val[3], val[4]);
break;
case CAC_TAG_NUMBER_OF_OBJECTS:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: Num objects = %hhd", *val);
/* make sure we do not overrun buffer */
prop->num_objects = MIN(val[0], CAC_MAX_OBJECTS);
break;
case CAC_TAG_TV_BUFFER:
case CAC_TAG_PKI_OBJECT:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: TV/PKI Object nr. %"SC_FORMAT_LEN_SIZE_T"u (0x%02x)", i, tag);
if (i >= CAC_MAX_OBJECTS)
return SC_SUCCESS;
parse_properties_object(card, tag, val, len, prop, &i);
break;
default:
/* ignore tags we don't understand */
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: Unknown (0x%02x)",tag );
break;
}
}
/* sanity */
if (i != prop->num_objects)
return SC_ERROR_INVALID_DATA;
return SC_SUCCESS;
}
/*
* 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, int type)
{
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 */
priv->object_type = CAC_OBJECT_TYPE_GENERIC;
if (cac_is_cert(priv, in_path)) {
priv->object_type = CAC_OBJECT_TYPE_CERT;
}
/* 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;
if (file_out != NULL) {
apdu.p2 = 0; /* first record, return FCI */
}
else {
apdu.p2 = (type == SC_CARD_TYPE_CAC_I)? 0x00 : 0x0C;
}
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);
/* This needs to come after the applet selection */
if (priv && in_path->len) {
/* get applet properties to know if we can treat the
* buffer as SimpleLTV and if we have PKI applet.
*
* Do this only if we select applets for reading
* (not during driver initialization)
*/
cac_properties_t prop;
size_t i = -1;
r = cac_get_properties(card, &prop);
if (r == SC_SUCCESS) {
for (i = 0; i < prop.num_objects; i++) {
sc_log(card->ctx, "Searching for our OID: 0x%02x 0x%02x = 0x%02x 0x%02x",
prop.objects[i][0], prop.objects[i][1],
in_path->value[0], in_path->value[1]);
if (memcmp(prop.objects[i],
in_path->value, 2) == 0)
break;
}
}
if (i < prop.num_objects) {
if (prop.privatekey[i])
priv->object_type = CAC_OBJECT_TYPE_CERT;
else if (prop.simpletlv[i] == 0)
priv->object_type = CAC_OBJECT_TYPE_TLV_FILE;
}
}
/* 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, card->type);
}
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 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);
}
2017-11-03 09:55:35 +00:00
/* 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);
}
static int cac_path_from_cardurl(sc_card_t *card, sc_path_t *path, cac_card_url_t *val, int len)
{
if (len < 10) {
return SC_ERROR_INVALID_DATA;
}
sc_mem_clear(path, sizeof(sc_path_t));
memcpy(path->aid.value, &val->rid, sizeof(val->rid));
memcpy(&path->aid.value[5], &val->applicationID, sizeof(val->applicationID));
path->aid.len = sizeof(val->rid) + sizeof(val->applicationID);
memcpy(path->value, &val->objectID, sizeof(val->objectID));
path->len = sizeof(val->objectID);
path->type = SC_PATH_TYPE_FILE_ID;
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 len=%"SC_FORMAT_LEN_SIZE_T"u path->type=%d (%x)",
path->aid.value[0], path->aid.value[1], path->aid.value[2],
path->aid.value[3], path->aid.value[4], path->aid.value[5],
path->aid.value[6], path->aid.len, path->value[0],
path->value[1], path->len, path->type, path->type);
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"rid=%x %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u appid= %x %x len=%"SC_FORMAT_LEN_SIZE_T"u objid= %x %x len=%"SC_FORMAT_LEN_SIZE_T"u",
val->rid[0], val->rid[1], val->rid[2], val->rid[3],
val->rid[4], sizeof(val->rid), val->applicationID[0],
val->applicationID[1], sizeof(val->applicationID),
val->objectID[0], val->objectID[1], sizeof(val->objectID));
return SC_SUCCESS;
}
static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, u8 *aid, int aid_len)
{
cac_object_t new_object;
cac_properties_t prop;
size_t i;
int r;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
/* Search for PKI applets. Ignore generic objects for now */
if (aid_len < 6 || memcmp(aid, CAC_1_RID "\x01", 6) != 0)
return SC_SUCCESS;
sc_mem_clear(&new_object.path, sizeof(sc_path_t));
memcpy(new_object.path.aid.value, aid, aid_len);
new_object.path.aid.len = aid_len;
/* 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);
r = cac_get_properties(card, &prop);
if (r < 0)
return SC_ERROR_INTERNAL;
for (i = 0; i < prop.num_objects; i++) {
/* don't fail just because we have more certs than we can support */
if (priv->cert_next >= MAX_CAC_SLOTS)
return SC_SUCCESS;
/* If the private key is not initialized, we can safely
* ignore this object here
*/
if (!prop.privatekey[i])
continue;
/* OID here has always 2B */
memcpy(new_object.path.value, &prop.objects[i], 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.fd = priv->cert_next+1;
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"ACA: pki_object found, cert_next=%d (%s),",
priv->cert_next, new_object.name);
cac_add_object_to_list(&priv->pki_list, &new_object);
priv->cert_next++;
}
return SC_SUCCESS;
}
static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card_url_t *val, int len)
{
cac_object_t new_object;
const cac_object_t *obj;
unsigned short object_id;
int r;
r = cac_path_from_cardurl(card, &new_object.path, val, len);
if (r != SC_SUCCESS) {
return r;
}
switch (val->cardApplicationType) {
case CAC_APP_TYPE_PKI:
/* we don't want to overflow the cac_label array. This test could
* go way if we create a label function that will create a unique label
* from a cert index.
*/
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.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);
priv->cert_next++;
break;
case CAC_APP_TYPE_GENERAL:
object_id = bebytes2ushort(val->objectID);
obj = cac_find_obj_by_id(object_id);
if (obj == NULL)
break; /* don't fail just because we don't recognize the object */
new_object.name = obj->name;
new_object.fd = 0;
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: gen_object found, objectID=%x (%s),", object_id, new_object.name);
cac_add_object_to_list(&priv->general_list, &new_object);
break;
case CAC_APP_TYPE_SKI:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: ski_object found");
break;
default:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: unknown object_object found (type=0x%02x)", val->cardApplicationType);
/* don't fail just because there is an unknown object in the CCC */
break;
}
return SC_SUCCESS;
}
static int cac_parse_cuid(sc_card_t *card, cac_private_data_t *priv, cac_cuid_t *val, size_t len)
{
size_t card_id_len;
if (len < sizeof(cac_cuid_t)) {
return SC_ERROR_INVALID_DATA;
}
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "gsc_rid=%s", sc_dump_hex(val->gsc_rid, sizeof(val->gsc_rid)));
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "manufacture id=%x", val->manufacturer_id);
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "cac_type=%d", val->card_type);
card_id_len = len - (&val->card_id - (u8 *)val);
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"card_id=%s (%"SC_FORMAT_LEN_SIZE_T"u)",
sc_dump_hex(&val->card_id, card_id_len),
card_id_len);
priv->cuid = *val;
priv->cac_id = malloc(card_id_len);
if (priv->cac_id == NULL) {
return SC_ERROR_OUT_OF_MEMORY;
}
memcpy(priv->cac_id, &val->card_id, card_id_len);
priv->cac_id_len = card_id_len;
return SC_SUCCESS;
}
static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv);
static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl,
size_t tl_len, u8 *val, size_t val_len)
{
size_t len = 0;
u8 *tl_end = tl + tl_len;
u8 *val_end = val + val_len;
sc_path_t new_path;
int r;
for (; (tl < tl_end) && (val< val_end); val += len) {
/* get the tag and the length */
u8 tag;
if (sc_simpletlv_read_tag(&tl, tl_end - tl, &tag, &len) != SC_SUCCESS)
break;
switch (tag) {
case CAC_TAG_CUID:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CUID");
r = cac_parse_cuid(card, priv, (cac_cuid_t *)val, len);
if (r < 0)
return r;
break;
case CAC_TAG_CC_VERSION_NUMBER:
/* ignore the version numbers for now */
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG:CC Version = 0x%02x", *val);
break;
case CAC_TAG_GRAMMAR_VERION_NUMBER:
/* ignore the version numbers for now */
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG:Grammar Version = 0x%02x", *val);
break;
case CAC_TAG_CARDURL:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CARDURL");
r = cac_parse_cardurl(card, priv, (cac_card_url_t *)val, len);
if (r < 0)
return r;
break;
/*
* The following are really for file systems cards. This code only cares about CAC VM cards
*/
case CAC_TAG_PKCS15:
/* TODO should verify that this is '0'. If it's not
* zero, we should drop out of here and let the PKCS 15
* code handle this card */
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:PKCS15 = 0x%02x", *val);
break;
case CAC_TAG_DATA_MODEL:
case CAC_TAG_CARD_APDU:
case CAC_TAG_CAPABILITY_TUPLES:
case CAC_TAG_STATUS_TUPLES:
case CAC_TAG_REDIRECTION:
case CAC_TAG_ERROR_CODES:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:FSSpecific(0x%02x)", tag);
break;
case CAC_TAG_ACCESS_CONTROL:
/* TODO handle access control later */
sc_log_hex(card->ctx, "TAG:ACCESS Control", val, len);
break;
case CAC_TAG_NEXT_CCC:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:NEXT CCC");
r = cac_path_from_cardurl(card, &new_path, (cac_card_url_t *)val, len);
if (r < 0)
return r;
r = cac_select_file_by_type(card, &new_path, NULL, SC_CARD_TYPE_CAC_II);
if (r < 0)
return r;
r = cac_process_CCC(card, priv);
if (r < 0)
return r;
break;
default:
/* ignore tags we don't understand */
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:Unknown (0x%02x)",tag );
break;
}
}
return SC_SUCCESS;
}
static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv)
{
u8 *tl = NULL, *val = NULL;
size_t tl_len, val_len;
int r;
r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len);
if (r < 0)
goto done;
r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len);
if (r < 0)
goto done;
r = cac_parse_CCC(card, priv, tl, tl_len, val, val_len);
done:
if (tl)
free(tl);
if (val)
free(val);
return r;
}
/* Service Applet Table (Table 5-21) should list all the applets on the
* card, which is a good start if we don't have CCC
*/
static int cac_parse_ACA_service(sc_card_t *card, cac_private_data_t *priv,
u8 *val, size_t val_len)
{
size_t len = 0;
u8 *val_end = val + val_len;
int r;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
for (; val < val_end; val += len) {
/* get the tag and the length */
u8 tag;
if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS)
break;
switch (tag) {
case CAC_TAG_APPLET_FAMILY:
if (len != 5)
break;
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: Applet Information: Family: 0x%02x", val[0]);
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
" Applet Version: 0x%02x 0x%02x 0x%02x 0x%02x",
val[1], val[2], val[3], val[4]);
break;
case CAC_TAG_NUMBER_APPLETS:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: Num applets = %hhd", *val);
break;
case CAC_TAG_APPLET_ENTRY:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: Applet Entry");
/* This is SimpleTLV prefixed with applet ID (1B) */
r = cac_parse_aid(card, priv, &val[3], val[2]);
if (r < 0)
return r;
break;
default:
/* ignore tags we don't understand */
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"TAG: Unknown (0x%02x)", tag);
break;
}
}
return SC_SUCCESS;
}
/* select a CAC-1 pki applet by index */
static int cac_select_pki_applet(sc_card_t *card, int index)
{
sc_path_t applet_path = cac_cac1_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_I);
}
/*
* Find the first existing CAC-1 applet. If none found, then this isn't a CAC-1
*/
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) {
*index_out = i;
return r;
}
}
return SC_ERROR_OBJECT_NOT_FOUND;
}
/*
* CAC-1 has been found, identify all the certs and general containers.
* This emulates CAC-2's CCC.
*/
static int cac_populate_cac_1(sc_card_t *card, int index, cac_private_data_t *priv)
{
int r, i;
cac_object_t pki_obj = cac_cac1_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 = cac_labels[i];
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CAC1: 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);
}
}
/* populate non-PKI objects */
for (i=0; i < cac_1_object_count; i++) {
r = cac_select_file_by_type(card, &cac_1_objects[i].path, NULL, SC_CARD_TYPE_CAC_I);
if (r == SC_SUCCESS) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CAC1: obj_object found, cert_next=%d (%s),", i, cac_1_objects[i].name);
cac_add_object_to_list(&priv->general_list, &cac_1_objects[i]);
}
}
/*
* create a cuid to simulate the cac 2 cuid.
*/
priv->cuid = cac_cac1_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;
#else
sc_log(card->ctx, "OpenSSL Required");
return SC_ERROR_NOT_SUPPORTED;
#endif /* ENABLE_OPENSSL */
}
return SC_SUCCESS;
}
static int cac_process_ACA(sc_card_t *card, cac_private_data_t *priv)
{
int r;
u8 *val = NULL;
size_t val_len;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
/* Assuming ACA is already selected */
r = cac_get_acr(card, CAC_ACR_SERVICE, &val, &val_len);
if (r < 0)
goto done;
r = cac_parse_ACA_service(card, priv, val, val_len);
if (r == SC_SUCCESS) {
priv->aca_path = malloc(sizeof(sc_path_t));
if (!priv->aca_path) {
r = SC_ERROR_OUT_OF_MEMORY;
goto done;
}
memcpy(priv->aca_path, &cac_ACA_Path, sizeof(sc_path_t));
}
done:
if (val)
free(val);
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
/*
* 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-2 specified in NIST Interagency Report 6887 -
* "Government Smart Card Interoperability Specification v2.1 July 2003" */
r = cac_select_CCC(card);
if (r == SC_SUCCESS) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "CCC found, is CAC-2");
if (!initialize) /* match card only */
return r;
priv = cac_new_private_data();
if (!priv)
return SC_ERROR_OUT_OF_MEMORY;
r = cac_process_CCC(card, priv);
if (r == SC_SUCCESS) {
card->type = SC_CARD_TYPE_CAC_II;
card->drv_data = priv;
return r;
}
}
2017-11-03 09:55:35 +00:00
/* Even some ALT tokens can be missing CCC so we should try with ACA */
r = cac_select_ACA(card);
if (r == SC_SUCCESS) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "ACA found, is CAC-2 without CCC");
if (!initialize) /* match card only */
return r;
if (!priv) {
priv = cac_new_private_data();
if (!priv)
return SC_ERROR_OUT_OF_MEMORY;
}
r = cac_process_ACA(card, priv);
if (r == SC_SUCCESS) {
card->type = SC_CARD_TYPE_CAC_II;
card->drv_data = priv;
return r;
2017-11-03 09:55:35 +00:00
}
}
/* is this a CAC-1 specified in DoD "CAC Applet Developer Guide" version 1.0 September 2002 */
r = cac_find_first_pki_applet(card, &index);
if (r == SC_SUCCESS) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "applet found, is CAC-1");
if (!initialize) /* match card only */
return r;
if (!priv) {
priv = cac_new_private_data();
if (!priv)
return SC_ERROR_OUT_OF_MEMORY;
}
r = cac_populate_cac_1(card, index, priv);
if (r == SC_SUCCESS) {
card->type = SC_CARD_TYPE_CAC_I;
card->drv_data = priv;
return r;
}
}
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 int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
{
/* CAC, like PIV needs Extra validation of (new) PIN during
* a PIN change request, to ensure it's not outside the
* FIPS 201 4.1.6.1 (numeric only) and * FIPS 140-2
* (6 character minimum) requirements.
*/
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
if (data->cmd == SC_PIN_CMD_CHANGE) {
int i = 0;
if (data->pin2.len < 6) {
return SC_ERROR_INVALID_PIN_LENGTH;
}
for(i=0; i < data->pin2.len; ++i) {
if (!isdigit(data->pin2.data[i])) {
return SC_ERROR_INVALID_DATA;
}
}
}
return iso_drv->ops->pin_cmd(card, data, tries_left);
}
static struct sc_card_operations cac_ops;
static struct sc_card_driver cac_drv = {
"Common Access Card (CAC)",
"cac",
&cac_ops,
NULL, 0, NULL
};
static struct sc_card_driver * sc_get_driver(void)
{
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
cac_ops = *iso_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.get_challenge = cac_get_challenge;
cac_ops.read_binary = cac_read_binary;
cac_ops.write_binary = cac_write_binary;
cac_ops.set_security_env = cac_set_security_env;
cac_ops.restore_security_env = cac_restore_security_env;
cac_ops.compute_signature = cac_compute_signature;
cac_ops.decipher = cac_decipher;
cac_ops.card_ctl = cac_card_ctl;
cac_ops.pin_cmd = cac_pin_cmd;
return &cac_drv;
}
struct sc_card_driver * sc_get_cac_driver(void)
{
return sc_get_driver();
}