/* * 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" #include "pkcs15.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; 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 ); len = MIN(left, 100); while (left > 0) { 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 || apdu.sw2 < 1) { /* we've either finished reading, or hit an error, break */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); left -= len; break; } /* Adjust the lengths */ left -= len; out_ptr += len; 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 = 0; 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_log(card->ctx, "returning cached value idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u", idx, count); if (idx > priv->cache_buf_len) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_END_REACHED); } len = MIN(count, priv->cache_buf_len-idx); memcpy(buf, &priv->cache_buf[idx], len); LOG_FUNC_RETURN(card->ctx, len); } sc_log(card->ctx, "clearing cache idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u", idx, count); 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; if (val_len < 1) { r = SC_ERROR_INVALID_DATA; 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; } 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); if (len && priv->cache_buf) memcpy(buf, &priv->cache_buf[idx], len); r = len; done: if (val) free(val); LOG_FUNC_RETURN(card->ctx, 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=%s, path->value=%s path->type=%d (%x)", sc_print_path(in_path), sc_dump_hex(in_path->value, 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, iso7816_select_file 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. * We only need to do this for private keys. */ if ((pathlen > 2) && (pathlen <= 4) && memcmp(path, "\x3F\x00", 2) == 0) { 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; LOG_FUNC_RETURN(ctx, 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) { LOG_FUNC_RETURN(card->ctx, 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; LOG_FUNC_RETURN(card->ctx, 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(); }